Skip to content

Commit cdf5900

Browse files
authored
Merge branch 'develop' into gtc-3084_new_geostore_endpts
2 parents dc726e4 + 41091f9 commit cdf5900

File tree

11 files changed

+132
-24
lines changed

11 files changed

+132
-24
lines changed

app/models/orm/assets.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
class Asset(Base):
55
__tablename__ = "assets"
66
asset_id = db.Column(db.UUID, primary_key=True)
7-
dataset = db.Column(db.String, nullable=False)
8-
version = db.Column(db.String, nullable=False)
7+
dataset = db.Column(db.String, nullable=False, index=True)
8+
version = db.Column(db.String, nullable=False, index=True)
99
asset_type = db.Column(db.String, nullable=False)
1010
asset_uri = db.Column(db.String, nullable=False)
1111
status = db.Column(db.String, nullable=False, default="pending")
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""empty message.
2+
3+
Revision ID: 3e524ef0525f
4+
Revises: 604bf4e66c2b
5+
Create Date: 2024-12-18 00:43:46.681427
6+
"""
7+
import sqlalchemy as sa
8+
from alembic import op
9+
10+
# revision identifiers, used by Alembic.
11+
revision = "3e524ef0525f"
12+
down_revision = "604bf4e66c2b"
13+
branch_labels = None
14+
depends_on = None
15+
16+
17+
def upgrade():
18+
# ### commands auto generated by Alembic - please adjust! ###
19+
op.create_index(op.f("ix_assets_dataset"), "assets", ["dataset"], unique=False)
20+
op.create_index(op.f("ix_assets_version"), "assets", ["version"], unique=False)
21+
op.add_column("dataset_metadata", sa.Column("subtitle", sa.String(), nullable=True))
22+
op.add_column("version_metadata", sa.Column("subtitle", sa.String(), nullable=True))
23+
# ### end Alembic commands ###
24+
25+
26+
def downgrade():
27+
# ### commands auto generated by Alembic - please adjust! ###
28+
op.drop_column("version_metadata", "subtitle")
29+
op.drop_column("dataset_metadata", "subtitle")
30+
op.drop_index(op.f("ix_assets_version"), table_name="assets")
31+
op.drop_index(op.f("ix_assets_dataset"), table_name="assets")
32+
# ### end Alembic commands ###

app/models/orm/mixins.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
class MetadataMixin:
55
title = db.Column(db.String)
6+
subtitle = db.Column(db.String)
67
spatial_resolution = db.Column(db.Numeric)
78
resolution_description = db.Column(db.String)
89
geographic_coverage = db.Column(db.String)

app/models/pydantic/creation_options.py

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,24 +122,69 @@ class RasterTileSetAssetCreationOptions(StrictBaseModel):
122122
"when input files are in different projections from each other."
123123
)
124124
)
125-
pixel_meaning: str
125+
pixel_meaning: str = Field(
126+
..., description="Description of what the pixel value in the "
127+
"raster represents. This is used to clarify the meaning of the raster "
128+
"and distinguish multiple raster tile sets based on the same dataset "
129+
"version. The pixel_meaning string should be fairly short, use all "
130+
"lower-case letters, and use underscores instead of spaces."
131+
)
126132
data_type: DataType
127-
nbits: Optional[int]
128-
calc: Optional[str]
133+
nbits: Optional[int] = Field(
134+
None,
135+
description="Advanced option that lets GDAL compress the data even "
136+
"more based on the number of bits you need."
137+
)
138+
calc: Optional[str] = Field(
139+
None,
140+
description="There are two modes for this field, one for rasterizing vector "
141+
"sources and one for transforming and/or combining one or more "
142+
"sources that are already raster. For rasterizing vector sources, "
143+
"this field should be an SQL expression that yields the desired "
144+
"raster value based on the fields of your vector dataset.\n\nFor raster "
145+
"sources, this should be a raster algebra expression, similar to that "
146+
"provided to gdal_calc (see "
147+
"https://gdal.org/en/stable/programs/gdal_calc.html), "
148+
"that transforms one or more input bands into one or more output "
149+
"bands. For use in this expression, each band in "
150+
"the sources is assigned an alphabetic variable (A-Z, then AA-AZ, "
151+
"etc.) in the order it exists in those sources, with those of the "
152+
"first source first, continuing with those of the second, and so on. "
153+
"So with two input sources of two bands each, they would be assigned "
154+
"to variables A and B (for the first source) and C and D (for the "
155+
"second source). The NumPy module is in scope, accessible as np"
156+
)
129157
band_count: int = 1
130158
union_bands: bool = False
131159
no_data: Optional[Union[List[NoDataType], NoDataType]]
132-
rasterize_method: Optional[RasterizeMethod]
160+
rasterize_method: Optional[RasterizeMethod] = Field(
161+
RasterizeMethod.value,
162+
description="For raster sources or default assets, 'value' (the "
163+
"default) means use the value from the last or only band processed, "
164+
"and 'count' means count the number of bands with data values."
165+
)
133166
resampling: ResamplingMethod = PIXETL_DEFAULT_RESAMPLING
134-
order: Optional[Order]
167+
order: Optional[Order] = Field(
168+
None,
169+
description="For vector default assets, order the features by the "
170+
"calculated raster value. For 'asc', the features are ordered by "
171+
"ascending calculated value so that the largest calculated value is "
172+
"used in the raster when there are overlapping features. For 'desc', "
173+
"the ordering is descending, so that the smallest calculated value "
174+
"is used when there are overlaps."
175+
)
135176
overwrite: bool = False
136177
subset: Optional[str]
137178
grid: Grid
138179
symbology: Optional[Symbology] = None
139180
compute_stats: bool = True
140181
compute_histogram: bool = False
141182
process_locally: bool = True
142-
auxiliary_assets: Optional[List[UUID]] = None
183+
auxiliary_assets: Optional[List[UUID]] = Field(
184+
None,
185+
description="Asset IDs of additional rasters you might want to include "
186+
"in your calc expression."
187+
)
143188
photometric: Optional[PhotometricType] = None
144189
num_processes: Optional[StrictInt] = None
145190
timeout_sec: Optional[StrictInt] = Field(
@@ -209,7 +254,15 @@ class VectorSourceCreationOptions(StrictBaseModel):
209254
Index(index_type=IndexType.gist.value, column_names=["geom_wm"]),
210255
Index(index_type=IndexType.hash.value, column_names=["gfw_geostore_id"]),
211256
],
212-
description="List of indices to add to table",
257+
description="List of indices to add to the database table representing "
258+
"the vector dataset. Each element of the indices field contains an "
259+
"index_type field (which is a string) and a column_names field (which "
260+
"is a list of field names included in this index). The possibilities "
261+
"for the index_type field are hash, btree, or gist. hash is efficient "
262+
"for standard exact-value lookups, while btree is efficient for range "
263+
"lookups. gist is used for geometry fields and can do "
264+
"intersection-type lookups. See "
265+
"https://www.postgresql.org/docs/current/indexes-types.html"
213266
)
214267
cluster: Optional[Index] = Field(None, description="Index to use for clustering.")
215268
table_schema: Optional[List[FieldType]] = Field(
@@ -331,7 +384,7 @@ class RasterTileCacheCreationOptions(TileCacheBaseModel):
331384
"default",
332385
description="Name space to use for raster tile cache. "
333386
"This will be part of the URI and will "
334-
"allow to create multiple raster tile caches per version,",
387+
"allow creation of multiple raster tile caches per version,",
335388
)
336389
symbology: Symbology = Field(..., description="Symbology to use for output tiles")
337390
source_asset_id: str = Field(

app/models/pydantic/metadata.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from uuid import UUID
44

55
from fastapi import HTTPException
6-
from pydantic import Field, validator, BaseModel
6+
from pydantic import BaseModel, Field, validator
77
from pydantic.utils import GetterDict
88

99
from .base import BaseRecord, StrictBaseModel
@@ -34,6 +34,7 @@ class Config:
3434

3535
class DatasetMetadata(CommonMetadata):
3636
title: Optional[str]
37+
subtitle: Optional[str]
3738
source: Optional[str]
3839
license: Optional[str]
3940
data_language: Optional[str]
@@ -51,6 +52,7 @@ class Config:
5152
"examples": [
5253
{
5354
"title": "Deforestation alerts (GLAD-S2)",
55+
"subtitle": "Sentinel-2 based deforestation alerts",
5456
"source": "Global Land Analysis and Discovery (GLAD), University of Maryland",
5557
"license": "[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)",
5658
"data_language": "en",

app/models/pydantic/versions.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ class Version(BaseRecord):
1818
metadata: Union[VersionMetadataOut, BaseModel]
1919
status: VersionStatus = VersionStatus.pending
2020

21-
# Each element of assets is a tuple (asset_type, assert_uri, asset_id)
22-
assets: List[Tuple[str, str, str]] = list()
21+
assets: List[Tuple[str, str, str]] = Field(
22+
list(),
23+
description="List of saved (non-pending and non-failed) assets, with "
24+
" elements in the form: [asset_type, asset_uri, asset_id]. The list "
25+
"of assets is sorted by the creation time of each asset."
26+
)
2327

2428

2529
class VersionCreateIn(StrictBaseModel):

app/routes/assets/asset.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
"""Assets are replicas of the original source files.
1+
"""Assets are usually alternate representations of the base dataset
2+
version, sometimes combining in extra data from other datasets.
23
34
Assets might be served in different formats, attribute values might be
45
altered, additional attributes added, and feature resolution might have
56
changed. Assets are either managed or unmanaged. Managed assets are
67
created by the API and users can rely on data integrity. Unmanaged
78
assets are only loosely linked to a dataset version and users must
8-
cannot rely on full integrity. We can only assume that unmanaged are
9-
based on the same version and do not know the processing history.
9+
cannot rely on full integrity. We can only assume that unmanaged assets
10+
are based on the same version and do not know the processing history.
1011
"""
1112

1213
from typing import List, Optional, Union
@@ -87,7 +88,9 @@ async def get_asset(
8788
*,
8889
asset_id: UUID = Path(...),
8990
) -> AssetResponse:
90-
"""Get a specific asset."""
91+
"""Get a specific asset. This provides information on the asset, including
92+
the asset id, the asset status, the asset URI, and creation & last update
93+
times."""
9194
try:
9295
row: ORMAsset = await assets.get_asset(asset_id)
9396
except RecordNotFoundError as e:

app/routes/datasets/asset.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ async def get_version_assets(
6666
description="The number of assets per page. Default is `10`.",
6767
),
6868
) -> Union[PaginatedAssetsResponse, AssetsResponse]:
69-
"""Get all assets for a given dataset version. The list of assets
70-
is sorted by the creation time of each asset.
69+
"""Get all assets for a given dataset version (including pending/failed assets).
70+
The list of assets is sorted by the creation time of each asset.
7171
7272
Will attempt to paginate if `page[size]` or `page[number]` is
7373
provided. Otherwise, it will attempt to return the entire list of

app/routes/datasets/dataset.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ async def update_dataset(
128128
request: DatasetUpdateIn,
129129
user: User = Depends(get_owner),
130130
) -> DatasetResponse:
131-
"""Update metadata, accessibility or ownership of a dataset."""
131+
"""Update metadata, accessibility or ownership of a dataset.
132+
133+
Individual fields of the metadata can be modified, without affecting other
134+
existing fields.
135+
"""
132136
input_data: Dict = request.dict(exclude_none=True, by_alias=True)
133137

134138
if request.owner_id is not None:

app/routes/datasets/versions.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
async def get_version(
8282
*, dv: Tuple[str, str] = Depends(dataset_version_dependency)
8383
) -> VersionResponse:
84-
"""Get basic metadata for a given version. The list of assets is sorted by
84+
"""Get basic metadata for a given version. The list of assets only includes
85+
saved (non-pending and non-failed) assets and is sorted by
8586
the creation time of each asset."""
8687

8788
dataset, version = dv
@@ -106,8 +107,8 @@ async def add_new_version(
106107
user: User = Depends(get_owner),
107108
response: Response,
108109
):
109-
"""Create a version for a given dataset by uploading the geospatial/tabular
110-
asset.
110+
"""Create a version for a given dataset by uploading the tabular, vector,
111+
or raster asset.
111112
112113
Only the dataset's owner or a user with `ADMIN` user role can do
113114
this operation.
@@ -373,6 +374,14 @@ async def get_stats(dv: Tuple[str, str] = Depends(dataset_version_dependency)):
373374
response_model=Union[FieldsMetadataResponse, RasterBandsMetadataResponse],
374375
)
375376
async def get_fields(dv: Tuple[str, str] = Depends(dataset_version_dependency)):
377+
"""Get the fields of a version. For a version with a vector default asset,
378+
these are the fields (attributes) of the features of the base vector dataset.
379+
380+
For a version with a raster default asset, the fields are all the raster
381+
tile sets that use the same grid as the raster default asset. Also
382+
included are some fields with special meaning such as 'area__ha',
383+
'latitude', and 'longitude'.
384+
"""
376385
dataset, version = dv
377386
orm_asset: ORMAsset = await assets.get_default_asset(dataset, version)
378387

0 commit comments

Comments
 (0)