Skip to content

Commit 2f9f2af

Browse files
authored
Merge pull request #55 from hydroshare/toolresource
add basic support for tool resource metadata schemas
2 parents f39a897 + 90404c5 commit 2f9f2af

14 files changed

Lines changed: 161 additions & 26 deletions

File tree

hsmodels/schemas/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@
3131
TimeSeriesMetadataInRDF,
3232
CSVFileMetadataInRDF,
3333
)
34-
from hsmodels.schemas.rdf.resource import CollectionMetadataInRDF, ResourceMap, ResourceMetadataInRDF
35-
from hsmodels.schemas.resource import CollectionMetadata, ResourceMetadata
34+
from hsmodels.schemas.rdf.resource import CollectionMetadataInRDF, ResourceMap, ResourceMetadataInRDF, WebAppMetadataInRDF
35+
from hsmodels.schemas.resource import CollectionMetadata, ResourceMetadata, WebAppMetadata
3636

3737
rdf_schemas = {
3838
ORE.ResourceMap: ResourceMap,
3939
HSTERMS.CompositeResource: ResourceMetadataInRDF,
4040
HSTERMS.CollectionResource: CollectionMetadataInRDF,
41+
HSTERMS.ToolResource: WebAppMetadataInRDF,
4142
HSTERMS.GeographicRasterAggregation: GeographicRasterMetadataInRDF,
4243
HSTERMS.GeographicFeatureAggregation: GeographicFeatureMetadataInRDF,
4344
HSTERMS.MultidimensionalAggregation: MultidimensionalMetadataInRDF,
@@ -53,6 +54,7 @@
5354
user_schemas = {
5455
ResourceMetadataInRDF: ResourceMetadata,
5556
CollectionMetadataInRDF: CollectionMetadata,
57+
WebAppMetadataInRDF: WebAppMetadata,
5658
GeographicRasterMetadataInRDF: GeographicRasterMetadata,
5759
GeographicFeatureMetadataInRDF: GeographicFeatureMetadata,
5860
MultidimensionalMetadataInRDF: MultidimensionalMetadata,

hsmodels/schemas/fields.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ class Relation(BaseMetadata):
2222

2323
type: RelationType = Field(title="Relation type", description="The type of relationship with the related resource")
2424
value: str = Field(
25-
max_length=500,
2625
title="Value",
2726
description="String expressing the Full text citation, URL link for, or description of the related resource",
2827
)
@@ -38,7 +37,7 @@ class CellInformation(BaseMetadata):
3837
model_config = ConfigDict(title='Raster Cell Metadata')
3938

4039
# TODO: Is there such a thing as "name" for CellInformation?
41-
name: str = Field(default=None, max_length=500, title="Name", description="Name of the cell information",)
40+
name: str = Field(default=None, title="Name", description="Name of the cell information",)
4241
rows: int = Field(default=None, title="Rows",
4342
description="The integer number of rows in the raster dataset",)
4443
columns: int = Field(
@@ -75,6 +74,7 @@ class Rights(BaseMetadata):
7574
default=None,
7675
title="URL",
7776
description="An object containing the URL pointing to a description of the license or rights statement",
77+
default=None
7878
)
7979

8080
@classmethod
@@ -277,7 +277,7 @@ class BandInformation(BaseMetadata):
277277

278278
model_config = ConfigDict(title='Raster Band Metadata')
279279

280-
name: str = Field(max_length=500, title="Name", description="A string containing the name of the raster band",
280+
name: str = Field(title="Name", description="A string containing the name of the raster band",
281281
)
282282
variable_name: Optional[str] = Field(
283283
default=None,
@@ -692,26 +692,26 @@ class BoxCoverage(base_models.BaseCoverage):
692692
description="A string containing a name for the place associated with the geographic coverage",
693693
)
694694
northlimit: float = Field(
695-
gt=-90,
696-
lt=90,
695+
gte=-90,
696+
lte=90,
697697
title="North limit",
698698
description="A floating point value containing the constant coordinate for the northernmost face or edge of the bounding box",
699699
)
700700
eastlimit: float = Field(
701-
gt=-180,
702-
lt=180,
701+
gte=-180,
702+
lte=180,
703703
title="East limit",
704704
description="A floating point value containing the constant coordinate for the easternmost face or edge of the bounding box",
705705
)
706706
southlimit: float = Field(
707-
gt=-90,
708-
lt=90,
707+
gte=-90,
708+
lte=90,
709709
title="South limit",
710710
description="A floating point value containing the constant coordinate for the southernmost face or edge of the bounding box",
711711
)
712712
westlimit: float = Field(
713-
gt=-180,
714-
lt=180,
713+
gte=-180,
714+
lte=180,
715715
title="West limit",
716716
description="A floating point value containing the constant coordinate for the westernmost face or edge of the bounding box",
717717
)
@@ -728,6 +728,9 @@ class BoxCoverage(base_models.BaseCoverage):
728728
@model_validator(mode='after')
729729
def compare_north_south(self):
730730
if self.northlimit < self.southlimit:
731+
if self.southlimit == 90 and self.northlimit == -90:
732+
# special case for global coverage
733+
return self
731734
raise ValueError(f"North latitude [{self.northlimit}] must be greater than or equal to South latitude [{self.southlimit}]")
732735
return self
733736

@@ -827,10 +830,10 @@ class PointCoverage(base_models.BaseCoverage):
827830
description="A string containing a name for the place associated with the geographic coverage",
828831
)
829832
east: float = Field(
830-
gt=-180, lt=180, title="East", description="The coordinate of the point location measured in the east direction"
833+
gte=-180, lte=180, title="East", description="The coordinate of the point location measured in the east direction"
831834
)
832835
north: float = Field(
833-
gt=-90, lt=90, title="North", description="The coordinate of the point location measured in the north direction"
836+
gte=-90, lte=90, title="North", description="The coordinate of the point location measured in the north direction"
834837
)
835838
units: str = Field(
836839
title="Units", description="The units applying to the unlabelled numeric values of north and east"

hsmodels/schemas/rdf/fields.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class ContributorInRDF(RDFBaseModel):
153153
phone: str = Field(default=None, json_schema_extra={"rdf_predicate": HSTERMS.phone})
154154
address: str = Field(default=None, json_schema_extra={"rdf_predicate": HSTERMS.address})
155155
organization: str = Field(default=None, json_schema_extra={"rdf_predicate": HSTERMS.organization})
156-
email: EmailStr = Field(default=None, json_schema_extra={"rdf_predicate": HSTERMS.email})
156+
email: str = Field(default=None, json_schema_extra={"rdf_predicate": HSTERMS.email})
157157
homepage: HttpUrl = Field(default=None, json_schema_extra={"rdf_predicate": HSTERMS.homepage})
158158
hydroshare_user_id: int = Field(default=None, json_schema_extra={"rdf_predicate": HSTERMS.hydroshare_user_id})
159159
ORCID: AnyUrl = Field(default=None, json_schema_extra={"rdf_predicate": HSTERMS.ORCID})

hsmodels/schemas/rdf/resource.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class BaseResource(BaseModel):
8585
awards: List[AwardInfoInRDF] = Field(json_schema_extra={"rdf_predicate": HSTERMS.awardInfo}, default=[])
8686
coverages: List[CoverageInRDF] = Field(json_schema_extra={"rdf_predicate": DC.coverage}, default=[])
8787
publisher: PublisherInRDF = Field(json_schema_extra={"rdf_predicate": DC.publisher}, default=None)
88-
citation: str = Field(json_schema_extra={"rdf_predicate": DCTERMS.bibliographicCitation})
88+
citation: str = Field(default= None, json_schema_extra={"rdf_predicate": DCTERMS.bibliographicCitation})
8989

9090
_parse_rdf_subject = model_validator(mode='before')(rdf_parse_rdf_subject)
9191
_parse_coverages = model_validator(mode='before')(parse_coverages)
@@ -130,3 +130,18 @@ class CollectionMetadataInRDF(BaseResource):
130130
@field_serializer('dc_type', 'rdf_type')
131131
def serialize_url(self, _type: URIRef, _info):
132132
return AnyUrl(_type)
133+
134+
135+
class WebAppMetadataInRDF(BaseResource):
136+
dc_type: AnyUrl = Field(
137+
json_schema_extra={"rdf_predicate": DC.type}, default=HSTERMS.ToolResource, frozen=True
138+
)
139+
rdf_type: AnyUrl = Field(
140+
json_schema_extra={"rdf_predicate": RDF.type}, frozen=True, default=HSTERMS.ToolResource
141+
)
142+
_label_literal = Literal["Web App Resource"]
143+
label: _label_literal = Field(default="Web App Resource", frozen=True, alias='label')
144+
145+
@field_serializer('dc_type', 'rdf_type')
146+
def serialize_url(self, _type: URIRef, _info):
147+
return AnyUrl(_type)

hsmodels/schemas/rdf/validators.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ def sort_creators(cls, creators):
7979
# assign creator_order to creators that don't have it
8080
creator_order_numbers = [c.creator_order for c in creators if c.creator_order is not None]
8181
if creator_order_numbers:
82-
if len(creator_order_numbers) != len(set(creator_order_numbers)):
83-
raise ValueError("creator_order values must be unique")
82+
#if len(creator_order_numbers) != len(set(creator_order_numbers)):
83+
# raise ValueError("creator_order values must be unique")
8484
max_order_number = max(creator_order_numbers)
8585
else:
8686
max_order_number = 0

hsmodels/schemas/resource.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,13 @@ class CollectionMetadata(BaseResourceMetadata):
196196
description="An object containing a URL that points to the HydroShare resource type selected from the hsterms namespace",
197197
json_schema_extra={"readOnly": True},
198198
)
199+
200+
201+
class WebAppMetadata(BaseResourceMetadata):
202+
type: Literal['ToolResource'] = Field(
203+
frozen=True,
204+
default="ToolResource",
205+
title="Resource Type",
206+
description="An object containing a URL that points to the HydroShare resource type selected from the hsterms namespace",
207+
json_schema_extra={"readOnly": True},
208+
)

hsmodels/utils.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
def to_coverage_dict(value):
55
value_dict = {}
6-
for key_value in value.split("; "):
7-
k, v = key_value.split("=")
8-
value_dict[k] = v
6+
for key_value in value.split(";"):
7+
if "=" in key_value:
8+
k, v = key_value.split("=")
9+
value_dict[k.strip()] = v.strip()
910
return value_dict
1011

1112

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
setup(
66
name='hsmodels',
7-
version='1.0.5',
7+
version='1.1.1',
88
packages=find_packages(include=['hsmodels', 'hsmodels.*', 'hsmodels.schemas.*', 'hsmodels.schemas.rdf.*'],
99
exclude=("tests",)),
1010
install_requires=[
1111
'rdflib<6.0.0',
12-
'pydantic==2.7.*',
12+
'pydantic==2.8.*',
1313
'email-validator'
1414
],
1515
url='https://github.com/hydroshare/hsmodels',

tests/data/json/webapp.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"title": "GeoTrust",
3+
"abstract": "This app is used to execute the scuint package for the MODFLOW-NWT model. During testing of the work, this app is linked to a deployed EC2 machine on AWS. Full instruction is provided at https://github.com/uva-hydroinformatics/Sciunit_HydroShare_Implementation for how a user could deploy this on AWS to reproduce this work.",
4+
"language": "eng",
5+
"subjects": [
6+
"MODFLOW-NWT-scuint"
7+
],
8+
"creators": [
9+
{
10+
"name": "Bakinam Essawy",
11+
"phone": "8034634471",
12+
"organization": "University of Virginia",
13+
"email": "btaessawy@gmail.com",
14+
"creator_order": 1,
15+
"hydroshare_user_id": 878,
16+
"identifiers": {}
17+
}
18+
],
19+
"contributors": [],
20+
"relations": [
21+
{
22+
"type": "The content of this resource is part of",
23+
"value": "Essawy, B. (2018). ModflowNwtCollection, HydroShare, http://www.hydroshare.org/resource/bf598099ed384540aaa9284b7343a717"
24+
}
25+
],
26+
"additional_metadata": {},
27+
"rights": {
28+
"statement": "This resource is shared under the Creative Commons Attribution CC BY.",
29+
"url": "http://creativecommons.org/licenses/by/4.0/"
30+
},
31+
"awards": [],
32+
"citation": "Essawy, B. (2018). GeoTrust, HydroShare, http://www.hydroshare.org/resource/126701df868e4da9872d9b533db34ae6"
33+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<rdf:RDF
3+
xmlns:hsterms="https://www.hydroshare.org/terms/"
4+
xmlns:dcterms="http://purl.org/dc/terms/"
5+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6+
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
7+
xmlns:dc="http://purl.org/dc/elements/1.1/"
8+
>
9+
<hsterms:ToolResource rdf:about="http://www.hydroshare.org/resource/126701df868e4da9872d9b533db34ae6">
10+
<dc:date>
11+
<dcterms:modified>
12+
<rdf:value>2018-06-12T17:35:56.675086+00:00</rdf:value>
13+
</dcterms:modified>
14+
</dc:date>
15+
<dc:identifier>
16+
<rdf:Description>
17+
<hsterms:hydroShareIdentifier rdf:resource="http://www.hydroshare.org/resource/126701df868e4da9872d9b533db34ae6"/>
18+
</rdf:Description>
19+
</dc:identifier>
20+
<dc:type>
21+
<rdf:Description rdf:about="https://www.hydroshare.org/terms/ToolResource">
22+
<rdfs:isDefinedBy rdf:resource="https://www.hydroshare.org/terms/"/>
23+
<rdfs:label>Web App Resource</rdfs:label>
24+
</rdf:Description>
25+
</dc:type>
26+
<dc:subject>MODFLOW-NWT-scuint</dc:subject>
27+
<dc:language>eng</dc:language>
28+
<dc:date>
29+
<dcterms:created>
30+
<rdf:value>2017-06-12T13:58:14.473992+00:00</rdf:value>
31+
</dcterms:created>
32+
</dc:date>
33+
<dc:title>GeoTrust</dc:title>
34+
<dc:description>
35+
<rdf:Description>
36+
<dcterms:abstract>This app is used to execute the scuint package for the MODFLOW-NWT model. During testing of the work, this app is linked to a deployed EC2 machine on AWS. Full instruction is provided at https://github.com/uva-hydroinformatics/Sciunit_HydroShare_Implementation for how a user could deploy this on AWS to reproduce this work.</dcterms:abstract>
37+
</rdf:Description>
38+
</dc:description>
39+
<dc:rights>
40+
<rdf:Description>
41+
<hsterms:URL rdf:resource="http://creativecommons.org/licenses/by/4.0/"/>
42+
<hsterms:rightsStatement>This resource is shared under the Creative Commons Attribution CC BY.</hsterms:rightsStatement>
43+
</rdf:Description>
44+
</dc:rights>
45+
<dc:creator>
46+
<rdf:Description>
47+
<hsterms:email>btaessawy@gmail.com</hsterms:email>
48+
<hsterms:name>Bakinam Essawy</hsterms:name>
49+
<hsterms:creatorOrder rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">1</hsterms:creatorOrder>
50+
<hsterms:hydroshare_user_id rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">878</hsterms:hydroshare_user_id>
51+
<hsterms:phone>8034634471</hsterms:phone>
52+
<hsterms:organization>University of Virginia</hsterms:organization>
53+
</rdf:Description>
54+
</dc:creator>
55+
<dc:relation>
56+
<rdf:Description>
57+
<dcterms:isPartOf>Essawy, B. (2018). ModflowNwtCollection, HydroShare, http://www.hydroshare.org/resource/bf598099ed384540aaa9284b7343a717</dcterms:isPartOf>
58+
</rdf:Description>
59+
</dc:relation>
60+
<dcterms:bibliographicCitation>Essawy, B. (2018). GeoTrust, HydroShare, http://www.hydroshare.org/resource/126701df868e4da9872d9b533db34ae6</dcterms:bibliographicCitation>
61+
</hsterms:ToolResource>
62+
</rdf:RDF>

0 commit comments

Comments
 (0)