Skip to content

Commit

Permalink
Merge pull request #226 from nfdi4plants/developer_swatelogic
Browse files Browse the repository at this point in the history
Developer swatelogic
  • Loading branch information
HLWeil authored Oct 6, 2023
2 parents 98b0e41 + 0190d7c commit 9f89e84
Show file tree
Hide file tree
Showing 20 changed files with 789 additions and 163 deletions.
1 change: 1 addition & 0 deletions build/GenerateIndexJs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ let ARCtrl_generate(rootPath: string) =
"ArcStudy";
"ArcInvestigation";
"Template"
"Templates"
"Organisation"
"JsWeb"
"ARC"
Expand Down
7 changes: 3 additions & 4 deletions src/ARCtrl/ARCtrl.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@
<Compile Include="Contracts\Contracts.Git.fs" />
<Compile Include="Contracts\Contracts.ARCtrl.fs" />
<Compile Include="Templates\Template.fs" />
<Compile Include="Templates\Templates.fs" />
<Compile Include="Templates\Template.Json.fs" />
<Compile Include="Templates\Template.Spreadsheet.fs" />
<Compile Include="Templates\Template.Web.fs" />
<Compile Include="ARC.fs" />
<None Include="README.md" />
<Compile Include="ARC.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Contract\ARCtrl.Contract.fsproj" />
Expand All @@ -49,7 +50,5 @@
<NpmPackage Name="isomorphic-fetch" Version="gt 3.0.0 lt 3.0.0" ResolutionStrategy="Max" />
</NpmDependencies>
</PropertyGroup>
<ItemGroup>
<Content Include="*.fsproj; **\*.fs; **\*.fsi" PackagePath="fable\" />
</ItemGroup>
<ItemGroup />
</Project>
2 changes: 1 addition & 1 deletion src/ARCtrl/Templates/Template.Json.fs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ module Templates =

let fromJsonString (jsonString: string) =
match decode jsonString with
| Ok templateMap -> templateMap
| Ok templateMap -> templateMap.Values |> Array.ofSeq
| Error exn -> failwithf "Error. Given json string cannot be parsed to Templates map: %A" exn

let toJsonString (spaces: int) (templateList: (string*Template) []) =
Expand Down
4 changes: 2 additions & 2 deletions src/ARCtrl/Templates/Template.Web.fs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ let getTemplates(url: string option) =
type JsWeb =
static member getTemplates(url: string option) =
async {
let! map = getTemplates(url)
return System.Collections.Generic.Dictionary(map)
let! templates = getTemplates(url)
return templates
}
|> Async.StartAsPromise
91 changes: 91 additions & 0 deletions src/ARCtrl/Templates/Templates.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
namespace ARCtrl.Template

open ARCtrl.ISA
open Fable.Core

/// This module contains unchecked helper functions for templates
[<RequireQualifiedAccess>]
module TemplatesAux =

let getComparer (matchAll: bool option) =
let matchAll = defaultArg matchAll false
let comparer = if matchAll then (&&) else (||)
comparer

let filterOnTags (tagGetter: Template -> OntologyAnnotation []) (queryTags: OntologyAnnotation []) (comparer: bool -> bool -> bool) (templates: Template []) =
templates
|> Array.filter(fun t ->
let templateTags = tagGetter t
let mutable isValid = None
for qt in queryTags do
let contains = templateTags |> Array.contains qt
match isValid, contains with
| None, any -> isValid <- Some any
| Some maybe, any -> isValid <- Some (comparer maybe any)
Option.defaultValue false isValid
)

[<AttachMembers>]
type Templates =

static member getDistinctTags (templates: Template []) =
templates |> Array.collect (fun t -> t.Tags) |> Array.distinct

/// <summary>
/// Returns all **distinct** `template.Tags` and `template.EndpointRepositories`
/// </summary>
/// <param name="templates"></param>
static member getDistinctEndpointRepositories (templates: Template []) =
templates |> Array.collect (fun t -> t.EndpointRepositories) |> Array.distinct

/// <summary>
/// Returns all **distinct** `template.Tags` and `template.EndpointRepositories`
/// </summary>
/// <param name="templates"></param>
static member getDistinctOntologyAnnotations (templates: Template []) =
let oas = ResizeArray()
for t in templates do
oas.AddRange(t.Tags)
oas.AddRange(t.EndpointRepositories)
oas
|> Array.ofSeq
|> Array.distinct

/// <summary>
/// Filter templates by `template.Tags`.
/// </summary>
/// <param name="queryTags">The ontology annotation to filter by.</param>
/// <param name="matchAll">Default: false. If true all `queryTags` must be contained in template, if false only 1 tags must be contained in template.</param>
static member filterByTags(queryTags: OntologyAnnotation [], ?matchAll: bool) =
fun (templates: Template []) ->
let comparer = TemplatesAux.getComparer matchAll
TemplatesAux.filterOnTags (fun t -> t.Tags) queryTags comparer templates

/// <summary>
/// Filter templates by `template.EndpointRepositories`.
/// </summary>
/// <param name="queryTags">The ontology annotation to filter by.</param>
/// <param name="matchAll">Default: false. If true all `queryTags` must be contained in template, if false only 1 tags must be contained in template.</param>
static member filterByEndpointRepositories(queryTags: OntologyAnnotation [], ?matchAll: bool) =
fun (templates: Template []) ->
let comparer = TemplatesAux.getComparer matchAll
TemplatesAux.filterOnTags (fun t -> t.EndpointRepositories) queryTags comparer templates

/// <summary>
/// Filters templates by template.Tags and template.EndpointRepositories
/// </summary>
/// <param name="queryTags">The ontology annotation to filter by.</param>
/// <param name="matchAll">Default: false. If true all `queryTags` must be contained in template, if false only 1 tags must be contained in template.</param>
static member filterByOntologyAnnotation(queryTags: OntologyAnnotation [], ?matchAll: bool) =
fun (templates: Template []) ->
let comparer = TemplatesAux.getComparer matchAll
TemplatesAux.filterOnTags (fun t -> Array.append t.Tags t.EndpointRepositories) queryTags comparer templates

/// <summary>
/// Filters templates by template.Organisation = `Organisation.DataPLANT`/`"DataPLANT"`.
/// </summary>
/// <param name="templates"></param>
static member filterByDataPLANT (templates: Template []) =
templates
|> Array.filter (fun t -> t.Organisation.IsOfficial())

1 change: 1 addition & 0 deletions src/ISA/ISA.Spreadsheet/AnnotationTable/ArcTable.fs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ let toFsWorksheet (table : ArcTable) =
let ws = FsWorksheet(table.Name)
let columns =
table.Columns
|> List.ofArray
|> List.sortBy classifyColumnOrder
|> List.collect CompositeColumn.toFsColumns
let maxRow = columns.Head.Length
Expand Down
91 changes: 77 additions & 14 deletions src/ISA/ISA/ArcTypes/ArcTable.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ open System.Collections.Generic
open ArcTableAux
open ColumnIndex

open Fable.Core.JsInterop
[<StringEnum>]
type TableJoinOptions =
/// Add only headers, no values
| Headers
/// Add headers and unit information without main value
| WithUnit
/// Add full columns
| WithValues

[<AttachMembers>]
type ArcTable(name: string, headers: ResizeArray<CompositeHeader>, values: System.Collections.Generic.Dictionary<int*int,CompositeCell>) =
Expand Down Expand Up @@ -39,7 +46,7 @@ type ArcTable(name: string, headers: ResizeArray<CompositeHeader>, values: Syste
let mutable isValid: bool = true
for columnIndex in 0 .. (this.ColumnCount - 1) do
let column : CompositeColumn = this.GetColumn(columnIndex)
isValid <- column.validate(?raiseException=raiseException)
isValid <- column.Validate(?raiseException=raiseException)
isValid

/// Will return true or false if table is valid.
Expand All @@ -57,7 +64,7 @@ type ArcTable(name: string, headers: ResizeArray<CompositeHeader>, values: Syste
with get() = ArcTableAux.getRowCount this.Values

member this.Columns
with get() = [for i = 0 to this.ColumnCount - 1 do this.GetColumn(i)]
with get() = [|for i = 0 to this.ColumnCount - 1 do this.GetColumn(i)|]

member this.Copy() : ArcTable =
ArcTable.create(
Expand Down Expand Up @@ -99,7 +106,7 @@ type ArcTable(name: string, headers: ResizeArray<CompositeHeader>, values: Syste
ArcTableAux.SanityChecks.validateNoDuplicateUnique newHeader otherHeaders
let c = { this.GetColumn(index) with Header = newHeader }
// Test if column is still valid with new header, if so insert header at index
if c.validate() then
if c.Validate() then
let setHeader = this.Headers.[index] <- newHeader
()
// if we force convert cells, we want to convert the existing cells to a valid cell type for the new header
Expand Down Expand Up @@ -137,7 +144,7 @@ type ArcTable(name: string, headers: ResizeArray<CompositeHeader>, values: Syste
SanityChecks.validateColumnIndex index this.ColumnCount true
SanityChecks.validateColumn(CompositeColumn.create(header, cells))
//
Unchecked.addColumn header cells index forceReplace this.Headers this.Values
Unchecked.addColumn header cells index forceReplace false this.Headers this.Values
Unchecked.fillMissingCells this.Headers this.Values

static member addColumn (header: CompositeHeader, ?cells: CompositeCell [],?index: int ,?forceReplace : bool) : (ArcTable -> ArcTable) =
Expand Down Expand Up @@ -202,7 +209,7 @@ type ArcTable(name: string, headers: ResizeArray<CompositeHeader>, values: Syste
columns
|> Array.iter (fun col ->
let prevHeadersCount = this.Headers.Count
Unchecked.addColumn col.Header col.Cells index forceReplace this.Headers this.Values
Unchecked.addColumn col.Header col.Cells index forceReplace false this.Headers this.Values
// Check if more headers, otherwise `ArcTableAux.insertColumn` replaced a column and we do not need to increase index.
if this.Headers.Count > prevHeadersCount then index <- index + 1
)
Expand Down Expand Up @@ -251,7 +258,9 @@ type ArcTable(name: string, headers: ResizeArray<CompositeHeader>, values: Syste
let h = this.Headers.[columnIndex]
let cells = [|
for i = 0 to this.RowCount - 1 do
this.TryGetCellAt(columnIndex, i).Value
match this.TryGetCellAt(columnIndex, i) with
| None -> failwithf "Unable to find cell for index: (%i, %i)" columnIndex i
| Some c -> c
|]
CompositeColumn.create(h, cells)

Expand Down Expand Up @@ -426,8 +435,49 @@ type ArcTable(name: string, headers: ResizeArray<CompositeHeader>, values: Syste
fun (table:ArcTable) ->
table.GetRow(index)

//member this.InsertParameterValue () =
// this.in
/// <summary>
/// This function can be used to join two arc tables.
/// </summary>
/// <param name="table">The table to join to this table.</param>
/// <param name="joinOptions">Can add only headers, header with unitized cell information, headers with values.</param>
/// <param name="forceReplace">if set to true will replace unique columns.</param>
member this.Join(table:ArcTable, ?joinOptions: TableJoinOptions, ?forceReplace: bool) : unit =
let joinOptions = defaultArg joinOptions TableJoinOptions.Headers
let forceReplace = defaultArg forceReplace false
let onlyHeaders = joinOptions = TableJoinOptions.Headers
let columns =
let pre = table.Columns
match joinOptions with
| Headers -> pre |> Array.map (fun c -> {c with Cells = [||]})
// this is the most problematic case. How do we decide which unit we want to propagate? All?
| WithUnit ->
pre |> Array.map (fun c ->
let unitsOpt = c.TryGetColumnUnits()
match unitsOpt with
| Some units ->
let toCompositeCell = fun unitOA -> CompositeCell.createUnitized ("", unitOA)
let unitCells = units |> Array.map (fun u -> toCompositeCell u)
{c with Cells = unitCells}
| None -> {c with Cells = [||]}
)
| WithValues -> pre
SanityChecks.validateNoDuplicateUniqueColumns columns
columns |> Array.iter (fun x -> SanityChecks.validateColumn x)
let mutable index = this.ColumnCount
columns
|> Array.iter (fun col ->
let prevHeadersCount = this.Headers.Count
Unchecked.addColumn col.Header col.Cells index forceReplace onlyHeaders this.Headers this.Values
// Check if more headers, otherwise `ArcTableAux.insertColumn` replaced a column and we do not need to increase index.
if this.Headers.Count > prevHeadersCount then index <- index + 1
)
Unchecked.fillMissingCells this.Headers this.Values

static member join(table:ArcTable, ?joinOptions: TableJoinOptions, ?forceReplace: bool) =
fun (this: ArcTable) ->
let copy = this.Copy()
copy.Join(table,?joinOptions=joinOptions,?forceReplace=forceReplace)
copy

static member insertParameterValue (t : ArcTable) (p : ProcessParameterValue) : ArcTable =
raise (System.NotImplementedException())
Expand Down Expand Up @@ -640,9 +690,10 @@ type ArcTable(name: string, headers: ResizeArray<CompositeHeader>, values: Syste
|> String.concat "\n"

member this.StructurallyEquals (other: ArcTable) =
let sort = Array.ofSeq >> Array.sortBy (function |KeyValue (key,_) -> key)
let n = this.Name = other.Name
let headers = Aux.compareSeq this.Headers other.Headers
let values = Aux.compareSeq (this.Values |> Seq.sortBy (fun x -> x.Key)) (other.Values |> Seq.sortBy (fun x -> x.Key))
let values = Aux.compareSeq (sort this.Values) (sort other.Values)
n && headers && values

/// <summary>
Expand All @@ -662,7 +713,19 @@ type ArcTable(name: string, headers: ResizeArray<CompositeHeader>, values: Syste

// it's good practice to ensure that this behaves using the same fields as Equals does:
override this.GetHashCode() =
let name = this.Name.GetHashCode()
let headers = this.Headers |> Seq.fold (fun state ele -> state + ele.GetHashCode()) 0
let bodyCells = this.Values |> Seq.sortBy (fun x -> x.Key) |> Seq.fold (fun state ele -> state + ele.GetHashCode()) 0
name + headers + bodyCells
//let v1,v2 =
let v =
[|
for KeyValue(k,v) in this.Values do
yield k, v
|]
|> Array.sortBy fst
// must remove tuples. Tuples handle unpredictable for GetHashCode in javascript.
|> Array.map (fun ((k1,k2),v) -> [|box k1; box k2; box v|] |> Aux.HashCodes.boxHashArray)
[|
box this.Name
Array.ofSeq >> Aux.HashCodes.boxHashArray <| this.Headers
Array.ofSeq >> Aux.HashCodes.boxHashArray <| v
|]
|> Aux.HashCodes.boxHashArray
|> fun x -> x :?> int
Loading

0 comments on commit 9f89e84

Please sign in to comment.