-
-
-
-
diff --git a/docs/content/FSharpSpreadsheetML.fsx b/docs/content/FSharpSpreadsheetML.fsx
deleted file mode 100644
index fd0231a..0000000
--- a/docs/content/FSharpSpreadsheetML.fsx
+++ /dev/null
@@ -1,36 +0,0 @@
-(*** hide ***)
-// This block of code is omitted in the generated HTML documentation. Use
-// it to define helpers that you do not want to show in the documentation.
-#I @"../../bin\ArcCommander\netcoreapp3.1"
-#r "DocumentFormat.OpenXml.dll"
-#r"FSharpSpreadsheetML.dll"
-
-open DocumentFormat.OpenXml
-open DocumentFormat.OpenXml.Packaging
-open DocumentFormat.OpenXml.Spreadsheet
-open FSharpSpreadsheetML
-
-/// Create new copy of the textXLSX
-let doc =
- let source = Spreadsheet.openSpreadsheet @"C:\Users\HLWei\source\repos\arcCommander\docs\content\data\FSharpSpreadsheetMLTest.xlsx" false
- source |> Spreadsheet.saveAs @"C:\Users\HLWei\source\repos\arcCommander\docs\content\data\FSharpSpreadsheetMLTestCopy.xlsx"
- source |> Spreadsheet.close
- Spreadsheet.openSpreadsheet @"C:\Users\HLWei\source\repos\arcCommander\docs\content\data\FSharpSpreadsheetMLTestCopy.xlsx" true
-
-let workBookPart = Spreadsheet.getWorkbookPart doc
-
-let sheet = SheetTransformation.firstSheetOfWorkbookPart workBookPart
-
-SheetTransformation.SSTSheets.getCellValueSSTAt workBookPart 1u 1u sheet
-
-SheetTransformation.SSTSheets.getRowValuesSSTAt workBookPart 2u sheet
-
-SheetTransformation.DirectSheets.insertRowAt ["omg";"this";"is";"test"] 2u sheet
-
-SheetTransformation.DirectSheets.appendRow ["last";"row";"in";"doc"] sheet
-
-SheetTransformation.DirectSheets.removeRowAt 3u sheet
-
-doc
-|> Spreadsheet.saveChanges
-|> Spreadsheet.close
\ No newline at end of file
diff --git a/docs/content/ISA_XLSX.IO.fsx b/docs/content/ISA_XLSX.IO.fsx
deleted file mode 100644
index e1697e6..0000000
--- a/docs/content/ISA_XLSX.IO.fsx
+++ /dev/null
@@ -1,82 +0,0 @@
-
-//#load "C:\Users\HLWei\source\diverse\OpenXML\.paket\load\DocumentFormat.OpenXml.fsx"
-#I @"../../bin\ArcCommander\netcoreapp3.1"
-
-#r "DocumentFormat.OpenXml.dll"
-#r"FSharpSpreadsheetML.dll"
-#r "ISA-XLSX.dll"
-
-open DocumentFormat.OpenXml
-open DocumentFormat.OpenXml.Packaging
-open DocumentFormat.OpenXml.Spreadsheet
-open FSharpSpreadsheetML
-
-open ISA
-open DataModel
-open InvestigationFile
-open ISA_XLSX.IO
-
-let source = __SOURCE_DIRECTORY__
-
-//let investPath = source + @"C:\Users\HLWei\source\diverse\XLSXUnzip\i_investigation - Copy.xlsx"
-
-//let path = @"C:\Users\HLWei\source\diverse\XLSXUnzip\test.xlsx"
-
-//InvestigationItem(identifier = "lul")
-//|> ISA_Investigation.createEmpty (path)
-
-
-//let doc = Spreadsheet.openSpreadsheet path true
-
-
-
-
-//ISA_Investigation.addStudy (StudyItem(identifier = "Study1",submissionDate = "1.2.3")) doc
-
-//ISA_Investigation.tryAddItemToStudy (Factor(name = "Factor1",factorType = "dwadw")) "Study1" doc
-
-//ISA_Investigation.tryAddItemToStudy (Factor(name = "Factor2",factorType = "ebeb")) "Study1" doc
-
-//ISA_Investigation.tryAddItemToStudy (Assay(FileName ="2")) "Study1" doc
-
-//ISA_Investigation.tryRemoveItemFromStudy (Factor(name = "Factor1")) "Study1" doc
-
-//ISA_Investigation.tryUpdateItemInStudy (Factor(name = "Factor2",factorType = "degebab",typeTermAccessionNumber = "5")) "Study1" doc
-
-//ISA_Investigation.tryAddItemToStudy (Person(firstName = "Max",lastName = "Mus")) "Study1" doc
-//ISA_Investigation.tryAddItemToStudy (Person(firstName = "Tim",lastName = "Taler")) "Study1" doc
-//ISA_Investigation.tryUpdateItemInStudy (Person(firstName = "Max",lastName = "Mus",phone = "12345")) "Study1" doc
-
-
-//doc.Close()
-
-//open ISA_Investigation
-
-//let item = (Factor(name = "Factor1")) :> ISAItem
-//let study = "Study1"
-
-
-//let workbookPart = doc |> Spreadsheet.getWorkbookPart
-
-//let sheet = SheetTransformation.firstSheetOfWorkbookPart workbookPart
-
-
-//let studyScope = tryGetStudyScope workbookPart study sheet
-
-//let itemScope =
-// studyScope
-// |> Option.map (fun studyScope -> tryGetItemScope workbookPart study studyScope item sheet)
-// |> Option.flatten
-// |> Option.get
-
-
-//let colI = tryFindColumnInItemScope workbookPart "Study" itemScope item sheet |> Option.get
-
-//[itemScope.From .. itemScope.To]
-//|> List.rev
-//|> List.fold (fun s rowI -> SheetTransformation.DirectSheets.removeValueAt colI rowI s) sheet
-//|> removeScopeIfEmpty workbookPart itemScope
-
-//[1]
-//|> Seq.skip 2
-
diff --git a/docs/content/data/FSharpSpreadsheetMLTest.xlsx b/docs/content/data/FSharpSpreadsheetMLTest.xlsx
deleted file mode 100644
index 91675ae..0000000
Binary files a/docs/content/data/FSharpSpreadsheetMLTest.xlsx and /dev/null differ
diff --git a/docs/content/data/FSharpSpreadsheetMLTestCopy.xlsx b/docs/content/data/FSharpSpreadsheetMLTestCopy.xlsx
deleted file mode 100644
index af02606..0000000
Binary files a/docs/content/data/FSharpSpreadsheetMLTestCopy.xlsx and /dev/null differ
diff --git a/docs/content/fsdocs-custom.css b/docs/content/fsdocs-custom.css
deleted file mode 100644
index 3f4834a..0000000
--- a/docs/content/fsdocs-custom.css
+++ /dev/null
@@ -1,209 +0,0 @@
-/* Navigation Pane */
-
-#navbarsExampleDefault .nav-header {
- font-size:12pt;
- color:#606060;
- margin-top:20px;
- font-weight: bold;
-}
-
-#navbarsExampleDefault .nav-link {
- font-size:11pt;
- color:#0088cc;
-}
-
-
-/* FSharp Code scrollbar */
-
-table.pre {
- border: hidden;
- overflow-x: auto;
- white-space: nowrap;
- display:block;
-}
-
-table.pre::-webkit-scrollbar-track
-{
- -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
- border-radius: 5px;
- background-color: #212d30;
-}
-
-table.pre::-webkit-scrollbar
-{
- height: 8px;
- background-color: #212d30;
- border-radius:5px
-}
-
-table.pre::-webkit-scrollbar-thumb
-{
- border-radius: 5px;
- -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
- background-color: #80b0b0;
-}
-
-pre {
- border: hidden;
- overflow-x: auto;
-}
-
-pre::-webkit-scrollbar-track {
- -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
- border-radius: 5px;
- background-color: #212d30;
-}
-
-pre::-webkit-scrollbar {
- height: 8px;
- background-color: #212d30;
- border-radius: 5px
-}
-
-pre::-webkit-scrollbar-thumb {
- border-radius: 5px;
- -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
- background-color: #80b0b0;
-}
-#main
-{
- text-align:justify;
-}
-
-#main h2
-{
- margin: 10px 0px 10px 0px;
-}
-
-#responsiveTable
-{
- overflow-x:auto;
-}
-
-#responsiveTable::-webkit-scrollbar-track
-{
- -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
- border-radius: 5px;
- background-color: #212d30;
-}
-
-#responsiveTable::-webkit-scrollbar
-{
- height: 8px;
- background-color: #212d30;
- border-radius:5px
-}
-
-#responsiveTable::-webkit-scrollbar-thumb
-{
- border-radius: 5px;
- -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
- background-color: #80b0b0;
-}
-
-#responsiveTable p
-{
- margin: 0px 0px 0px 0px;
-}
-
-#responsiveTable table
-{
- border: 2px solid #2E75B6;
- border-radius:50px;
-}
-
-#responsiveTable tr:nth-child(even)
-{
- background-color: #BDD7EE;
-}
-
-#responsiveTable tr
-{
- border: 1px solid #2E75B6;
-}
-
-#responsiveTable th
-{
- background-color:#2E75B6;
- color:white;
-}
-
-#responsiveTable th, td
-{
- text-align: left;
- padding: 0px 10px 0px 10px;
-}
-
-.fileExamples
-{
- overflow-x: auto;
- white-space: nowrap;
- display:block;
-
-}
-
-.fileExamples pre
-{
- background-color: white;
- color:black;
-}
-
-.fileExamples::-webkit-scrollbar-track
-{
- -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
- border-radius: 5px;
- background-color: #212d30;
-}
-
-.fileExamples::-webkit-scrollbar
-{
- height: 8px;
- background-color: #212d30;
- border-radius:5px
-}
-
-.fileExamples::-webkit-scrollbar-thumb
-{
- border-radius: 5px;
- -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
- background-color: #80b0b0;
-}
-
-#exampleToggle
-{
- border-color: #2E75B6;
-}
-
-#SourceCode, #Author, #APILink
-{
- text-align:center;
- color:#2E75B6;
- padding: 2px 6px 2px 6px;
- /*border-radius: 25px;
- border: 2px solid #2E75B6;*/
- text-decoration:none;
-
-}
-
-
-
-#Author:hover, #SourceCode:hover , #APILink:hover
-{
- background-color:#2E75B6;
- color:white;
-}
-
-
-
-table.HeadAPI {
- width: 100%
-}
-
-td.Head {
- padding: 0px;
-}
-
-td.API {
- padding: 0px;
- text-align: right;
-}
diff --git a/docs/content/img/logo.png b/docs/content/img/logo.png
deleted file mode 100644
index 2dcecfb..0000000
Binary files a/docs/content/img/logo.png and /dev/null differ
diff --git a/docs/content/index.fsx b/docs/content/index.fsx
deleted file mode 100644
index 85c3f15..0000000
--- a/docs/content/index.fsx
+++ /dev/null
@@ -1,22 +0,0 @@
-(*** hide ***)
-// This block of code is omitted in the generated HTML documentation. Use
-// it to define helpers that you do not want to show in the documentation.
-
-#I @"../../bin\ArcCommander\netcoreapp3.1"
-#r"FSharpSpreadsheetML.dll"
-
-open FSharpSpreadsheetML
-(**
-# ArcCommander
-
-
-FSharp Library
-
-
-
----
-*)
-
-
-Array.init 10 (fun i -> i, i * 2)
-(*** include-it ***)
\ No newline at end of file
diff --git a/global.json b/global.json
index 7abee74..08fc58c 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "5.0.100",
- "rollForward": "latestMajor"
+ "version": "6.0.100",
+ "rollForward": "latestMinor"
}
}
\ No newline at end of file
diff --git a/publishBinariesLnx.cmd b/publishBinariesLnx.cmd
deleted file mode 100644
index 4589f1b..0000000
--- a/publishBinariesLnx.cmd
+++ /dev/null
@@ -1,4 +0,0 @@
-@echo off
-dotnet fake build -t publishBinariesLinux
-echo DONE!
-timeout 5 >nul
\ No newline at end of file
diff --git a/publishBinariesMac.cmd b/publishBinariesMac.cmd
deleted file mode 100644
index 22bd08c..0000000
--- a/publishBinariesMac.cmd
+++ /dev/null
@@ -1,4 +0,0 @@
-@echo off
-dotnet fake build -t publishBinariesMac
-echo DONE!
-timeout 5 >nul
\ No newline at end of file
diff --git a/publishBinariesWin.cmd b/publishBinariesWin.cmd
deleted file mode 100644
index 096a8b3..0000000
--- a/publishBinariesWin.cmd
+++ /dev/null
@@ -1,4 +0,0 @@
-@echo off
-dotnet fake build -t publishBinariesWin
-echo DONE!
-timeout 5 >nul
\ No newline at end of file
diff --git a/src/ArcCommander/.gitignore b/src/ArcCommander/.gitignore
deleted file mode 100644
index aba35e3..0000000
--- a/src/ArcCommander/.gitignore
+++ /dev/null
@@ -1,27 +0,0 @@
-# exclude everything..
-*
-#.. except ..
-
-
-#Include ARC folders
-!.arc/
-!.arc/**
-!assays/
-!assays/**
-!studies/
-!studies/**
-!workflows/
-!workflows/**
-!runs/
-!runs/**
-
-
-#Include ARC top level files
-!.gitattributes
-!.gitignore
-!README.md
-
-
-#Include ISA top level files
-!isa.investigation.xlsx
-!*isa.study.xlsx
diff --git a/src/ArcCommander/APIs/ArcAPI.fs b/src/ArcCommander/APIs/ArcAPI.fs
index aa134be..0213d13 100644
--- a/src/ArcCommander/APIs/ArcAPI.fs
+++ b/src/ArcCommander/APIs/ArcAPI.fs
@@ -8,6 +8,22 @@ open ArcCommander.ArgumentProcessing
open ISADotNet
open ISADotNet.XLSX
+open arcIO.NET
+open arcIO.NET.Converter
+
+module API =
+
+ module Investigation =
+
+ let getProcesses (investigation) =
+ investigation.Studies
+ |> Option.defaultValue [] |> List.collect (fun s ->
+ s.Assays
+ |> Option.defaultValue [] |> List.collect (fun a ->
+ a.ProcessSequence |> Option.defaultValue []
+ )
+ )
+
/// ArcCommander API functions that get executed by top level subcommand verbs.
module ArcAPI =
@@ -73,7 +89,8 @@ module ArcAPI =
//GitHelper.executeGitCommand workDir $"commit -m \"Initial commit\""
if containsFlag "Gitignore" arcArgs then
- let gitignoreAppPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".gitignore")
+ log.Warn("The default GitIgnore is an experimental feature. Be careful and double check that all your wanted files are being tracked.")
+ let gitignoreAppPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "defaultGitignore")
let gitignoreArcPath = Path.Combine(workDir, ".gitignore")
log.Trace($"Copy .gitignore from {gitignoreAppPath} to {gitignoreArcPath}")
File.Copy(gitignoreAppPath, gitignoreArcPath)
@@ -97,52 +114,10 @@ module ArcAPI =
log.Info("Start Arc Update")
- let assayRootFolder = AssayConfiguration.getRootFolderPath arcConfiguration
-
- let investigationFilePath = IsaModelConfiguration.getInvestigationFilePath arcConfiguration
-
- let assayNames =
- DirectoryInfo(assayRootFolder).GetDirectories()
- |> Array.map (fun d -> d.Name)
-
- let investigation =
- try Investigation.fromFile investigationFilePath
- with
- | :? FileNotFoundException ->
- Investigation.empty
- | err ->
- log.Fatal($"{err.ToString()}")
- raise (Exception(""))
-
- let rec updateInvestigationAssays (assayNames : string list) (investigation : Investigation) =
- match assayNames with
- | a :: t ->
- let assayFilePath = IsaModelConfiguration.getAssayFilePath a arcConfiguration
- let assayFileName = IsaModelConfiguration.getAssayFileName a arcConfiguration
- let factors,protocols,persons,assay = AssayFile.Assay.fromFile assayFilePath
- let studies = investigation.Studies
-
- match studies with
- | Some studies ->
- match studies |> Seq.tryFind (API.Study.getAssays >> Option.defaultValue [] >> API.Assay.existsByFileName assayFileName) with
- | Some study ->
- study
- |> API.Study.mapAssays (API.Assay.updateByFileName API.Update.UpdateByExistingAppendLists assay)
- |> API.Study.mapFactors (List.append factors >> List.distinctBy (fun f -> f.Name))
- |> API.Study.mapProtocols (List.append protocols >> List.distinctBy (fun p -> p.Name))
- |> API.Study.mapContacts (List.append persons >> List.distinctBy (fun p -> p.FirstName,p.LastName))
- |> fun s -> API.Study.updateBy ((=) study) API.Update.UpdateAll s studies
- | None ->
- Study.fromParts (Study.StudyInfo.create a "" "" "" "" "" []) [] [] factors [assay] protocols persons
- |> API.Study.add studies
- | None ->
- [Study.fromParts (Study.StudyInfo.create a "" "" "" "" "" []) [] [] factors [assay] protocols persons]
- |> API.Investigation.setStudies investigation
- |> updateInvestigationAssays t
- | [] -> investigation
-
- updateInvestigationAssays (assayNames |> List.ofArray) investigation
- |> Investigation.toFile investigationFilePath
+ let arcDir = GeneralConfiguration.getWorkDirectory arcConfiguration
+
+ Investigation.fromArcFolder arcDir
+ |> Investigation.overWrite arcDir
/// Export the complete ARC as a JSON object.
let export (arcConfiguration : ArcConfiguration) (arcArgs : Map) =
@@ -151,73 +126,82 @@ module ArcAPI =
log.Info("Start Arc Export")
- let investigationFilePath = IsaModelConfiguration.getInvestigationFilePath arcConfiguration
-
- let assayNames = AssayConfiguration.getAssayNames arcConfiguration
-
- let investigation =
- try Investigation.fromFile investigationFilePath
- with
- | :? FileNotFoundException ->
- Investigation.empty
- | err ->
- log.Fatal($"{err.Message}")
- raise (Exception(""))
-
- let rec updateInvestigationAssays (assayNames : string list) (investigation : Investigation) =
- match assayNames with
- | a :: t ->
- let assayFilePath = IsaModelConfiguration.getAssayFilePath a arcConfiguration
- let assayFileName = (IsaModelConfiguration.getAssayFileName a arcConfiguration).Replace("\\","/")
- let factors,protocols,persons,assay = AssayFile.Assay.fromFile assayFilePath
- let studies = investigation.Studies
-
- match studies with
- | Some studies ->
- match studies |> Seq.tryFind (API.Study.getAssays >> Option.defaultValue [] >> API.Assay.existsByFileName assayFileName) with
- | Some study ->
- study
- |> API.Study.mapAssays (API.Assay.updateByFileName API.Update.UpdateByExistingAppendLists assay)
- |> API.Study.mapFactors (List.append factors >> List.distinctBy (fun f -> f.Name))
- |> API.Study.mapProtocols (List.append protocols >> List.distinctBy (fun p -> p.Name))
- |> API.Study.mapContacts (List.append persons >> List.distinctBy (fun p -> p.FirstName,p.LastName))
- |> fun s -> API.Study.updateBy ((=) study) API.Update.UpdateAll s studies
- | None ->
- Study.fromParts (Study.StudyInfo.create a "" "" "" "" "" []) [] [] factors [assay] protocols persons
- |> API.Study.add studies
- | None ->
- [Study.fromParts (Study.StudyInfo.create a "" "" "" "" "" []) [] [] factors [assay] protocols persons]
- |> API.Investigation.setStudies investigation
- |> updateInvestigationAssays t
- | [] -> investigation
-
- let output = updateInvestigationAssays (assayNames |> List.ofArray) investigation
+ let workDir = GeneralConfiguration.getWorkDirectory arcConfiguration
+
+ let investigation = arcIO.NET.Investigation.fromArcFolder workDir
if containsFlag "ProcessSequence" arcArgs then
- let output =
- output.Studies
- |> Option.defaultValue [] |> List.collect (fun s ->
- s.Assays
- |> Option.defaultValue [] |> List.collect (fun a ->
- a.ProcessSequence |> Option.defaultValue []
- )
- )
+ let output = API.Investigation.getProcesses investigation
match tryGetFieldValueByName "Output" arcArgs with
| Some p -> ArgumentProcessing.serializeToFile p output
| None -> ()
- //System.Console.Write(ArgumentProcessing.serializeToString output)
log.Debug(ArgumentProcessing.serializeToString output)
else
-
+
match tryGetFieldValueByName "Output" arcArgs with
- | Some p -> ISADotNet.Json.Investigation.toFile p output
+ | Some p -> ISADotNet.Json.Investigation.toFile p investigation
| None -> ()
- //System.Console.Write(ISADotNet.Json.Investigation.toString output)
- log.Debug(ISADotNet.Json.Investigation.toString output)
+ log.Debug(ISADotNet.Json.Investigation.toString investigation)
+
+ /// Convert the complete ARC to a target format.
+ let convert (arcConfiguration : ArcConfiguration) (arcArgs : Map) =
+
+ let log = Logging.createLogger "ArcConvertLog"
+
+ log.Info("Start Arc Convert")
+
+ let workDir = GeneralConfiguration.getWorkDirectory arcConfiguration
+
+ let nameRoot = getFieldValueByName "Target" arcArgs
+ let converterName = $"arc-convert-{nameRoot}"
+ let repoOwner = "nfdi4plants"
+ let repoName = "converters"
+
+ log.Info (converterName)
+
+ let assayIdentifier = tryGetFieldValueByName "AssayIdentifier" arcArgs
+ let studyIdentifier = tryGetFieldValueByName "StudyIdentifier" arcArgs
+
+ log.Info("Fetch converter dll")
+
+ let assembly =
+ if nameRoot.Contains ".dll" then
+ System.Reflection.Assembly.LoadFile nameRoot
+ else
+ let dll = ArcConversion.getDll repoOwner repoName $"{converterName}.dll"
+ System.Reflection.Assembly.Load dll
+ let converter =
+ ArcConversion.callMethodOfAssembly converterName "create" assembly :?> ARCconverter
+ log.Info("Load ARC")
+
+ let i,s,a = ArcConversion.getISA studyIdentifier assayIdentifier workDir
+
+ log.Info("Run conversion")
+
+ match converter with
+ | ARCtoCSV f ->
+ ArcConversion.handleCSV i s a workDir nameRoot converter
+ | ARCtoTSV f ->
+ ArcConversion.handleTSV i s a workDir nameRoot converter
+ | ARCtoXLSX f ->
+ ArcConversion.handleXLSX i s a workDir nameRoot converter
+ | ARCtoJSON f ->
+ ArcConversion.handleJSON i s a workDir nameRoot converter
+ | _ -> failwith "no other converter defined"
+ |> function
+ | Ok messages ->
+ log.Info $"Successfully converted to {nameRoot}"
+ ArcConversion.writeMessages workDir messages
+ | Error messages ->
+ ArcConversion.writeMessages workDir messages
+ log.Error $"Arc could not be converted to {nameRoot}, as some required values could not be retreived"
+ if ArcConversion.promptYesNo "Do you want missing fields to be written back into ARC? (y/n)" then
+ ArcConversion.handleTransformations workDir converterName messages
+
/// Returns true if called anywhere in an ARC.
let isArc (arcConfiguration : ArcConfiguration) (arcArgs : Map) = raise (NotImplementedException())
\ No newline at end of file
diff --git a/src/ArcCommander/APIs/AssayAPI.fs b/src/ArcCommander/APIs/AssayAPI.fs
index 029acb8..baa5efe 100644
--- a/src/ArcCommander/APIs/AssayAPI.fs
+++ b/src/ArcCommander/APIs/AssayAPI.fs
@@ -10,7 +10,8 @@ open ISADotNet
open ISADotNet.XLSX
open ISADotNet.XLSX.AssayFile
-open FSharpSpreadsheetML
+open FsSpreadsheet.ExcelIO
+open arcIO.NET
/// ArcCommander Assay API functions that get executed by the assay focused subcommand verbs
module AssayAPI =
@@ -205,7 +206,7 @@ module AssayAPI =
assay1.Comments = assay2.Comments
// read assay metadata information from assay file
- let _, _, _, oldAssayAssayFile = AssayFile.Assay.fromFile assayFilepath
+ let _, oldAssayAssayFile = AssayFile.Assay.fromFile assayFilepath
let getNewAssay oldAssay =
ArgumentProcessing.Prompt.createIsaItemQuery
@@ -273,7 +274,7 @@ module AssayAPI =
let assayFilePath = IsaModelConfiguration.getAssayFilePath assayIdentifier arcConfiguration
- let _, _, _, assay = Assay.fromFile assayFilePath
+ let _, assay = Assay.fromFile assayFilePath
let studyIdentifier =
match getFieldValueByName "StudyIdentifier" assayArgs with
@@ -309,6 +310,7 @@ module AssayAPI =
let info = Study.StudyInfo.create studyIdentifier "" "" "" "" (IsaModelConfiguration.getStudyFileName studyIdentifier arcConfiguration) []
Study.fromParts info [] [] [] [assay] [] []
|> API.Study.add studies
+ |> List.filter (fun s -> s <> Study.empty)
| None ->
log.Info($"Study with the identifier {studyIdentifier} does not exist yet, creating it now.")
if StudyAPI.StudyFile.exists arcConfiguration studyIdentifier |> not then
@@ -657,7 +659,7 @@ module AssayAPI =
if System.IO.File.Exists assayFilePath then
try
- let _, _, p, a = AssayFile.Assay.fromFile assayFilePath
+ let p, a = AssayFile.Assay.fromFile assayFilePath
p, Some a
with
| err ->
@@ -753,7 +755,7 @@ module AssayAPI =
if System.IO.File.Exists assayFilePath then
try
- let _,_,p,a = AssayFile.Assay.fromFile assayFilePath
+ let p,a = AssayFile.Assay.fromFile assayFilePath
p, Some a
with
| err ->
diff --git a/src/ArcCommander/APIs/ConfigurationAPI.fs b/src/ArcCommander/APIs/ConfigurationAPI.fs
index 6cea7eb..f20287b 100644
--- a/src/ArcCommander/APIs/ConfigurationAPI.fs
+++ b/src/ArcCommander/APIs/ConfigurationAPI.fs
@@ -2,6 +2,7 @@
open ArcCommander
open ArgumentProcessing
+open arcIO.NET
/// ArcCommander Configuration API functions that get executed by the configuration focused subcommand verbs
module ConfigurationAPI =
diff --git a/src/ArcCommander/APIs/ExternalExecutables.fs b/src/ArcCommander/APIs/ExternalExecutables.fs
index e80036d..fdd61cd 100644
--- a/src/ArcCommander/APIs/ExternalExecutables.fs
+++ b/src/ArcCommander/APIs/ExternalExecutables.fs
@@ -6,6 +6,7 @@ open System
open System.IO
open System.Diagnostics
open Argu
+open arcIO.NET
/// Functions for trying to run external tools, given the command line arguments can not be parsed.
module ExternalExecutables =
@@ -82,9 +83,9 @@ module ExternalExecutables =
let roev = reviseOutput ev.Data
if matchCmdErrMsg roev || matchBashErrMsg roev then
log.Error("No executable, command or script file with given argument name known.")
- handleExceptionMessage log e2
+ Logging.handleExceptionMessage log e2
raise (Exception())
- else checkNonLog roev (sprintf "External Tool ERROR: %s" >> log.Error)
+ else Logging.checkNonLog roev (sprintf "External Tool ERROR: %s" >> log.Error)
)
let sbOutput = Text.StringBuilder() // StringBuilder for TRACE output (verbosity 2)
sbOutput.Append("External tool: ") |> ignore
@@ -98,9 +99,9 @@ module ExternalExecutables =
sbOutput.Append(char charAsInt) |> ignore
p.WaitForExit()
log.Trace(sbOutput.ToString()) // it is fine that the logging occurs after the external tool has done its job
- with e3 -> handleExceptionMessage log e3
+ with e3 -> Logging.handleExceptionMessage log e3
None
// If neither parsing, nor external executable tool search led to success, just return the error message
| None ->
- handleExceptionMessage log e2
+ Logging.handleExceptionMessage log e2
None
\ No newline at end of file
diff --git a/src/ArcCommander/APIs/GitAPI.fs b/src/ArcCommander/APIs/GitAPI.fs
index 995686a..eb6b518 100644
--- a/src/ArcCommander/APIs/GitAPI.fs
+++ b/src/ArcCommander/APIs/GitAPI.fs
@@ -4,7 +4,7 @@ open ArcCommander
open ArgumentProcessing
open Fake.IO
open System.IO
-
+open arcIO.NET
module GitAPI =
@@ -28,7 +28,9 @@ module GitAPI =
let repoDir = GeneralConfiguration.getWorkDirectory arcConfiguration
let remoteAddress = getFieldValueByName "RepositoryAddress" gitArgs
-
+
+ let merge = containsFlag "Merge" gitArgs
+
let branch =
match tryGetFieldValueByName "BranchName" gitArgs with
| Some branchName -> $" -b {branchName}"
@@ -40,7 +42,7 @@ module GitAPI =
else
""
- if System.IO.Directory.GetFileSystemEntries repoDir |> Array.isEmpty then
+ if merge then
log.Trace("Downloading into current folder.")
executeGitCommand repoDir $"clone {lfsConfig} {remoteAddress}{branch} ." |> ignore
else
@@ -113,7 +115,7 @@ module GitAPI =
allFilesPlusSizes
|> List.iter (fun (file,size) ->
- /// Track files larger than the git lfs threshold with git lfs. If no threshold is set, track no files with git lfs
+ // Track files larger than the git lfs threshold with git lfs. If no threshold is set, track no files with git lfs
match gitLfsThreshold with
| Some thr when size > thr -> trackWithLFS file
| _ -> trackWithAdd file
diff --git a/src/ArcCommander/APIs/InvestigationAPI.fs b/src/ArcCommander/APIs/InvestigationAPI.fs
index c9927ab..357dd41 100644
--- a/src/ArcCommander/APIs/InvestigationAPI.fs
+++ b/src/ArcCommander/APIs/InvestigationAPI.fs
@@ -5,7 +5,7 @@ open ArcCommander.ArgumentProcessing
open ISADotNet
open ISADotNet.XLSX
-
+open arcIO.NET
/// ArcCommander Investigation API functions that get executed by the investigation focused subcommand verbs.
module InvestigationAPI =
@@ -66,7 +66,7 @@ module InvestigationAPI =
let originalInvestigation = Investigation.fromFile investigationFilePath
- API.Investigation.update updateOption originalInvestigation investigation
+ API.Investigation.updateBy updateOption originalInvestigation investigation
|> Investigation.toFile investigationFilePath
/// Opens the existing investigation info in the ARC with the text editor set in globalArgs.
@@ -87,7 +87,7 @@ module InvestigationAPI =
(Investigation.InvestigationInfo.fromRows 1 >> fun (_,_,_,item) -> Investigation.fromParts item [] [] [] [] [])
investigation
- API.Investigation.update API.Update.UpdateAllAppendLists investigation editedInvestigation
+ API.Investigation.updateBy API.Update.UpdateAllAppendLists investigation editedInvestigation
|> Investigation.toFile investigationFilePath
/// Deletes the existing investigation file in the ARC if the given identifier matches the identifier set in the investigation file.
diff --git a/src/ArcCommander/APIs/RemoteAccessAPI.fs b/src/ArcCommander/APIs/RemoteAccessAPI.fs
index 9384abb..8d15aa8 100644
--- a/src/ArcCommander/APIs/RemoteAccessAPI.fs
+++ b/src/ArcCommander/APIs/RemoteAccessAPI.fs
@@ -2,7 +2,7 @@
open ArcCommander
open ArcCommander.ArgumentProcessing
-
+open arcIO.NET
module RemoteAccessAPI =
diff --git a/src/ArcCommander/APIs/StudyAPI.fs b/src/ArcCommander/APIs/StudyAPI.fs
index 4a0aee3..c66c11d 100644
--- a/src/ArcCommander/APIs/StudyAPI.fs
+++ b/src/ArcCommander/APIs/StudyAPI.fs
@@ -7,6 +7,7 @@ open System
open System.IO
open ISADotNet
open ISADotNet.XLSX
+open arcIO.NET
/// ArcCommander Study API functions that get executed by the study focused subcommand verbs.
module StudyAPI =
@@ -333,25 +334,14 @@ module StudyAPI =
let log = Logging.createLogger "StudyShowLog"
log.Info("Start Study Show")
-
+
+ let arcDir = GeneralConfiguration.getWorkDirectory arcConfiguration
let identifier = getFieldValueByName "Identifier" studyArgs
+ let study = Study.readByIdentifier arcDir identifier
- let investigationFilePath = IsaModelConfiguration.tryGetInvestigationFilePath arcConfiguration |> Option.get
-
- let investigation = Investigation.fromFile investigationFilePath
-
- match investigation.Studies with
- | Some studies ->
- match API.Study.tryGetByIdentifier identifier studies with
- | Some study ->
- study
- |> Prompt.serializeXSLXWriterOutput Study.StudyInfo.toRows
- |> log.Debug
- | None ->
- log.Error($"Study with the identifier {identifier} does not exist in the investigation file.")
- ()
- | None ->
- log.Error("The investigation does not contain any studies.")
+ study
+ |> Prompt.serializeXSLXWriterOutput Study.StudyInfo.toRows
+ |> log.Debug
/// Lists all study identifiers registered in this ARC's investigation file.
@@ -359,47 +349,9 @@ module StudyAPI =
let log = Logging.createLogger "StudyListLog"
- log.Info("Start Study List")
+ let arcDir = GeneralConfiguration.getWorkDirectory arcConfiguration
- let investigationFilePath = IsaModelConfiguration.tryGetInvestigationFilePath arcConfiguration |> Option.get
- log.Debug($"InvestigationFile: {investigationFilePath}")
-
- let investigation = Investigation.fromFile investigationFilePath
-
- let studyFileIdentifiers = set (IsaModelConfiguration.findStudyIdentifiers arcConfiguration)
-
- let studyIdentifiers =
- investigation.Studies
- |> Option.defaultValue []
- |> List.choose (fun s ->
- match s.Identifier with
- | None | Some "" ->
- log.Warn("Study does not have identifier")
- None
- | Some i -> Some i
- )
- |> set
-
- let onlyRegistered = Set.difference studyIdentifiers studyFileIdentifiers
- let onlyInitialized = Set.difference studyFileIdentifiers studyIdentifiers
- let combined = Set.union studyIdentifiers studyFileIdentifiers
-
- if not onlyRegistered.IsEmpty then
- log.Warn("The ARC contains following registered studies that have no associated file:")
- onlyRegistered
- |> Seq.iter ((sprintf "WARN: %s") >> log.Warn)
- log.Info($"You can init the study file using \"arc s init\"")
-
- if not onlyInitialized.IsEmpty then
- log.Warn("The ARC contains study files with the following identifiers not registered in the investigation:")
- onlyInitialized
- |> Seq.iter ((sprintf "WARN: %s") >> log.Warn)
- log.Info($"You can register the study using \"arc s register\"")
-
- if combined.IsEmpty then
- log.Error("The ARC does not contain any studies.")
-
- combined
+ Study.list arcDir
|> Seq.iter (fun identifier ->
log.Debug(sprintf "Study: %s" identifier)
)
@@ -999,12 +951,10 @@ module StudyAPI =
let name = getFieldValueByName "DesignType" designArgs
let design =
- DesignDescriptors.fromString
+ OntologyAnnotation.fromString
name
(getFieldValueByName "TypeTermAccessionNumber" designArgs)
- (getFieldValueByName "TypeTermSourceREF" designArgs)
-
- []
+ (getFieldValueByName "TypeTermSourceREF" designArgs)
let investigationFilePath = IsaModelConfiguration.tryGetInvestigationFilePath arcConfiguration |> Option.get
@@ -1111,11 +1061,10 @@ module StudyAPI =
let name = getFieldValueByName "DesignType" designArgs
let design =
- DesignDescriptors.fromString
+ OntologyAnnotation.fromString
name
(getFieldValueByName "TypeTermAccessionNumber" designArgs)
(getFieldValueByName "TypeTermSourceREF" designArgs)
- []
let studyIdentifier = getFieldValueByName "StudyIdentifier" designArgs
diff --git a/src/ArcCommander/ArcCommander.fsproj b/src/ArcCommander/ArcCommander.fsproj
index 9e6b3f6..84d2467 100644
--- a/src/ArcCommander/ArcCommander.fsproj
+++ b/src/ArcCommander/ArcCommander.fsproj
@@ -18,19 +18,20 @@
https://github.com/nfdi4plants/arcCommander/blob/developer/LICENSEhttps://github.com/nfdi4plants/arcCommander/blob/developer/RELEASE_NOTES.mdarc
-
-
+
+
-
+
+
@@ -54,22 +55,31 @@
+
+
+
+
+
+
+ PreserveNewest
+
+
+
-
+
-
+
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/src/ArcCommander/ArcConfiguration.fs b/src/ArcCommander/ArcConfiguration.fs
index 934b26b..b328f60 100644
--- a/src/ArcCommander/ArcConfiguration.fs
+++ b/src/ArcCommander/ArcConfiguration.fs
@@ -2,6 +2,7 @@
open System.IO
open IniData
+open arcIO.NET
/// Contains settings about the arc
type ArcConfiguration =
diff --git a/src/ArcCommander/ArcConversion.fs b/src/ArcCommander/ArcConversion.fs
new file mode 100644
index 0000000..1ca194a
--- /dev/null
+++ b/src/ArcCommander/ArcConversion.fs
@@ -0,0 +1,140 @@
+namespace ArcCommander
+
+open Logging
+open ISADotNet
+open ArcCommander.ArgumentProcessing
+
+open System
+open System.IO
+open System.Diagnostics
+open Argu
+open JsonDSL
+open arcIO.NET
+open arcIO.NET.Converter
+open ISADotNet.QueryModel
+open FsSpreadsheet.DSL
+open FsSpreadsheet.ExcelIO
+open FSharp.Data
+open Octokit
+
+/// Functions for trying to run external tools, given the command line arguments can not be parsed.
+module ArcConversion =
+
+ let getDll (repoOwner : string) (repoName : string) (dllname : string) =
+
+ let client = new GitHubClient(new ProductHeaderValue("ArcCommander"));
+
+ let releases = client.Repository.Release.GetAll(repoOwner, repoName);
+ let latest = releases.Result.[0]
+ let asset =
+ latest.Assets
+ |> Seq.find (fun r -> r.Name = dllname)
+ match Http.Request(asset.BrowserDownloadUrl).Body with
+ | HttpResponseBody.Text s -> failwithf "no dll"
+ | HttpResponseBody.Binary b -> b
+
+ let callMethodOfAssembly (typeName : string) (methodName : string) (assembly : System.Reflection.Assembly) =
+ let t = assembly.GetType(typeName)
+ let m = t.GetMethod(methodName)
+ m.Invoke(null,[||])
+
+
+ let getISA (studyIdentifier : string option) (assayIdentifier : string option) arcDir : QInvestigation*QStudy*QAssay=
+ let i = Investigation.fromArcFolder arcDir
+ let s =
+ let s =
+ match studyIdentifier with
+ | Option.Some si -> i.Studies.Value |> List.find (fun s -> s.Identifier.Value = si)
+ | None -> i.Studies.Value.Head
+ let ps =
+ (s.ProcessSequence |> Option.defaultValue [])
+ @
+ (s.Assays |> Option.defaultValue [] |> List.collect (fun a -> a.ProcessSequence |> Option.defaultValue []))
+ {s with ProcessSequence = Option.Some ps}
+ let a =
+ match assayIdentifier with
+ | Option.Some ai -> s.Assays.Value |> List.find (fun a -> a.FileName.Value = Assay.nameToFileName ai)
+ | None -> s.Assays.Value.Head
+
+ QInvestigation.fromInvestigation(i),
+ QStudy.fromStudy(s),
+ QAssay.fromAssay(a,[])
+
+ let prompt (msg:string) =
+ System.Console.Write(msg)
+ System.Console.ReadLine().Trim()
+ |> function | "" -> None | s -> Option.Some s
+ |> Option.map (fun s -> s.Replace ("\"","\\\""))
+
+ let rec promptYesNo msg =
+ match prompt (sprintf "%s [Yn]: " msg) with
+ | Option.Some "Y" | Option.Some "y" -> true
+ | Option.Some "N" | Option.Some "n" -> false
+ | _ -> System.Console.WriteLine("Sorry, invalid answer"); promptYesNo msg
+
+ let handleTransformations arcDir namePrefix (messages : exn list) =
+ let i = Investigation.fromArcFolder arcDir
+ let s = i.Studies.Value.Head
+ let transformations =
+ messages
+ |> ISADotNet.QueryModel.ErrorHandling.getStudyformations namePrefix
+ |> List.distinct
+ let updatedStudy =
+ transformations
+ |> List.fold (fun s transformation -> transformation.Transform s) s
+ let updatedArc =
+ i
+ |> API.Investigation.mapStudies
+ (API.Study.updateByIdentifier API.Update.UpdateAll updatedStudy)
+ |> API.Investigation.update
+ Study.overWrite arcDir updatedStudy
+ Investigation.overWrite arcDir updatedArc
+
+ let writeMessages (arcDir : string) (messages : exn list) =
+ let messagesOutPath = Path.Combine(arcDir,".arc/OutputMessages.txt")
+ messages
+ |> List.map (fun m -> m.ToString())
+ |> List.toArray
+ |> Array.distinct
+ |> fun messages ->
+ File.WriteAllLines(messagesOutPath,messages)
+
+ let handleXLSX (i : QInvestigation) (s : QStudy) (a : QAssay) arcDir converterName (f : arcIO.NET.Converter.ARCconverter) =
+ let outPath = Path.Combine([|arcDir;".arc";$"{converterName}.xlsx"|])
+ match f.ConvertXLSX(i,s,a) with
+ | Some (workbook,messages) ->
+ let wb = workbook.Parse()
+ wb.ToFile(outPath)
+ Ok(messages |> List.choose (fun m -> m.TryException()))
+ | NoneOptional messages | NoneRequired messages ->
+ Error(messages |> List.choose (fun m -> m.TryException()))
+
+ let handleCSV (i : QInvestigation) (s : QStudy) (a : QAssay) arcDir converterName (f : arcIO.NET.Converter.ARCconverter) =
+ let outPath = Path.Combine([|arcDir;".arc";$"{converterName}.csv"|])
+ match f.ConvertCSV(i,s,a) with
+ | Some (workbook,messages) ->
+ let wb = workbook.Parse()
+ FsSpreadsheet.CsvIO.Writer.toFile(outPath,wb,Separator = ',')
+ Ok(messages |> List.choose (fun m -> m.TryException()))
+ | NoneOptional messages | NoneRequired messages ->
+ Error(messages |> List.choose (fun m -> m.TryException()))
+
+ let handleTSV (i : QInvestigation) (s : QStudy) (a : QAssay) arcDir converterName (f : arcIO.NET.Converter.ARCconverter) =
+ let outPath = Path.Combine([|arcDir;".arc";$"{converterName}.csv"|])
+ match f.ConvertCSV(i,s,a) with
+ | Some (workbook,messages) ->
+ let wb = workbook.Parse()
+ FsSpreadsheet.CsvIO.Writer.toFile(outPath,wb,Separator = '\t')
+ Ok(messages |> List.choose (fun m -> m.TryException()))
+ | NoneOptional messages | NoneRequired messages ->
+ Error(messages |> List.choose (fun m -> m.TryException()))
+
+ let handleJSON (i : QInvestigation) (s : QStudy) (a : QAssay) arcDir converterName (f : arcIO.NET.Converter.ARCconverter) =
+ let outPath = Path.Combine([|arcDir;".arc";$"{converterName}.json"|])
+ match f.ConvertJsn(i,s,a) with
+ | JEntity.Some (node,messages) ->
+ let s = node.ToJsonString()
+ File.WriteAllText(outPath,s)
+ Ok(messages |> List.map (fun m -> m.AsException()))
+ | JEntity.NoneOptional messages | JEntity.NoneRequired messages ->
+ Error(messages |> List.map (fun m -> m.AsException()))
\ No newline at end of file
diff --git a/src/ArcCommander/ArgumentProcessing.fs b/src/ArcCommander/ArgumentProcessing.fs
index ca92646..5f8dd95 100644
--- a/src/ArcCommander/ArgumentProcessing.fs
+++ b/src/ArcCommander/ArgumentProcessing.fs
@@ -8,7 +8,7 @@ open System.Diagnostics
open System.Text
open System.Text.Json
open Argu
-
+open arcIO.NET
/// Functions for processing arguments.
module ArgumentProcessing =
@@ -28,20 +28,23 @@ module ArgumentProcessing =
Tooltip : string
IsMandatory : bool
IsFlag : bool
+ IsFileName : bool
}
- let createAnnotatedArgument arg tt mand isFlag =
+ let createAnnotatedArgument arg tt mand isFlag isFN =
{
Arg = arg
Tooltip = tt
IsMandatory = mand
IsFlag = isFlag
+ IsFileName = isFN
}
+ // classic forbidden symbols for file names in Windows (which also includes Linux/macOS)
+ let private forbiddenSymbols = [|'/'; '\\'; '"'; '>'; '<'; '?'; '='; '*'; '|'|]
+
/// Characters that must not occur in file names.
let private forbiddenChars =
- // classic forbidden symbols for file names in Windows (which also includes Linux/macOS)
- let forbiddenSymbols = [|'/'; '\\'; '"'; '>'; '<'; '?'; '='; '*'; '|'|]
let controlChars = Array.init 32 (fun i -> char i)
Array.append controlChars forbiddenSymbols
@@ -77,6 +80,12 @@ module ArgumentProcessing =
log.Error $"Identifier/filename \"{str}\" is a reserved filename. Please choose another one."
raise (Exception "")
+ /// Takes a string and checks if it is longer than 31 chars
+ let private checkForNameLength (str : string) =
+ let log = Logging.createLogger "ArgumentProcessingCheckForFileNameLength"
+ if seq str |> Seq.length |> (<) 31 then
+ log.Warn $"Identifier/filename \"{str}\" is longer than 31 characters, which might cause problems in excel sheets."
+
/// Returns true if the argument flag of name k was given by the user.
let containsFlag k (arguments : Map) =
let log = Logging.createLogger "ArgumentProcessingContainsFlagLog"
@@ -144,7 +153,7 @@ module ArgumentProcessing =
| [||] ->
let toolTip = (FSharpValue.MakeUnion (unionCase, [||]) :?> 'T).Usage
let value,isFlag = if Map.containsKey unionCase.Name m then Some Flag,true else None,true
- unionCase.Name,createAnnotatedArgument value toolTip isMandatory isFlag
+ unionCase.Name,createAnnotatedArgument value toolTip isMandatory isFlag isFileAttribute
| [|c|] when c.PropertyType.Name = "String" ->
let toolTip = (FSharpValue.MakeUnion (unionCase, [|box ""|]) :?> 'T).Usage
let value, isFlag =
@@ -155,13 +164,14 @@ module ArgumentProcessing =
if isFileAttribute then
iterForbiddenChars str
checkForReservedFns str
+ checkForNameLength str
replaceSpace str
else str
Field adjustedStr
|> Some,
false
| None -> None, false
- unionCase.Name, createAnnotatedArgument value toolTip isMandatory isFlag
+ unionCase.Name, createAnnotatedArgument value toolTip isMandatory isFlag isFileAttribute
| _ ->
log.Fatal($"Cannot parse argument {unionCase.Name} because its parsing rules were not yet implemented.")
raise (Exception(""))
@@ -239,13 +249,20 @@ module ArgumentProcessing =
elif arg.IsFlag then
sprintf "Remove # below to set flag: %s" arg.Tooltip
else sprintf "%s" arg.Tooltip
+ let fileComment =
+ $"""# FileName: The value of this argument will be used as a file or folder name.
+# FileName: Please refrain from using the following characters: {forbiddenSymbols |> Seq.map string |> String.concat " "}.
+# FileName: Please write a value of length at most 31 characters."""
let value =
match arg.Arg with
| Some (Flag) -> sprintf "%s" key
| Some (Field v) -> sprintf "%s:%s" key v
| None when arg.IsFlag -> sprintf "#%s" key
| None -> sprintf "%s:" key
- sprintf "#%s\n%s" comment value
+ if arg.IsFileName then
+ sprintf "#%s\n%s\n%s" comment fileComment value
+ else
+ sprintf "#%s\n%s" comment value
)
|> Array.reduce (fun a b -> a + "\n\n" + b)
|> sprintf "%s\n\n%s" header
@@ -260,6 +277,7 @@ module ArgumentProcessing =
if key.Contains "Identifier" then
iterForbiddenChars trValu
checkForReservedFns trValu
+ checkForNameLength trValu
replaceSpace trValu
else trValu
match s.Split c with
diff --git a/src/ArcCommander/Authentication.fs b/src/ArcCommander/Authentication.fs
index d5e1492..f172652 100644
--- a/src/ArcCommander/Authentication.fs
+++ b/src/ArcCommander/Authentication.fs
@@ -1,7 +1,6 @@
namespace ArcCommander
open IdentityModel.OidcClient;
-open Microsoft.Net.Http.Server;
open System;
open System.Collections.Generic;
open System.Diagnostics;
@@ -11,18 +10,18 @@ open System.Text;
open System.Text.Json
open System.Text.Json.Serialization
open System.Threading.Tasks;
-open JWT.Builder
-open JWT.Algorithms
+//open JWT.Builder
+//open JWT.Algorithms
module Authentication =
- let decodeResponse (response : string) =
+ //let decodeResponse (response : string) =
- JwtBuilder.Create()
- |> fun b -> b.WithAlgorithm(new HMACSHA256Algorithm()) // symmetric
- //|> fun b -> b.WithSecret(secret)
- //|> fun b -> b.MustVerifySignature()
- |> fun b -> b.Decode(response)
+ // JwtBuilder.Create()
+ // |> fun b -> b.WithAlgorithm(new HMACSHA256Algorithm()) // symmetric
+ // //|> fun b -> b.WithSecret(secret)
+ // //|> fun b -> b.MustVerifySignature()
+ // |> fun b -> b.Decode(response)
/// Fields returned by the token service
type IdentityToken =
@@ -49,9 +48,9 @@ module Authentication =
static member ofJson (jsonString : string) =
ISADotNet.JsonExtensions.fromString jsonString
- static member ofJwt (jwtResponse : string) =
- decodeResponse jwtResponse
- |> IdentityToken.ofJson
+ //static member ofJwt (jwtResponse : string) =
+ // decodeResponse jwtResponse
+ // |> IdentityToken.ofJson
/// Create a standard html site text with given string as body
let fillHTML s = $"
{s}
"
@@ -154,8 +153,8 @@ module Authentication =
return result
}
- []
/// Try to get the token information from a token service specified in the arc configuration
+ []
let tryLogin (log : NLog.Logger) authority (arcConfiguration : ArcConfiguration) =
log.Info("Initiate login protocol")
@@ -172,8 +171,8 @@ module Authentication =
module OAuth2 =
- []
/// Try to get the token information from a token service specified in the arc configuration
+ []
let tryLogin (log : NLog.Logger) authority (arcConfiguration : ArcConfiguration) =
raise (NotImplementedException("Login via OAuth2 authorization protocol is not yet implemented"))
\ No newline at end of file
diff --git a/src/ArcCommander/CLIArguments/ArcArgs.fs b/src/ArcCommander/CLIArguments/ArcArgs.fs
index 23a4619..ba407a3 100644
--- a/src/ArcCommander/CLIArguments/ArcArgs.fs
+++ b/src/ArcCommander/CLIArguments/ArcArgs.fs
@@ -1,8 +1,24 @@
namespace ArcCommander.CLIArguments
/// ------------ TOP LEVEL ------------ ///
+
+open ArcCommander
open Argu
+open ArgumentProcessing
+
+type ArcConvertArgs =
+
+ | [][][] Target of target : string
+ | [][][] StudyIdentifier of study_identifier : string
+ | [][][] AssayIdentifier of assay_identifier : string
+ interface IArgParserTemplate with
+ member this.Usage =
+ match this with
+ | Target _ -> "Target format to which arc should be exported."
+ | StudyIdentifier _ -> "Identifier of the study to be exported"
+ | AssayIdentifier _ -> "Identifier of the assay to be exported"
+
type ArcInitArgs =
| [] Owner of owner : string
@@ -18,7 +34,7 @@ type ArcInitArgs =
match this with
| Owner _ -> "Owner of the ARC"
| Branch _ -> "Name of the git branch to be created"
- | RepositoryAddress _ -> "Github address"
+ | RepositoryAddress _ -> "Git repository address"
| EditorPath _ -> "The path leading to the editor used for text prompts (Default in Windows is Notepad; Default in Unix systems is Nano)"
| GitLFSByteThreshold _ -> "The git LFS file size threshold in bytes. File larger than this threshold will be tracked by git LFS (Default Value is 150000000 Bytes ~ 150 MB)."
| Gitignore _ -> "Use this flag if you want a default .gitignore to be added to the initialized repo"
@@ -56,6 +72,7 @@ type ArcGetArgs =
| [][][] RepositoryAddress of repository_address:string
| [][] BranchName of branch_name:string
| [][] NoLFS
+ | [][] Merge
interface IArgParserTemplate with
member this.Usage =
@@ -63,3 +80,12 @@ type ArcGetArgs =
| RepositoryAddress _ -> "Git remote address from which to pull the ARC"
| BranchName _ -> "Branch of the remote address which should be used. If none is given, uses \"main\""
| NoLFS _ -> "Does download only the pointers of LFS files, not the file content itself. Ideal for when you're only interested in the experimental metadata, not the data itself."
+ | Merge _ -> "Merges the repository into the current folder. Fails, if the current folder isn't empty."
+
+type ArcServerArgs =
+ | [][] Port of port_address : string
+
+ interface IArgParserTemplate with
+ member this.Usage =
+ match this with
+ | Port _ -> ""
\ No newline at end of file
diff --git a/src/ArcCommander/CLIArguments/AssayArgs.fs b/src/ArcCommander/CLIArguments/AssayArgs.fs
index 0b8a45e..ad93f80 100644
--- a/src/ArcCommander/CLIArguments/AssayArgs.fs
+++ b/src/ArcCommander/CLIArguments/AssayArgs.fs
@@ -68,8 +68,8 @@ type AssayUpdateArgs =
/// CLI arguments for interactively editing existing assay metadata.
type AssayEditArgs =
- | [][][] StudyIdentifier of study_identifier : string
- | [][][][] AssayIdentifier of assay_identifier : string
+ | [][][] StudyIdentifier of study_identifier : string
+ | [][][][] AssayIdentifier of assay_identifier : string
interface IArgParserTemplate with
member this.Usage =
diff --git a/src/ArcCommander/CLIArguments/InvestigationArgs.fs b/src/ArcCommander/CLIArguments/InvestigationArgs.fs
index 8b7ca1d..f531470 100644
--- a/src/ArcCommander/CLIArguments/InvestigationArgs.fs
+++ b/src/ArcCommander/CLIArguments/InvestigationArgs.fs
@@ -1,13 +1,14 @@
namespace ArcCommander.CLIArguments
open Argu
+open ArcCommander.ArgumentProcessing
/// CLI arguments for creating a new investigation file for the arc
// in the case of investigations 'empty' does not mean empty file but rather an
// investigation without studies/assays. To reflect the need for metadata here,
// this command is called `create` instead of `init`
type InvestigationCreateArgs =
- | [][][] Identifier of investigation_identifier:string
+ | [][][][] Identifier of investigation_identifier:string
| [] Title of title:string
| [] Description of description:string
| [] SubmissionDate of submission_date:string
@@ -24,7 +25,7 @@ type InvestigationCreateArgs =
/// CLI arguments updating the arc's existing investigation file
type InvestigationUpdateArgs =
- | [][][] Identifier of investigation_identifier:string
+ | [][][][] Identifier of investigation_identifier:string
| [] Title of title:string
| [] Description of description:string
| [] SubmissionDate of submission_date:string
diff --git a/src/ArcCommander/Commands/ArcCommand.fs b/src/ArcCommander/Commands/ArcCommand.fs
index 132ba55..ab4ffb3 100644
--- a/src/ArcCommander/Commands/ArcCommand.fs
+++ b/src/ArcCommander/Commands/ArcCommand.fs
@@ -11,8 +11,10 @@ type ArcCommand =
///Commands
| [] Init of init_args : ParseResults
| [] Export of export_args : ParseResults
+ | [] Convert of convert_args : ParseResults
| [] Sync of sync_args : ParseResults
| [] Get of get_args : ParseResults
+ | [] Server of server_args : ParseResults
| [][] Update
| [][] Version
///Subcommands
@@ -30,6 +32,7 @@ type ArcCommand =
| Init _ -> "Initialize basic folder structure"
| Update _ -> "Update items in the arc against each other"
| Export _ -> "Exports the full arc to a json object"
+ | Convert _ -> "Converts the arc to a target format."
| Sync _ -> "Syncronize the ARC with its upstream repository. Commits changes made in the ARC. If a remote is set or is given, also pulls from there and pushes all previously made commits."
| Get _ -> "Download an ARC from a remote repository (e.g. from gitlab)"
| RemoteAccess _ -> "Subcommands for handling access functionality to remote repositories"
@@ -37,4 +40,5 @@ type ArcCommand =
| Study _ -> "Study functions"
| Assay _ -> "Assay functions"
| Configuration _ -> "Configuration editing"
- | Version _ -> "Get the ArcCommander's current version"
\ No newline at end of file
+ | Version _ -> "Get the ArcCommander's current version"
+ | Server _ -> "Start the ArcCommander as a local server"
\ No newline at end of file
diff --git a/src/ArcCommander/GitHelper.fs b/src/ArcCommander/GitHelper.fs
index 4c98a6d..6a0140b 100644
--- a/src/ArcCommander/GitHelper.fs
+++ b/src/ArcCommander/GitHelper.fs
@@ -3,6 +3,7 @@
open System.Diagnostics
open System.Runtime.InteropServices
open System.IO
+open arcIO.NET
module GitHelper =
diff --git a/src/ArcCommander/IniData.fs b/src/ArcCommander/IniData.fs
index 8683cfa..fa19886 100644
--- a/src/ArcCommander/IniData.fs
+++ b/src/ArcCommander/IniData.fs
@@ -6,6 +6,7 @@ open System.Runtime.InteropServices
open IniParser
open IniParser.Model
+open arcIO.NET
type OS =
| Windows
diff --git a/src/ArcCommander/Logging.fs b/src/ArcCommander/Logging.fs
index da77324..5aa5cd8 100644
--- a/src/ArcCommander/Logging.fs
+++ b/src/ArcCommander/Logging.fs
@@ -81,36 +81,6 @@ module Logging =
// activate config for logger
LogManager.Configuration <- config
- /// Creates a new logger with the given name. Configuration details are obtained from the generateConfig function.
- let createLogger (loggerName : string) =
-
- // new instance of "Logger" with activated config
- let logger = LogManager.GetLogger(loggerName)
-
- logger
-
- /// Takes a logger and an exception and separates usage and error messages. Usage messages will be printed into the console while error messages will be logged.
- let handleExceptionMessage (log : NLog.Logger) (exn : Exception) =
- // separate usage message (Argu) and error messages. Error messages shall be logged, usage messages shall not, empty error message shall not appear at all
- let isUsageMessage = exn.Message.Contains("USAGE") || exn.Message.Contains("SUBCOMMANDS")
- let isErrorMessage = exn.Message.Contains("ERROR")
- let isEmptyMessage = exn.Message = ""
- match isUsageMessage, isErrorMessage, isEmptyMessage with
- | true,true,false -> // exception message contains usage AND error messages
- let eMsg, uMsg =
- exn.Message.Split(Environment.NewLine) // '\n' leads to parsing problems
- |> fun arr ->
- arr |> Array.find (fun t -> t.Contains("ERROR")),
- arr |> Array.filter (fun t -> t.Contains("ERROR") |> not) |> String.concat "\n" // Argu usage instruction shall not be logged as error
- log.Error(eMsg)
- printfn "%s" uMsg
- | true,false,false -> printfn "%s" exn.Message // exception message contains usage message but NO error message
- | false,false,true -> () // empty error message
- | _ -> log.Error(exn) // everything else will be a non-empty error message
-
- /// Checks if a message (string) is empty and if it is not, applies a logging function to it.
- let checkNonLog s (logging : string -> unit) = if s <> "" then logging s
-
/// Deletes unwanted new lines at the end of an output.
let rec reviseOutput (output : string) =
if output = null then ""
diff --git a/src/ArcCommander/Program.fs b/src/ArcCommander/Program.fs
index bd0533a..80c5e0e 100644
--- a/src/ArcCommander/Program.fs
+++ b/src/ArcCommander/Program.fs
@@ -13,6 +13,7 @@ open System.IO
open System.Text
open System.Diagnostics
open Argu
+open arcIO.NET
/// Runs the given command with the given arguments and configuration. If mandatory arguments are missing, or the "forceEditor" flag is set, opens a prompt asking for additional input.
let processCommand (arcConfiguration : ArcConfiguration) commandF (r : ParseResults<'T>) =
@@ -214,6 +215,8 @@ let handleCommand arcConfiguration command =
// Verbs
| Init r -> processCommand arcConfiguration ArcAPI.init r
| Export r -> processCommand arcConfiguration ArcAPI.export r
+ | Convert r -> processCommand arcConfiguration ArcAPI.convert r
+ | Server r -> processCommand arcConfiguration Server.start r
| Update -> processCommandWithoutArgs arcConfiguration ArcAPI.update
| Version -> processCommandWithoutArgs arcConfiguration ArcAPI.version
// Git Verbs
diff --git a/src/ArcCommander/Server.fs b/src/ArcCommander/Server.fs
new file mode 100644
index 0000000..283d084
--- /dev/null
+++ b/src/ArcCommander/Server.fs
@@ -0,0 +1,109 @@
+namespace ArcCommander
+
+open System.IO
+open Microsoft.AspNetCore.Builder
+open Microsoft.AspNetCore.Hosting
+open Microsoft.Extensions.Hosting
+open Microsoft.Extensions.DependencyInjection
+open Giraffe
+open ArcCommander.ArgumentProcessing
+open Microsoft.AspNetCore.Http
+open Microsoft.AspNetCore.Cors.Infrastructure
+open Microsoft.AspNetCore.StaticFiles
+
+module Server =
+
+ /// Test-API function
+ let numberHandler : HttpHandler =
+ let funFunction myInt = $"Your number is {myInt}!"
+ fun (next : HttpFunc) (ctx : HttpContext) ->
+ task {
+ let! number = ctx.BindJsonAsync()
+ let nextNumber = funFunction number
+ // das machen wir so!
+ return! json {| ``is this your number?`` = nextNumber |} next ctx
+ }
+
+ /// API function for checking the application's version.
+ let versionHandler : HttpHandler =
+ fun (next : HttpFunc) (ctx : HttpContext) ->
+ task {
+ let ver = System.AssemblyVersionInformation.AssemblyVersion
+ return! json ver next ctx
+ }
+
+ /// Endpoints
+ let webApp =
+ // TO DO: establish versioning for APIs: e.g. `localhost/api/v1/ping`, "v1" should be the ArcCommander's version
+ choose [
+ GET >=> choose [
+ route "/version" >=> versionHandler
+ route "/ping" >=> text "pong"
+ ]
+ POST >=> choose [
+ route "/ping" >=> numberHandler
+ ]
+ subRoute "/v1" (
+ subRoute "/arc" (
+ choose [
+ GET >=> route "/docs" >=> htmlView ArcApi.Docs.view
+ POST >=> choose [
+ route "/get" >=> ArcAPIHandler.isaJsonToARCHandler
+ route "/init" >=> ArcAPIHandler.arcInitHandler
+ route "/import" >=> ArcAPIHandler.arcImportHandler
+ ]
+ ]
+ )
+ )
+ ]
+
+ let corsPolicyName = "_myAllowSpecificOrigins"
+
+ let corsPolicyConfig =
+ fun (b : CorsPolicyBuilder) ->
+ b
+ .AllowAnyHeader()
+ .AllowAnyMethod()
+ .AllowAnyOrigin()
+ |> ignore
+
+ let configureApp (app : IApplicationBuilder) =
+ let provider = new FileExtensionContentTypeProvider()
+ provider.Mappings.Add(".yaml", "application/x-yaml")
+ app.UseStaticFiles(
+ let opt = new StaticFileOptions()
+ opt.ContentTypeProvider <- provider
+ opt
+ ) |> ignore
+ // Add Giraffe to the ASP.NET Core pipeline
+ app.UseCors(corsPolicyName) |> ignore
+ app.UseGiraffe webApp
+
+ let configureServices (services : IServiceCollection) =
+ // Add Giraffe dependencies
+ services.AddCors(fun options -> options.AddPolicy(corsPolicyName, corsPolicyConfig)) |> ignore
+ services.AddGiraffe() |> ignore
+
+ let start arcConfiguration (arcServerArgs : Map) =
+
+ let port =
+ tryGetFieldValueByName "Port" arcServerArgs
+ |> Option.defaultValue "5000"
+
+ // https://trustbit.tech/blog/2021/03/12/introduction-to-web-programming-in-f-sharp-with-giraffe-part-3
+ // This only works because we added webroot folder to be included in .fsproj
+ /// returns folder of dll in bin/.. somethingsomething
+ let contentRoot = Directory.GetCurrentDirectory()
+ let webRoot = Path.Combine(contentRoot, "Server/WebRoot")
+
+ Host.CreateDefaultBuilder()
+ .ConfigureWebHostDefaults(
+ fun webHostBuilder ->
+ webHostBuilder
+ .UseWebRoot(webRoot)
+ .UseUrls([|$"http://*:{port}"|])
+ .Configure(configureApp)
+ .ConfigureServices(configureServices)
+ |> ignore)
+ .Build()
+ .Run()
\ No newline at end of file
diff --git a/src/ArcCommander/Server/ApiDocs/ArcApi.Docs.fs b/src/ArcCommander/Server/ApiDocs/ArcApi.Docs.fs
new file mode 100644
index 0000000..a311113
--- /dev/null
+++ b/src/ArcCommander/Server/ApiDocs/ArcApi.Docs.fs
@@ -0,0 +1,3 @@
+module ArcApi.Docs
+
+let view = Docs.baseViews "IArcAPI_v1.yaml"
\ No newline at end of file
diff --git a/src/ArcCommander/Server/ApiDocs/BaseView.Docs.fs b/src/ArcCommander/Server/ApiDocs/BaseView.Docs.fs
new file mode 100644
index 0000000..6e0896a
--- /dev/null
+++ b/src/ArcCommander/Server/ApiDocs/BaseView.Docs.fs
@@ -0,0 +1,39 @@
+module Docs
+
+open Giraffe.ViewEngine
+
+let private head =
+ head [] [
+ meta [_charset "utf-8"]
+ meta [_name "viewport"; _content "width=device-width, initial-scale=1"]
+ meta [_name "description"; _content "SwaggerUI"]
+ title [] [str "SwaggerUI"]
+ link [_rel "shortcut icon"; _type "image/png"; _href "https://raw.githubusercontent.com/nfdi4plants/Branding/master/icons/DataPLANT/favicons/favicon_bg_transparent.png" ]
+ link [_rel "stylesheet"; _href "https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui.css"]
+ ]
+
+let private js_binder (yamlFileName:string) =
+ sprintf """
+ const url = new URL('./%s', window.location.origin)
+ window.onload = () => {
+ window.ui = SwaggerUIBundle({
+ url: url.href,
+ dom_id: '#swagger-ui',
+ });
+ };
+ """ yamlFileName
+
+let private body (yamlFileName:string) =
+ body [] [
+ div [_id "swagger-ui"] []
+ script [_src "https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js"; _crossorigin ""] []
+ script [] [
+ rawText (js_binder yamlFileName)
+ ]
+ ]
+
+let baseViews (yamlFileName:string) =
+ html [] [
+ head
+ body yamlFileName
+ ]
\ No newline at end of file
diff --git a/src/ArcCommander/Server/ArcAPIHandler.fs b/src/ArcCommander/Server/ArcAPIHandler.fs
new file mode 100644
index 0000000..6b07083
--- /dev/null
+++ b/src/ArcCommander/Server/ArcAPIHandler.fs
@@ -0,0 +1,78 @@
+module ArcAPIHandler
+
+open Giraffe
+open Microsoft.AspNetCore.Http
+open System
+open System.IO
+open System.IO.Compression
+open ArcCommander
+open ArcCommander.APIs
+open ISADotNet
+
+///
+let isaJsonToARCHandler : HttpHandler =
+ fun (next : HttpFunc) (ctx : HttpContext) ->
+ // modified from: https://stackoverflow.com/questions/17232414/creating-a-zip-archive-in-memory-using-system-io-compression
+ task {
+ let isaJson = ctx.Request.Body
+ let ms = new MemoryStream()
+ let! _ = isaJson.CopyToAsync(ms)
+ let isaJsonBA = ms.ToArray()
+
+ let getByteArray (fileName : string) (data : byte []) =
+ try
+ use ms = new MemoryStream()
+ (
+ use archive = new ZipArchive(ms, ZipArchiveMode.Create)
+ let entry = archive.CreateEntry(fileName)
+ use entryStream = entry.Open()
+ use bw = new BinaryWriter(entryStream)
+ bw.Write(data)
+ )
+ (ms.ToArray())
+ with e -> failwithf "Cannot zip stream %s: %s" fileName e.Message
+ let res = getByteArray "arc.json" isaJsonBA
+ return! ctx.WriteBytesAsync res
+ }
+
+//type InitConfig = {
+// path : string
+//}
+
+let arcInitHandler : HttpHandler =
+ fun (next : HttpFunc) (ctx : HttpContext) ->
+ task {
+ let! config = ctx.BindJsonAsync<{|path : string|}>()
+ //let arcConfig = IniData.createDefault
+ //ArcAPI.init
+
+ return! json config next ctx
+ }
+
+//type AlibiJson = {
+// Alles : string
+//}
+
+/// Gets an ISA-JSON string via POST request and returns a byte array of a zipped archive of an ARC created from it.
+let arcImportHandler : HttpHandler =
+ fun (next : HttpFunc) (ctx : HttpContext) ->
+ task {
+ let! isaJsonString = ctx.BindJsonAsync()
+
+ let tmpDir = Path.Combine(Path.GetTempPath(), "tmpArc")
+ let tmpZip = Path.Combine(Path.GetTempPath(), "tmpArc.zip")
+
+ ISADotNet.Json.Investigation.fromString isaJsonString
+ |> fun i -> {i with Remarks = []}
+ |> arcIO.NET.Arc.importFromInvestigation tmpDir
+
+ System.IO.Compression.ZipFile.CreateFromDirectory(tmpDir,tmpZip )
+
+ Directory.Delete(tmpDir, true)
+
+ let byteArc : byte [] = File.ReadAllBytes tmpZip
+
+ File.Delete tmpZip
+
+ return! ctx.WriteBytesAsync byteArc
+ }
\ No newline at end of file
diff --git a/src/ArcCommander/Server/Version.fs b/src/ArcCommander/Server/Version.fs
new file mode 100644
index 0000000..2801571
--- /dev/null
+++ b/src/ArcCommander/Server/Version.fs
@@ -0,0 +1,13 @@
+// Auto-Generated by FAKE; do not edit
+namespace System
+open System.Reflection
+
+[]
+[]
+[]
+do ()
+
+module internal AssemblyVersionInformation =
+ let [] AssemblyTitle = "ArcCommander"
+ let [] AssemblyVersion = "0.5.0"
+ let [] AssemblyMetadata_ReleaseDate = "12.04.2023"
diff --git a/src/ArcCommander/Server/WebRoot/IArcAPI_v1.yaml b/src/ArcCommander/Server/WebRoot/IArcAPI_v1.yaml
new file mode 100644
index 0000000..ef39aa3
--- /dev/null
+++ b/src/ArcCommander/Server/WebRoot/IArcAPI_v1.yaml
@@ -0,0 +1,80 @@
+openapi: 3.0.3
+info:
+ version: 0.0.1
+ title: ARC API v1
+ description: |-
+ ARC API docs description
+ email: info@nfdi4plants.org
+servers:
+ - url: "http://localhost:5000"
+ description: "Local Test"
+paths:
+ /ping:
+ get:
+ summary: "Test function to verify client server connection."
+ description: "This function is only used for testing connection. If client has connection to server this request will return `pong`."
+ operationId: GET_ping
+ responses:
+ 200:
+ description: "OK"
+ content:
+ text/plain:
+ schema:
+ type: string
+ examples:
+ Only_Result:
+ summary: This api must always return "pong".
+ value: "pong"
+ post:
+ summary: "Test function to verify client server connection."
+ description: "This function is only used for testing connection. If client has connection to server and requests an integer, this request will return it."
+ operationId: POST_ping
+ requestBody:
+ description: "Test POST API request."
+ content:
+ application/json:
+ schema:
+ type: integer
+ examples:
+ the_answer:
+ summary: any number
+ value: 42
+ responses:
+ 200:
+ description: "OK"
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ "is this your number?":
+ type: string
+ examples:
+ the_answer:
+ value:
+ "is this your number?": "Your number is 42!"
+
+ /v1/arc/get:
+ post:
+ summary: "Takes an ISA-JSON as byte stream and returns a byte stream, consisting of the corresponding ARC created from it as a ZIP archive."
+ description: |
+ Give an ISA-JSON as byte stream via Http Request.
+ The ArcCommander server as backend will process and create the resulting ARC out of it. The ARC gets compressed as a ZIP archive.
+ This ZIP archive is then returned as a byte stream again.
+ operationId: POST_v1/arc/get
+ requestBody:
+ description: |
+ A JSON file, consisting of ISA, in the form of a byte stream.
+ content:
+ "*/*":
+ schema:
+ type: string
+ format: byte
+ responses:
+ 200:
+ description: "OK"
+ content:
+ "*/*":
+ schema:
+ type: string
+ format: byte
\ No newline at end of file
diff --git a/src/ArcCommander/defaultGitignore b/src/ArcCommander/defaultGitignore
new file mode 100644
index 0000000..7904a6b
--- /dev/null
+++ b/src/ArcCommander/defaultGitignore
@@ -0,0 +1,83 @@
+# ----- macos rules -----
+# taken from https://github.com/github/gitignore/blob/main/Global/macOS.gitignore
+
+
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+
+
+
+
+# ----- windows rules -----
+# taken from https://github.com/github/gitignore/blob/main/Global/Windows.gitignore
+
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+
+
+
+
+# ----- linux rules -----
+# taken from https://github.com/github/gitignore/blob/main/Global/Linux.gitignore
+
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
\ No newline at end of file
diff --git a/tests/ArcCommander.Tests.NetCore/ArcCommander.Tests.NetCore.fsproj b/tests/ArcCommander.Tests.NetCore/ArcCommander.Tests.NetCore.fsproj
index d75b94a..9377398 100644
--- a/tests/ArcCommander.Tests.NetCore/ArcCommander.Tests.NetCore.fsproj
+++ b/tests/ArcCommander.Tests.NetCore/ArcCommander.Tests.NetCore.fsproj
@@ -19,12 +19,9 @@
-
+
-
-
-
diff --git a/tests/ArcCommander.Tests.NetCore/ArcCommander/StudyTests.fs b/tests/ArcCommander.Tests.NetCore/ArcCommander/StudyTests.fs
index b1ed2b7..7e34cce 100644
--- a/tests/ArcCommander.Tests.NetCore/ArcCommander/StudyTests.fs
+++ b/tests/ArcCommander.Tests.NetCore/ArcCommander/StudyTests.fs
@@ -196,7 +196,6 @@ let testStudyContacts =
let studyBeforeChangingIt =
ISADotNet.XLSX.Investigation.fromFile investigationToCopy
|> API.Investigation.getStudies
- |> Option.get
|> API.Study.tryGetByIdentifier studyIdentifier
|> Option.get