Skip to content

Add timing#42

Merged
pendingintent merged 13 commits intomasterfrom
add-timing
Dec 17, 2025
Merged

Add timing#42
pendingintent merged 13 commits intomasterfrom
add-timing

Conversation

@pendingintent
Copy link
Owner

Added USDM JSON generators for arms and epochs.

Added new Stuidy Timing editor as a preliminary step to introduce ScheduledActivityInstances to the SOA

Copilot AI review requested due to automatic review settings December 17, 2025 16:26
window_lower=window_lower,
)
create_timing(soa_id, payload)
return RedirectResponse(url=f"/ui/soa/{soa_id}/timings", status_code=303)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix

AI 3 months ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

window_lower=window_lower,
)
update_timing(soa_id, timing_id, payload)
return RedirectResponse(url=f"/ui/soa/{soa_id}/timings", status_code=303)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix

AI 3 months ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

@router.post("/ui/soa/{soa_id}/timings/{timing_id}/delete")
def ui_delete_timing(request: Request, soa_id: int, timing_id: int):
delete_timing(soa_id, timing_id)
return RedirectResponse(url=f"/ui/soa/{soa_id}/timings", status_code=303)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix

AI 3 months ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

@pendingintent pendingintent self-assigned this Dec 17, 2025
@pendingintent pendingintent added the enhancement New feature or request label Dec 17, 2025
@pendingintent pendingintent added this to the v1.2-beta milestone Dec 17, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a Study Timing editor as part of the USDM (Unified Study Definition Model) integration effort. The changes enable users to create, edit, and manage timing information for Schedule of Activities (SoA), laying groundwork for ScheduledActivityInstances.

Key changes:

  • New timing CRUD endpoints with audit trail support
  • USDM JSON generators for arms, epochs, and timings
  • UI templates for managing study timing entities
  • Code junction logic to prevent duplicate code entries when updating timing attributes

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/soa_builder/web/routers/timings.py Core timing CRUD API and UI endpoints with code junction logic
src/soa_builder/web/schemas.py Pydantic schemas for timing creation and updates
src/soa_builder/web/templates/timings.html HTML template for timing management UI
src/soa_builder/web/templates/base.html Navigation link added for Study Timing page
src/soa_builder/web/initialize_database.py Database schema for timing and timing_audit tables
src/soa_builder/web/audit.py Audit trail function for timing operations
src/soa_builder/web/utils.py Helper function to fetch study timing types from DDF terminology
src/soa_builder/web/app.py Router registration and schema relocation
src/usdm/generate_study_timings.py USDM JSON generator for timings
src/usdm/generate_study_epochs.py USDM JSON generator for epochs
src/usdm/generate_arms.py USDM JSON generator for arms
tests/test_timings.py API endpoint tests for timing CRUD operations
tests/test_timing_audit.py Database-level audit trail verification tests
tests/test_timing_audit_endpoint.py HTTP endpoint tests for timing audit
tests/test_timings_code_junction.py Tests verifying code junction behavior for type and relative_to_from fields

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

str,
str,
]:
logger = logging.getLogger("usdm.generate_arms")
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger name should be 'usdm.generate_epochs' to match the module name, not 'usdm.generate_arms'.

Copilot uses AI. Check for mistakes.
else:
content = resp.json()
parsed_url = urlparse(url)
code_system = parsed_url.scheme + "//:" + parsed_url.netloc
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URL scheme construction is incorrect. The format 'scheme://:netloc' creates an invalid URL. Should be 'scheme://netloc' (removing the colon after //).

Suggested change
code_system = parsed_url.scheme + "//:" + parsed_url.netloc
code_system = parsed_url.scheme + "://" + parsed_url.netloc

Copilot uses AI. Check for mistakes.
return s or None


def _get_epoch_code_values(soa_id: int, type: str, code: str) -> Tuple[
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameter 'type' shadows the built-in type. Consider renaming to 'type_value' or 'epoch_type'.

Copilot uses AI. Check for mistakes.


def build_usdm_activities(soa_id: int) -> List[Dict[str, Any]]:
"""
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function name 'build_usdm_activities' is misleading; it builds epochs, not activities. Should be renamed to 'build_usdm_epochs'.

Suggested change
"""
"""
Backwards-compatible wrapper for :func:`build_usdm_epochs`.
This wrapper is retained to avoid breaking existing callers that still
reference :func:`build_usdm_activities`. New code should call
:func:`build_usdm_epochs` directly.
"""
return build_usdm_epochs(soa_id)
def build_usdm_epochs(soa_id: int) -> List[Dict[str, Any]]:
"""

Copilot uses AI. Check for mistakes.
- previousId?: string | null
- nextId?: string | null
- notes?: string[]
- instanceType: "Activity"
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation incorrectly states instanceType as 'Activity' when the code sets it to 'StudyEpoch' (line 150). Update documentation to match implementation.

Suggested change
- instanceType: "Activity"
- instanceType: "StudyEpoch"

Copilot uses AI. Check for mistakes.
- decode: string
- instanceTye: "Code"
}
- popultionIds?: int[]
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'popultionIds' to 'populationIds'.

Suggested change
- popultionIds?: int[]
- populationIds?: int[]

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +61
decode = term.get("submissionValue")

return code_system, code_system_version, decode
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable 'decode' is only assigned inside the for loop when a matching term is found. If no match is found, the function returns an undefined variable, causing a NameError. Initialize decode to a default value (e.g., None or empty string) before the loop.

Copilot uses AI. Check for mistakes.
Spelling

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 17, 2025 16:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

str,
str,
]:
logger = logging.getLogger("usdm.generate_arms")
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger name 'usdm.generate_arms' does not match the module name. It should be 'usdm.generate_epochs' to correctly identify log messages from this module.

Suggested change
logger = logging.getLogger("usdm.generate_arms")
logger = logging.getLogger("usdm.generate_epochs")

Copilot uses AI. Check for mistakes.
return code_system, code_system_version, decode


def build_usdm_activities(soa_id: int) -> List[Dict[str, Any]]:
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function name 'build_usdm_activities' is misleading. It should be named 'build_usdm_epochs' to match the module's purpose and the returned data structure which contains StudyEpoch objects.

Copilot uses AI. Check for mistakes.
Comment on lines +128 to +130
code_system, code_system_version, decode = _get_epoch_code_values(
soa_id, type, code
)
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function '_get_epoch_code_values' may not return all three expected values if the API call fails or no matching term is found, leading to unpacking errors. Ensure the function always returns a tuple of three values or handle potential exceptions.

Suggested change
code_system, code_system_version, decode = _get_epoch_code_values(
soa_id, type, code
)
try:
code_system, code_system_version, decode = _get_epoch_code_values(
soa_id, type, code
)
except Exception:
code_system = None
code_system_version = None
decode = None

Copilot uses AI. Check for mistakes.
return s or None


def _get_type_code_tuple(soa_id: int, code_uid: str) -> Tuple[str, str, str, str]:
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return type should be Tuple[List[str], List[str], List[str], List[str]] as the function returns lists, not individual strings. The return statement shows lists being returned for all four values.

Copilot uses AI. Check for mistakes.
code_decode = [r[2] for r in rows]
code_system_version = [r[3] for r in rows]

return code_code, code_decode, code_system, code_system_version
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return type should be Tuple[List[str], List[str], List[str], List[str]] as the function returns lists, not individual strings. The return statement shows lists being returned for all four values.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +47
def _get_data_origin_type_tuple(
soa_id: int, code_uid: str
) -> Tuple[str, str, str, str]:
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return type should be Tuple[List[str], List[str], List[str], List[str]] as the function returns lists, not individual strings. The return statement shows lists being returned for all four values.

Copilot uses AI. Check for mistakes.
code_decode = [r[2] for r in rows]
code_system_version = [r[3] for r in rows]

return code_code, code_decode, code_system, code_system_version
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return type should be Tuple[List[str], List[str], List[str], List[str]] as the function returns lists, not individual strings. The return statement shows lists being returned for all four values.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +14
<a href="/">Home</a> |
<a href="/ui/soa/{{ soa_id }}/timings">Study Timing</a> |
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template variable 'soa_id' may be undefined on pages where no SOA context exists, causing broken links or template errors. Consider conditionally rendering this link only when 'soa_id' is available.

Suggested change
<a href="/">Home</a> |
<a href="/ui/soa/{{ soa_id }}/timings">Study Timing</a> |
<a href="/">Home</a> |
{% if soa_id %}
<a href="/ui/soa/{{ soa_id }}/timings">Study Timing</a> |
{% endif %}

Copilot uses AI. Check for mistakes.
sv_to_code = {}
# Mapping for Relative To/From (C201265)
try:
sv_to_code_rtf = get_study_timing_type("C201265")
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable name 'sv_to_code_rtf' is ambiguous. Consider renaming to 'sv_to_code_relative_to_from' for clarity.

Copilot uses AI. Check for mistakes.
Comment on lines 2151 to 2156
"""Visit update handled in routers/visits.py"""


"""Visit detail handled in routers/visits.py"""


"""Activity creation handled in routers/activities.py"""


"""Activity update handled in routers/activities.py"""


"""Activity detail handled in routers/activities.py"""


"""Epoch CRUD and reorder endpoints refactored into epochs_router."""
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comment strings lack context and do not follow proper docstring or comment formatting. Consider removing them or converting to proper single-line comments with '#' for better code clarity.

Copilot uses AI. Check for mistakes.
Spelling

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 17, 2025 16:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

str,
str,
]:
logger = logging.getLogger("usdm.generate_arms")
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger name 'usdm.generate_arms' is incorrect for the epochs module. It should be 'usdm.generate_epochs' to match the module name.

Copilot uses AI. Check for mistakes.
top_terms = content.get("terms")
for term in top_terms:
if term.get("conceptId") == code:
decode = term.get("submissionValue")
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable 'decode' is assigned inside a conditional loop but used outside without initialization. If no matching term is found, 'decode' will be undefined when returned, causing a NameError.

Copilot uses AI. Check for mistakes.
return s or None


def _get_epoch_code_values(soa_id: int, type: str, code: str) -> Tuple[
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _get_epoch_code_values function lacks test coverage. Consider adding tests to verify the CDISC API integration, error handling when the API is unavailable, and correct parsing of the response.

Copilot uses AI. Check for mistakes.
used_nums.add(int(tail))
else:
logger.warning(
"Invalid timing_uid format encountered (ignored): %s",
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning message 'Invalid timing_uid format encountered (ignored)' could be more helpful by explaining what the expected format is. Consider: 'Invalid timing_uid format encountered (expected Timing_): %s'

Suggested change
"Invalid timing_uid format encountered (ignored): %s",
"Invalid timing_uid format encountered (expected Timing_<number>, ignored): %s",

Copilot uses AI. Check for mistakes.
@pendingintent pendingintent merged commit 7cd6f28 into master Dec 17, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants