From 6715a8e1ce675069a2da65e3a8ce339986125188 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Thu, 20 Feb 2025 14:06:31 +0100 Subject: [PATCH 01/30] refactor: src layout + split app files from docker --- Dockerfile => .docker/Dockerfile | 0 docker-compose.yml => .docker/compose.yml | 0 {app => .docker}/entrypoint.sh | 0 {app => src/shacl-api}/main.py | 0 {app => src/shacl-api}/shapesfile.ttl | 0 {app => src/shacl-api}/webapp/main.py | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename Dockerfile => .docker/Dockerfile (100%) rename docker-compose.yml => .docker/compose.yml (100%) rename {app => .docker}/entrypoint.sh (100%) rename {app => src/shacl-api}/main.py (100%) rename {app => src/shacl-api}/shapesfile.ttl (100%) rename {app => src/shacl-api}/webapp/main.py (100%) diff --git a/Dockerfile b/.docker/Dockerfile similarity index 100% rename from Dockerfile rename to .docker/Dockerfile diff --git a/docker-compose.yml b/.docker/compose.yml similarity index 100% rename from docker-compose.yml rename to .docker/compose.yml diff --git a/app/entrypoint.sh b/.docker/entrypoint.sh similarity index 100% rename from app/entrypoint.sh rename to .docker/entrypoint.sh diff --git a/app/main.py b/src/shacl-api/main.py similarity index 100% rename from app/main.py rename to src/shacl-api/main.py diff --git a/app/shapesfile.ttl b/src/shacl-api/shapesfile.ttl similarity index 100% rename from app/shapesfile.ttl rename to src/shacl-api/shapesfile.ttl diff --git a/app/webapp/main.py b/src/shacl-api/webapp/main.py similarity index 100% rename from app/webapp/main.py rename to src/shacl-api/webapp/main.py From 13a59ac4b83a64e496a12327f7f8808584550b53 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Thu, 20 Feb 2025 14:06:44 +0100 Subject: [PATCH 02/30] feat: pyproject file --- pyproject.toml | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..bb9fc52 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,41 @@ +[project] +name = "shacl-api" +version = "0.1.0" +authors = [ + { name="Swiss Data Science Center", email="contact@datascience.ch" }, +] +description = "Web service for the TopBraid SHACL validator." +readme = "README.md" +requires-python = ">=3.11" +classifiers = [ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", +] +license = "AGPL-3.0" +license-files = ["LICEN[CS]E*"] + +[project.urls] +Homepage = "https://github.com/pypa/sampleproject" +Issues = "https://github.com/pypa/sampleproject/issues" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +dependencies = [ + "rdflib", + "uvicorn", + "fastapi", +] + +[project.optional-dependencies] +dev = [ + "ruff", + "pre-commit", +] +test = [ + "pytest", +] +webapp = [ + "streamlit", +] From b287f0c7a272b502988917303d6b1ce8c58eb37e Mon Sep 17 00:00:00 2001 From: cmdoret Date: Thu, 20 Feb 2025 14:07:34 +0100 Subject: [PATCH 03/30] refactor: drop shapes file --- src/shacl-api/shapesfile.ttl | 1527 ---------------------------------- 1 file changed, 1527 deletions(-) delete mode 100644 src/shacl-api/shapesfile.ttl diff --git a/src/shacl-api/shapesfile.ttl b/src/shacl-api/shapesfile.ttl deleted file mode 100644 index 6a5a4f2..0000000 --- a/src/shacl-api/shapesfile.ttl +++ /dev/null @@ -1,1527 +0,0 @@ -prefix schema: -prefix sd: -prefix bio: -prefix spe: -prefix rdf: -prefix sh: -prefix xsd: -prefix shsh: -prefix owl: -prefix dcterms: -prefix imag: -prefix md4i: -prefix rdfs: -prefix dct: -prefix vann: - - - - a owl:Ontology ; - sh:entailment sh:Rules ; - sh:declare [ - sh:prefix "imag" ; - sh:namespace ; - ] . - imag:fairlevel0Shape a sh:NodeShape - . - - imag:fairlevel1Shape a sh:NodeShape ; - sh:targetClass schema:SoftwareSourceCode ; - sh:property - [sh:path schema:license ; - sh:minCount 1; - sh:message "Fair level 1"] , - - [sh:path schema:citation ; - sh:minCount 1; - sh:message "Fair level 1"] , - - [sh:path schema:description ; - sh:minCount 1; - sh:message "Fair level 1"] , - - [sh:path schema:url ; - sh:minCount 1; - sh:message "Fair level 1"] , - - [sh:path schema:dateCreated ; - sh:minCount 1; - sh:message "Fair level 1"] , - - [sh:path schema:datePublished ; - sh:minCount 1; - sh:message "Fair level 1"] , - - [sh:path schema:image ; - sh:minCount 1; - sh:message "Fair level 1"] - . - - imag:fairlevel2Shape a sh:NodeShape ; - sh:targetClass schema:SoftwareSourceCode ; - sh:property - [sh:path sd:readme ; - sh:minCount 1 ; - sh:message "Fair level 2"] , - - [sh:path schema:programmingLanguage ; - sh:minCount 1 ; - sh:message "Fair level 2"] , - - [sh:path schema:featureList ; - sh:minCount 1 ; - sh:message "Fair level 2"] ; - sh:and (imag:fairlevel1Shape) - . - - imag:fairlevel3Shape a sh:NodeShape ; - sh:targetClass schema:SoftwareSourceCode ; - sh:property - [sh:path schema:softwareRequirements ; - sh:minCount 1 ; - sh:message "Fair level 3"] , - - [sh:path schema:supportingData ; - sh:minCount 1 ; - sh:message "Fair level 3"] ; - sh:and (imag:fairlevel2Shape) - . - - imag:fairlevel4Shape a sh:NodeShape ; - sh:targetClass schema:SoftwareSourceCode ; - sh:property - [sh:path schema:memoryRequirements ; - sh:minCount 1; - sh:message "Fair level 4"] , - - [sh:path sd:hasDocumentation ; - sh:minCount 1; - sh:message "Fair level 4"] , - - [sh:path sd:hasSoftwareImage ; - sh:minCount 1; - sh:message "Fair level 4"] ; - sh:and (imag:fairlevel3Shape) - . - - imag:fairlevel5Shape a sh:NodeShape ; - sh:targetClass schema:SoftwareSourceCode ; - sh:property - [sh:path sd:hasExecutableInstructions ; - sh:minCount 1; - sh:message "Fair level 5"] ; - sh:and (imag:fairlevel4Shape) - . - -imag:FeatureTaggerRule a sh:SPARQLRule ; - sh:name "Feature tagger" ; - sh:description "This shape is used to extend the initial tagging of a software with it's higher level tags, for ease of retrieval/filtering" ; - sh:construct """ - PREFIX rdfs: - PREFIX schema: - CONSTRUCT { - $this schema:featureList ?parentFeatureLabel . - $this schema:featureList ?feature . - } - WHERE { - $this schema:featureList ?feature . - ?featureClass rdfs:label ?feature . - ?featureClass rdfs:subClassOf ?parentFeature. - ?parentFeature rdfs:label ?parentFeatureLabel . - } - """ . - -schema:SoftwareSourceCode -a sh:NodeShape, rdfs:Class ; -sh:property schema:SoftwareApplication-applicationCategory , - schema:CreativeWork-citation , - schema:SoftwareSourceCode-codeRepository , - schema:CreativeWork-conditionsOfAccess , - schema:CreativeWork-dateCreated , - schema:CreativeWork-datePublished , - schema:Thing-description , - schema:SoftwareApplication-featureList , - schema:Thing-image , - schema:CreativeWork-isAccessibleForFree , - schema:CreativeWork-isBasedOn , - imag:isPluginModuleOfShape, - imag:relatedToOrganizationShape, - schema:CreativeWork-license , - schema:CreativeWork-maintainer , - schema:SoftwareApplication-memoryRequirements , - schema:Thing-name , - schema:SoftwareApplication-operatingSystem , - schema:CreativeWork-producer , - schema:SoftwareSourceCode-programmingLanguage , - schema:SoftwareApplication-softwareRequirements , - schema:SoftwareApplication-processorRequirements , - imag:SoftwareApplication-requiresGPUShape , - schema:SoftwareApplication-supportingData , - schema:Thing-url , - schema:Thing-identifier , - sd:SoftwareApplication-hasAcknowledgements , - sd:SoftwareApplication-hasDocumentation , - sd:SoftwareApplication-hasExecutableInstructions , - imag:SoftwareApplication-hasExecutableNotebook , - sd:SoftwareApplication-hasParameter , - sd:SoftwareApplication-readme , - sd:SoftwareApplication-hasFunding , - sd:SoftwareConfiguration-hasSoftwareImage , - imag:SoftwareApplication-imagingModalityShape , - imag:fairLevelShape ; - sh:rule [ - a sh:TripleRule ; - sh:subject sh:this ; - sh:predicate imag:fairLevel ; - sh:object "Fair level 0" ; - sh:condition imag:fairlevel0Shape ]; - sh:rule [ - a sh:TripleRule ; - sh:subject sh:this ; - sh:predicate imag:fairLevel ; - sh:object "Fair level 1" ; - sh:condition imag:fairlevel1Shape ]; - - sh:rule [ - a sh:TripleRule ; - sh:subject sh:this ; - sh:predicate imag:fairLevel ; - sh:object "Fair level 2" ; - sh:condition imag:fairlevel2Shape ; - ] ; - sh:rule [ - a sh:TripleRule ; - sh:subject sh:this ; - sh:predicate imag:fairLevel ; - sh:object "Fair level 3" ; - sh:condition imag:fairlevel3Shape ; - ] ; - sh:rule [ - a sh:TripleRule ; - sh:subject sh:this ; - sh:predicate imag:fairLevel ; - sh:object "Fair level 4" ; - sh:condition imag:fairlevel4Shape - ] ; - sh:rule [ - a sh:TripleRule ; - sh:subject sh:this ; - sh:predicate imag:fairLevel ; - sh:object "Fair level 5" ; - sh:condition imag:fairlevel5Shape - ] ; - sh:rule imag:FeatureTaggerRule ; -. -imag:fairLevelShape a sh:PropertyShape ; - sh:path imag:fairLevel ; - sh:datatype xsd:string ; - sh:minCount 1 ; - . - -imag:SoftwareApplication-imagingModalityShape - a sh:PropertyShape ; - sh:path imag:imagingModality ; - sh:datatype xsd:string ; - . - -schema:SoftwareApplication-applicationCategory - a sh:PropertyShape ; - sh:path schema:applicationCategory ; - sh:datatype xsd:string ; - . -schema:SoftwareApplication-processorRequirements - a sh:PropertyShape ; - sh:path schema:processorRequirements ; - sh:datatype xsd:string ; - sh:in ("ARM64" "AMD64" "None") - . - -imag:SoftwareApplication-requiresGPUShape - a sh:PropertyShape ; - sh:path imag:requiresGPU ; - sh:datatype xsd:boolean . - -schema:CreativeWork-citation - a sh:PropertyShape ; - sh:path schema:citation ; - sh:minCount 1 ; - sh:datatype xsd:string ; - sh:pattern "^http.*" ; -. -schema:SoftwareSourceCode-codeRepository - a sh:PropertyShape ; - sh:path schema:codeRepository ; - sh:pattern "^http.*" ; - sh:minCount 1 ; - sh:maxCount 1 ; -. - -schema:CreativeWork-conditionsOfAccess - a sh:PropertyShape ; - sh:path schema:conditionsOfAccess ; - sh:datatype xsd:string ; - -. - -schema:CreativeWork-dateCreated - a sh:PropertyShape ; - sh:path schema:dateCreated ; - sh:minCount 1 ; - sh:maxCount 1 ; - sh:datatype xsd:date ; - . - -schema:CreativeWork-datePublished - a sh:PropertyShape ; - sh:path schema:datePublished ; - sh:datatype xsd:date ; - sh:minCount 1 ; -. - -schema:Thing-description - a sh:PropertyShape ; - sh:path schema:description ; - sh:datatype xsd:string ; - sh:maxLength 2000 ; - sh:minCount 1 ; -. - -schema:SoftwareApplication-featureList - a sh:PropertyShape ; - sh:path schema:featureList ; - sh:datatype xsd:string ; -. - -schema:Thing-image - a sh:PropertyShape ; - sh:path schema:image ; - sh:class schema:ImageObject ; - sh:minCount 1 ; -. - -schema:ImagingObject - a sh:NodeShape, rdfs:Class ; - sh:property schema:Thing-url ; - sh:property schema:CreativeWork-keywords ; - . - - -schema:CreativeWork-isAccessibleForFree - a sh:PropertyShape ; - sh:path schema:isAccessibleForFree ; - sh:datatype xsd:boolean ; -. - -schema:CreativeWork-isBasedOn - a sh:PropertyShape ; - sh:path schema:isBasedOn ; - sh:pattern "^http.*" ; - . - -imag:isPluginModuleOfShape - a sh:PropertyShape ; - sh:path imag:isPluginModuleOf ; - sh:datatype xsd:string ; - . - -imag:relatedToOrganizationShape - a sh:PropertyShape ; - sh:path imag:relatedToOrganization ; - sh:datatype xsd:string ; - . - -schema:CreativeWork-keywords - a sh:PropertyShape ; - sh:path schema:keywords ; - sh:datatype xsd:string ; - sh:in ("logo" "illustrative image" "before image" "after image") ; - sh:minCount 1 ; - sh:maxCount 1 ; -. - -schema:CreativeWork-license - a sh:PropertyShape ; - sh:path schema:license ; - sh:pattern ".*spdx\\.org.*" ; - sh:minCount 1 ; -. - -schema:CreativeWork-maintainer - a sh:PropertyShape ; - sh:path schema:maintainer ; - sh:minCount 1 ; - sh:or ( - [ - sh:class schema:Organization ; - ] - [ - sh:class schema:Person ; - ] - ) ; -. - -schema:Person -a sh:NodeShape ; - sh:property schema:Thing-name ; - sh:property md4i:orcidIdShape ; - sh:property schema:Person-affiliation -. - -schema:Person-email - a sh:PropertyShape ; - sh:path schema:email ; - sh:datatype xsd:string ; -. - -schema:Person-familyName - a sh:PropertyShape ; - sh:path schema:familyName ; - sh:datatype xsd:string ; - sh:minCount 1 ; - . - -schema:Person-givenName - a sh:PropertyShape ; - sh:path schema:givenName ; - sh:datatype xsd:string ; - sh:minCount 1 ; -. - -md4i:orcidIdShape - a sh:PropertyShape ; - sh:path md4i:orcidId ; - sh:datatype xsd:string ; - . - -schema:Person-affiliation - a sh:PropertyShape ; - sh:path schema:affiliation ; - sh:datatype xsd:string ; -. - -schema:Organization -a sh:NodeShape ; -sh:property schema:Organization-legalName ; -sh:property md4i:hasRorIdShape ; -. - -schema:SoftwareApplication-memoryRequirements - a sh:PropertyShape ; - sh:path schema:memoryRequirements ; - sh:or ( - [sh:datatype xsd:string ] - [sh:hasValue "None"]) -. - -schema:Thing-name - a sh:PropertyShape ; - sh:path schema:name ; - sh:datatype xsd:string ; - sh:minCount 1 ; - sh:maxLength 60 -. - -schema:SoftwareApplication-operatingSystem - a sh:PropertyShape ; - sh:path schema:operatingSystem ; - sh:in ("Linux" "Windows" "MacOS" "Other") ; -. - -schema:CreativeWork-producer - a sh:PropertyShape ; - sh:path schema:producer ; - sh:minCount 1 ; - sh:or ( - [ - sh:class schema:Organization ; - ] - [ - sh:class schema:Person ; - ] - ) ; -. - -schema:SoftwareSourceCode-programmingLanguage - a sh:PropertyShape ; - sh:path schema:programmingLanguage ; - sh:datatype xsd:string ; -. - - -schema:SoftwareApplication-softwareRequirements - a sh:PropertyShape ; - sh:path schema:softwareRequirements ; - sh:datatype xsd:string ; -. - -schema:SoftwareApplication-softwareVersion - a sh:PropertyShape ; - sh:path schema:softwareVersion ; - sh:datatype xsd:string ; - sh:pattern "[0-9]+\\.[0-9]+\\.[0-9]+" ; - sh:minCount 1 ; - . - -schema:SoftwareApplication-supportingData - a sh:PropertyShape ; - sh:path schema:supportingData ; - sh:class schema:DataFeed ; -. - -schema:Thing-url - a sh:PropertyShape ; - sh:path schema:url ; - sh:pattern "^http.*" ; -. - -sd:SoftwareApplication-hasAcknowledgements - a sh:PropertyShape ; - sh:path sd:hasAcknowledgements ; - sh:datatype xsd:string ; - . - -sd:SoftwareApplication-hasDocumentation - a sh:PropertyShape ; - sh:path sd:hasDocumentation ; - sh:pattern "^http.*" ; - . - -sd:SoftwareApplication-hasExecutableInstructions - a sh:PropertyShape ; - sh:path sd:hasExecutableInstructions ; - sh:datatype xsd:string ; - . - -imag:SoftwareApplication-hasExecutableNotebook - a sh:PropertyShape ; - sh:path imag:hasExecutableNotebook ; - sh:class imag:ExecutableNotebook ; - . - -sd:SoftwareApplication-hasParameter - a sh:PropertyShape ; - sh:path sd:hasParameter ; - sh:class bio:FormalParameter ; - . - -sd:SoftwareApplication-readme - a sh:PropertyShape ; - sh:path sd:readme ; - sh:pattern "^http.*" ; - . - -schema:DataFeed - a sh:NodeShape ; - sh:property schema:Thing-description , - schema:DataDownload-contentURL , - schema:Dataset-measurementTechnique , - schema:Thing-name , - schema:Dataset-variableMeasured ; - . - -schema:DataDownload-contentURL - a sh:PropertyShape ; - sh:path schema:contentUrl ; - sh:pattern "^http.*" ; - sh:minCount 1 ; - . - - - -sd:SoftwareApplication-hasFunding - a sh:PropertyShape ; - sh:path sd:hasFunding ; - sh:class sd:FundingInformation ; - . - - -schema:Thing-identifier - a sh:PropertyShape ; - sh:path schema:identifier ; - sh:datatype xsd:string ; - sh:minCount 1 ; -. - -sd:FundingInformation-fundingSource - a sh:PropertyShape ; - sh:path sd:fundingSource ; - sh:class schema:Organization ; - sh:minCount 1 ; - . -sd:FundingInformation-fundingGrant a sh:PropertyShape ; - sh:path sd:fundingGrant ; - sh:datatype xsd:string ; - sh:minCount 1. - -sd:FundingInformation - a sh:NodeShape ; - sh:property schema:Thing-identifier, - sd:FundingInformation-fundingGrant, - sd:FundingInformation-fundingSource ; - . - - - -bio:FormalParameter - a sh:NodeShape ; - sh:property schema:Thing-description , - schema:CreativeWork-encodingFormat, - schema:Thing-name, - sd:DatasetSpecification-hasDimensionality, - sd:DatasetSpecification-hasFormat , - schema:PropertyValueSpecification-defaultValue, - schema:PropertyValueSpecification-valueRequired - . - -schema:CreativeWork-encodingFormat - a sh:PropertyShape ; - sh:path schema:encodingFormat ; - sh:pattern ".*iana.org/assignments/media-types/.*" ; -. - -sd:DatasetSpecification-hasDimensionality - a sh:PropertyShape ; - sh:path sd:hasDimensionality ; - sh:datatype xsd:integer ; - sh:minExclusive 0 ; - . - -sd:DatasetSpecification-hasFormat - a sh:PropertyShape ; - sh:path sd:hasFormat ; - sh:datatype xsd:string ; - . - -schema:PropertyValueSpecification-defaultValue - a sh:PropertyShape ; - sh:path schema:defaultValue ; - sh:datatype xsd:string ; - -. - -schema:PropertyValueSpecification-valueRequired - a sh:PropertyShape ; - sh:path schema:valueRequired ; - sh:datatype xsd:boolean ; -. - -sd:SoftwareConfiguration-hasSoftwareImage - a sh:PropertyShape ; - sh:path sd:hasSoftwareImage ; - sh:class sd:SoftwareImage; - . - -sd:SoftwareImage a sh:NodeShape ; - sh:property schema:Thing-name , - schema:Thing-description , - schema:SoftwareApplication-softwareVersion , - sd:SoftwareImage-availableInRegistry ; - . - -imag:ExecutableNotebook a sh:NodeShape ; - sh:property schema:Thing-name , - schema:Thing-description , - schema:Thing-url . - - - - -sd:SoftwareImage-availableInRegistry - a sh:PropertyShape ; - sh:pattern "^http.*" ; - sh:path sd:availableInRegistry ; - . - -schema:Dataset-measurementTechnique - a sh:PropertyShape ; - sh:path schema:measurementTechnique ; - sh:datatype xsd:string ; -. - -schema:Dataset-variableMeasured - a sh:PropertyShape ; - sh:path schema:variableMeasured ; - sh:datatype xsd:string ; -. - -schema:Organization-legalName - a sh:PropertyShape ; - sh:path schema:legalName ; - sh:datatype xsd:string ; - sh:minCount 1 ; -. - -md4i:hasRorIdShape - a sh:PropertyShape ; - sh:path md4i:hasRorId ; - sh:pattern "^http.*" ; - . - - - a owl:Ontology ; - dct:abstract "The Imaging Plaza ontology provides concepts (classes and properties) to model human and machine readable imaging software metadata. It is the result of re-using external pre-existing vocabularies such as schema.org and the software description ontology, together with custom imaging specific objects and properties." ; - dct:contributor "Robin Franken", "Carlos Vivar Rios" ; - dct:created "2024-04-01"^^xsd:date ; - dct:creator "Robin Franken" ; - dct:description "The Imaging Plaza ontology provides concepts (classes and properties) to model human and machine readable imaging software metadata. It is the result of re-using external pre-existing vocabularies such as schema.org and the software description ontology, together with custom imaging specific objects and properties. The ontology contains purely semantic information (labels, definitions etc.) and is meant to be used in conjuction with a graph of enumeration values and a graph containing SHACL shapes to provide more machine readable restrictions on values of properties." ; - dct:license ; - dct:modified "2024-08-15"^^xsd:date ; - dct:title "Imaging Plaza Ontology" ; - vann:preferredNamespacePrefix "imag" ; - vann:preferredNamespaceUri "https://imaging-plaza.epfl.ch/ontology#"^^xsd:string ; - owl:versionInfo "0.7.0"^^xsd:string - . - -schema:SoftwareSourceCode - a rdfs:Class ; - rdfs:comment "A software application." ; - rdfs:label "Software application" ; - rdfs:subClassOf schema:CreativeWork . - -schema:license a rdf:Property; - rdfs:comment "A license document that applies to this content, typically indicated by URL."; - rdfs:label "License" . - -schema:citation a rdf:Property; - rdfs:comment "A DOI https URL citation to publication about this software. Use Zenodo to generate if needed. e.g. https://doi.org/10.1016/j.procs.2017.03.009"; - rdfs:label "Citation DOI" . - -schema:contentURL a rdf:Property; - rdfs:comment "Actual bytes of the media object, for example the image file or video file."; - rdfs:label "Content URL". - -schema:dateCreated a rdf:Property; - rdfs:comment "The date on which the repository was created."; - rdfs:label "Date created" . - -schema:datePublished a rdf:Property; - rdfs:comment "Latest published git release date, else, the publish date of the doi."; - rdfs:label "Date published" . - -schema:maintainer a rdf:Property; - rdfs:comment "A maintainer of a Dataset, software package or other Project. A maintainer is a Person or Organization that manages contributions to, and/or publication of, some (typically complex) artifact."; - rdfs:label "Maintainer" . - -schema:memoryRequirements a rdf:Property; - rdfs:comment "Minimum RAM requirements in GB to run the software, 0 if not applicable."; - rdfs:label "Memory requirements" . - -imag:fairLevel a rdf:Property; - rdfs:comment "A score of 1-5 rating the Findability, Accessibility, Interoperability and Reusability of a piece of software based on the presence of certain properties for an instance of software"; - rdfs:label "FAIR Level" . - -schema:affiliation a rdf:Property; - rdfs:comment "An organization that this person is affiliated with. For example, a university (EPFL/ETHZ), lab, company." ; - rdfs:label "Affiliation". - -schema:applicationCategory a rdf:Property; - rdfs:comment "Type of software application, e.g. 'Plugin, gui, desktop app, library'."; - rdfs:label "Application category" . - -schema:codeRepository a rdf:Property; - rdfs:comment "Primary link to the repository where the un-compiled, human readable code and related code is located (GitLab, GitHub, CodePlex etc.)."; - rdfs:label "Code repository" . - -schema:conditionsOfAccess a rdf:Property; - rdfs:comment "Conditions that affect the availability of, or method(s) of access to, an item. e.g. log-in required "; - rdfs:label "Conditions of access" . - -schema:description a rdf:Property; - rdfs:comment "A description of the item."; - rdfs:label "Description" . - -schema:featureList a rdf:Property; - rdfs:comment "Features or modules provided by this application (and possibly required by other applications)."; - rdfs:label "Feature list" . - -schema:image a rdf:Property; - rdfs:comment "An image of the item."; - rdfs:label "Image" . - -schema:isAccessibleForFree a rdf:Property; - rdfs:comment "A flag to signal that the item, event, or place is accessible for free."; - rdfs:label "Is accessible for free" . - -schema:isBasedOn a rdf:Property; - rdfs:comment "A resource from which this work is derived or from which it is a modification or adaption."; - rdfs:label "Is based on" . - -imag:isPluginModuleOf a rdf:Property; - rdfs:comment "The resource for which this tool is a plugin or module." ; - rdfs:label "Is plugin/module of" . - -imag:relatedToOrganization a rdf:Property ; - rdfs:comment "The lab/institution/group/project/organization to which a software is related and/or affiliated to" ; - rdfs:label "Related to organization" . - -schema:familyName a rdf:Property; - rdfs:comment "Family name. In the U.S., the last name of a Person."; - rdfs:label "Family name" . - -schema:givenName a rdf:Property; - rdfs:comment "Given name. In the U.S., the first name of a Person."; - rdfs:label "Given name" . - -schema:name a rdf:Property; - rdfs:comment "The name of a person, in the US their first and last name(s) seperated by spaces"; - rdfs:label "Name" . - -md4i:orcidId a rdf:Property ; - rdfs:comment "The ORCID ID of a person" ; - rdfs:label "Has ORCID ID" . - -md4i:hasRorId a rdf:Property ; - rdfs:comment "A Research Organization Registry identifier, that points to a research organization" ; - rdfs:label "Has ROR ID" . - -schema:legalName a rdf:Property; - rdfs:comment "The official name of the organization, e.g. the registered company name."; - rdfs:label "Legal name" . - -schema:operatingSystem a rdf:Property; - rdfs:comment "Operating systems supported (Windows 7/10/11, OS X 10.6, Android 1.6)."; - rdfs:label "Operating system" . - -schema:producer a rdf:Property; - rdfs:comment "The person or organization who produced the work (e.g. music album, movie, TV/radio series etc.)."; - rdfs:label "Producer" . - -schema:programmingLanguage a rdf:Property; - rdfs:comment "The computer programming language."; - rdfs:label "Programming Language" . - -schema:softwareRequirements a rdf:Property; - rdfs:comment "Special third party dependencies (e.g. CUDA, Tensorflow etc. related requirements)."; - rdfs:label "Software requirements" . - -schema:softwareVersion a rdf:Property; - rdfs:comment "Version of the software instance."; - rdfs:label "Software version" . - -schema:url a rdf:Property; - rdfs:comment "Fill in any URL related to this repository. e.g. related lab or project website."; - rdfs:label "URL" . - -schema:identifier a rdf:Property; - rdfs:comment "Represents any kind of unique string to identify something, such as ISBNs, GTIN codes, UUIDs etc."; - rdfs:label "Identifier" . - -schema:defaultValue a rdf:Property; - rdfs:comment "The default value of the input. For properties that expect a literal, the default is a literal value, for properties that expect an object, it's an ID reference to one of the current values."; - rdfs:label "Default value" . - -schema:valueRequired a rdf:Property; - rdfs:comment "Whether the property must be filled in to complete the action. Default is false."; - rdfs:label "Value required" . - -schema:supportingData a rdf:Property; - rdfs:comment "Supporting data for a SoftwareApplication."; - rdfs:label "Supporting data" . - -schema:distribution a rdf:Property; - rdfs:comment "A downloadable form of this dataset, at a specific location, in a specific format." ; - rdfs:label "Distribution" . - -schema:encodingFormat a rdf:Property; - rdfs:comment "Expressed using a MIME format (http://www.iana.org/assignments/media-types/media-types.xhtml)."; - rdfs:label "Encoding format" . - -schema:measurementTechnique a rdf:Property; - rdfs:comment "A technique or technology used in a Dataset corresponding to the method used for measuring the corresponding variable(s)." ; - rdfs:label "Measurement technique" . - -schema:variableMeasured a rdf:Property; - rdfs:comment "The variables that are measured in some dataset, described as text."; - rdfs:label "Variable measured" . - -imag:imagingModality a rdf:Property; - rdfs:comment "Technique or method used to create images of the interior of a body or an object for which this software was designed."; - rdfs:label "Imaging modality" . - -schema:keywords a rdf:Property; - rdfs:comment "The keywords/tags used to describe this content."@en; - rdfs:label "Keywords"@en . - -imag:requiresGPU a rdf:Property; - rdfs:comment "Stipulates whether something requires a graphical processing unit such as a NVIDEA or AMD graphics card"; - rdfs:label "Requires GPU" . - -schema:processorRequirements a rdf:Property; - rdfs:comment "Processor architecture required to run the application."; - rdfs:label "Processor requirements" . - -sd:hasInput a rdf:Property; - rdfs:comment "Property that links a model configuration to the input types expected by it."@en; - rdfs:label "Has input"@en . - -sd:hasOutput a rdf:Property; - rdfs:comment "Property that expresses what the outputs of a model are"@en; - rdfs:label "Has output"@en . - -sd:hasParameter a rdf:Property; - rdfs:comment "Property that indicates the parameters of a model configuration"@en; - rdfs:label "Has parameter"@en . - -sd:hasFunding a rdf:Property; - rdfs:comment "Property that links a software project to its funding information"@en; - rdfs:label "Has funding information"@en . - -sd:hasSoftwareImage a rdf:Property; - rdfs:comment "Function to link a function with its corresponding container"@en; - rdfs:label "Has software image"@en . - -sd:fundingSource a rdf:Property; - rdfs:comment "Link to an organization funding a software/component"@en; - rdfs:label "Funding source"@en; - rdfs:subPropertyOf owl:topObjectProperty . - -sd:readme a rdf:Property; - rdfs:comment "URL to the Readme file of a software component"@en; - rdfs:label "Readme"@en; - rdfs:subPropertyOf . - -sd:hasDocumentation a rdf:Property; - rdfs:comment "Pointer to the documentation of the model"@en; - rdfs:label "Has documentation"@en . - -sd:hasExecutableInstructions a rdf:Property; - rdfs:comment "URL to Human-readable instructions that indicate how a software component should be executed."@en; - rdfs:label "Has executable instructions"@en . - -imag:hasExecutableNotebook a rdf:Property; - rdfs:comment "Property that links a software component with an executable notebook (e.g., Jupyter notebook)."@en; - rdfs:label "Has executable notebook"@en . - -sd:hasAcknowledgements a rdf:Property; - rdfs:comment "String with the people, organizations and other contributors acknowledged by the authors."@en; - rdfs:label "Has acknowledgements"@en . - -sd:fundingGrant a rdf:Property; - rdfs:comment "Grant number used for funding"@en; - rdfs:label "Funding grant"@en; - rdfs:subPropertyOf . - -sd:hasFormat a rdf:Property; - rdfs:comment "Format followed by a file. For example, txt, nc, etc."@en; - rdfs:label "Has format"@en . - -sd:availableInRegistry a rdf:Property; - rdfs:comment "URL to registry of where software image can be found. For example, https://hub.docker.com/r/pytorch/pytorch"@en; - rdfs:label "Available in registry"@en . - -sd:hasDimensionality a rdf:Property; - rdfs:comment "Property to indicate dimensionality of the input or output of a dataset specification"@en; - rdfs:label "Has dimensionality"@en . - - -#_________________________________________# - - - -schema:Person - a rdfs:Class ; - rdfs:comment "A person (alive, dead, undead, or fictional)." ; - rdfs:label "Person" ; - rdfs:subClassOf schema:Thing ; - owl:equivalentClass ; -. - -schema:Organization - a rdfs:Class ; - rdfs:comment "An organization such as a school, NGO, corporation, club, etc." ; - rdfs:label "Organization" ; - rdfs:subClassOf schema:Thing ; -. - -schema:ComputerLanguage - a rdfs:Class ; - rdfs:comment "This type covers computer programming languages such as Scheme and Lisp, as well as other language-like computer representations. Natural languages are best represented with the Language type." ; - rdfs:label "Computer language" ; - rdfs:subClassOf schema:Intangible ; -. - - -schema:DataFeed - a rdfs:Class ; - rdfs:comment "A single feed providing structured information about one or more entities or topics." ; - rdfs:label "Data feed" ; - rdfs:subClassOf schema:Dataset ; -. - - -schema:Grant - a rdfs:Class ; - rdfs:comment "A grant, typically financial or otherwise quantifiable, of resources." ; - rdfs:label "Grant" ; - rdfs:subClassOf schema:Intangible ; -. - -sd:FundingInformation a rdfs:Class ; - rdfs:comment "A class to represent the funding information of a software project"@en ; - rdfs:label "Funding Information"@en ; - . - - -sd:DatasetSpecification a rdfs:Class ; - rdfs:subClassOf ; - rdfs:comment "Class designed to describe a type of input or output used or produced by a model. For example, Topoflow has several inputs. One of them is a text file with precipitation values. The representation of this input is an instance of a dataset specification."@en ; - rdfs:label "Dataset Specification"@en ; - . - -bio:FormalParameter a rdfs:Class ; - rdfs:subClassOf ; - rdfs:comment "A FormalParameter is an identified variable used to stand for the actual value(s) that are consumed/produced by a set of steps"@en ; - rdfs:label "Formal Parameter"@en ; - - . -imag:ExecutableNotebook a rdfs:Class ; - rdfs:comment "An object representing an executable notebook such as Jupyter Notebook" ; - rdfs:label "Executable Notebook"@en ; - . - -sd:SoftwareImage - a rdfs:Class ; - rdfs:subClassOf sd:Software ; - rdfs:comment "An image that virtualizes the functionality of a given software. For example, a Docker container."@en ; - rdfs:label "Software Image"@en ; - . - - - -imag:EnumerationType a rdfs:Class ; - rdfs:subClassOf schema:Enumeration ; - rdfs:comment "EnumerationType class used to group all enumeration classes under" ; - rdfs:label "Enumeration Type" ; -. - -imag:ImagingModalityEnum a rdfs:Class ; - rdfs:comment "Enumeration class for imaging modality" ; - rdfs:label "Imaging Modality Enumeration" ; - rdfs:subClassOf imag:EnumerationType ; -. - -imag:MRI a imag:ImagingModalityEnum ; - rdfs:label "MRI" ; -. - -imag:CAT a imag:ImagingModalityEnum ; - rdfs:label "CAT" ; -. - -imag:XRay a imag:ImagingModalityEnum ; - rdfs:label "X-ray" ; -. - -imag:Any a imag:ImagingModalityEnum ; - rdfs:label "Any" ; -. - -imag:AffiliationEnum a rdfs:Class ; - rdfs:comment "Enumeration class for affiliation" ; - rdfs:label "Affiliation Enumeration" ; - rdfs:subClassOf imag:EnumerationType ; -. - -imag:EPFL a imag:AffiliationEnum ; - rdfs:label "EPFL" ; -. - -imag:UNIL a imag:AffiliationEnum ; - rdfs:label "UNIL" ; -. - -imag:ETHZ a imag:AffiliationEnum ; - rdfs:label "ETHZ" ; -. - -imag:UZH a imag:AffiliationEnum ; - rdfs:label "UZH" ; -. - -imag:NoneAffiliation a imag:AffiliationEnum ; - rdfs:label "None" ; -. - -imag:IsPluginModuleOfEnum a rdfs:Class ; - rdfs:comment "Enumeration class for plugin module" ; - rdfs:label "Plugin Module Enumeration" ; - rdfs:subClassOf imag:EnumerationType ; -. - -imag:Fiji a imag:IsPluginModuleOfEnum ; - rdfs:label "Fiji" ; -. - -imag:Napari a imag:IsPluginModuleOfEnum ; - rdfs:label "napari" ; -. - -imag:Icy a imag:IsPluginModuleOfEnum ; - rdfs:label "icy" ; -. - -imag:QuPath a imag:IsPluginModuleOfEnum ; - rdfs:label "qupath" ; -. - -imag:OMERO a imag:IsPluginModuleOfEnum ; - rdfs:label "OMERO" ; -. - -imag:Pyxu a imag:IsPluginModuleOfEnum ; - rdfs:label "pyxu" ; -. - -imag:NonePluginModule a imag:IsPluginModuleOfEnum ; - rdfs:label "None" ; -. - -imag:ApplicationCategoryEnum a rdfs:Class ; - rdfs:comment "Enumeration class for application category" ; - rdfs:label "Application Category Enumeration" ; - rdfs:subClassOf imag:EnumerationType ; -. - -imag:DesktopApp a imag:ApplicationCategoryEnum ; - rdfs:label "Desktop app" ; -. - -imag:Plugin a imag:ApplicationCategoryEnum ; - rdfs:label "Plugin" ; -. - -imag:WebApp a imag:ApplicationCategoryEnum ; - rdfs:label "Web app" ; -. - -imag:Notebook a imag:ApplicationCategoryEnum ; - rdfs:label "Notebook" ; -. - -imag:Library a imag:ApplicationCategoryEnum ; - rdfs:label "Library" ; -. - -imag:ImageObjectTagEnum a rdfs:Class ; - rdfs:comment "Enumeration class for image object tag" ; - rdfs:label "Image Object Tag Enumeration" ; - rdfs:subClassOf imag:EnumerationType ; -. - -imag:Logo a imag:ImageObjectTagEnum ; - rdfs:label "logo" ; -. - -imag:IllustrativeImage a imag:ImageObjectTagEnum ; - rdfs:label "illustrative image" ; -. - -imag:BeforeImage a imag:ImageObjectTagEnum ; - rdfs:label "before image" ; -. - -imag:AfterImage a imag:ImageObjectTagEnum ; - rdfs:label "after image" ; -. - -imag:AnimatedImage a imag:ImageObjectTagEnum ; - rdfs:label "animated image" ; -. - -imag:OperatingSystemEnum a rdfs:Class ; - rdfs:comment "Enumeration class for operating system" ; - rdfs:label "Operating System Enumeration" ; - rdfs:subClassOf imag:EnumerationType ; -. - -imag:Windows a imag:OperatingSystemEnum ; - rdfs:label "Windows" ; -. - -imag:Linux a imag:OperatingSystemEnum ; - rdfs:label "Linux" ; -. - -imag:MacOS a imag:OperatingSystemEnum ; - rdfs:label "MacOS" ; -. - -imag:OtherOS a imag:OperatingSystemEnum ; - rdfs:label "Other" ; -. - -imag:ProcessorRequirementsEnum a rdfs:Class ; - rdfs:comment "Enumeration class for processor requirements" ; - rdfs:label "Processor Requirements Enumeration" ; - rdfs:subClassOf imag:EnumerationType ; -. - -imag:ARM64 a imag:ProcessorRequirementsEnum ; - rdfs:label "ARM64" ; -. - -imag:AMD64 a imag:ProcessorRequirementsEnum ; - rdfs:label "AMD64" ; -. - -imag:NoneProcessorRequirement a imag:ProcessorRequirementsEnum ; - rdfs:label "None" ; -. - -imag:FeatureEnum a rdfs:Class ; - rdfs:comment "Enumeration class for software features" ; - rdfs:label "Feature Enumeration" ; - rdfs:subClassOf imag:EnumerationType ; -. - -imag:Annotation a imag:FeatureEnum ; - rdfs:label "Annotation" ; -. - -imag:Labelling a imag:FeatureEnum ; - rdfs:label "Labelling" ; -. - -imag:Drawing a imag:FeatureEnum ; - rdfs:label "Drawing" ; -. - -imag:OpticalFlow a imag:FeatureEnum ; - rdfs:label "Optical flow" ; -. - -imag:DigitalImageCorrelation a imag:FeatureEnum ; - rdfs:label "Digital image correlation" ; -. - -imag:MotionEstimation a imag:FeatureEnum ; - rdfs:label "Motion estimation" ; -. - -imag:Registration a imag:FeatureEnum ; - rdfs:label "Registration" ; -. - -imag:Stitching a imag:FeatureEnum ; - rdfs:label "Stitching" ; -. - -imag:DriftCorrection a imag:FeatureEnum ; - rdfs:label "Drift correction" ; -. - -imag:Denoising a imag:FeatureEnum ; - rdfs:label "Denoising" ; -. - -imag:Deblurring a imag:FeatureEnum ; - rdfs:label "Deblurring" ; -. - -imag:Smoothing a imag:FeatureEnum ; - rdfs:label "Smoothing" ; -. - -imag:Deconvolution a imag:FeatureEnum ; - rdfs:label "Deconvolution" ; -. - -imag:Preprocessing a imag:FeatureEnum ; - rdfs:label "Preprocessing" ; -. - -imag:Filtering a imag:FeatureEnum ; - rdfs:label "Filtering" ; -. - -imag:ImageSimplification a imag:FeatureEnum ; - rdfs:label "Image simplification" ; -. - -imag:ImageEnhancement a imag:FeatureEnum ; - rdfs:label "Image enhancement" ; -. - -imag:Segmentation a imag:FeatureEnum ; - rdfs:label "Segmentation" ; -. - -imag:Classification a imag:FeatureEnum ; - rdfs:label "Classification" ; -. - -imag:ObjectDetection a imag:FeatureEnum ; - rdfs:label "Object detection" ; -. - -imag:ObjectRecognition a imag:FeatureEnum ; - rdfs:label "Object recognition" ; -. - -imag:Clustering a imag:FeatureEnum ; - rdfs:label "Clustering" ; -. - -imag:Grouping a imag:FeatureEnum ; - rdfs:label "Grouping" ; -. - -imag:PatternRecognition a imag:FeatureEnum ; - rdfs:label "Pattern recognition" ; -. - -imag:TemplateMatching a imag:FeatureEnum ; - rdfs:label "Template matching" ; -. - -imag:InstanceSegmentation a imag:FeatureEnum ; - rdfs:label "Instance segmentation" ; -. - -imag:PixelClassification a imag:FeatureEnum ; - rdfs:label "Pixel classification" ; -. - -imag:FeatureExtraction a imag:FeatureEnum ; - rdfs:label "Feature extraction" ; -. - -imag:DimensionalityReduction a imag:FeatureEnum ; - rdfs:label "Dimensionality reduction" ; -. - -imag:DirectionalImageAnalysis a imag:FeatureEnum ; - rdfs:label "Directional image analysis" ; -. - -imag:PoseEstimation a imag:FeatureEnum ; - rdfs:label "Pose estimation" ; -. - -imag:PoseInformation a imag:FeatureEnum ; - rdfs:label "Pose information" ; -. - -imag:PoseDetection a imag:FeatureEnum ; - rdfs:label "Pose detection" ; -. - -imag:OrientationAnalysis a imag:FeatureEnum ; - rdfs:label "Orientation analysis" ; -. - -imag:SceneReconstruction a imag:FeatureEnum ; - rdfs:label "Scene reconstruction" ; -. - -imag:Reconstruction3d a imag:FeatureEnum ; - rdfs:label "3d reconstruction" ; -. - -imag:ModelGeneration3d a imag:FeatureEnum ; - rdfs:label "3d model generation" ; -. - -imag:MorphologicalReconstruction a imag:FeatureEnum ; - rdfs:label "Morphological reconstruction" ; -. - -imag:DepthEstimation a imag:FeatureEnum ; - rdfs:label "Depth estimation" ; -. - -imag:CurvatureEstimation a imag:FeatureEnum ; - rdfs:label "Curvature estimation" ; -. - -imag:ShadingEstimation a imag:FeatureEnum ; - rdfs:label "Shading estimation" ; -. - -imag:Modelling a imag:FeatureEnum ; - rdfs:label "Modelling" ; -. - -imag:Simulation a imag:FeatureEnum ; - rdfs:label "Simulation" ; -. - -imag:Visualization a imag:FeatureEnum ; - rdfs:label "Visualization" ; -. - -imag:Rendering a imag:FeatureEnum ; - rdfs:label "Rendering" ; -. - -imag:StatisticsMeasurement a imag:FeatureEnum ; - rdfs:label "Statistics measurement" ; -. - -imag:QualityMetricMeasurement a imag:FeatureEnum ; - rdfs:label "Quality metric measurement" ; -. - -imag:DistributionMeasurements a imag:FeatureEnum ; - rdfs:label "Distribution measurements" ; -. - -imag:ImageQualityAssessment a imag:FeatureEnum ; - rdfs:label "Image quality assessment" ; -. - -imag:MeshGeneration a imag:FeatureEnum ; - rdfs:label "Mesh generation" ; -. - -imag:Voxelization a imag:FeatureEnum ; - rdfs:label "Voxelization" ; -. - -imag:SurfaceParameterisation a imag:FeatureEnum ; - rdfs:label "Surface parameterisation" ; -. - -imag:MorphologicalAnalysis a imag:FeatureEnum ; - rdfs:label "Morphological analysis" ; -. - -imag:FeatureDetection a imag:FeatureEnum ; - rdfs:label "Feature detection" ; -. - -imag:Tracking a imag:FeatureEnum ; - rdfs:label "Tracking" ; -. - -imag:VisualQuestionAnswering a imag:FeatureEnum ; - rdfs:label "Visual question answering" ; -. - -imag:SoftwareRequirementsEnum a rdfs:Class ; - rdfs:comment "Enumeration class for software requirements" ; - rdfs:label "Software Requirements Enumeration" ; - rdfs:subClassOf imag:EnumerationType ; -. - -imag:PyTorch_2_2 a imag:SoftwareRequirementsEnum ; - rdfs:label "PyTorch 2.2" ; -. - -imag:PyTorch_2_1 a imag:SoftwareRequirementsEnum ; - rdfs:label "PyTorch 2.1" ; -. - -imag:PyTorch_2_0 a imag:SoftwareRequirementsEnum ; - rdfs:label "PyTorch 2.0" ; -. - -imag:PyTorch_1_13 a imag:SoftwareRequirementsEnum ; - rdfs:label "PyTorch 1.13" ; -. - -imag:Tensorflow_2_15 a imag:SoftwareRequirementsEnum ; - rdfs:label "Tensorflow 2.15" ; -. - -imag:Tensorflow_2_4 a imag:SoftwareRequirementsEnum ; - rdfs:label "Tensorflow 2.4" ; -. - -imag:Tensorflow_2_2 a imag:SoftwareRequirementsEnum ; - rdfs:label "Tensorflow 2.2" ; -. - -imag:Tensorflow_2_0 a imag:SoftwareRequirementsEnum ; - rdfs:label "Tensorflow 2.0" ; -. - -imag:Tensorflow_1_8 a imag:SoftwareRequirementsEnum ; - rdfs:label "Tensorflow 1.8" ; -. - -imag:Tensorflow_1_5 a imag:SoftwareRequirementsEnum ; - rdfs:label "Tensorflow 1.5" ; -. - -imag:Tensorflow_1_4 a imag:SoftwareRequirementsEnum ; - rdfs:label "Tensorflow 1.4" ; -. - -imag:CUDA_11_8 a imag:SoftwareRequirementsEnum ; - rdfs:label "CUDA 11.8" ; -. - -imag:CUDA_12_1 a imag:SoftwareRequirementsEnum ; - rdfs:label "CUDA 12.1" ; -. - -imag:CUDA_10_1 a imag:SoftwareRequirementsEnum ; - rdfs:label "CUDA 10.1" ; -. - -imag:CUDA_9_0 a imag:SoftwareRequirementsEnum ; - rdfs:label "CUDA 9.0" ; -. - -imag:NoneSoftwareRequirement a imag:SoftwareRequirementsEnum ; - rdfs:label "None" ; -. - -imag:ComputerLanguageEnum a rdfs:Class ; - rdfs:comment "Enumeration class for computer languages" ; - rdfs:label "Computer Language Enumeration" ; - rdfs:subClassOf imag:EnumerationType ; -. - -imag:Python a imag:ComputerLanguageEnum ; - rdfs:label "Python" ; -. - -imag:Scala a imag:ComputerLanguageEnum ; - rdfs:label "Scala" ; -. - -imag:Java a imag:ComputerLanguageEnum ; - rdfs:label "Java" ; -. - -imag:JavaScript a imag:ComputerLanguageEnum ; - rdfs:label "JavaScript" ; -. - -imag:GoLang a imag:ComputerLanguageEnum ; - rdfs:label "GoLang" ; -. - -imag:Csharp a imag:ComputerLanguageEnum ; - rdfs:label "C#" ; -. - -imag:C a imag:ComputerLanguageEnum ; - rdfs:label "C" ; -. - -imag:Bash a imag:ComputerLanguageEnum ; - rdfs:label "bash" ; -. - -imag:Shell a imag:ComputerLanguageEnum ; - rdfs:label "shell" ; -. - -imag:R a imag:ComputerLanguageEnum ; - rdfs:label "R" ; -. - -imag:Rust a imag:ComputerLanguageEnum ; - rdfs:label "Rust" ; -. - -imag:Cplusplus a imag:ComputerLanguageEnum ; - rdfs:label "C++" ; -. - -imag:MatLab a imag:ComputerLanguageEnum ; - rdfs:label "MATLAB" ; -. From bda986e3229cedf4511f7ddeebc995625b995c32 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Thu, 20 Feb 2025 14:09:17 +0100 Subject: [PATCH 04/30] refactor: standard project structure for example files and tools --- {bench => examples/bench}/SPAQLrequests.ipynb | 0 {bench => examples/bench}/SPARQLConstructor.ipynb | 0 {bench => examples/bench}/example.json | 0 {bench => examples/bench}/example.ttl | 0 {bench => examples/bench}/jsonld-ttl.ipynb | 0 {bench => examples/bench}/requests.ipynb | 0 {bench => examples/bench}/test.json | 0 requirements.txt | 2 -- {ci => tools/ci}/develop_watcher.sh | 0 {ci => tools/ci}/prod_watcher.sh | 0 10 files changed, 2 deletions(-) rename {bench => examples/bench}/SPAQLrequests.ipynb (100%) rename {bench => examples/bench}/SPARQLConstructor.ipynb (100%) rename {bench => examples/bench}/example.json (100%) rename {bench => examples/bench}/example.ttl (100%) rename {bench => examples/bench}/jsonld-ttl.ipynb (100%) rename {bench => examples/bench}/requests.ipynb (100%) rename {bench => examples/bench}/test.json (100%) delete mode 100644 requirements.txt rename {ci => tools/ci}/develop_watcher.sh (100%) rename {ci => tools/ci}/prod_watcher.sh (100%) diff --git a/bench/SPAQLrequests.ipynb b/examples/bench/SPAQLrequests.ipynb similarity index 100% rename from bench/SPAQLrequests.ipynb rename to examples/bench/SPAQLrequests.ipynb diff --git a/bench/SPARQLConstructor.ipynb b/examples/bench/SPARQLConstructor.ipynb similarity index 100% rename from bench/SPARQLConstructor.ipynb rename to examples/bench/SPARQLConstructor.ipynb diff --git a/bench/example.json b/examples/bench/example.json similarity index 100% rename from bench/example.json rename to examples/bench/example.json diff --git a/bench/example.ttl b/examples/bench/example.ttl similarity index 100% rename from bench/example.ttl rename to examples/bench/example.ttl diff --git a/bench/jsonld-ttl.ipynb b/examples/bench/jsonld-ttl.ipynb similarity index 100% rename from bench/jsonld-ttl.ipynb rename to examples/bench/jsonld-ttl.ipynb diff --git a/bench/requests.ipynb b/examples/bench/requests.ipynb similarity index 100% rename from bench/requests.ipynb rename to examples/bench/requests.ipynb diff --git a/bench/test.json b/examples/bench/test.json similarity index 100% rename from bench/test.json rename to examples/bench/test.json diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d1f80a3..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -uvicorn -fastapi \ No newline at end of file diff --git a/ci/develop_watcher.sh b/tools/ci/develop_watcher.sh similarity index 100% rename from ci/develop_watcher.sh rename to tools/ci/develop_watcher.sh diff --git a/ci/prod_watcher.sh b/tools/ci/prod_watcher.sh similarity index 100% rename from ci/prod_watcher.sh rename to tools/ci/prod_watcher.sh From 221e95e286da48ef5439b504d28d74b6d6842129 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Thu, 20 Feb 2025 14:32:07 +0100 Subject: [PATCH 05/30] docs: fill metadata in pyproject.toml --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bb9fc52..593803f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,8 +15,8 @@ license = "AGPL-3.0" license-files = ["LICEN[CS]E*"] [project.urls] -Homepage = "https://github.com/pypa/sampleproject" -Issues = "https://github.com/pypa/sampleproject/issues" +Homepage = "https://github.com/sdsc-ordes/shacl-api" +Issues = "https://github.com/sdsc-ordes/shacl-api/issues" [build-system] requires = ["hatchling"] From 7417137405fff6a9bdbe929f9a8c107bbb9ae110 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 14:01:41 +0100 Subject: [PATCH 06/30] chore(docker): add dockerignore --- .docker/Dockerfile | 30 +++++++++++++++++++++++------- .docker/api.Dockerfile | 28 ++++++++++++++++++++++++++++ .dockerignore | 1 + 3 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 .docker/api.Dockerfile create mode 100644 .dockerignore diff --git a/.docker/Dockerfile b/.docker/Dockerfile index e16b0bf..4733a98 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -1,6 +1,9 @@ -FROM ghcr.io/ashleycaselli/shacl:1.4.3_89205df +FROM ghcr.io/ashleycaselli/shacl:1.4.3_89205df AS base +### API-only build-stage ### +FROM base AS api +ENV SHAPES_FILE_URL=https://github.com/sdsc-ordes/imaging-plaza-ontology/releases/download/v0.8/ImagingOntologyCombined.ttl.gz ENV PYTHONUNBUFFERED=1 RUN apk update && apk add \ @@ -17,14 +20,27 @@ RUN groupadd --gid 1000 appuser \ USER appuser WORKDIR /shacl -COPY requirements.txt requirements.txt +COPY . . -RUN pip3 install --break-system-packages -r requirements.txt && \ - pip3 install --break-system-packages python-multipart streamlit rdflib +RUN pip3 install --break-system-packages -e . -COPY ./app app -COPY ./tests tests +EXPOSE 15400 + +ENTRYPOINT ["bash", "/shacl/src/entrypoint.sh", "api"] + +### API + Webapp build-stage ### +FROM api AS webapp + +USER root +RUN apk update && apk add \ + py3-pyarrow \ + && rm -rf /var/lib/apt/lists/* +USER appuser + +RUN pip3 install --break-system-packages -e '.[webapp]' ENV PATH="${PATH}:/home/appuser/.local/bin" +EXPOSE 8501 + +ENTRYPOINT ["bash", "/shacl/app/entrypoint.sh", "webapp"] -ENTRYPOINT ["bash", "/shacl/app/entrypoint.sh"] diff --git a/.docker/api.Dockerfile b/.docker/api.Dockerfile new file mode 100644 index 0000000..72416cc --- /dev/null +++ b/.docker/api.Dockerfile @@ -0,0 +1,28 @@ +FROM ghcr.io/ashleycaselli/shacl:1.4.3_89205df + +ENV SHAPES_FILE_URL=https://github.com/sdsc-ordes/imaging-plaza-ontology/releases/download/v0.8/ImagingOntologyCombined.ttl.gz +ENV PYTHONUNBUFFERED=1 + +RUN apk update && apk add \ + bash \ + curl \ + python3 \ + py3-pip \ + py3-pyarrow \ + shadow \ + && rm -rf /var/lib/apt/lists/* + +RUN groupadd --gid 1000 appuser \ + && useradd --uid 1000 --gid 1000 -m appuser + +USER appuser +WORKDIR /shacl +COPY . . + +RUN pip3 install --break-system-packages -e . +# pip3 install --break-system-packages python-multipart streamlit rdflib + + +EXPOSE 15400 + +ENTRYPOINT ["bash", "/shacl/src/entrypoint.sh"] diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.env From 03469543c644923d0b2eebae5ce44ffea52fc9fd Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 14:11:39 +0100 Subject: [PATCH 07/30] feat(docker): multi-stage build with shapes download --- .docker/Dockerfile | 32 +++++++++++++++++++++++++------- .docker/api.Dockerfile | 28 ---------------------------- .docker/compose.yml | 16 ++++++++++------ .docker/entrypoint.sh | 38 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 71 insertions(+), 43 deletions(-) delete mode 100644 .docker/api.Dockerfile diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 4733a98..2a72e6e 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -1,18 +1,32 @@ +ARG VERSION=latest + FROM ghcr.io/ashleycaselli/shacl:1.4.3_89205df AS base ### API-only build-stage ### FROM base AS api -ENV SHAPES_FILE_URL=https://github.com/sdsc-ordes/imaging-plaza-ontology/releases/download/v0.8/ImagingOntologyCombined.ttl.gz -ENV PYTHONUNBUFFERED=1 +# Metadata +LABEL org.opencontainers.image.source https://github.com/sdsc-ordes/shacl-api +LABEL org.opencontainers.image.licenses AGPL-3.0 +LABEL org.opencontainers.image.title "SHACL API" +LABEL org.opencontainers.image.description "API for validating RDF data against SHACL shapes." +LABEL org.opencontainers.image.version ${VERSION} + +# Environment +ENV API_PORT=15400 +ENV WEBAPP_PORT=8501 +ENV SHAPES_PATH=/data/shapes.ttl +ENV SHAPES_URL='https://github.com/sdsc-ordes/imaging-plaza-ontology/releases/download/v0.8/ImagingOntologyCombined.ttl.gz' RUN apk update && apk add \ bash \ curl \ + gzip \ python3 \ py3-pip \ - py3-pyarrow \ shadow \ + tar \ + zip \ && rm -rf /var/lib/apt/lists/* RUN groupadd --gid 1000 appuser \ @@ -22,15 +36,18 @@ USER appuser WORKDIR /shacl COPY . . +ENV PYTHONUNBUFFERED=1 RUN pip3 install --break-system-packages -e . -EXPOSE 15400 +EXPOSE ${API_PORT} -ENTRYPOINT ["bash", "/shacl/src/entrypoint.sh", "api"] +ENTRYPOINT ["bash", "/shacl/.docker/entrypoint.sh", "api"] ### API + Webapp build-stage ### FROM api AS webapp +LABEL org.opencontainers.image.version ${VERSION}-webapp + USER root RUN apk update && apk add \ py3-pyarrow \ @@ -40,7 +57,8 @@ USER appuser RUN pip3 install --break-system-packages -e '.[webapp]' ENV PATH="${PATH}:/home/appuser/.local/bin" -EXPOSE 8501 -ENTRYPOINT ["bash", "/shacl/app/entrypoint.sh", "webapp"] +EXPOSE ${WEBAPP_PORT} + +ENTRYPOINT ["bash", "/shacl/.docker/entrypoint.sh", "webapp"] diff --git a/.docker/api.Dockerfile b/.docker/api.Dockerfile deleted file mode 100644 index 72416cc..0000000 --- a/.docker/api.Dockerfile +++ /dev/null @@ -1,28 +0,0 @@ -FROM ghcr.io/ashleycaselli/shacl:1.4.3_89205df - -ENV SHAPES_FILE_URL=https://github.com/sdsc-ordes/imaging-plaza-ontology/releases/download/v0.8/ImagingOntologyCombined.ttl.gz -ENV PYTHONUNBUFFERED=1 - -RUN apk update && apk add \ - bash \ - curl \ - python3 \ - py3-pip \ - py3-pyarrow \ - shadow \ - && rm -rf /var/lib/apt/lists/* - -RUN groupadd --gid 1000 appuser \ - && useradd --uid 1000 --gid 1000 -m appuser - -USER appuser -WORKDIR /shacl -COPY . . - -RUN pip3 install --break-system-packages -e . -# pip3 install --break-system-packages python-multipart streamlit rdflib - - -EXPOSE 15400 - -ENTRYPOINT ["bash", "/shacl/src/entrypoint.sh"] diff --git a/.docker/compose.yml b/.docker/compose.yml index fd44f7d..5430bb8 100644 --- a/.docker/compose.yml +++ b/.docker/compose.yml @@ -1,9 +1,13 @@ -version: '3' services: - core_api: - build: . - container_name: "shacl-api" + shacl_api: + environment: + env_file: .example.env + build: + context: .. + dockerfile: .docker/Dockerfile + target: webapp ports: - - "8000:15400" + - "8001:15400" + - "8501:8501" volumes: - - ./app/:/shacl/app + - .:/shacl diff --git a/.docker/entrypoint.sh b/.docker/entrypoint.sh index b907990..9bccf14 100644 --- a/.docker/entrypoint.sh +++ b/.docker/entrypoint.sh @@ -1,4 +1,38 @@ #!/bin/bash -nohup streamlit run /shacl/app/webapp/main.py & -python3 -m uvicorn app.main:app --host 0.0.0.0 --port 15400 + +set -eu + +extract_file() { + ARCHIVE="$1" + OUTPUT="$2" + + case "$ARCHIVE" in + *.gz) + gzip -dc "$ARCHIVE" > "$OUTPUT" + ;; + *.zip) + unzip -p "$ARCHIVE" > "$OUTPUT" + ;; + *.tar.gz|*.tgz) + tar -xzOf "$ARCHIVE" > "$OUTPUT" + ;; + *) + mv "$ARCHIVE" "$OUTPUT" + ;; + esac +} + +# Download and extract shapes +mkdir -p "$(dirname "$SHAPES_PATH")" +TMP_FILE=$(mktemp) +curl -fsSL "$SHAPES_URL" -o "$TMP_FILE" +extract_file "$TMP_FILE" "$SHAPES_PATH" + +# Start webapp if enabled +if [ "$1" -eq "webapp" ]; then + nohup streamlit run /shacl/src/shacl-api/webapp.py --server.port "${WEBAPP_PORT:-8501}" & +fi + +# Start API server +python3 -m uvicorn shacl_api.server:app --host 0.0.0.0 --port "${API_PORT:-15400}" From 46e2a881c1d5efcdbee88941fe3751cc4d08dd6e Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 14:11:50 +0100 Subject: [PATCH 08/30] chore: include example env file --- .example.env | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .example.env diff --git a/.example.env b/.example.env new file mode 100644 index 0000000..55c3383 --- /dev/null +++ b/.example.env @@ -0,0 +1,4 @@ +WEBAPP_PORT=8501 +API_PORT=15400 +SHAPES_PATH=/data/shapes.ttl +SHAPES_URL='https://github.com/sdsc-ordes/imaging-plaza-ontology/releases/download/v0.8/ImagingOntologyCombined.ttl.gz' From 91a9aff55c67099a8d84eae356a18bc0cb90f3f7 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 14:12:08 +0100 Subject: [PATCH 09/30] refactor: flatten source tree --- src/shacl-api/{main.py => server.py} | 7 +++++-- src/shacl-api/{webapp/main.py => webapp.py} | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) rename src/shacl-api/{main.py => server.py} (95%) rename src/shacl-api/{webapp/main.py => webapp.py} (97%) diff --git a/src/shacl-api/main.py b/src/shacl-api/server.py similarity index 95% rename from src/shacl-api/main.py rename to src/shacl-api/server.py index 9a57b1f..67e9189 100644 --- a/src/shacl-api/main.py +++ b/src/shacl-api/server.py @@ -10,6 +10,7 @@ import json app = FastAPI() +SHAPES_PATH = os.getenv("SHAPES_PATH", "/app/shapesfile.ttl") @app.get("/") def index(): @@ -49,7 +50,7 @@ async def validateJsonLD(item: Request, ttl_input = str(graph.serialize(destination='datafile.ttl',format='turtle')) - output = subprocess.run(["shaclvalidate.sh", "-datafile", "datafile.ttl", "-shapesfile", "/app/shapesfile.ttl"], stdout=subprocess.PIPE) + output = subprocess.run(["shaclvalidate.sh", "-datafile", "datafile.ttl", "-shapesfile", SHAPES_PATH], stdout=subprocess.PIPE) os.remove("datafile.ttl") @@ -102,7 +103,7 @@ async def inferenceJsonLD(item: Request, graph.namespace_manager.bind('schema', SCHEMA, override=True, replace=True) ttl_input= str(graph.serialize(destination='datafile.ttl',format='turtle')) - output = subprocess.run(["shaclinfer.sh", "-datafile", "datafile.ttl", "-shapesfile", "/app/shapesfile.ttl"], stdout=subprocess.PIPE) + output = subprocess.run(["shaclinfer.sh", "-datafile", "datafile.ttl", "-shapesfile", "SHAPES_PATH"], stdout=subprocess.PIPE) os.remove("datafile.ttl") @@ -130,6 +131,7 @@ def validate(datafile:str=Form(...), datafile = base64.b64decode(str.encode(datafile)) shapesfile = base64.b64decode(str.encode(shapesfile)) + # TODO: use tempfile package with open("datafile.ttl", 'wb') as f: f.write(datafile) @@ -154,6 +156,7 @@ def inference(datafile:str=Form(...), shapesfile = base64.b64decode(str.encode(shapesfile)) print(datafile) + # TODO: use tempfile package with open("datafile.ttl", 'wb') as f: f.write(datafile) diff --git a/src/shacl-api/webapp/main.py b/src/shacl-api/webapp.py similarity index 97% rename from src/shacl-api/webapp/main.py rename to src/shacl-api/webapp.py index 83f216b..95ba5bb 100644 --- a/src/shacl-api/webapp/main.py +++ b/src/shacl-api/webapp.py @@ -1,7 +1,9 @@ +import os import streamlit as st import requests import base64 # This interface is for uploading the files and getting the files back +API_PORT = os.getenv("API_PORT", 15400) st.set_page_config(layout="wide") @@ -19,7 +21,7 @@ with colc: port = st.radio("Which port is the API using?", - ("15400", "8000")) + (f"{API_PORT}", "8000")) st.markdown("""---""") From f145bc86ad6a16011806f8d3302b7ec67354ff0a Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 14:12:27 +0100 Subject: [PATCH 10/30] fix: specify src layout in pyproject.toml --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 593803f..805f300 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,3 +39,6 @@ test = [ webapp = [ "streamlit", ] + +[tool.hatch.build.targets.wheel] +packages = ["src/shacl-api"] From 0930fae14da9d005649e6ba991e6e3cffce3ecf8 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 14:12:36 +0100 Subject: [PATCH 11/30] chore: add makefile --- Makefile | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fa17e87 --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +VERSION = latest +IMAGE = ghcr.io/sdsc-ordes/shacl-api +CONTAINER_RUNTIME ?= docker + +.PHONY: docker-build +docker-build: ## Build the Docker image + @echo "🐋 Building docker image" + + $(CONTAINER_RUNTIME) build \ + -f .docker/Dockerfile \ + -t $(IMAGE):$(VERSION) \ + --build-arg VERSION=$(VERSION) \ + --target api . + + $(CONTAINER_RUNTIME) build \ + -f .docker/Dockerfile \ + -t $(IMAGE):$(VERSION)-webapp \ + --build-arg VERSION=$(VERSION) \ + --target webapp . + +.PHONY: docker-push +docker-push: docker-build ## Push the Docker image + @echo "🐋 Pushing docker image" + $(CONTAINER_RUNTIME) push $(IMAGE):$(VERSION) + $(CONTAINER_RUNTIME) push $(IMAGE):$(VERSION)-webapp + +docker-compose-up: ## Run the Docker Compose stack + @echo "🐋 Running docker-compose" + $(CONTAINER_RUNTIME) compose \ + -f .docker/compose.yml \ + up --build + +.PHONY: help +help: + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' + +.DEFAULT_GOAL := help From c6a77eae3ed805755971d0330381a4c1968e44e2 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 14:20:55 +0100 Subject: [PATCH 12/30] docs: udpate makefile helm message --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index fa17e87..3d6a18c 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ IMAGE = ghcr.io/sdsc-ordes/shacl-api CONTAINER_RUNTIME ?= docker .PHONY: docker-build -docker-build: ## Build the Docker image +docker-build: ## Build Docker images @echo "🐋 Building docker image" $(CONTAINER_RUNTIME) build \ @@ -19,7 +19,7 @@ docker-build: ## Build the Docker image --target webapp . .PHONY: docker-push -docker-push: docker-build ## Push the Docker image +docker-push: docker-build ## Push Docker images @echo "🐋 Pushing docker image" $(CONTAINER_RUNTIME) push $(IMAGE):$(VERSION) $(CONTAINER_RUNTIME) push $(IMAGE):$(VERSION)-webapp From 1c836b66259d8ede51407b0ea5303dd5b9c34b12 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 18:19:59 +0100 Subject: [PATCH 13/30] feat(docker): make shapes download optional --- .docker/Dockerfile | 1 + .docker/entrypoint.sh | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 2a72e6e..333c5d9 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -16,6 +16,7 @@ LABEL org.opencontainers.image.version ${VERSION} ENV API_PORT=15400 ENV WEBAPP_PORT=8501 ENV SHAPES_PATH=/data/shapes.ttl +# unset or set to "" to disable shapes download ENV SHAPES_URL='https://github.com/sdsc-ordes/imaging-plaza-ontology/releases/download/v0.8/ImagingOntologyCombined.ttl.gz' RUN apk update && apk add \ diff --git a/.docker/entrypoint.sh b/.docker/entrypoint.sh index 9bccf14..b54d274 100644 --- a/.docker/entrypoint.sh +++ b/.docker/entrypoint.sh @@ -23,10 +23,12 @@ extract_file() { } # Download and extract shapes -mkdir -p "$(dirname "$SHAPES_PATH")" -TMP_FILE=$(mktemp) -curl -fsSL "$SHAPES_URL" -o "$TMP_FILE" -extract_file "$TMP_FILE" "$SHAPES_PATH" +if [ -z "$SHAPES_URL" ]; then + mkdir -p "$(dirname "$SHAPES_PATH")" + TMP_FILE=$(mktemp) + curl -fsSL "$SHAPES_URL" -o "$TMP_FILE" + extract_file "$TMP_FILE" "$SHAPES_PATH" +fi # Start webapp if enabled if [ "$1" -eq "webapp" ]; then From 1d21f6c07e1c693ead263e7e41e441262e41cb94 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 18:20:28 +0100 Subject: [PATCH 14/30] chore(docker): no exit on unbound variable --- .docker/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docker/entrypoint.sh b/.docker/entrypoint.sh index b54d274..e909bf9 100644 --- a/.docker/entrypoint.sh +++ b/.docker/entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash -set -eu +set -e extract_file() { ARCHIVE="$1" From 9112b55f5a2189b1fc94fbf71a1628472a29d7ed Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 18:36:44 +0100 Subject: [PATCH 15/30] fix: pyproject syntax --- pyproject.toml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 805f300..61676e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,12 @@ classifiers = [ license = "AGPL-3.0" license-files = ["LICEN[CS]E*"] +dependencies = [ + "rdflib", + "uvicorn", + "fastapi", +] + [project.urls] Homepage = "https://github.com/sdsc-ordes/shacl-api" Issues = "https://github.com/sdsc-ordes/shacl-api/issues" @@ -22,11 +28,6 @@ Issues = "https://github.com/sdsc-ordes/shacl-api/issues" requires = ["hatchling"] build-backend = "hatchling.build" -dependencies = [ - "rdflib", - "uvicorn", - "fastapi", -] [project.optional-dependencies] dev = [ From 2b60096cbeb3d8169510749110d754fd3f122bff Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 19:46:44 +0100 Subject: [PATCH 16/30] fix: missing deps in pyproject --- pyproject.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 61676e5..cd7abf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,7 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + [project] name = "shacl-api" version = "0.1.0" @@ -15,20 +19,16 @@ license = "AGPL-3.0" license-files = ["LICEN[CS]E*"] dependencies = [ + "fastapi", + "python-multipart", "rdflib", "uvicorn", - "fastapi", ] [project.urls] Homepage = "https://github.com/sdsc-ordes/shacl-api" Issues = "https://github.com/sdsc-ordes/shacl-api/issues" -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - - [project.optional-dependencies] dev = [ "ruff", From 522fcb4208d42cde330a63e66ec3a30e91cd6d7e Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 19:47:24 +0100 Subject: [PATCH 17/30] fix(docker): downloaded file paths --- .docker/Dockerfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 333c5d9..640d04b 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -15,9 +15,10 @@ LABEL org.opencontainers.image.version ${VERSION} # Environment ENV API_PORT=15400 ENV WEBAPP_PORT=8501 -ENV SHAPES_PATH=/data/shapes.ttl +ENV SHAPES_PATH=/shacl/data/shapes.ttl # unset or set to "" to disable shapes download ENV SHAPES_URL='https://github.com/sdsc-ordes/imaging-plaza-ontology/releases/download/v0.8/ImagingOntologyCombined.ttl.gz' +ENV PATH="${PATH}:/home/appuser/.local/bin" RUN apk update && apk add \ bash \ @@ -36,13 +37,14 @@ RUN groupadd --gid 1000 appuser \ USER appuser WORKDIR /shacl COPY . . +COPY .docker/entrypoint.sh /entrypoint.sh ENV PYTHONUNBUFFERED=1 RUN pip3 install --break-system-packages -e . EXPOSE ${API_PORT} -ENTRYPOINT ["bash", "/shacl/.docker/entrypoint.sh", "api"] +ENTRYPOINT ["bash", "/entrypoint.sh", "api"] ### API + Webapp build-stage ### FROM api AS webapp @@ -57,9 +59,8 @@ USER appuser RUN pip3 install --break-system-packages -e '.[webapp]' -ENV PATH="${PATH}:/home/appuser/.local/bin" EXPOSE ${WEBAPP_PORT} -ENTRYPOINT ["bash", "/shacl/.docker/entrypoint.sh", "webapp"] +ENTRYPOINT ["bash", "/entrypoint.sh", "webapp"] From 5a8a176350fafd4b0655788035d6248c0dbb20f5 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 19:47:37 +0100 Subject: [PATCH 18/30] fix(compose): relative mount point --- .docker/compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docker/compose.yml b/.docker/compose.yml index 5430bb8..9c4a97f 100644 --- a/.docker/compose.yml +++ b/.docker/compose.yml @@ -10,4 +10,4 @@ services: - "8001:15400" - "8501:8501" volumes: - - .:/shacl + - ../src:/shacl/src From ce3bb87dbacb91e6c1b674551595c3370150c3e5 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 19:48:03 +0100 Subject: [PATCH 19/30] fix(docker): update uvicorn module path in entrypoint --- .docker/entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.docker/entrypoint.sh b/.docker/entrypoint.sh index e909bf9..c77c61d 100644 --- a/.docker/entrypoint.sh +++ b/.docker/entrypoint.sh @@ -31,10 +31,10 @@ if [ -z "$SHAPES_URL" ]; then fi # Start webapp if enabled -if [ "$1" -eq "webapp" ]; then +if [ "$1" == "webapp" ]; then nohup streamlit run /shacl/src/shacl-api/webapp.py --server.port "${WEBAPP_PORT:-8501}" & fi # Start API server -python3 -m uvicorn shacl_api.server:app --host 0.0.0.0 --port "${API_PORT:-15400}" +python3 -m uvicorn src.shacl-api.server:app --host 0.0.0.0 --port "${API_PORT:-15400}" From dd1ac7e1f99c1455540302d84ff0a7d12db788dc Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 19:48:27 +0100 Subject: [PATCH 20/30] fix: saner default shapes path in webserver --- src/shacl-api/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shacl-api/server.py b/src/shacl-api/server.py index 67e9189..48ed58a 100644 --- a/src/shacl-api/server.py +++ b/src/shacl-api/server.py @@ -10,7 +10,7 @@ import json app = FastAPI() -SHAPES_PATH = os.getenv("SHAPES_PATH", "/app/shapesfile.ttl") +SHAPES_PATH = os.getenv("SHAPES_PATH", "shapesfile.ttl") @app.get("/") def index(): From 17e4384e2028deed8c3c2fbd1579f1901cf303bc Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 19:51:32 +0100 Subject: [PATCH 21/30] style: ruff format --- src/shacl-api/server.py | 272 ++++++++++++++++++++++------------------ src/shacl-api/webapp.py | 45 ++++--- tests/inference.py | 16 ++- tests/validation.py | 30 ++++- 4 files changed, 212 insertions(+), 151 deletions(-) diff --git a/src/shacl-api/server.py b/src/shacl-api/server.py index 48ed58a..df4b7d3 100644 --- a/src/shacl-api/server.py +++ b/src/shacl-api/server.py @@ -12,162 +12,184 @@ app = FastAPI() SHAPES_PATH = os.getenv("SHAPES_PATH", "shapesfile.ttl") + @app.get("/") def index(): - return {"title": "Hello, welcome to the SHACL API v0.0.5. Compatible with Ontology v0.8."} + return { + "title": "Hello, welcome to the SHACL API v0.0.5. Compatible with Ontology v0.8." + } + @app.post("/validate-jsonld") -async def validateJsonLD(item: Request, - jsonInput: bool | None = None, - jsonOutput: bool | None = None, - verbose: bool | None = None): - - data = await item.json() - data = data["data"] - if verbose: - print(data) - - if jsonInput: - if verbose: - print("Assuming json") - print(f"Type: {type(data)}") - print("Cleaning newlines") - data = json.dumps(data) - else: - if verbose: - print("Assuming string") - print(f"Type: {type(data)}") - print("Cleaning newlines") - data = data.splitlines() - data = ' '.join(data) - - # This is generating the datafile necesary to run inference. - graph = Graph() - graph.parse(data=data, format="json-ld") - - SCHEMA = Namespace("http://schema.org/") - graph.namespace_manager.bind('schema', SCHEMA, override=True, replace=True) - ttl_input = str(graph.serialize(destination='datafile.ttl',format='turtle')) - - - output = subprocess.run(["shaclvalidate.sh", "-datafile", "datafile.ttl", "-shapesfile", SHAPES_PATH], stdout=subprocess.PIPE) - - os.remove("datafile.ttl") - - ### Read and provide JSON-LD - graph = Graph() - graph.parse(data=output.stdout, format="turtle") - SCHEMA = Namespace("http://schema.org/") - graph.namespace_manager.bind('schema', SCHEMA, override=True, replace=True) - jsonld = str(graph.serialize(format='json-ld')) - - if jsonOutput: - jsonld = json.loads(jsonld) - - return {"jsonldOutput": jsonld, - "ttlOutput": output.stdout, - "jsonldInput": data, - "ttlInput": ttl_input} +async def validateJsonLD( + item: Request, + jsonInput: bool | None = None, + jsonOutput: bool | None = None, + verbose: bool | None = None, +): + data = await item.json() + data = data["data"] + if verbose: + print(data) + + if jsonInput: + if verbose: + print("Assuming json") + print(f"Type: {type(data)}") + print("Cleaning newlines") + data = json.dumps(data) + else: + if verbose: + print("Assuming string") + print(f"Type: {type(data)}") + print("Cleaning newlines") + data = data.splitlines() + data = " ".join(data) + + # This is generating the datafile necesary to run inference. + graph = Graph() + graph.parse(data=data, format="json-ld") + + SCHEMA = Namespace("http://schema.org/") + graph.namespace_manager.bind("schema", SCHEMA, override=True, replace=True) + ttl_input = str(graph.serialize(destination="datafile.ttl", format="turtle")) + + output = subprocess.run( + ["shaclvalidate.sh", "-datafile", "datafile.ttl", "-shapesfile", SHAPES_PATH], + stdout=subprocess.PIPE, + ) + + os.remove("datafile.ttl") + + ### Read and provide JSON-LD + graph = Graph() + graph.parse(data=output.stdout, format="turtle") + SCHEMA = Namespace("http://schema.org/") + graph.namespace_manager.bind("schema", SCHEMA, override=True, replace=True) + jsonld = str(graph.serialize(format="json-ld")) + + if jsonOutput: + jsonld = json.loads(jsonld) + + return { + "jsonldOutput": jsonld, + "ttlOutput": output.stdout, + "jsonldInput": data, + "ttlInput": ttl_input, + } @app.post("/inference-jsonld") -async def inferenceJsonLD(item: Request, - jsonInput: bool | None = None, - jsonOutput: bool | None = None, - verbose: bool | None = None): - - data = await item.json() - data = data["data"] - if verbose: - print(data) - - if jsonInput: - if verbose: - print("Assuming json") - print(f"Type: {type(data)}") - print("Cleaning newlines") - data = json.dumps(data) - else: - if verbose: - print("Assuming string") - print(f"Type: {type(data)}") - print("Cleaning newlines") - data = data.splitlines() - data = ' '.join(data) - - # This is generating the datafile necesary to run inference. - graph = Graph() - graph.parse(data=data, format="json-ld") - - SCHEMA = Namespace("http://schema.org/") - graph.namespace_manager.bind('schema', SCHEMA, override=True, replace=True) - ttl_input= str(graph.serialize(destination='datafile.ttl',format='turtle')) - - output = subprocess.run(["shaclinfer.sh", "-datafile", "datafile.ttl", "-shapesfile", "SHAPES_PATH"], stdout=subprocess.PIPE) - - os.remove("datafile.ttl") - - ### Read and provide JSON-LD - graph = Graph() - graph.parse(data=output.stdout, format="turtle") - SCHEMA = Namespace("http://schema.org/") - graph.namespace_manager.bind('schema', SCHEMA, override=True, replace=True) - jsonld = str(graph.serialize(format='json-ld')) - - if jsonOutput: - jsonld = json.loads(jsonld) - - return {"jsonldOutput": jsonld, - "ttlOutput": output.stdout, - "jsonldInput": data, - "ttlInput": ttl_input} +async def inferenceJsonLD( + item: Request, + jsonInput: bool | None = None, + jsonOutput: bool | None = None, + verbose: bool | None = None, +): + data = await item.json() + data = data["data"] + if verbose: + print(data) + + if jsonInput: + if verbose: + print("Assuming json") + print(f"Type: {type(data)}") + print("Cleaning newlines") + data = json.dumps(data) + else: + if verbose: + print("Assuming string") + print(f"Type: {type(data)}") + print("Cleaning newlines") + data = data.splitlines() + data = " ".join(data) + + # This is generating the datafile necesary to run inference. + graph = Graph() + graph.parse(data=data, format="json-ld") + + SCHEMA = Namespace("http://schema.org/") + graph.namespace_manager.bind("schema", SCHEMA, override=True, replace=True) + ttl_input = str(graph.serialize(destination="datafile.ttl", format="turtle")) + + output = subprocess.run( + ["shaclinfer.sh", "-datafile", "datafile.ttl", "-shapesfile", "SHAPES_PATH"], + stdout=subprocess.PIPE, + ) + os.remove("datafile.ttl") + + ### Read and provide JSON-LD + graph = Graph() + graph.parse(data=output.stdout, format="turtle") + SCHEMA = Namespace("http://schema.org/") + graph.namespace_manager.bind("schema", SCHEMA, override=True, replace=True) + jsonld = str(graph.serialize(format="json-ld")) + + if jsonOutput: + jsonld = json.loads(jsonld) + + return { + "jsonldOutput": jsonld, + "ttlOutput": output.stdout, + "jsonldInput": data, + "ttlInput": ttl_input, + } @app.post("/validate") -def validate(datafile:str=Form(...), - shapesfile:str=Form(...)): - +def validate(datafile: str = Form(...), shapesfile: str = Form(...)): datafile = base64.b64decode(str.encode(datafile)) shapesfile = base64.b64decode(str.encode(shapesfile)) # TODO: use tempfile package - with open("datafile.ttl", 'wb') as f: - f.write(datafile) - - with open("shapesfile.ttl", 'wb') as f: - f.write(shapesfile) - - output = subprocess.run(["shaclvalidate.sh", "-datafile", "datafile.ttl", "-shapesfile", "shapesfile.ttl"], stdout=subprocess.PIPE) + with open("datafile.ttl", "wb") as f: + f.write(datafile) + + with open("shapesfile.ttl", "wb") as f: + f.write(shapesfile) + + output = subprocess.run( + [ + "shaclvalidate.sh", + "-datafile", + "datafile.ttl", + "-shapesfile", + "shapesfile.ttl", + ], + stdout=subprocess.PIPE, + ) os.remove("datafile.ttl") os.remove("shapesfile.ttl") - with open("validationTest.ttl", 'wb') as f: - f.write(output.stdout) + with open("validationTest.ttl", "wb") as f: + f.write(output.stdout) return {"output": output.stdout} @app.post("/inference") -def inference(datafile:str=Form(...), - shapesfile:str=Form(...)): - +def inference(datafile: str = Form(...), shapesfile: str = Form(...)): datafile = base64.b64decode(str.encode(datafile)) shapesfile = base64.b64decode(str.encode(shapesfile)) print(datafile) # TODO: use tempfile package - with open("datafile.ttl", 'wb') as f: - f.write(datafile) - - with open("shapesfile.ttl", 'wb') as f: - f.write(shapesfile) + with open("datafile.ttl", "wb") as f: + f.write(datafile) + + with open("shapesfile.ttl", "wb") as f: + f.write(shapesfile) - output = subprocess.run(["shaclinfer.sh", "-datafile", "datafile.ttl", "-shapesfile", "shapesfile.ttl"], stdout=subprocess.PIPE) + output = subprocess.run( + ["shaclinfer.sh", "-datafile", "datafile.ttl", "-shapesfile", "shapesfile.ttl"], + stdout=subprocess.PIPE, + ) os.remove("datafile.ttl") os.remove("shapesfile.ttl") - with open("validationTest.ttl", 'wb') as f: - f.write(output.stdout) + with open("validationTest.ttl", "wb") as f: + f.write(output.stdout) return {"output": output.stdout} diff --git a/src/shacl-api/webapp.py b/src/shacl-api/webapp.py index 95ba5bb..6fee617 100644 --- a/src/shacl-api/webapp.py +++ b/src/shacl-api/webapp.py @@ -1,27 +1,31 @@ import os import streamlit as st -import requests +import requests import base64 -# This interface is for uploading the files and getting the files back + +# This interface is for uploading the files and getting the files back API_PORT = os.getenv("API_PORT", 15400) st.set_page_config(layout="wide") -st.image("https://datascience.ch/wp-content/uploads/2020/09/logo-SDSC-transparent.png", width=200) +st.image( + "https://datascience.ch/wp-content/uploads/2020/09/logo-SDSC-transparent.png", + width=200, +) st.title("shaclAPI") -cola, colb, colc, _,_,_,_ = st.columns(7) +cola, colb, colc, _, _, _, _ = st.columns(7) with cola: st.write("Validation and Inference") with colb: - option = st.radio("Which analysis would you like to do?", - ("Validation", "Inference")) - + option = st.radio( + "Which analysis would you like to do?", ("Validation", "Inference") + ) + with colc: - port = st.radio("Which port is the API using?", - (f"{API_PORT}", "8000")) + port = st.radio("Which port is the API using?", (f"{API_PORT}", "8000")) st.markdown("""---""") @@ -50,9 +54,8 @@ st.markdown("## SHACL Output") calculate = st.button("Calculate") - if calculate: - payload = {"datafile": datafile64, - "shapesfile": shapesfile64} + if calculate: + payload = {"datafile": datafile64, "shapesfile": shapesfile64} if option == "Validation": url = f"http://127.0.0.1:{port}/validate" @@ -62,9 +65,9 @@ r = requests.post(url, data=payload) output = r.json()["output"] with st.expander("See Output"): - st.code(output) #line_numbers + st.code(output) # line_numbers - st.download_button('Download TTL', output, file_name='output.ttl') + st.download_button("Download TTL", output, file_name="output.ttl") st.markdown("""---""") @@ -74,7 +77,8 @@ with col4: st.markdown("## How to use the API in python?") with st.expander("See Code"): - st.code(""" + st.code( + """ import requests import base64 with open('../tests/tests-files/val_imagingID.ttl', 'rb') as file: @@ -94,15 +98,20 @@ output = r.json()["output"] - """, language="python") + """, + language="python", + ) with col5: st.markdown("## How to use the API in javascript?") with st.expander("See Code"): - st.code(""" + st.code( + """ SOON - """, language="javascript") + """, + language="javascript", + ) st.markdown("""---""") st.markdown("Developed with <3 by ORDES SDSC Team.") diff --git a/tests/inference.py b/tests/inference.py index c3bd35a..b048ad8 100644 --- a/tests/inference.py +++ b/tests/inference.py @@ -1,10 +1,20 @@ import subprocess + def runInferenceTest(): - output = subprocess.run(["shaclinfer.sh", "-datafile", "tests-files/inf_imagingID.ttl", "-shapesfile", "tests-files/inf_ImagingOntologyShapes.ttl"], stdout=subprocess.PIPE) + output = subprocess.run( + [ + "shaclinfer.sh", + "-datafile", + "tests-files/inf_imagingID.ttl", + "-shapesfile", + "tests-files/inf_ImagingOntologyShapes.ttl", + ], + stdout=subprocess.PIPE, + ) - with open("inferenceTest.ttl", 'wb') as f: + with open("inferenceTest.ttl", "wb") as f: f.write(output.stdout) -runInferenceTest() \ No newline at end of file +runInferenceTest() diff --git a/tests/validation.py b/tests/validation.py index d557f0c..9d43ba1 100644 --- a/tests/validation.py +++ b/tests/validation.py @@ -1,17 +1,37 @@ import subprocess + def runValidationTest(): - output = subprocess.run(["shaclvalidate.sh", "-datafile", "tests-files/val_imagingID.ttl", "-shapesfile", "tests-files/val_ImagingOntologyShapes.ttl"], stdout=subprocess.PIPE) + output = subprocess.run( + [ + "shaclvalidate.sh", + "-datafile", + "tests-files/val_imagingID.ttl", + "-shapesfile", + "tests-files/val_ImagingOntologyShapes.ttl", + ], + stdout=subprocess.PIPE, + ) - with open("validationTest.ttl", 'wb') as f: + with open("validationTest.ttl", "wb") as f: f.write(output.stdout) + def runFailedInferenceTest(): - output = subprocess.run(["shaclvalidate.sh", "-datafile", "tests-files/val_fail_imagingID.ttl", "-shapesfile", "tests-files/val_fail_ImagingOntologyShapes.ttl"], stdout=subprocess.PIPE) + output = subprocess.run( + [ + "shaclvalidate.sh", + "-datafile", + "tests-files/val_fail_imagingID.ttl", + "-shapesfile", + "tests-files/val_fail_ImagingOntologyShapes.ttl", + ], + stdout=subprocess.PIPE, + ) - with open("validationFailTest.ttl", 'wb') as f: + with open("validationFailTest.ttl", "wb") as f: f.write(output.stdout) runValidationTest() -runFailedInferenceTest() \ No newline at end of file +runFailedInferenceTest() From 56eea125a6554455b8d2a530dfeee3d92fcc22e0 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 19:51:49 +0100 Subject: [PATCH 22/30] chore(make): format recipe --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 3d6a18c..c0527fd 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,10 @@ docker-compose-up: ## Run the Docker Compose stack -f .docker/compose.yml \ up --build +.PHONY: format +format: ## Format python code + ruff format . + .PHONY: help help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' From 4aa7b3d6b552b2d26033af6bfcfbd36ea3a08beb Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 19:57:05 +0100 Subject: [PATCH 23/30] chore(make): add install recipe --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c0527fd..27adabb 100644 --- a/Makefile +++ b/Makefile @@ -31,8 +31,12 @@ docker-compose-up: ## Run the Docker Compose stack up --build .PHONY: format -format: ## Format python code - ruff format . +format: install ## Format python code + ruff format src + +.PHONY: install +install: ## Setup project for development + pip install -e '.[webapp,test,dev]' .PHONY: help help: From 17f134b4759bbb852fe4516892acd22d9466ad08 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 20:03:26 +0100 Subject: [PATCH 24/30] fix: matching default value for SHAPES_PATH between env file and dockerfile --- .example.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.example.env b/.example.env index 55c3383..fc5f7a5 100644 --- a/.example.env +++ b/.example.env @@ -1,4 +1,4 @@ WEBAPP_PORT=8501 API_PORT=15400 -SHAPES_PATH=/data/shapes.ttl +SHAPES_PATH=/shacl/data/shapes.ttl SHAPES_URL='https://github.com/sdsc-ordes/imaging-plaza-ontology/releases/download/v0.8/ImagingOntologyCombined.ttl.gz' From 29240e288a6c3d51cbd0ae6512a2668a4e53068d Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 20:13:09 +0100 Subject: [PATCH 25/30] docs(readme): update usage instructions --- README.md | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d0d6648..8823692 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,33 @@ # shaclAPI -Web server wrapping the TopBraid Shacl API tool. -Implementated for Imaging Plaza. +Web server for RDF data validation, wrapping the TopBraid Shacl API tool. Based on https://github.com/SDSC-ORD/shacl ## How to use it? +### With docker -## How to use docker? +We provide a helper make recipe to build two docker images, a small "headless" version with only the REST server, and a larger image that bundles the REST server and a streamlit web application. These two images are differentiated by their tag: `` vs `-webapp`. ``` -docker build -t sdsc-ordes/shacl-api:latest . +make docker-build ``` -``` -docker run -it --rm -p 8000:15400 -p 8501:8501 sdsc-ordes/shacl-api:latest -docker run -it --rm -p 7200:15400 -p 3000:8501 sdsc-ordes/shacl-api:latest -``` +The docker images can be run as follows: ``` -docker-compose up # add -d for detached +# Only REST API +docker run -it --rm -p 8000:15400 sdsc-ordes/shacl-api:latest +# REST API + web server +docker run -it --rm -p 8000:15400 -p 8501:8501 sdsc-ordes/shacl-api:latest-webapp ``` -## LOGS -``` -if [ $1 == validate ] ; then - set -- shaclvalidate.sh "$@" -elif [ $1 == infer ] ; then - set -- shaclinfer.sh "$@" -``` +## With docker compose + +For development, it may be more convenient to use our docker compose stack. ``` -root@cbb169b97823:/# shaclvalidate.sh -Missing -datafile, e.g.: -datafile myfile.ttl -root@cbb169b97823:/# shaclinfer.sh -Missing -datafile, e.g.: -datafile myfile.ttl +make docker-compose-up ``` From d68cef97f02c140f9efefd6a0575f84a7a263662 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 20:14:41 +0100 Subject: [PATCH 26/30] chore(make): add placeholder test recipe --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 27adabb..331c2b0 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,10 @@ format: install ## Format python code install: ## Setup project for development pip install -e '.[webapp,test,dev]' +.PHONY: test +test: install ## Run unit tests + pytest + .PHONY: help help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' From 445d4051eb4fff8bd31132c328b8b3ca882964f1 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 20:45:21 +0100 Subject: [PATCH 27/30] chore(make): add lint recipe --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 331c2b0..73e609d 100644 --- a/Makefile +++ b/Makefile @@ -34,10 +34,15 @@ docker-compose-up: ## Run the Docker Compose stack format: install ## Format python code ruff format src +.PHONY: lint +lint: install ## Lint python code + ruff check src + .PHONY: install install: ## Setup project for development pip install -e '.[webapp,test,dev]' + .PHONY: test test: install ## Run unit tests pytest From c45ba6fb881184bdc0c4c48fbf3899a3a615db2a Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 20:45:32 +0100 Subject: [PATCH 28/30] style: remove unused imports --- src/shacl-api/server.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/shacl-api/server.py b/src/shacl-api/server.py index df4b7d3..9dacc6c 100644 --- a/src/shacl-api/server.py +++ b/src/shacl-api/server.py @@ -1,11 +1,10 @@ from fastapi import FastAPI, Request, Form -from fastapi.responses import JSONResponse import subprocess import os import base64 from rdflib import Graph -from rdflib.namespace import Namespace, NamespaceManager +from rdflib.namespace import Namespace import json From d7eca88e8e5be68b1abf79f36d45a222b1f3ec98 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 20:59:18 +0100 Subject: [PATCH 29/30] refactor: .docker/ -> tools/docker --- Makefile | 6 +++--- {.docker => tools/docker}/Dockerfile | 2 +- {.docker => tools/docker}/compose.yml | 6 +++--- {.docker => tools/docker}/entrypoint.sh | 0 4 files changed, 7 insertions(+), 7 deletions(-) rename {.docker => tools/docker}/Dockerfile (96%) rename {.docker => tools/docker}/compose.yml (63%) rename {.docker => tools/docker}/entrypoint.sh (100%) diff --git a/Makefile b/Makefile index 73e609d..bb51924 100644 --- a/Makefile +++ b/Makefile @@ -7,13 +7,13 @@ docker-build: ## Build Docker images @echo "🐋 Building docker image" $(CONTAINER_RUNTIME) build \ - -f .docker/Dockerfile \ + -f tools/docker/Dockerfile \ -t $(IMAGE):$(VERSION) \ --build-arg VERSION=$(VERSION) \ --target api . $(CONTAINER_RUNTIME) build \ - -f .docker/Dockerfile \ + -f tools/docker/Dockerfile \ -t $(IMAGE):$(VERSION)-webapp \ --build-arg VERSION=$(VERSION) \ --target webapp . @@ -27,7 +27,7 @@ docker-push: docker-build ## Push Docker images docker-compose-up: ## Run the Docker Compose stack @echo "🐋 Running docker-compose" $(CONTAINER_RUNTIME) compose \ - -f .docker/compose.yml \ + -f tools/docker/compose.yml \ up --build .PHONY: format diff --git a/.docker/Dockerfile b/tools/docker/Dockerfile similarity index 96% rename from .docker/Dockerfile rename to tools/docker/Dockerfile index 640d04b..f3bc53e 100644 --- a/.docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -37,7 +37,7 @@ RUN groupadd --gid 1000 appuser \ USER appuser WORKDIR /shacl COPY . . -COPY .docker/entrypoint.sh /entrypoint.sh +COPY ./tools/docker/entrypoint.sh /entrypoint.sh ENV PYTHONUNBUFFERED=1 RUN pip3 install --break-system-packages -e . diff --git a/.docker/compose.yml b/tools/docker/compose.yml similarity index 63% rename from .docker/compose.yml rename to tools/docker/compose.yml index 9c4a97f..535e2fd 100644 --- a/.docker/compose.yml +++ b/tools/docker/compose.yml @@ -3,11 +3,11 @@ services: environment: env_file: .example.env build: - context: .. - dockerfile: .docker/Dockerfile + context: ../.. + dockerfile: ./tools/docker/Dockerfile target: webapp ports: - "8001:15400" - "8501:8501" volumes: - - ../src:/shacl/src + - ../../src:/shacl/src diff --git a/.docker/entrypoint.sh b/tools/docker/entrypoint.sh similarity index 100% rename from .docker/entrypoint.sh rename to tools/docker/entrypoint.sh From 0e8a86799379ab3e5940e147be851aeb8d49b7e1 Mon Sep 17 00:00:00 2001 From: cmdoret Date: Fri, 21 Feb 2025 21:03:33 +0100 Subject: [PATCH 30/30] refactor: examples/bench -> examples/{notebooks,data} --- examples/{bench => data}/example.json | 0 examples/{bench => data}/example.ttl | 0 examples/{bench => data}/test.json | 0 examples/{bench => notebooks}/SPAQLrequests.ipynb | 0 examples/{bench => notebooks}/SPARQLConstructor.ipynb | 0 examples/{bench => notebooks}/jsonld-ttl.ipynb | 0 examples/{bench => notebooks}/requests.ipynb | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename examples/{bench => data}/example.json (100%) rename examples/{bench => data}/example.ttl (100%) rename examples/{bench => data}/test.json (100%) rename examples/{bench => notebooks}/SPAQLrequests.ipynb (100%) rename examples/{bench => notebooks}/SPARQLConstructor.ipynb (100%) rename examples/{bench => notebooks}/jsonld-ttl.ipynb (100%) rename examples/{bench => notebooks}/requests.ipynb (100%) diff --git a/examples/bench/example.json b/examples/data/example.json similarity index 100% rename from examples/bench/example.json rename to examples/data/example.json diff --git a/examples/bench/example.ttl b/examples/data/example.ttl similarity index 100% rename from examples/bench/example.ttl rename to examples/data/example.ttl diff --git a/examples/bench/test.json b/examples/data/test.json similarity index 100% rename from examples/bench/test.json rename to examples/data/test.json diff --git a/examples/bench/SPAQLrequests.ipynb b/examples/notebooks/SPAQLrequests.ipynb similarity index 100% rename from examples/bench/SPAQLrequests.ipynb rename to examples/notebooks/SPAQLrequests.ipynb diff --git a/examples/bench/SPARQLConstructor.ipynb b/examples/notebooks/SPARQLConstructor.ipynb similarity index 100% rename from examples/bench/SPARQLConstructor.ipynb rename to examples/notebooks/SPARQLConstructor.ipynb diff --git a/examples/bench/jsonld-ttl.ipynb b/examples/notebooks/jsonld-ttl.ipynb similarity index 100% rename from examples/bench/jsonld-ttl.ipynb rename to examples/notebooks/jsonld-ttl.ipynb diff --git a/examples/bench/requests.ipynb b/examples/notebooks/requests.ipynb similarity index 100% rename from examples/bench/requests.ipynb rename to examples/notebooks/requests.ipynb