Skip to content

Commit b8cc976

Browse files
authored
Merge pull request #2 from KatKatKateryna/kate/resources
Kate/resources
2 parents 6c7d15c + 87a8857 commit b8cc976

31 files changed

+4349
-793
lines changed

models/ConfigData.py

Lines changed: 78 additions & 298 deletions
Large diffs are not rendered by default.

models/top_level/LoggingConfig.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class LoggingLevel(Enum):
1414

1515
# data classes
1616
@dataclass(kw_only=True)
17-
class RotationConfig:
17+
class LoggingRotationConfig:
1818
# Not currently used in the UI
1919
mode: str | None = None
2020
when: str | None = None
@@ -29,10 +29,10 @@ class LoggingConfig:
2929

3030
# fields with default values:
3131
level: LoggingLevel = field(default_factory=lambda: LoggingLevel.ERROR)
32-
logfile: str = field(default="")
3332

3433
# optional fields:
34+
logfile: str | None = None
3535
logformat: str | None = None
3636
dateformat: str | None = None
3737
# TODO: Not currently used in the UI
38-
# rotation: RotationConfig | None = None
38+
# rotation: LoggingRotationConfig | None = None

models/top_level/MetadataConfig.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44

55

66
# records
7-
class KeywordType(Enum):
7+
class MetadataKeywordTypeEnum(Enum):
88
DISCIPLINE = "discipline"
99
TEMPORAL = "temporal"
1010
PLACE = "place"
1111
THEME = "theme"
1212
STRATUM = "stratum"
1313

1414

15-
class Role(Enum):
15+
class MetadataRoleEnum(Enum):
1616
AUTHOR = "author"
1717
COAUTHOR = "coAuthor"
1818
COLLABORATOR = "collaborator"
@@ -37,31 +37,33 @@ class Role(Enum):
3737

3838
# data classes
3939
@dataclass(kw_only=True)
40-
class IdentificationConfig:
40+
class MetadataIdentificationConfig:
4141
title: str | dict = field(default_factory=lambda: "")
4242
description: str | dict = field(default_factory=lambda: "")
4343
keywords: list | dict = field(default_factory=lambda: [])
44-
keywords_type: KeywordType = field(default_factory=lambda: KeywordType.THEME)
44+
keywords_type: MetadataKeywordTypeEnum = field(
45+
default_factory=lambda: MetadataKeywordTypeEnum.THEME
46+
)
4547
terms_of_service: str = field(
4648
default="https://creativecommons.org/licenses/by/4.0/"
4749
)
4850
url: str = field(default="https://example.org")
4951

5052

5153
@dataclass(kw_only=True)
52-
class LicenseConfig:
54+
class MetadataLicenseConfig:
5355
name: str = field(default="CC-BY 4.0 license")
5456
url: str = field(default="https://creativecommons.org/licenses/by/4.0/")
5557

5658

5759
@dataclass(kw_only=True)
58-
class ProviderConfig:
60+
class MetadataProviderConfig:
5961
name: str = field(default="Organization Name")
6062
url: str = field(default="https://pygeoapi.io")
6163

6264

6365
@dataclass(kw_only=True)
64-
class ContactConfig:
66+
class MetadataContactConfig:
6567
name: str = field(default="Lastname, Firstname")
6668
position: str = field(default="Position Title")
6769
address: str = field(default="Mailing Address")
@@ -75,19 +77,27 @@ class ContactConfig:
7577
url: str = field(default="Contact URL")
7678
hours: str = field(default="Mo-Fr 08:00-17:00")
7779
instructions: str = field(default="During hours of service. Off on weekends.")
78-
role: Role = field(default_factory=lambda: Role.POINTOFCONTACT)
80+
role: MetadataRoleEnum = field(
81+
default_factory=lambda: MetadataRoleEnum.POINTOFCONTACT
82+
)
7983

8084

8185
@dataclass(kw_only=True)
8286
class MetadataConfig:
8387
"""Placeholder class for Metadata configuration data."""
8488

85-
identification: IdentificationConfig = field(
86-
default_factory=lambda: IdentificationConfig()
89+
identification: MetadataIdentificationConfig = field(
90+
default_factory=lambda: MetadataIdentificationConfig()
91+
)
92+
license: MetadataLicenseConfig = field(
93+
default_factory=lambda: MetadataLicenseConfig()
94+
)
95+
provider: MetadataProviderConfig = field(
96+
default_factory=lambda: MetadataProviderConfig()
97+
)
98+
contact: MetadataContactConfig = field(
99+
default_factory=lambda: MetadataContactConfig()
87100
)
88-
license: LicenseConfig = field(default_factory=lambda: LicenseConfig())
89-
provider: ProviderConfig = field(default_factory=lambda: ProviderConfig())
90-
contact: ContactConfig = field(default_factory=lambda: ContactConfig())
91101

92102
def get_invalid_properties(self):
93103
"""Checks the values of mandatory fields: identification (title, description, keywords)."""
Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,128 @@
11
from dataclasses import dataclass, field
2+
from datetime import datetime
23
from enum import Enum
34

4-
from .utils import InlineList
5+
from .providers import ProviderPostgresql, ProviderMvtProxy, ProviderWmsFacade
6+
from .utils import (
7+
InlineList,
8+
bbox_from_list,
9+
get_enum_value_from_string,
10+
is_valid_string,
11+
)
12+
from .providers.records import CrsAuthorities
13+
from .providers.records import Languages
514

615

716
# records
8-
class ResourceTypes(Enum):
17+
class ResourceTypesEnum(Enum):
918
COLLECTION = "collection"
1019
STAC = "stac-collection"
1120

1221

13-
class ProviderTypes(Enum):
14-
FEATURE = "feature"
15-
MAP = "map"
16-
TILE = "tile"
22+
class ResourceVisibilityEnum(Enum):
23+
NONE = ""
24+
DEFAULT = "default"
25+
HIDDEN = "hidden"
1726

1827

1928
# data classes
2029
@dataclass(kw_only=True)
21-
class ProviderTemplate:
22-
"""Class to represent a Provider configuration template."""
30+
class ResourceLinkTemplate:
31+
"""Class to represent a Link configuration template."""
2332

24-
type: ProviderTypes
25-
name: str
26-
data: str
33+
type: str = ""
34+
rel: str = ""
35+
href: str = ""
2736

28-
# optional fields:
29-
id_field: str | None = None
30-
title_field: str | None = None
31-
geometry: dict | None = None
32-
options: dict | None = None
33-
format: dict | None = None
37+
# optional
38+
title: str | None = None
39+
hreflang: Languages | None = None
40+
length: int | None = None
3441

3542

3643
@dataclass(kw_only=True)
37-
class LinkTemplate:
38-
"""Class to represent a Link configuration template."""
44+
class ResourceSpatialConfig:
45+
bbox: InlineList = field(default_factory=lambda: InlineList([-180, -90, 180, 90]))
46+
47+
# optional, but with assumed default value:
48+
crs: str = field(default="http://www.opengis.net/def/crs/OGC/1.3/CRS84")
49+
50+
# we need these as separate properties so that Enum class values can be set&selected in the UI
51+
@property
52+
def crs_authority(self):
53+
crs_auth_id = self.crs.split("http://www.opengis.net/def/crs/")[
54+
-1
55+
] # OGC/1.3/CRS84
56+
auth_string = "/".join(crs_auth_id.split("/")[:-1])
57+
return get_enum_value_from_string(CrsAuthorities, auth_string)
3958

40-
type: str
41-
rel: str
42-
title: str
43-
href: str
44-
hreflang: str
59+
@property
60+
def crs_id(self):
61+
return self.crs.split("/")[-1]
4562

4663

4764
@dataclass(kw_only=True)
48-
class SpatialConfig:
49-
bbox: InlineList = field(default_factory=lambda: InlineList([-180, -90, 180, 90]))
50-
crs: str = field(default="http://www.opengis.net/def/crs/OGC/1.3/CRS84")
65+
class ResourceTemporalConfig:
66+
67+
# optional
68+
begin: str | datetime | None = None
69+
end: str | datetime | None = None
70+
trs: str | None = None
5171

5272

5373
@dataclass(kw_only=True)
54-
class ExtentsConfig:
74+
class ResourceExtentsConfig:
5575
"""Class to represent Extents configuration template."""
5676

5777
# fields with default values:
58-
spatial: dict = field(default_factory=lambda: SpatialConfig())
78+
spatial: ResourceSpatialConfig = field(
79+
default_factory=lambda: ResourceSpatialConfig()
80+
)
5981

60-
# optional fields:
61-
temporal: dict | None = None
82+
# optional
83+
temporal: ResourceTemporalConfig | None = None
6284

6385

6486
@dataclass(kw_only=True)
6587
class ResourceConfigTemplate:
6688
"""Class to represent a Resource configuration template."""
6789

6890
# fields with default values:
69-
type: ResourceTypes = field(default_factory=lambda: ResourceTypes.COLLECTION)
91+
type: ResourceTypesEnum = field(
92+
default_factory=lambda: ResourceTypesEnum.COLLECTION
93+
)
7094
title: str | dict = field(default="")
7195
description: str | dict = field(default="")
7296
keywords: list | dict = field(default_factory=lambda: [])
73-
links: list[LinkTemplate] = field(default_factory=lambda: [])
74-
extents: ExtentsConfig = field(default_factory=lambda: ExtentsConfig())
75-
providers: list[ProviderTemplate] = field(default_factory=lambda: [])
97+
links: list[ResourceLinkTemplate] = field(default_factory=lambda: [])
98+
extents: ResourceExtentsConfig = field(
99+
default_factory=lambda: ResourceExtentsConfig()
100+
)
101+
# for providers, the types have to be explicitly listed so they are picked up on deserialization
102+
providers: list[
103+
ProviderPostgresql | ProviderMvtProxy | ProviderWmsFacade | dict
104+
] = field(default_factory=lambda: [])
105+
106+
# optional
107+
visibility: ResourceVisibilityEnum | None = None
108+
# limits, linked-data: ignored for now
76109

77110
# Overwriding __init__ method to pass 'instance_name' as an input but not make it an instance property
78111
# This will allow to have a clean 'asdict(class)' output without 'instance_name' in it
79112
def __init__(
80113
self,
81114
*,
82115
instance_name: str,
83-
type: str = "collection",
116+
type: ResourceTypesEnum = ResourceTypesEnum.COLLECTION,
84117
title: str = "",
85118
description: str = "",
86119
keywords: dict = None,
87-
links: list[LinkTemplate] = None,
88-
extents: ExtentsConfig = None,
89-
providers: list[ProviderTemplate] = None
120+
links: list[ResourceLinkTemplate] = None,
121+
extents: ResourceExtentsConfig = None,
122+
providers: list[
123+
ProviderPostgresql | ProviderMvtProxy | ProviderWmsFacade
124+
] = None,
125+
visibility: ResourceVisibilityEnum | None = None
90126
):
91127
self._instance_name = instance_name
92128
self.type = type
@@ -115,7 +151,38 @@ def __init__(
115151
self.links = links
116152
self.extents = extents
117153
self.providers = providers
154+
self.visibility = visibility
118155

119156
@property
120157
def instance_name(self):
121158
return self._instance_name
159+
160+
def validate_reassign_bbox(self) -> bool:
161+
"""Validates the bbox values and converts to int/float if needed."""
162+
try:
163+
self.extents.spatial.bbox = bbox_from_list(self.extents.spatial.bbox)
164+
return True
165+
except ValueError:
166+
self.extents.spatial.bbox = InlineList([-180, -90, 180, 90])
167+
return False
168+
169+
def get_invalid_properties(self):
170+
"""Checks the values of mandatory fields: identification (title, description, keywords)."""
171+
all_invalid_fields = []
172+
173+
if not isinstance(self.type, ResourceTypesEnum):
174+
all_invalid_fields.append("type")
175+
if len(self.title) == 0:
176+
all_invalid_fields.append("title")
177+
if len(self.description) == 0:
178+
all_invalid_fields.append("description")
179+
if len(self.keywords) == 0:
180+
all_invalid_fields.append("keywords")
181+
if len(self.providers) == 0:
182+
all_invalid_fields.append("providers")
183+
if not is_valid_string(self.extents.spatial.crs):
184+
all_invalid_fields.append("extents.spatial.crs")
185+
if len(self.extents.spatial.bbox) < 4:
186+
all_invalid_fields.append("extents.spatial.bbox")
187+
188+
return all_invalid_fields

0 commit comments

Comments
 (0)