Skip to content

Commit 57b0abe

Browse files
committed
More linter
1 parent 1bb5cea commit 57b0abe

File tree

10 files changed

+211
-110
lines changed

10 files changed

+211
-110
lines changed

alembic/versions/add6266277c7_.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Create Date: 2025-09-12 04:18:05.931799
66
77
"""
8+
89
import sqlalchemy as sa
910

1011
from alembic import op
@@ -15,10 +16,13 @@
1516
depends_on = None
1617
down_revision = None
1718

19+
1820
def upgrade():
1921
# ### commands auto generated by Alembic - please adjust! ###
2022
with op.batch_alter_table("workspaces_long_quests", schema=None) as batch_op:
21-
batch_op.add_column(sa.Column("type", sa.Integer(), nullable=False, server_default="0"))
23+
batch_op.add_column(
24+
sa.Column("type", sa.Integer(), nullable=False, server_default="0")
25+
)
2226
batch_op.add_column(sa.Column("url", sa.Unicode(), nullable=True))
2327

2428
# ### end Alembic commands ###

api/core/config.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@ class Settings(BaseSettings):
88
DATABASE_URL: str = "postgresql+asyncpg://user:pass@localhost:5432/dbname"
99
DEBUG: bool = False
1010

11-
WS_LONGFORM_SCHEMA_URL: str = "https://raw.githubusercontent.com/TaskarCenterAtUW/asr-imagery-list/refs/heads/main/schema/schema.json"
11+
WS_LONGFORM_SCHEMA_URL: str = (
12+
"https://raw.githubusercontent.com/TaskarCenterAtUW/asr-imagery-list/refs/heads/main/schema/schema.json"
13+
)
1214
WS_OSM_HOST: str = "https://osm.workspaces-dev.sidewalks.washington.edu"
1315

1416
# JWT Settings
15-
JWT_SECRET: str = "your-secret-key"
17+
JWT_SECRET: str = "your-secret-key"
1618
JWT_ALGORITHM: str = "HS256"
17-
JWT_EXPIRATION: int = 24 * 60 # 1d
19+
JWT_EXPIRATION: int = 24 * 60 # 1d
1820

1921
model_config = SettingsConfigDict(
2022
env_file=".env",
2123
env_file_encoding="utf-8",
2224
)
2325

26+
2427
settings = Settings()

api/core/database.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# Create declarative base for models
1313
Base = declarative_base()
1414

15+
1516
async def get_session() -> AsyncSession:
1617
"""Dependency for getting async database session.
1718

api/core/security.py

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77
from jose import JWTError, jwt
88

99
security = HTTPBearer()
10+
11+
1012
class UserInfo:
1113
scheme: str
1214
credentials: str
1315
projectGroups: list[str]
1416

15-
async def validate_token(credentials: HTTPAuthorizationCredentials = Depends(security)) -> UserInfo:
17+
18+
async def validate_token(
19+
credentials: HTTPAuthorizationCredentials = Depends(security),
20+
) -> UserInfo:
1621
"""Dependency to get current authenticated user."""
1722

1823
credentials_exception = HTTPException(
@@ -32,35 +37,38 @@ async def validate_token(credentials: HTTPAuthorizationCredentials = Depends(sec
3237
raise credentials_exception
3338

3439
async with httpx.AsyncClient() as client:
35-
headers = {
36-
'Authorization': 'Bearer ' + credentials.credentials,
37-
'Content-Type': 'application/json',
38-
}
39-
40-
authorizationUrl = os.environ.get("TM_TDEI_BACKEND_URL", "https://portal-api-dev.tdei.us/api/v1/") + "/project-group-roles/" + user_id + "?page_no=1&page_size=50"
41-
response = await client.get(authorizationUrl, headers=headers)
42-
43-
# token is not valid or server unavailable
44-
if response.status_code != 200:
45-
raise credentials_exception
40+
headers = {
41+
"Authorization": "Bearer " + credentials.credentials,
42+
"Content-Type": "application/json",
43+
}
44+
45+
authorizationUrl = (
46+
os.environ.get(
47+
"TM_TDEI_BACKEND_URL", "https://portal-api-dev.tdei.us/api/v1/"
48+
)
49+
+ "/project-group-roles/"
50+
+ user_id
51+
+ "?page_no=1&page_size=50"
52+
)
53+
response = await client.get(authorizationUrl, headers=headers)
54+
55+
# token is not valid or server unavailable
56+
if response.status_code != 200:
57+
raise credentials_exception
4658

47-
try:
48-
content = response.read()
49-
j = json.loads(content)
50-
except json.JSONDecodeError:
51-
raise credentials_exception
59+
try:
60+
content = response.read()
61+
j = json.loads(content)
62+
except json.JSONDecodeError:
63+
raise credentials_exception
5264

53-
pgs = []
54-
for i in j:
55-
pgs.append(i["tdei_project_group_id"])
65+
pgs = []
66+
for i in j:
67+
pgs.append(i["tdei_project_group_id"])
5668

5769
r = UserInfo()
5870
r.scheme = credentials.scheme
5971
r.credentials = credentials.credentials
6072
r.projectGroups = pgs
6173

6274
return r
63-
64-
65-
66-

api/main.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,47 +30,66 @@
3030
# Include routers
3131
app.include_router(workspaces_router)
3232

33+
3334
@app.get("/health")
3435
async def health_check():
3536
return {"status": "ok"}
3637

38+
3739
@app.get("/")
3840
async def root():
3941
"""Root endpoint."""
4042
logger.debug("Root endpoint called")
4143
return {"message": "Welcome to Workspaces API!"}
4244

43-
def get_workspace_service(session: AsyncSession = Depends(get_session)) -> WorkspaceService:
45+
46+
def get_workspace_service(
47+
session: AsyncSession = Depends(get_session),
48+
) -> WorkspaceService:
4449
repository = WorkspaceRepository(session)
4550
return WorkspaceService(repository)
4651

52+
4753
# This API route catches anything not otherwise defined above--MUST be last in this file
4854
#
4955
# h/t: https://stackoverflow.com/questions/70610266/proxy-an-external-website-using-python-fast-api-not-supporting-query-params
5056
#
51-
@app.api_route("/{full_path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"])
57+
@app.api_route(
58+
"/{full_path:path}",
59+
methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"],
60+
)
5261
async def catch_all(
5362
request: Request,
5463
current_user: UserInfo = Depends(validate_token),
5564
service: WorkspaceService = Depends(get_workspace_service),
5665
):
57-
validWorkspace = await service.get_workspace(current_user.projectGroups, int(request.headers.get("X-Workspace") or "-1"))
66+
validWorkspace = await service.get_workspace(
67+
current_user.projectGroups, int(request.headers.get("X-Workspace") or "-1")
68+
)
5869

59-
if(validWorkspace is None):
70+
if validWorkspace is None:
6071
raise HTTPException(
6172
status_code=status.HTTP_401_UNAUTHORIZED,
6273
detail="Invalid authentication credentials",
6374
headers={"WWW-Authenticate": "Bearer"},
6475
)
6576

66-
print(f"Path proxied: {request.url} workspace_id: {request.headers.get("X-Workspace")}")
77+
print(
78+
f"Path proxied: {request.url} workspace_id: {request.headers.get("X-Workspace")}"
79+
)
6780

68-
url = httpx.URL(path=request.url.path.strip(), query=request.url.query.encode("utf-8"))
81+
url = httpx.URL(
82+
path=request.url.path.strip(), query=request.url.query.encode("utf-8")
83+
)
6984
client = httpx.AsyncClient(base_url=settings.WS_OSM_HOST)
7085

7186
new_headers = list()
72-
new_headers.append((bytes("Authorization", "utf-8"), request.headers.get("Authorization")))
73-
new_headers.append((bytes("X-Workspace", "utf-8"), bytes(str(validWorkspace.id), "utf-8")))
87+
new_headers.append(
88+
(bytes("Authorization", "utf-8"), request.headers.get("Authorization"))
89+
)
90+
new_headers.append(
91+
(bytes("X-Workspace", "utf-8"), bytes(str(validWorkspace.id), "utf-8"))
92+
)
7493
new_headers.append((bytes("Host", "utf-8"), bytes(client.base_url.host, "utf-8")))
7594

7695
rp_req = client.build_request(

api/src/workspaces/models.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ class ExternalAppsDefinitionType(Enum):
2222
PUBLIC = 1
2323
PROJECT_GROUP = 2
2424

25+
2526
class Workspace(Base):
26-
""" Workspaces """
27+
"""Workspaces"""
2728

2829
__tablename__ = "workspaces"
2930

@@ -45,29 +46,27 @@ class Workspace(Base):
4546

4647
geometry = Column(Geometry("MULTIPOLYGON", srid=4326))
4748

48-
externalAppAccess = Column(SmallInteger, nullable=False, default=ExternalAppsDefinitionType.NONE.value)
49+
externalAppAccess = Column(
50+
SmallInteger, nullable=False, default=ExternalAppsDefinitionType.NONE.value
51+
)
4952

5053
kartaViewToken = Column(Unicode)
5154

5255
longFormQuestDef: Mapped[list["WorkspaceLongQuest"]] = relationship(
53-
"WorkspaceLongQuest",
54-
uselist=False,
55-
lazy="joined",
56-
cascade="all, delete"
56+
"WorkspaceLongQuest", uselist=False, lazy="joined", cascade="all, delete"
5757
)
5858

5959
imageryListDef: Mapped[list["WorkspaceImagery"]] = relationship(
60-
"WorkspaceImagery",
61-
uselist=False,
62-
lazy="joined",
63-
cascade="all, delete"
60+
"WorkspaceImagery", uselist=False, lazy="joined", cascade="all, delete"
6461
)
6562

63+
6664
class QuestDefinitionType(Enum):
6765
NONE = 0
6866
JSON = 1
6967
URL = 2
7068

69+
7170
class WorkspaceLongQuest(Base):
7271
"""Stores mobile app quest definitions for a workspace"""
7372

@@ -79,10 +78,13 @@ class WorkspaceLongQuest(Base):
7978
type = Column(Integer, nullable=False, default=QuestDefinitionType.NONE.value)
8079
url = Column(Unicode, nullable=True, default=None)
8180

82-
modifiedAt = Column(DateTime, nullable=False, default=func.now(), onupdate=func.now())
81+
modifiedAt = Column(
82+
DateTime, nullable=False, default=func.now(), onupdate=func.now()
83+
)
8384
modifiedBy = Column(UUID(as_uuid=True), nullable=False)
8485
modifiedByName = Column(Unicode, nullable=False)
8586

87+
8688
class WorkspaceImagery(Base):
8789
"""Stores imagery list for a workspace"""
8890

@@ -91,6 +93,8 @@ class WorkspaceImagery(Base):
9193
workspace_id = Column(Integer, ForeignKey(Workspace.id), primary_key=True)
9294
definition = Column(JSON, nullable=True, default=None)
9395

94-
modifiedAt = Column(DateTime, nullable=False, default=func.now(), onupdate=func.now())
96+
modifiedAt = Column(
97+
DateTime, nullable=False, default=func.now(), onupdate=func.now()
98+
)
9599
modifiedBy = Column(UUID(as_uuid=True), nullable=False)
96-
modifiedByName = Column(Unicode, nullable=False)
100+
modifiedByName = Column(Unicode, nullable=False)

api/src/workspaces/repository.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ class WorkspaceRepository:
1212
def __init__(self, session: AsyncSession):
1313
self.session = session
1414

15-
async def create(self, projectGroupIds: list[str], workspace_data: WorkspaceCreate) -> Workspace:
15+
async def create(
16+
self, projectGroupIds: list[str], workspace_data: WorkspaceCreate
17+
) -> Workspace:
1618
workspace = Workspace(**workspace_data.model_dump())
1719
try:
18-
if(workspace.tdeiProjectGroupId not in projectGroupIds):
19-
raise ValueError("User does not have permissions to create a workspace in that project group.")
20+
if workspace.tdeiProjectGroupId not in projectGroupIds:
21+
raise ValueError(
22+
"User does not have permissions to create a workspace in that project group."
23+
)
2024

2125
self.session.add(workspace)
2226
await self.session.commit()
@@ -28,8 +32,13 @@ async def create(self, projectGroupIds: list[str], workspace_data: WorkspaceCrea
2832
f"Workspace with ID {workspace_data.id} already exists"
2933
)
3034

31-
async def get_by_id(self, projectGroupIds: list[str], workspace_id: int) -> Workspace:
32-
query = select(Workspace).where(Workspace.id == workspace_id and Workspace.tdeiProjectGroupId.in_(projectGroupIds))
35+
async def get_by_id(
36+
self, projectGroupIds: list[str], workspace_id: int
37+
) -> Workspace:
38+
query = select(Workspace).where(
39+
Workspace.id == workspace_id
40+
and Workspace.tdeiProjectGroupId.in_(projectGroupIds)
41+
)
3342
result = await self.session.execute(query)
3443
workspace = result.scalar_one_or_none()
3544

@@ -38,16 +47,30 @@ async def get_by_id(self, projectGroupIds: list[str], workspace_id: int) -> Work
3847
return workspace
3948

4049
async def get_all(self, projectGroupIds: list[str]) -> list[Workspace]:
41-
query = select(Workspace).where(Workspace.tdeiProjectGroupId.in_(projectGroupIds))
50+
query = select(Workspace).where(
51+
Workspace.tdeiProjectGroupId.in_(projectGroupIds)
52+
)
4253
result = await self.session.execute(query)
4354
return list(result.scalars().all())
4455

45-
async def update(self, projectGroupIds: list[str], workspace_id: int, workspace_data: WorkspaceUpdate) -> Workspace:
56+
async def update(
57+
self,
58+
projectGroupIds: list[str],
59+
workspace_id: int,
60+
workspace_data: WorkspaceUpdate,
61+
) -> Workspace:
4662
update_data = workspace_data.model_dump(exclude_unset=True)
4763
if not update_data:
4864
raise ValueError("No fields to update")
4965

50-
query = update(Workspace).where(Workspace.id == workspace_id and Workspace.tdeiProjectGroupId.in_(projectGroupIds)).values(**update_data)
66+
query = (
67+
update(Workspace)
68+
.where(
69+
Workspace.id == workspace_id
70+
and Workspace.tdeiProjectGroupId.in_(projectGroupIds)
71+
)
72+
.values(**update_data)
73+
)
5174
result = await self.session.execute(query)
5275

5376
if result.rowcount == 0:
@@ -57,7 +80,10 @@ async def update(self, projectGroupIds: list[str], workspace_id: int, workspace_
5780
return await self.get_by_id(projectGroupIds, workspace_id)
5881

5982
async def delete(self, projectGroupIds: list[str], workspace_id: int) -> None:
60-
query = delete(Workspace).where(Workspace.id == workspace_id and Workspace.tdeiProjectGroupId.in_(projectGroupIds))
83+
query = delete(Workspace).where(
84+
Workspace.id == workspace_id
85+
and Workspace.tdeiProjectGroupId.in_(projectGroupIds)
86+
)
6187
result = await self.session.execute(query)
6288

6389
if result.rowcount == 0:

0 commit comments

Comments
 (0)