|
1 | 1 | from dataclasses import dataclass, field |
| 2 | +from datetime import datetime |
2 | 3 | from enum import Enum |
3 | 4 |
|
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 |
5 | 14 |
|
6 | 15 |
|
7 | 16 | # records |
8 | | -class ResourceTypes(Enum): |
| 17 | +class ResourceTypesEnum(Enum): |
9 | 18 | COLLECTION = "collection" |
10 | 19 | STAC = "stac-collection" |
11 | 20 |
|
12 | 21 |
|
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" |
17 | 26 |
|
18 | 27 |
|
19 | 28 | # data classes |
20 | 29 | @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.""" |
23 | 32 |
|
24 | | - type: ProviderTypes |
25 | | - name: str |
26 | | - data: str |
| 33 | + type: str = "" |
| 34 | + rel: str = "" |
| 35 | + href: str = "" |
27 | 36 |
|
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 |
34 | 41 |
|
35 | 42 |
|
36 | 43 | @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) |
39 | 58 |
|
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] |
45 | 62 |
|
46 | 63 |
|
47 | 64 | @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 |
51 | 71 |
|
52 | 72 |
|
53 | 73 | @dataclass(kw_only=True) |
54 | | -class ExtentsConfig: |
| 74 | +class ResourceExtentsConfig: |
55 | 75 | """Class to represent Extents configuration template.""" |
56 | 76 |
|
57 | 77 | # fields with default values: |
58 | | - spatial: dict = field(default_factory=lambda: SpatialConfig()) |
| 78 | + spatial: ResourceSpatialConfig = field( |
| 79 | + default_factory=lambda: ResourceSpatialConfig() |
| 80 | + ) |
59 | 81 |
|
60 | | - # optional fields: |
61 | | - temporal: dict | None = None |
| 82 | + # optional |
| 83 | + temporal: ResourceTemporalConfig | None = None |
62 | 84 |
|
63 | 85 |
|
64 | 86 | @dataclass(kw_only=True) |
65 | 87 | class ResourceConfigTemplate: |
66 | 88 | """Class to represent a Resource configuration template.""" |
67 | 89 |
|
68 | 90 | # fields with default values: |
69 | | - type: ResourceTypes = field(default_factory=lambda: ResourceTypes.COLLECTION) |
| 91 | + type: ResourceTypesEnum = field( |
| 92 | + default_factory=lambda: ResourceTypesEnum.COLLECTION |
| 93 | + ) |
70 | 94 | title: str | dict = field(default="") |
71 | 95 | description: str | dict = field(default="") |
72 | 96 | 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 |
76 | 109 |
|
77 | 110 | # Overwriding __init__ method to pass 'instance_name' as an input but not make it an instance property |
78 | 111 | # This will allow to have a clean 'asdict(class)' output without 'instance_name' in it |
79 | 112 | def __init__( |
80 | 113 | self, |
81 | 114 | *, |
82 | 115 | instance_name: str, |
83 | | - type: str = "collection", |
| 116 | + type: ResourceTypesEnum = ResourceTypesEnum.COLLECTION, |
84 | 117 | title: str = "", |
85 | 118 | description: str = "", |
86 | 119 | 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 |
90 | 126 | ): |
91 | 127 | self._instance_name = instance_name |
92 | 128 | self.type = type |
@@ -115,7 +151,38 @@ def __init__( |
115 | 151 | self.links = links |
116 | 152 | self.extents = extents |
117 | 153 | self.providers = providers |
| 154 | + self.visibility = visibility |
118 | 155 |
|
119 | 156 | @property |
120 | 157 | def instance_name(self): |
121 | 158 | 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