Skip to content

[ENH] Add /imaging-modalities route for getting available imaging modality term instances & labels#513

Merged
alyssadai merged 7 commits intomainfrom
enh-502
Dec 11, 2025
Merged

[ENH] Add /imaging-modalities route for getting available imaging modality term instances & labels#513
alyssadai merged 7 commits intomainfrom
enh-502

Conversation

@rmanaem
Copy link
Copy Markdown
Collaborator

@rmanaem rmanaem commented Dec 4, 2025

Changes proposed in this pull request:

  • add imaging modalities endpoints and term metadata support
  • wire imaging modalities router into app
  • include imaging metadata in term instance responses
  • add imaging modality enum entry
  • add tests for imaging modalities routes

Example response from instances endpoint:

{
  "nb:Image": [
    {
      "TermURL": "nidm:T1Weighted",
      "Label": "T1-weighted image",
      "abbreviation": "T1w",
      "data_type": "anat"
    },
    {
      "TermURL": "nidm:FlowWeighted",
      "Label": "Blood-Oxygen-Level Dependent image",
      "abbreviation": "bold",
      "data_type": "func"
    }
  ]
}

Example response from vocab endpoint:

[
  {
    "vocabulary_name": "Neurobagel vocabulary of imaging modality terms",
    "namespace_url": "http://purl.org/nidash/nidm#",
    "namespace_prefix": "nidm",
    "version": "1.0.0",
    "terms": [
      {
        "name": "T1-weighted image",
        "id": "T1Weighted",
        "abbreviation": "T1w",
        "data_type": "anat"
      },
      {
        "name": "T2-weighted image",
        "id": "T2Weighted",
        "abbreviation": "T2w",
        "data_type": "anat"
      },
      {
        "name": "Diffusion-weighted image",
        "id": "DiffusionWeighted",
        "abbreviation": "dwi",
        "data_type": "anat"
      },
      {
        "name": "Blood-Oxygen-Level Dependent image",
        "id": "FlowWeighted",
        "abbreviation": "bold",
        "data_type": "func"
      },
      {
        "name": "Arterial Spin Labeling",
        "id": "ArterialSpinLabeling",
        "abbreviation": "asl",
        "data_type": "perf"
      },
      {
        "name": "Electroencephalography",
        "id": "Electroencephalography",
        "abbreviation": "eeg",
        "data_type": "eeg"
      },
      {
        "name": "Magnetoencephalography",
        "id": "Magnetoencephalography",
        "abbreviation": "meg",
        "data_type": "meg"
      },
      {
        "name": "Positron Emission Tomography",
        "id": "PositronEmissionTomography",
        "abbreviation": "pet",
        "data_type": "pet"
      }
    ]
  }
]

Checklist

This section is for the PR reviewer

  • PR has an interpretable title with a prefix ([ENH], [FIX], [REF], [TST], [CI], [MNT], [INF], [MODEL], [DOC]) (see our Contributing Guidelines for more info)
  • PR has a label for the release changelog or skip-release (to be applied by maintainers only)
  • PR links to GitHub issue with mention Closes #XXXX
  • Tests pass
  • Checks pass
  • If the PR changes the SPARQL query template, the default Neurobagel query file has also been regenerated

For new features:

  • Tests have been added

For bug fixes:

  • There is at least one test that would fail under the original bug conditions.

Summary by Sourcery

Add an imaging modalities API surface and extend term instance metadata to support imaging-specific fields.

New Features:

  • Expose a new /imaging-modalities route and corresponding /imaging-modalities/vocab endpoint for standardized imaging modality terms.

Enhancements:

  • Extend term instance responses for nb:Image to include imaging-specific metadata such as abbreviation and data_type.
  • Load and configure imaging modality vocabularies alongside existing assessment and diagnosis vocabularies.

Tests:

  • Add tests covering the new imaging modalities endpoints and their integration with vocabulary lookup.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Dec 4, 2025

Reviewer's Guide

Adds a new /imaging-modalities API surface built on the existing route factory, wires it into the app and configuration fetching, and extends term retrieval logic to enrich imaging modality term instances with vocabulary-derived metadata such as abbreviation and data_type, with corresponding tests.

Sequence diagram for the new /imaging-modalities term instance retrieval

sequenceDiagram
    actor Client
    participant FastAPIApp
    participant ImagingModalitiesRouter
    participant RouteFactory
    participant GetInstancesHandler
    participant CRUD as CRUD_get_terms
    participant VocabConfig

    Client->>FastAPIApp: GET /imaging-modalities
    FastAPIApp->>ImagingModalitiesRouter: Route request
    activate ImagingModalitiesRouter
    ImagingModalitiesRouter->>RouteFactory: create_get_instances_handler(DataElementURI.image)
    RouteFactory-->>ImagingModalitiesRouter: GetInstancesHandler
    ImagingModalitiesRouter->>GetInstancesHandler: Invoke handler
    deactivate ImagingModalitiesRouter
    activate GetInstancesHandler

    GetInstancesHandler->>VocabConfig: Load standardized vocabulary for Image
    VocabConfig-->>GetInstancesHandler: namespace_terms for nb:Image

    GetInstancesHandler->>CRUD: get_terms(data_element_URI="nb:Image")
    activate CRUD
    CRUD->>CRUD: Build term_label_dicts
    CRUD->>CRUD: Find matched_term in namespace_terms by term_id
    CRUD->>CRUD: Create term_entry with TermURL and Label
    CRUD->>CRUD: Add abbreviation and data_type when data_element_URI is nb:Image
    CRUD-->>GetInstancesHandler: list of term_entry objects
    deactivate CRUD

    GetInstancesHandler-->>FastAPIApp: JSON response with imaging modality term instances
    deactivate GetInstancesHandler
    FastAPIApp-->>Client: 200 OK with enriched imaging modality terms
Loading

Updated class diagram for DataElementURI and imaging modalities routing

classDiagram
    class DataElementURI {
        <<enumeration>>
        assessment : str = "nb:Assessment"
        diagnosis : str = "nb:Diagnosis"
        image : str = "nb:Image"
    }

    class StandardizedTermVocabularyNamespace {
        +str prefix
        +str uri
        +str label
    }

    class StandardizedTermVocabularyResponse {
        +list~StandardizedTermVocabularyNamespace~ namespaces
    }

    class ImagingModalitiesRouter {
        +GET_imaging_modalities()
        +GET_imaging_modalities_vocab() StandardizedTermVocabularyResponse
    }

    class RouteFactory {
        +create_get_instances_handler(data_element_uri: str)
        +create_get_vocab_handler(data_element_uri: str)
    }

    DataElementURI <.. ImagingModalitiesRouter : uses
    RouteFactory <.. ImagingModalitiesRouter : uses
    StandardizedTermVocabularyResponse <.. ImagingModalitiesRouter : returns
    StandardizedTermVocabularyNamespace <.. StandardizedTermVocabularyResponse : contains
Loading

File-Level Changes

Change Details Files
Expose imaging modalities as a first-class standardized term type with its own router and vocab configuration support.
  • Introduce DataElementURI.image enum value representing nb:Image.
  • Register Image as a configurable standardized variable id when fetching vocabularies from the configuration repository.
  • Add a new imaging_modalities FastAPI router that defines /imaging-modalities and /imaging-modalities/vocab endpoints using the existing route_factory handlers.
  • Include the new imaging_modalities router in the main application so the endpoints are served.
app/api/models.py
app/main.py
app/api/routers/imaging_modalities.py
Extend generic term retrieval to add imaging-specific metadata for imaging modality instances.
  • Refactor get_terms to first construct a base term entry with TermURL and Label, then conditionally enrich it when the data element URI corresponds to nb:Image.
  • Look up the matched term object from the configured namespace terms based on term ID and, for imaging, include abbreviation and data_type fields (or None if missing).
  • Preserve existing behavior and warnings for terms whose vocabularies are not recognized while adding the new metadata path.
app/api/crud.py
Add tests for imaging modalities endpoints and integrate imaging modalities into existing attribute factory tests.
  • Add a test for /imaging-modalities that verifies graph instances are returned with labels plus imaging-specific vocabulary metadata (abbreviation and data_type).
  • Configure env_settings.ALL_VOCABS in tests to include an nb:Image vocabulary with imaging modality terms and patch httpx.AsyncClient.post to return appropriate SPARQL results.
  • Extend parametrized tests for generic vocab endpoints to cover the imaging-modalities route and nb:Image vocab content in the mocked ALL_VOCABS.
tests/test_attribute_factory_routes.py

Assessment against linked issues

Issue Objective Addressed Explanation
#502 Add imaging modalities API support by introducing /imaging-modalities and /imaging-modalities/vocab routes, wiring them into the app, and shaping term instance responses so that imaging modality instances include Label, abbreviation, and data_type while preserving the existing response shape for other attributes.
#502 Extend startup vocabulary loading/configuration so that imaging modalities (nb:Image) are treated as a configurable standardized variable and their vocabularies (including abbreviation and data_type) are loaded alongside other vocab types.
#502 Add documentation and tests for imaging modalities: update communities documentation to note imaging modalities’ different handling, and provide test coverage for imaging modalities routes including happy-path vocab lookup, behavior with missing metadata, and auth behavior consistent with other standardized-term routes. The PR adds a happy-path test for /imaging-modalities with vocab lookup and extends the generic vocab endpoint test to include nb:Image, but it does not add explicit tests for missing metadata or auth behavior. There is also no change to the communities README or other documentation about imaging modalities in the diff.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@rmanaem rmanaem added the pr-minor Non-breaking feature or enhancement, will increment minor version (0.+1.0) label Dec 4, 2025
Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

We've reviewed this pull request using the Sourcery rules engine

@codecov
Copy link
Copy Markdown

codecov bot commented Dec 4, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.94%. Comparing base (8e07fb1) to head (9af3aea).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #513      +/-   ##
==========================================
+ Coverage   96.87%   96.94%   +0.06%     
==========================================
  Files          32       33       +1     
  Lines        1152     1177      +25     
==========================================
+ Hits         1116     1141      +25     
  Misses         36       36              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rmanaem rmanaem requested a review from alyssadai December 4, 2025 16:00
Copy link
Copy Markdown
Contributor

@alyssadai alyssadai left a comment

Choose a reason for hiding this comment

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

Thanks @rmanaem! I haven't tested this out locally, but the logic all makes sense to me 🎉

I left some suggestions mostly related to renaming some local variables for easier maintenance, since this will be our first instances endpoint that returns term metadata other than just labels + URIs, and some minor refactoring to reduce duplication.

Approving now, but feel free to re-request a review if you want another pair of eyes on the changes!

@alyssadai alyssadai added the release Create a release when this PR is merged label Dec 11, 2025
@alyssadai alyssadai merged commit a37055e into main Dec 11, 2025
13 checks passed
@alyssadai alyssadai deleted the enh-502 branch December 11, 2025 17:07
@neurobagel-bot
Copy link
Copy Markdown
Contributor

🚀 PR was released in v0.8.0 🚀

@neurobagel-bot neurobagel-bot bot added the released This issue/pull request has been released. label Dec 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-minor Non-breaking feature or enhancement, will increment minor version (0.+1.0) release Create a release when this PR is merged released This issue/pull request has been released.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add /imaging-modalities route for getting available imaging modality term instances & labels

2 participants