Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/seis_lab_data/db/queries/datasetcategories.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
from typing import Literal

from sqlmodel.ext.asyncio.session import AsyncSession
from sqlmodel import select
from sqlmodel import (
func,
select,
)

from ...db import models
from ...schemas import identifiers
Expand All @@ -22,7 +25,7 @@ async def list_dataset_categories(
limit = page_size
offset = page_size * (page - 1)
statement = select(models.DatasetCategory).order_by(
models.DatasetCategory.name["en"].astext.desc()
func.lower(models.DatasetCategory.name["en"].astext)
)
if en_name_filter is not None:
statement = statement.where(
Expand All @@ -46,8 +49,8 @@ async def collect_all_dataset_categories(
order_by: Literal["name_en", "name_pt"] = "name_en",
) -> list[models.DatasetCategory]:
order_by_clause = {
"name_pt": models.DatasetCategory.name["pt"].astext.desc(),
}.get(order_by, models.DatasetCategory.name["en"].astext.desc())
"name_pt": func.lower(models.DatasetCategory.name["pt"].astext),
}.get(order_by, func.lower(models.DatasetCategory.name["en"].astext))

statement = select(models.DatasetCategory).order_by(order_by_clause)
if en_name_filter is not None:
Expand Down
4 changes: 2 additions & 2 deletions src/seis_lab_data/db/queries/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ async def list_asset_discovery_configurations(
statement = (
select(models.AssetDiscoveryConfiguration)
.options(*_SELECT_IN_LOAD_OPTIONS)
.order_by(models.AssetDiscoveryConfiguration.name.asc())
.order_by(models.AssetDiscoveryConfiguration.name)
)
if name_filter is not None:
statement = statement.where(
Expand All @@ -84,7 +84,7 @@ async def collect_all_asset_discovery_configurations(
statement = (
select(models.AssetDiscoveryConfiguration)
.options(*_SELECT_IN_LOAD_OPTIONS)
.order_by(models.AssetDiscoveryConfiguration.name.asc())
.order_by(models.AssetDiscoveryConfiguration.name)
)
if name_filter is not None:
statement = statement.where(
Expand Down
5 changes: 4 additions & 1 deletion src/seis_lab_data/db/queries/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,14 @@ async def list_projects(
pt_name_filter: str | None = None,
spatial_intersect: shapely.Polygon | None = None,
temporal_extent: filter_schemas.TemporalExtentFilterValue | None = None,
only_internal: bool = False,
) -> tuple[list[models.Project], int | None]:
"""Return all projects regardless of status. Intended for admin use."""
"""Return all projects. Intended for admin use."""
statement = _build_project_statement(
en_name_filter, pt_name_filter, spatial_intersect, temporal_extent
)
if only_internal:
statement = statement.where(models.Project.status != ProjectStatus.PUBLISHED)
limit = page_size
offset = page_size * (page - 1)
return await _exec_project_list(session, statement, limit, offset, include_total)
Expand Down
5 changes: 5 additions & 0 deletions src/seis_lab_data/db/queries/surveymissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,16 @@ async def list_survey_missions(
pt_name_filter: str | None = None,
spatial_intersect: shapely.Polygon | None = None,
temporal_extent: filter_schemas.TemporalExtentFilterValue | None = None,
only_internal: bool = False,
) -> tuple[list[models.SurveyMission], int | None]:
"""Return all survey missions regardless of status. Intended for admin use."""
statement = _build_survey_mission_statement(
project_id, en_name_filter, pt_name_filter, spatial_intersect, temporal_extent
)
if only_internal:
statement = statement.where(
models.SurveyMission.status != SurveyMissionStatus.PUBLISHED
)
limit = page_size
offset = page_size * (page - 1)
return await _exec_survey_mission_list(
Expand Down
134 changes: 95 additions & 39 deletions src/seis_lab_data/db/queries/surveyrelatedrecords.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

import shapely
from sqlalchemy.orm import selectinload
from sqlalchemy.orm import aliased, selectinload
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlmodel import (
exists,
Expand All @@ -24,12 +24,15 @@
def _apply_survey_related_record_filters(
statement,
survey_mission_id: identifiers.SurveyMissionId | None = None,
project_id: identifiers.ProjectId | None = None,
en_name_filter: str | None = None,
pt_name_filter: str | None = None,
spatial_intersect: shapely.Polygon | None = None,
temporal_extent: filter_schemas.TemporalExtentFilterValue | None = None,
asset_path_fragment_filter: str | None = None,
record_ids: list[identifiers.SurveyRelatedRecordId] | None = None,
dataset_category_id: identifiers.DatasetCategoryId | None = None,
workflow_stage_id: identifiers.WorkflowStageId | None = None,
):
"""Apply the common survey-related record search filters to a statement.

Expand Down Expand Up @@ -60,6 +63,13 @@ def _apply_survey_related_record_filters(
statement = statement.where(
models.SurveyRelatedRecord.survey_mission_id == survey_mission_id
)
if project_id is not None:
# aliased so this join doesn't collide with the unaliased SurveyMission
# join that `_restrict_to_accessible`/`_restrict_to_owned` add later
mission = aliased(models.SurveyMission)
statement = statement.join(
mission, models.SurveyRelatedRecord.survey_mission_id == mission.id
).where(mission.project_id == project_id)
if temporal_extent is not None:
if temporal_extent.begin is not None:
statement = statement.where(
Expand All @@ -70,6 +80,14 @@ def _apply_survey_related_record_filters(
statement = statement.where(
models.SurveyRelatedRecord.temporal_extent_end <= temporal_extent.end
)
if dataset_category_id is not None:
statement = statement.where(
models.SurveyRelatedRecord.dataset_category_id == dataset_category_id
)
if workflow_stage_id is not None:
statement = statement.where(
models.SurveyRelatedRecord.workflow_stage_id == workflow_stage_id
)
if asset_path_fragment_filter is not None:
statement = statement.where(
exists(
Expand All @@ -90,12 +108,15 @@ def _apply_survey_related_record_filters(

def _build_survey_related_record_statement(
survey_mission_id: identifiers.SurveyMissionId | None = None,
project_id: identifiers.ProjectId | None = None,
en_name_filter: str | None = None,
pt_name_filter: str | None = None,
spatial_intersect: shapely.Polygon | None = None,
temporal_extent: filter_schemas.TemporalExtentFilterValue | None = None,
asset_path_fragment_filter: str | None = None,
record_ids: list[identifiers.SurveyRelatedRecordId] | None = None,
dataset_category_id: identifiers.DatasetCategoryId | None = None,
workflow_stage_id: identifiers.WorkflowStageId | None = None,
):
statement = (
select(models.SurveyRelatedRecord)
Expand All @@ -106,16 +127,24 @@ def _build_survey_related_record_statement(
)
.options(selectinload(models.SurveyRelatedRecord.dataset_category))
.options(selectinload(models.SurveyRelatedRecord.workflow_stage))
# adding all assets too, since they will always be a small list
.options(selectinload(models.SurveyRelatedRecord.assets))
# also adding relationships with other records - only first order relationships are loaded, not the full tree
.options(selectinload(models.SurveyRelatedRecord.related_to_links))
.options(selectinload(models.SurveyRelatedRecord.subject_links))
)
statement = _apply_survey_related_record_filters(
statement,
survey_mission_id,
en_name_filter,
pt_name_filter,
spatial_intersect,
temporal_extent,
asset_path_fragment_filter,
record_ids,
statement=statement,
survey_mission_id=survey_mission_id,
project_id=project_id,
en_name_filter=en_name_filter,
pt_name_filter=pt_name_filter,
spatial_intersect=spatial_intersect,
temporal_extent=temporal_extent,
asset_path_fragment_filter=asset_path_fragment_filter,
record_ids=record_ids,
dataset_category_id=dataset_category_id,
workflow_stage_id=workflow_stage_id,
)
return statement.order_by(
models.SurveyRelatedRecord.temporal_extent_end.desc().nullslast()
Expand All @@ -130,21 +159,25 @@ def _build_survey_related_record_id_statement(
temporal_extent: filter_schemas.TemporalExtentFilterValue | None = None,
asset_path_fragment_filter: str | None = None,
record_ids: list[identifiers.SurveyRelatedRecordId] | None = None,
dataset_category_id: identifiers.DatasetCategoryId | None = None,
workflow_stage_id: identifiers.WorkflowStageId | None = None,
):
"""Build a statement selecting only the ids of matching records.

Intended for reuse as a subquery/executed-upfront id list (e.g. by
bulk-update commands), where loading full records would be wasteful.
"""
return _apply_survey_related_record_filters(
select(models.SurveyRelatedRecord.id),
survey_mission_id,
en_name_filter,
pt_name_filter,
spatial_intersect,
temporal_extent,
asset_path_fragment_filter,
record_ids,
statement=select(models.SurveyRelatedRecord.id),
survey_mission_id=survey_mission_id,
en_name_filter=en_name_filter,
pt_name_filter=pt_name_filter,
spatial_intersect=spatial_intersect,
temporal_extent=temporal_extent,
asset_path_fragment_filter=asset_path_fragment_filter,
record_ids=record_ids,
dataset_category_id=dataset_category_id,
workflow_stage_id=workflow_stage_id,
)


Expand All @@ -165,6 +198,7 @@ async def _exec_survey_related_record_list(
async def list_published_survey_related_records(
session: AsyncSession,
survey_mission_id: identifiers.SurveyMissionId | None = None,
project_id: identifiers.ProjectId | None = None,
page: int = 1,
page_size: int = 20,
include_total: bool = False,
Expand All @@ -174,15 +208,20 @@ async def list_published_survey_related_records(
temporal_extent: filter_schemas.TemporalExtentFilterValue | None = None,
asset_path_fragment_filter: str | None = None,
record_ids: list[identifiers.SurveyRelatedRecordId] | None = None,
dataset_category_id: identifiers.DatasetCategoryId | None = None,
workflow_stage_id: identifiers.WorkflowStageId | None = None,
) -> tuple[list[models.SurveyRelatedRecord], int | None]:
statement = _build_survey_related_record_statement(
survey_mission_id,
en_name_filter,
pt_name_filter,
spatial_intersect,
temporal_extent,
asset_path_fragment_filter,
record_ids,
survey_mission_id=survey_mission_id,
project_id=project_id,
en_name_filter=en_name_filter,
pt_name_filter=pt_name_filter,
spatial_intersect=spatial_intersect,
temporal_extent=temporal_extent,
asset_path_fragment_filter=asset_path_fragment_filter,
record_ids=record_ids,
dataset_category_id=dataset_category_id,
workflow_stage_id=workflow_stage_id,
).where(models.SurveyRelatedRecord.status == SurveyRelatedRecordStatus.PUBLISHED)
limit = page_size
offset = page_size * (page - 1)
Expand Down Expand Up @@ -219,6 +258,7 @@ async def list_accessible_survey_related_records(
session: AsyncSession,
user_id: str,
survey_mission_id: identifiers.SurveyMissionId | None = None,
project_id: identifiers.ProjectId | None = None,
page: int = 1,
page_size: int = 20,
include_total: bool = False,
Expand All @@ -228,16 +268,21 @@ async def list_accessible_survey_related_records(
temporal_extent: filter_schemas.TemporalExtentFilterValue | None = None,
asset_path_fragment_filter: str | None = None,
record_ids: list[identifiers.SurveyRelatedRecordId] | None = None,
dataset_category_id: identifiers.DatasetCategoryId | None = None,
workflow_stage_id: identifiers.WorkflowStageId | None = None,
) -> tuple[list[models.SurveyRelatedRecord], int | None]:
statement = _restrict_to_accessible(
_build_survey_related_record_statement(
survey_mission_id,
en_name_filter,
pt_name_filter,
spatial_intersect,
temporal_extent,
asset_path_fragment_filter,
record_ids,
survey_mission_id=survey_mission_id,
project_id=project_id,
en_name_filter=en_name_filter,
pt_name_filter=pt_name_filter,
spatial_intersect=spatial_intersect,
temporal_extent=temporal_extent,
asset_path_fragment_filter=asset_path_fragment_filter,
record_ids=record_ids,
dataset_category_id=dataset_category_id,
workflow_stage_id=workflow_stage_id,
),
user_id,
)
Expand Down Expand Up @@ -351,6 +396,7 @@ async def count_survey_related_records_matching(
async def list_survey_related_records(
session: AsyncSession,
survey_mission_id: identifiers.SurveyMissionId | None = None,
project_id: identifiers.ProjectId | None = None,
page: int = 1,
page_size: int = 20,
include_total: bool = False,
Expand All @@ -360,17 +406,27 @@ async def list_survey_related_records(
temporal_extent: filter_schemas.TemporalExtentFilterValue | None = None,
asset_path_fragment_filter: str | None = None,
record_ids: list[identifiers.SurveyRelatedRecordId] | None = None,
only_internal: bool = False,
dataset_category_id: identifiers.DatasetCategoryId | None = None,
workflow_stage_id: identifiers.WorkflowStageId | None = None,
) -> tuple[list[models.SurveyRelatedRecord], int | None]:
"""Return all records regardless of status. Intended for admin use."""
"""Return all records. Intended for admin use."""
statement = _build_survey_related_record_statement(
survey_mission_id,
en_name_filter,
pt_name_filter,
spatial_intersect,
temporal_extent,
asset_path_fragment_filter,
record_ids,
survey_mission_id=survey_mission_id,
project_id=project_id,
en_name_filter=en_name_filter,
pt_name_filter=pt_name_filter,
spatial_intersect=spatial_intersect,
temporal_extent=temporal_extent,
asset_path_fragment_filter=asset_path_fragment_filter,
record_ids=record_ids,
dataset_category_id=dataset_category_id,
workflow_stage_id=workflow_stage_id,
)
if only_internal:
statement = statement.where(
models.SurveyRelatedRecord.status != SurveyRelatedRecordStatus.PUBLISHED
)
limit = page_size
offset = page_size * (page - 1)
return await _exec_survey_related_record_list(
Expand Down
11 changes: 7 additions & 4 deletions src/seis_lab_data/db/queries/workflowstages.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
from typing import Literal

from sqlmodel.ext.asyncio.session import AsyncSession
from sqlmodel import select
from sqlmodel import (
func,
select,
)

from ...db import models
from .common import _get_total_num_records
Expand All @@ -22,7 +25,7 @@ async def list_workflow_stages(
limit = page_size
offset = page_size * (page - 1)
statement = select(models.WorkflowStage).order_by(
models.WorkflowStage.name["en"].astext.desc()
models.WorkflowStage.name["en"].astext
)
if en_name_filter is not None:
statement = statement.where(
Expand All @@ -46,8 +49,8 @@ async def collect_all_workflow_stages(
order_by: Literal["name_en", "name_pt"] = "name_en",
) -> list[models.WorkflowStage]:
order_by_clause = {
"name_pt": models.WorkflowStage.name["pt"].astext.desc(),
}.get(order_by, models.WorkflowStage.name["en"].astext.desc())
"name_pt": func.lower(models.WorkflowStage.name["pt"].astext),
}.get(order_by, func.lower(models.WorkflowStage.name["en"].astext))

statement = select(models.WorkflowStage).order_by(order_by_clause)
if en_name_filter is not None:
Expand Down
4 changes: 4 additions & 0 deletions src/seis_lab_data/localization.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ def translate_localizable(
current_lang: str,
) -> str:
return getattr(value, current_lang, value.en) or ""


def translate_localizable_dict(value: dict[str, str], current_lang: str) -> str:
return value.get(current_lang, "en") or ""
2 changes: 2 additions & 0 deletions src/seis_lab_data/operations/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ async def list_projects(
pt_name_filter: str | None = None,
spatial_intersect: shapely.Polygon | None = None,
temporal_extent: filter_schemas.TemporalExtentFilterValue | None = None,
only_internal: bool = False,
) -> tuple[list[models.Project], int | None]:
kwargs = dict(
page=page,
Expand All @@ -420,6 +421,7 @@ async def list_projects(
elif not {constants.ROLE_ADMIN, constants.ROLE_SYSTEM_ADMIN}.isdisjoint(
initiator.roles
):
kwargs.update(only_internal=only_internal)
return await project_queries.list_projects(session, **kwargs)
else:
return await project_queries.list_accessible_projects(
Expand Down
Loading
Loading