Skip to content
Snippets Groups Projects
Select Git revision
  • 0f26c94dd09aa62a8e6191e4de58faa69b248fd2
  • main default protected
  • dev protected
  • dev-doc
  • v0.20.2
  • v0.20.1
  • v0.20
  • v0.19.1
  • v0.19
  • v0.18.4
  • v0.18.3
  • v0.18.1
  • v0.18
  • v0.17.1
  • v0.17
  • v0.16.2
  • v0.16.1
  • v0.16
  • v0.15.2
  • v0.15.1
  • v0.15
  • v0.14.1
  • v0.14
  • v0.13.1
24 results

files.jl

Blame
  • files.jl 9.12 KiB
    
    struct WorkingDirectory
        controller
        root::String # could be hidden (not stored)
        path::Validator{String}
        content::Observable{Vector{String}}
    end
    
    WorkingDirectory(controller, root::String, path::String="") = WorkingDirectory(controller, root, Validator(path), Observable(String[]))
    
    function workingdir(controller, root::String, path::String=""; secure::Bool=true)
        if secure
            root = realpath(root)
        end
        isdir(root) || throw(ArgumentError("cannot find root directory"))
        if isempty(path)
            path = "."
        elseif isabspath(path)
            path = relpath(path, root)
            startswith(path, "..") && throw(ArgumentError("path not below root: $path"))
        end
        wd = WorkingDirectory(controller, root, path)
        on(wd.path, Val(1)) do newpath
            try
                if isempty(newpath)
                    newpath = "."
                elseif isabspath(newpath)
                    fullpath = newpath
                    newpath = relpath(newpath, root)
                end
                fullpath = joinpath(root, newpath)
                if secure
                    fullpath = realpath(fullpath)
                end
                if startswith(relpath(fullpath, root), "..")
                    @logmsg SecurityAlert "Path outside of chroot" path=newpath client=identifyclient(controller)
                    throw(Exception)
                elseif !isdir(fullpath)
                    @debug "Path is not a directory" path=newpath
                    tryopenfile(controller, fullpath; reload=true)
                    throw(Exception)
                end
                return Validate(newpath)
            catch
                return Invalidate()
            end
        end
        on(wd.path.model) do dir
            @assert !isempty(dir)
            content = [entry for entry in readdir(joinpath(root, dir))
                       if entry[1] != '.']
            if dir != "."
                pushfirst!(content, "..")
            end
            wd.content[] = content
        end
        return wd
    end
    
    function getworkingdir(controller)
        hub = gethub(controller)
        wd = nothing
        try
            wd = hub[:workingdirectory]
        catch
            hub[:workingdirectory] = wd = workingdir(hub, pwd())
        end
        return wd
    end
    
    getpath(wd::WorkingDirectory) = getview!(wd.path)
    getpath(controller) = getpath(getworkingdir(controller))
    
    function cwd(wd, subdir::String="")
        path = getpath(wd)
        if !isempty(subdir)
            path[] = normpath(joinpath(path[], subdir))
        end
        return path[]
    end
    
    getwdcontent(wd::WorkingDirectory) = wd.content
    getwdcontent(controller) = getwdcontent(getworkingdir(controller))
    
    
    # open/save
    
    function tryopenfile(controller, path; reload::Bool=false)
        hub = gethub(controller)
        refresh_view = reload
        if path isa Ref
            input, path = path, path[]
            if haskey(hub, :input)
                @assert hub[:input] === input
            else
                hub[:input] = input
            end
            isnothing(path) && return
        end
        if refresh_view
            turn_load_animation_on(controller)
        end
        #
        records = loadfile(path)
        if isempty(records)
            @info "Cannot load file" file=path
            return
        elseif haskey(hub, :input)
            hub[:input][] = isabspath(path) ? relpath(path) : path
        end
        # tag_lut
        tag_lut = ObservableTag[]
        if hasproperty(records, :tagcolors)
            existingtags = records.tags
            existingcolors = records.tagcolors
            for (tag, color) in zip(existingtags, existingcolors)
                original = TagModel(tag, color, true)
                push!(tag_lut, ObservableTag(original; color=color, active=true))
            end
        elseif hasproperty(records, :tags)
            default_convention = Dict(
                                      :small_motion => :grey50,
                                      :stop_large => :green,
                                      :run_large => :black,
                                      :cast_large => :red,
                                      :hunch_large => :blue,
                                      :back_large => :cyan,
                                      :roll_large => :yellow,
                                     )
            fallback_color = theme[:LarvaPlot][:fallback_color]
            existingtags = records.tags
            for tag in existingtags
                active = tag in keys(default_convention)
                color = active ? default_convention[tag] : fallback_color
                original = TagModel(tag)
                push!(tag_lut, ObservableTag(original; color=color, active=active))
            end
        end
        #
        hub[:output] = records.output
        getpath(hub)[] = dirname(path)
        newcontroller(hub, :larva, LarvaController,
                      records.tracks, tag_lut, records.timestamps)
        if refresh_view
            globalrefresh(hub[:frontend], hub)
        end
    end
    
    function loadfile(path)
        file = Loaders.load(path)
        data = isempty(file.run) ? file.timeseries : file.run
        # tracks
        times = PlanarLarvae.times(data)
        if data isa PlanarLarvae.LarvaBase.Larvae
            tracks = [LarvaModel(id, ts, times) for (id, ts) in pairs(data)]
        else
            tracks = [LarvaModel(track, times) for track in values(data)]
        end
        # dataset
        metadata = getmetadata(file)
        if isempty(metadata)
            metadata = extract_metadata_from_filepath(path)
            run = get!(metadata, :date_time, "NA")
        end
        output = Dataset([Run(run; metadata...)])
        #
        labels = getlabels(file)
        existingtags = labels[:names]
        if haskey(labels, :colors)
            tagcolors = labels[:colors]
            return (tracks=tracks, timestamps=times, tags=existingtags, tagcolors=tagcolors, output=output)
        else
            return (tracks=tracks, timestamps=times, tags=existingtags, output=output)
        end
    end
    
    getoutput(controller) = gethub(controller)[:output]
    
    interpolate(s="yyyymmdd_HHMMSS") = Dates.format(Dates.now(), s)
    
    function savetofile(controller, file; datafile=nothing)
        if '{' in file
            prefix, remainder = split(file, '{'; limit=2)
            if '}' in remainder
                parts = split(remainder, '}')
                if length(parts) == 2
                    infix, suffix = parts
                else
                    infix, suffix = join(parts[1:end-1], '}'), parts[end]
                end
                file = prefix * interpolate(infix) * suffix
            end
        end
        filepath = joinpath(cwd(controller), file)
        dataset = getoutput(controller)
        @assert length(dataset) == 1
        run = first(values(dataset))
        lut = gettags(controller)[]
        larvafilter = getlarvafilter(controller)
        empty = true
        for larvainfo in larvafilter.entries
            if larvainfo.included[]
                id = larvainfo.id
                larva = getlarva(controller, id)
                timeseries = [t for (t, _) in larva.fullstates]
                track = Track(id, timeseries)
                labels = Union{String, Vector{String}}[]
                for timestep in larva.alignedsteps
                    tags = getusertags(larva, timestep; lut=lut)
                    tags = [tag.name[] for tag in tags if tag.active[]]
                    if isempty(tags)
                        tags = String[] # convert Any to String
                    end
                    push!(labels, length(tags) == 1 ? tags[1] : tags)
                end
                track[:labels] = labels
                run[id] = track
                empty = false
            end
        end
        metadata = run.attributes[:metadata]
        mdt = gethub(controller)[:metadatatable][]
        for item in mdt.entries
            name = item.name.model[]
            value = item.value.model[]
            if !(isempty(name) || isempty(value))
                metadata[Symbol(name)] = value
            end
        end
        if empty
            @info "Not any larva included"
        else
            @info "Saving to file" file=filepath
            dataset = encodelabels(dataset)
            labels = dataset.attributes[:labels]
            if labels isa Vector
                labels = Dict{Symbol, Any}(:names => labels)
                dataset.attributes[:labels] = labels
            end
            colors = [lookup(lut, label, true).color[] for label in labels[:names]]
            labels[:colors] = colors
            if isnothing(datafile)
                datafile = getinputfile(controller)[]
                if isempty(datafile)
                    datafile = gethub(controller)[:input][]
                end
            end
            datafilepath = isfile(datafile) ? datafile : joinpath(cwd(controller), datafile)
            push_dependency!(dataset, datafilepath)
            PlanarLarvae.Datasets.to_json_file(filepath, dataset)
        end
    end
    
    function getinputfile(controller)
        hub = gethub(controller)
        inputfile = nothing
        try
            inputfile = hub[:inputfile]
        catch
            hub[:inputfile] = inputfile = Observable{Union{Nothing, String}}("")
            on(inputfile) do f
                if isnothing(f)
                    inputfile.val = ""
                else
                    tryopenfile(controller, f; reload=true)
                end
            end
        end
        return inputfile
    end
    
    function getoutputfile(controller)
        hub = gethub(controller)
        outputfile = nothing
        try
            outputfile = hub[:outputfile]
        catch
            hub[:outputfile] = outputfile = Observable{Union{Nothing, String}}("{yyyymmdd_HHMMSS}.labels")
            on(outputfile) do file
                if isnothing(file)
                    outputfile.val = file = "{yyyymmdd_HHMMSS}.labels"
                else
                    savetofile(hub, file)
                end
            end
        end
        return outputfile
    end