-
Notifications
You must be signed in to change notification settings - Fork 1
Update visit UI #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update visit UI #44
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,7 +35,6 @@ | |||||||||||||||||||||||
| from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse | ||||||||||||||||||||||||
| from fastapi.staticfiles import StaticFiles | ||||||||||||||||||||||||
| from fastapi.templating import Jinja2Templates | ||||||||||||||||||||||||
| from pydantic import BaseModel | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| from ..normalization import normalize_soa | ||||||||||||||||||||||||
| from .initialize_database import _connect, _init_db | ||||||||||||||||||||||||
|
|
@@ -78,7 +77,20 @@ | |||||||||||||||||||||||
| from .routers.arms import delete_arm | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Avoid binding visit helpers directly to allow fresh reloads in tests | ||||||||||||||||||||||||
| from .schemas import ArmCreate, SOACreate, SOAMetadataUpdate, VisitCreate | ||||||||||||||||||||||||
| from .schemas import ( | ||||||||||||||||||||||||
| ArmCreate, | ||||||||||||||||||||||||
| SOACreate, | ||||||||||||||||||||||||
| SOAMetadataUpdate, | ||||||||||||||||||||||||
| VisitCreate, | ||||||||||||||||||||||||
| VisitUpdate, | ||||||||||||||||||||||||
| ConceptsUpdate, | ||||||||||||||||||||||||
| # FreezeCreate, | ||||||||||||||||||||||||
| CellCreate, | ||||||||||||||||||||||||
| # BulkActivities, | ||||||||||||||||||||||||
| # MatrixVisit, | ||||||||||||||||||||||||
| # MatrixActivity, | ||||||||||||||||||||||||
| MatrixImport, | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| from .utils import ( | ||||||||||||||||||||||||
| get_next_code_uid as _get_next_code_uid, | ||||||||||||||||||||||||
| get_next_concept_uid as _get_next_concept_uid, | ||||||||||||||||||||||||
|
|
@@ -1047,51 +1059,23 @@ def _rollback_preview(soa_id: int, freeze_id: int) -> dict: | |||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # ------ Schemas -----# | ||||||||||||||||||||||||
| class ConceptsUpdate(BaseModel): | ||||||||||||||||||||||||
| concept_codes: List[str] | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| class FreezeCreate(BaseModel): | ||||||||||||||||||||||||
| version_label: Optional[str] = None | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| class CellCreate(BaseModel): | ||||||||||||||||||||||||
| visit_id: int | ||||||||||||||||||||||||
| activity_id: int | ||||||||||||||||||||||||
| status: str | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| class BulkActivities(BaseModel): | ||||||||||||||||||||||||
| names: List[str] | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| class MatrixVisit(BaseModel): | ||||||||||||||||||||||||
| name: str | ||||||||||||||||||||||||
| label: Optional[str] = None | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| class MatrixActivity(BaseModel): | ||||||||||||||||||||||||
| name: str | ||||||||||||||||||||||||
| statuses: List[str] | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| class MatrixImport(BaseModel): | ||||||||||||||||||||||||
| visits: List[MatrixVisit] | ||||||||||||||||||||||||
| activities: List[MatrixActivity] | ||||||||||||||||||||||||
| reset: bool = True | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def _fetch_matrix(soa_id: int): | ||||||||||||||||||||||||
| conn = _connect() | ||||||||||||||||||||||||
| cur = conn.cursor() | ||||||||||||||||||||||||
| # Epochs not part of matrix axes currently; retrieved separately where needed. | ||||||||||||||||||||||||
| cur.execute( | ||||||||||||||||||||||||
| "SELECT id,name,label,order_index,epoch_id FROM visit WHERE soa_id=? ORDER BY order_index", | ||||||||||||||||||||||||
| "SELECT id,name,label,order_index,epoch_id,description FROM visit WHERE soa_id=? ORDER BY order_index", | ||||||||||||||||||||||||
| (soa_id,), | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| visits = [ | ||||||||||||||||||||||||
| dict(id=r[0], name=r[1], label=r[2], order_index=r[3], epoch_id=r[4]) | ||||||||||||||||||||||||
| dict( | ||||||||||||||||||||||||
| id=r[0], | ||||||||||||||||||||||||
| name=r[1], | ||||||||||||||||||||||||
| label=r[2], | ||||||||||||||||||||||||
| order_index=r[3], | ||||||||||||||||||||||||
| epoch_id=r[4], | ||||||||||||||||||||||||
| description=r[5], | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| for r in cur.fetchall() | ||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||
| # Activities: include optional label/description if schema supports them | ||||||||||||||||||||||||
|
|
@@ -5755,6 +5739,32 @@ def ui_set_visit_epoch( | |||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @app.post("/ui/soa/{soa_id}/update_visit", response_class=HTMLResponse) | ||||||||||||||||||||||||
| def ui_update_visit( | ||||||||||||||||||||||||
| request: Request, | ||||||||||||||||||||||||
| soa_id: int, | ||||||||||||||||||||||||
| visit_id: int = Form(...), | ||||||||||||||||||||||||
| name: Optional[str] = Form(None), | ||||||||||||||||||||||||
| label: Optional[str] = Form(None), | ||||||||||||||||||||||||
| description: Optional[str] = Form(None), | ||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||
| """Form handler to update a Visit's mutable fields (name/label/description).""" | ||||||||||||||||||||||||
| # Build payload with provided fields; blanks should clear values | ||||||||||||||||||||||||
| payload = VisitUpdate( | ||||||||||||||||||||||||
| name=name, | ||||||||||||||||||||||||
| label=label, | ||||||||||||||||||||||||
| description=description, | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| visits_router.update_visit(soa_id, visit_id, payload) | ||||||||||||||||||||||||
| except Exception: | ||||||||||||||||||||||||
| # Let redirect proceed; detailed errors will appear in API logs | ||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||
|
Comment on lines
+5760
to
+5762
|
||||||||||||||||||||||||
| except Exception: | |
| # Let redirect proceed; detailed errors will appear in API logs | |
| pass | |
| except Exception as exc: | |
| # Log the exception but still redirect to the edit page | |
| logging.exception( | |
| "Failed to update visit %s for SOA %s via UI handler: %s", | |
| visit_id, | |
| soa_id, | |
| exc, | |
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function accepts a
nameparameter but the inline form in edit.html doesn't provide a name input field, sonamewill always be None. This creates an inconsistency where the API could unintentionally clear the visit name. Consider removing thenameparameter from this handler since it's not used in the UI.