Skip to content

Commit 10b9daa

Browse files
committed
Revert "Renaming"
This reverts commit 8ace729.
1 parent 8ace729 commit 10b9daa

File tree

5 files changed

+202
-212
lines changed

5 files changed

+202
-212
lines changed

api/src/workspaces/models.py

Lines changed: 70 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,100 @@
1-
import json
2-
from datetime import datetime
3-
from typing import Any, Optional
4-
from uuid import UUID
1+
from enum import Enum
52

6-
import requests
7-
from geoalchemy2 import WKBElement
8-
from jsonschema import ValidationError, validate
9-
from pydantic import BaseModel, ConfigDict, Field, Json, field_validator
10-
from typing_extensions import Annotated
3+
from geoalchemy2 import Geometry
4+
from sqlalchemy import (
5+
JSON,
6+
UUID,
7+
Column,
8+
DateTime,
9+
ForeignKey,
10+
Integer,
11+
SmallInteger,
12+
Unicode,
13+
)
14+
from sqlalchemy.orm import Mapped, relationship
15+
from sqlalchemy.sql import func
1116

12-
from api.core.config import Settings
13-
from api.src.workspaces.schemas import ExternalAppsDefinitionType, QuestDefinitionType
17+
from api.core.database import Base
1418

15-
class WorkspaceLongQuestBase(BaseModel):
1619

17-
workspace_id: int
20+
class ExternalAppsDefinitionType(Enum):
21+
NONE = 0
22+
PUBLIC = 1
23+
PROJECT_GROUP = 2
1824

19-
definition: Optional[str]
20-
type: QuestDefinitionType
21-
url: Optional[str]
2225

23-
modifiedAt: datetime
24-
modifiedBy: UUID
25-
modifiedByName: str
26+
class Workspace(Base):
27+
"""Workspaces"""
2628

27-
model_config = ConfigDict(from_attributes=True)
29+
__tablename__ = "workspaces"
2830

29-
def validate_definition(self, data, value):
30-
if QuestDefinitionType[data["type"]] == QuestDefinitionType.NONE:
31-
if not value:
32-
return None
33-
raise ValidationError("'definition' field not allowed.")
31+
id = Column(Integer, primary_key=True)
32+
type = Column(Unicode, nullable=False)
3433

35-
if QuestDefinitionType[data["type"]] != QuestDefinitionType.JSON:
36-
return value
34+
title = Column(Unicode, nullable=False)
35+
description = Column(Unicode)
3736

38-
if not value:
39-
raise ValidationError("This field is required.")
40-
if data["url"]:
41-
raise ValidationError("'url' field not allowed.")
37+
tdeiProjectGroupId = Column(UUID(as_uuid=True), nullable=False)
38+
tdeiRecordId = Column(UUID(as_uuid=True))
39+
tdeiServiceId = Column(UUID(as_uuid=True))
4240

43-
try:
44-
parsed = json.loads(value)
45-
if not parsed or not isinstance(parsed, dict):
46-
raise ValidationError("must be a JSON object.")
47-
validate_json_against_schema(parsed, Settings.WS_LONGFORM_SCHEMA_URL)
48-
except json.JSONDecodeError as e:
49-
return ValidationError(f"{e}")
50-
except ValidationError as e:
51-
raise ValidationError(f"{e}")
41+
tdeiMetadata = Column(JSON)
5242

53-
return value
43+
createdAt = Column(DateTime, nullable=False, default=func.now())
44+
createdBy = Column(UUID(as_uuid=True), nullable=False)
45+
createdByName = Column(Unicode)
5446

55-
def validate_url(self, data, value):
56-
if QuestDefinitionType[data["type"]] == QuestDefinitionType.NONE:
57-
if not value:
58-
return None
59-
raise ValidationError("'url' field not allowed.")
47+
geometry = Column(Geometry("MULTIPOLYGON", srid=4326))
6048

61-
if QuestDefinitionType[data["type"]] != QuestDefinitionType.URL:
62-
return value
49+
externalAppAccess = Column(
50+
SmallInteger, nullable=False, default=ExternalAppsDefinitionType.NONE.value
51+
)
6352

64-
if not value:
65-
raise ValidationError("This field is required.")
66-
if data["definition"]:
67-
raise ValidationError("'definition' field not allowed.")
53+
kartaViewToken = Column(Unicode)
6854

69-
return value
55+
longFormQuestDef: Mapped[list["WorkspaceLongQuest"]] = relationship(
56+
"WorkspaceLongQuest", uselist=False, lazy="joined", cascade="all, delete"
57+
)
7058

71-
class WorkspaceLongQuestUpdate(BaseModel):
72-
definition: Optional[str]
73-
url: Optional[str]
59+
imageryListDef: Mapped[list["WorkspaceImagery"]] = relationship(
60+
"WorkspaceImagery", uselist=False, lazy="joined", cascade="all, delete"
61+
)
7462

75-
class WorkspaceImageryBase(BaseModel):
7663

77-
workspace_id: int
64+
class QuestDefinitionType(Enum):
65+
NONE = 0
66+
JSON = 1
67+
URL = 2
7868

79-
# Note the below column is of the JSON *database* type vs string type, so we're not
80-
# using pydantic's JSON mapping, hence this is not defined as Optional[Json[Any]]
81-
definition: Optional[list[Any]]
8269

83-
modifiedAt: datetime
84-
modifiedBy: UUID
85-
modifiedByName: str
70+
class WorkspaceLongQuest(Base):
71+
"""Stores mobile app quest definitions for a workspace"""
8672

87-
model_config = ConfigDict(from_attributes=True)
73+
__tablename__ = "workspaces_long_quests"
8874

89-
class WorkspaceImageryUpdate(BaseModel):
90-
definition: Optional[str]
75+
workspace_id = Column(Integer, ForeignKey(Workspace.id), primary_key=True)
9176

92-
class WorkspaceBase(BaseModel):
77+
definition = Column(Unicode, nullable=True, default=None)
78+
type = Column(Integer, nullable=False, default=QuestDefinitionType.NONE.value)
79+
url = Column(Unicode, nullable=True, default=None)
9380

94-
id: int
95-
type: str = Field(...)
81+
modifiedAt = Column(
82+
DateTime, nullable=False, default=func.now(), onupdate=func.now()
83+
)
84+
modifiedBy = Column(UUID(as_uuid=True), nullable=False)
85+
modifiedByName = Column(Unicode, nullable=False)
9686

97-
title: str = Field(...)
98-
description: Optional[str]
9987

100-
tdeiProjectGroupId: UUID
101-
tdeiRecordId: Optional[UUID]
102-
tdeiServiceId: Optional[UUID]
88+
class WorkspaceImagery(Base):
89+
"""Stores imagery list for a workspace"""
10390

104-
tdeiMetadata: Optional[Json[Any]]
91+
__tablename__ = "workspaces_imagery"
10592

106-
createdAt: datetime
107-
createdBy: UUID
108-
createdByName: str
93+
workspace_id = Column(Integer, ForeignKey(Workspace.id), primary_key=True)
94+
definition = Column(JSON, nullable=True, default=None)
10995

110-
geometry: Optional[Annotated[str, WKBElement]]
111-
112-
externalAppAccess: ExternalAppsDefinitionType
113-
114-
kartaViewToken: Optional[str]
115-
116-
longFormQuestDef: Optional[WorkspaceLongQuestBase]
117-
118-
imageryListDef: Optional[WorkspaceImageryBase]
119-
120-
model_config = ConfigDict(from_attributes=True)
121-
122-
# there are some legacy records with '', which is not valid JSON, so map those to None
123-
@field_validator("*", mode="before")
124-
@classmethod
125-
def empty_str_to_none(cls, v):
126-
if v == "":
127-
return None
128-
return v
129-
130-
131-
class WorkspaceCreate(WorkspaceBase):
132-
pass
133-
134-
class WorkspaceUpdate(BaseModel):
135-
title: Optional[str] = None
136-
description: Optional[str] = None
137-
externalAppAccess: Optional[ExternalAppsDefinitionType] = None
138-
longFormQuestDef: Optional[WorkspaceLongQuestBase] = None
139-
imageryListDef: Optional[WorkspaceImageryBase] = None
140-
141-
class WorkspaceResponse(WorkspaceBase):
142-
pass
143-
144-
145-
def validate_json_against_schema(json, schema_url) -> bool:
146-
"""
147-
Validate a JSON string against a JSON schema from a URL.
148-
Returns True if valid, raises ValidationError if not.
149-
"""
150-
# Fetch the schema
151-
response = requests.get(schema_url)
152-
response.raise_for_status()
153-
schema = response.json()
154-
155-
# Validate
156-
validate(instance=json, schema=schema)
157-
return True
96+
modifiedAt = Column(
97+
DateTime, nullable=False, default=func.now(), onupdate=func.now()
98+
)
99+
modifiedBy = Column(UUID(as_uuid=True), nullable=False)
100+
modifiedByName = Column(Unicode, nullable=False)

api/src/workspaces/repository.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from typing import Any
21
from sqlalchemy import delete, select, update
32
from sqlalchemy.exc import IntegrityError
43
from sqlalchemy.ext.asyncio import AsyncSession
@@ -58,7 +57,7 @@ async def update(
5857
self,
5958
projectGroupIds: list[str],
6059
workspace_id: int,
61-
workspace_data: Any,
60+
workspace_data: WorkspaceUpdate,
6261
) -> Workspace:
6362
update_data = workspace_data.model_dump(exclude_unset=True)
6463
if not update_data:

api/src/workspaces/routes.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
from typing import Any
21
from fastapi import APIRouter, Depends, HTTPException, status
32
from sqlalchemy.ext.asyncio import AsyncSession
43

54
from api.core.database import get_session
65
from api.core.logging import get_logger
76
from api.core.security import UserInfo, validate_token
8-
from api.src.workspaces.models import WorkspaceCreate, WorkspaceLongQuestBase, WorkspaceLongQuestUpdate, WorkspaceResponse, WorkspaceUpdate
97
from api.src.workspaces.repository import WorkspaceRepository
10-
8+
from api.src.workspaces.schemas import (
9+
WorkspaceCreate,
10+
WorkspaceLongQuestBase,
11+
WorkspaceResponse,
12+
WorkspaceUpdate,
13+
)
1114
from api.src.workspaces.service import WorkspaceService
1215

1316
# Set up logger for this module
@@ -76,11 +79,8 @@ async def update_workspace(
7679
current_user: UserInfo = Depends(validate_token),
7780
) -> WorkspaceResponse:
7881
try:
79-
newWorkspaceRecord = await service.get_workspace(current_user.projectGroups, workspace_id)
80-
newWorkspaceRecord.model_copy(update=workspace_data.model_dump(exclude_unset=True))
81-
8282
workspace = await service.update_workspace(
83-
current_user.projectGroups, workspace_id, newWorkspaceRecord
83+
current_user.projectGroups, workspace_id, workspace_data
8484
)
8585
return workspace
8686
except Exception as e:
@@ -142,7 +142,7 @@ async def get_long_quest_settings(
142142
@router.patch("/{workspace_id}/quests/long/settings", response_model=WorkspaceLongQuestBase)
143143
async def update_long_quest_settings(
144144
workspace_id: int,
145-
longform_quest_data: WorkspaceLongQuestUpdate,
145+
workspace_data: WorkspaceUpdate,
146146
service: WorkspaceService = Depends(get_workspace_service),
147147
current_user: UserInfo = Depends(validate_token),
148148
) -> WorkspaceLongQuestBase | None:
@@ -151,10 +151,7 @@ async def update_long_quest_settings(
151151
current_user.projectGroups, workspace_id
152152
) # type: ignore
153153

154-
update_data = longform_quest_data.model_dump(exclude_unset=True)
155-
156-
if(workspace.longFormQuestDef is not None):
157-
workspace.longFormQuestDef.model_copy(update=update_data)
154+
# workspace.longFormQuestDef = longform_quest_data
158155

159156
updatedWorkspace = await service.update_workspace(
160157
current_user.projectGroups, workspace_id, workspace

0 commit comments

Comments
 (0)