diff --git a/.gitignore b/.gitignore index f75c38fa..ade51a2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ Manifest.toml + ################################################################################ # DrWatson Project Structure # ################################################################################ diff --git a/docs/Project.toml b/docs/Project.toml index 449af013..2b7927df 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,4 +1,5 @@ [deps] +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" # not actually needed; but for doctheme DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" @@ -12,4 +13,4 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" [compat] -Documenter = "0.24.6" +Documenter = "1" diff --git a/docs/build_docs_with_style.jl b/docs/build_docs_with_style.jl new file mode 100644 index 00000000..ce3c05be --- /dev/null +++ b/docs/build_docs_with_style.jl @@ -0,0 +1,73 @@ +CI = get(ENV, "CI", nothing) == "true" || get(ENV, "GITHUB_TOKEN", nothing) !== nothing + +import Pkg +Pkg.pkg"add Documenter@1" + +# Load documenter +using Documenter +using DocumenterTools: Themes +ENV["JULIA_DEBUG"] = "Documenter" + +# For easier debugging when downloading from a specific branch. +github_user = "JuliaDynamics" +branch = "master" +download_path = "https://raw.githubusercontent.com/$github_user/doctheme/$branch" + +import Downloads +for file in ("juliadynamics-lightdefs.scss", "juliadynamics-darkdefs.scss", "juliadynamics-style.scss") + Downloads.download("$download_path/$file", joinpath(@__DIR__, file)) +end + +# create the themes +for w in ("light", "dark") + header = read(joinpath(@__DIR__, "juliadynamics-style.scss"), String) + theme = read(joinpath(@__DIR__, "juliadynamics-$(w)defs.scss"), String) + write(joinpath(@__DIR__, "juliadynamics-$(w).scss"), header*"\n"*theme) +end + +# compile the themes +Themes.compile(joinpath(@__DIR__, "juliadynamics-light.scss"), joinpath(@__DIR__, "src/assets/themes/documenter-light.css")) +Themes.compile(joinpath(@__DIR__, "juliadynamics-dark.scss"), joinpath(@__DIR__, "src/assets/themes/documenter-dark.css")) + +# Download and apply CairoMakie plotting style +using CairoMakie +Downloads.download("$download_path/style.jl", joinpath(@__DIR__, "style.jl")) +include("style.jl") + +function build_docs_with_style(pages, modules...; bib = nothing, authors = "George Datseris", draft = false, kwargs...) + settings = ( + modules = [modules...], + format = Documenter.HTML( + prettyurls = CI, + assets = [ + asset("https://fonts.googleapis.com/css?family=Montserrat|Source+Code+Pro&display=swap", class=:css), + ], + collapselevel = 3, + ), + sitename = "$(modules[1]).jl", + authors, + pages, + draft, + doctest = false, + checkdocs = :exported, + linkcheck_timeout = 2, + # The following Documenter fails will NOT ERROR the docbuild! + warnonly = [:doctest, :missing_docs], + kwargs... + ) + + if isnothing(bib) + makedocs(; settings...) + else + makedocs(; plugins=[bib], settings...) + end + + if CI + deploydocs( + repo = "github.com/JuliaDynamics/$(modules[1]).jl.git", + target = "build", + push_preview = true + ) + end + +end diff --git a/docs/make.jl b/docs/make.jl index 4f415bab..9536b80d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,58 +1,29 @@ cd(@__DIR__) -using Pkg -CI = get(ENV, "CI", nothing) == "true" || get(ENV, "GITHUB_TOKEN", nothing) !== nothing -CI && Pkg.activate(@__DIR__) -CI && Pkg.instantiate() using DrWatson -using Documenter, DataFrames, Parameters, Dates, JLD2, UnPack -using DocumenterTools: Themes +using UnPack -# %% -# download the themes -for file in ("juliadynamics-lightdefs.scss", "juliadynamics-darkdefs.scss", "juliadynamics-style.scss") - download("https://raw.githubusercontent.com/JuliaDynamics/doctheme/master/$file", joinpath(@__DIR__, file)) -end -# create the themes -for w in ("light", "dark") - header = read(joinpath(@__DIR__, "juliadynamics-style.scss"), String) - theme = read(joinpath(@__DIR__, "juliadynamics-$(w)defs.scss"), String) - write(joinpath(@__DIR__, "juliadynamics-$(w).scss"), header*"\n"*theme) -end -# compile the themes -Themes.compile(joinpath(@__DIR__, "juliadynamics-light.scss"), joinpath(@__DIR__, "src/assets/themes/documenter-light.css")) -Themes.compile(joinpath(@__DIR__, "juliadynamics-dark.scss"), joinpath(@__DIR__, "src/assets/themes/documenter-dark.css")) +# Convert workflow +import Literate -isdir(datadir()) && rm(datadir(); force = true, recursive = true) +Literate.markdown(joinpath(@__DIR__, "src", "workflow.jl"), joinpath(@__DIR__, "src"); credit = false) -using Literate -Literate.markdown("src/workflow.jl", "src"; credit = false) +pages = [ + "Introduction" => "index.md", + "DrWatson Workflow Tutorial" => "workflow.md", + "Project Setup" => "project.md", + "Naming Simulations" => "name.md", + "Saving Tools" => "save.md", + "Running & Listing Simulations" => "run&list.md", + "Real World Examples" => "real_world.md" +] -makedocs(modules = [DrWatson, UnPack], - sitename= "DrWatson", - authors = "George Datseris and contributors.", - doctest = false, - format = Documenter.HTML( - prettyurls = CI, - assets = [ - "assets/logo.ico", - asset("https://fonts.googleapis.com/css?family=Quicksand|Montserrat|Source+Code+Pro|Lora&display=swap", class=:css), - ], - ), - pages = [ - "Introduction" => "index.md", - "DrWatson Workflow Tutorial" => "workflow.md", - "Project Setup" => "project.md", - "Naming Simulations" => "name.md", - "Saving Tools" => "save.md", - "Running & Listing Simulations" => "run&list.md", - "Real World Examples" => "real_world.md" - ], +import Downloads +Downloads.download( + "https://raw.githubusercontent.com/JuliaDynamics/doctheme/master/build_docs_with_style.jl", + joinpath(@__DIR__, "build_docs_with_style.jl") ) +include("build_docs_with_style.jl") -if CI - deploydocs( - repo = "github.com/JuliaDynamics/DrWatson.jl.git", - target = "build", - push_preview = true - ) -end +build_docs_with_style(pages, DrWatson, UnPack; + expandfirst = ["index.md"], warnonly = true, +) diff --git a/docs/src/name.md b/docs/src/name.md index 1e4c1fd0..fe5fc1a1 100644 --- a/docs/src/name.md +++ b/docs/src/name.md @@ -27,11 +27,7 @@ tostringdict tosymboldict ``` -Notice that we also re-export the convenient `@pack!, @unpack` tools from [UnPack.jl](https://github.com/mauro3/UnPack.jl), because they play very well with [`@dict`](@ref) and similar functions. Be aware of the syntactic `,` difference: `d = @dict a b c` versus `@unpack a, b, c = d`. -```@docs -@unpack -@pack! -``` +DrWatson also re-exports `@pack!, @unpack` tools from [UnPack.jl](https://github.com/mauro3/UnPack.jl), because they play very well with [`@dict`](@ref) and similar functions. Be aware of the syntactic `,` difference: `d = @dict a b c` versus `@unpack a, b, c = d`. ## Customizing `savename` You can customize [`savename`](@ref) for your own Types. For example you could make it so that it only uses some specific keys instead of all of them, only specific types, or you could make it access data in a different way (maybe even loading files!). You can even make it have a custom `prefix`! diff --git a/docs/src/real_world.md b/docs/src/real_world.md index 10d37277..6822926a 100644 --- a/docs/src/real_world.md +++ b/docs/src/real_world.md @@ -96,7 +96,7 @@ path/to/project/data/sim/bk_N=50_T=10050_seed=1111_ΔT=1.jld2 ``` and each file is a dictionary that has my data fields: `:U, :V, :simulation`, but also `:gitcommit, :script`. When I read this file I know exactly what was the source code that produced it (provided that I am not sloppy and commit code changes regularly :P). -## Customizing `savename` +## [Customizing `savename`](@id customizing_savename) Here is a simple example for customizing [`savename`](@ref). We are using a common struct `Experiment` across different experiments with cats and mice. We first define the relevant types. @@ -355,7 +355,7 @@ using DrWatson general_args2 = Dict( "model" => "barkley", "noise" => [0.075, 0.050, 0.025], - "noise2" => [1.0, ComputedParameter(["noise", "N"], (x,y) -> 2x + y)], + "noise2" => [1.0, Derived(["noise", "N"], (x,y) -> 2x + y)], "noisy_training" => true, "N" => 100, ) @@ -617,7 +617,7 @@ julia> expensive_computation(5) ``` ## Taking project input-output automation to 11 -The point of this section is to show how far one can take the interplay between [`savename`](@ref) and [`produce_or_load`](@ref) to **automate project input-to-output and eliminate as many duplicate lines of code as possible**. Read [Customizing `savename`](@ref) first, as knowledge of that section is used here. +The point of this section is to show how far one can take the interplay between [`savename`](@ref) and [`produce_or_load`](@ref) to **automate project input-to-output and eliminate as many duplicate lines of code as possible**. Read [Customizing `savename`](@ref customizing_savename) first, as knowledge of that section is used here. The key ingredient is that [`produce_or_load`](@ref) was made to work well with [`savename`](@ref). You can use this to automate the input-to-output pipeline of your project by following these steps: 1. Define a custom struct that represents the input configuration for an experiment or a simulation. diff --git a/docs/src/run&list.md b/docs/src/run&list.md index 072d72f5..b450bfc7 100644 --- a/docs/src/run&list.md +++ b/docs/src/run&list.md @@ -4,6 +4,7 @@ It is very often the case that you want to run "batch simulations", i.e. just submit a bunch of different simulations, all using same algorithms and code but just different parameters. This scenario always requires the user to prepare a set of simulation parameter containers which are then passed into some kind of "main" function that starts the simulation. To make the preparation part simpler we provide the following functionality: + ```@docs dict_list dict_list_count diff --git a/docs/src/workflow.jl b/docs/src/workflow.jl index b26aaba2..b64f6b61 100644 --- a/docs/src/workflow.jl +++ b/docs/src/workflow.jl @@ -50,6 +50,7 @@ initialize_project("DrWatsonExample"; authors="Datseris", force=true) # This project is now active by default so we can start adding packages # that we will be using in the project. We'll add the following for demonstrating using Pkg +Pkg.develop("DrWatson") # hide Pkg.add(["Statistics", "JLD2"]) # ## 2. Write some scripts diff --git a/docs/style.jl b/docs/style.jl new file mode 100644 index 00000000..168f16ee --- /dev/null +++ b/docs/style.jl @@ -0,0 +1,38 @@ +# Color theme definitions +struct CyclicContainer <: AbstractVector{String} + c::Vector{String} + n::Int +end +CyclicContainer(c) = CyclicContainer(c, 0) + +Base.length(c::CyclicContainer) = length(c.c) +Base.size(c::CyclicContainer) = size(c.c) +Base.iterate(c::CyclicContainer, state=1) = iterate(c.c, state) +Base.getindex(c::CyclicContainer, i) = c.c[(i-1)%length(c.c) + 1] +Base.getindex(c::CyclicContainer, i::AbstractArray) = c.c[i] +function Base.getindex(c::CyclicContainer) + c.n += 1 + c[c.n] +end +Base.iterate(c::CyclicContainer, i = 1) = iterate(c.c, i) + +COLORSCHEME = [ + "#7143E0", + "#0A9A84", + "#191E44", + "#AF9327", + "#701B80", + "#2E6137", +] + +COLORS = CyclicContainer(COLORSCHEME) +LINESTYLES = CyclicContainer(["-", ":", "--", "-."]) + +# other styling elements for Makie +set_theme!(; + palette = (color = COLORSCHEME,), + fontsize = 22, + figure_padding = 8, + size = (800, 400), + linewidth = 3.0, +) diff --git a/src/DrWatson.jl b/src/DrWatson.jl index 4a8865b8..4a4fc30c 100644 --- a/src/DrWatson.jl +++ b/src/DrWatson.jl @@ -56,8 +56,8 @@ using Requires # Update messages using Scratch const env_var = "DRWATSON_UPDATE_MSG" -const display_update = true -const update_version = "2.12.0" +const display_update = false +const update_version = "-" const update_name = "update_v$update_version" # Get scratch space for this package @@ -85,13 +85,6 @@ function __init__() """ \nUpdate message: DrWatson v$update_version - - `produce_or_load` now allows using arbitrary functions when extracting a file - name from the input configuration container. Effectively this means that you can - use `Base.hash` instead of `savename`, allowing using `produce_or_load` with - configuration containers that have too many parameters, or too complicated, - to be uniquely mapped to a string via `savename`. A section "`produce_or_load` - with hash codes" in Real World Examples highlights this possibility! - To disable future update messages see: https://juliadynamics.github.io/DrWatson.jl/stable/#Installing-and-Updating-1 \n diff --git a/src/dict_list.jl b/src/dict_list.jl index 607f4dc1..08672987 100644 --- a/src/dict_list.jl +++ b/src/dict_list.jl @@ -15,7 +15,7 @@ To restrict some values in the dictionary so that they only appear in the resulting dictionaries, if a certain condition is met, the macro [`@onlyif`](@ref) can be used on those values. -To compute some parameters on creation of `dict_list` as a function +To compute some parameters on creation of `dict_list` as a function of other specified parameters, use the type [`Derived`](@ref). Use the function [`dict_list_count`](@ref) to get the number of @@ -301,9 +301,11 @@ julia> dict_list(d) # only in case `:a` is `1` the dictionary will get key `:c` """ Derived(parameters::Vector{Union{String,Symbol}}, function::Function) -Wrap the name(s) of a parameter(s) and a function. After the -possible parameter combinations are created, [`dict_list`](@ref) will replace instances of -Derived by the result of the function func, evaluated with the value of + Derived(parameter::Union{String,Symbol}, function::Function) + +Wrap the name(s) of a parameter(s) and a function. After the +possible parameter combinations are created, [`dict_list`](@ref) will replace instances of +Derived by the result of the function func, evaluated with the value of the parameter(s). ## Examples @@ -324,7 +326,7 @@ julia> dict_list(p) Dict(:α => 1, :solver => "SolverB", :β => 1) Dict(:α => 2, :solver => "SolverB", :β => 4) ``` -A vector of parameter names can also be passed when the accompanying function +A vector of parameter names can also be passed when the accompanying function uses multiple arguments: ```julia julia> p2 = Dict(:α => [1, 2], @@ -355,26 +357,24 @@ struct Derived{T} func::Function end -""" - Derived(independentP :: Union{String,Symbol}, func::Function) -Constructs a Derived from a single independent parameter. -""" -function Derived(independentP::Union{String,Symbol}, func::Function) +# convenience dispatch +function Derived(independentP::Union{String,Symbol}, func::Function) return Derived([independentP], func) end """ produce_computed_parameter(dicts) + Receive an array of parameter dictionaries, and for each one, evaluate -the computed parameters after the possible combination of +the computed parameters after the possible combination of parameters has been created. """ function produce_derived_parameters(dicts) for dict in dicts replace!(dict) do (k,v) - if isa(v,Derived) - k => v.func((dict[param] for param in v.independentParam)...) + if isa(v,Derived) + k => v.func((dict[param] for param in v.independentParam)...) else return k => v end