Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
node_modules/
lib/
.vscode/
.venv/
__pycache__/
*.pyc
.DS_Store
.idea
7 changes: 7 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ npm test ||
false;
)

# Check Black (Python formatter)
python -m black backend --check ||
(
echo '🤔 Black Check Failed. Run: python -m black backend';
false;
)

# Check TypeScript
npm run test-compile ||
(
Expand Down
64 changes: 64 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Findable Backend (FastAPI)

This folder contains a minimal FastAPI backend that exposes a stubbed **query-to-facets** endpoint for experimentation and PoC work.

## Requirements

- Python 3.9+
- `pip`

## Set up a Python virtual environment

From the repo root:

```bash
python -m venv backend/.venv
source backend/.venv/bin/activate
python -m pip install --upgrade pip
```

## Install dependencies

From the repo root:

```bash
pip install -r backend/requirements.txt
```

## Run the server

From the repo root:

```bash
uvicorn backend.main:app --reload
```

The app will start on `http://127.0.0.1:8000` by default.

## Format code

From the repo root:

```bash
python -m black backend
```

### Endpoint

- **Method:** `POST`
- **Path:** `/api/v0/facets`
- **Request body:**

```json
{ "query": "string" }
```

- **Example request:**

```bash
curl -sS -X POST "http://127.0.0.1:8000/api/v0/facets" \
-H "Content-Type: application/json" \
-d '{ "query": "public bam files for latino patients with diabetes" }'
```

- **Response:** currently returns a **hard-coded** JSON structure representing resolved facets for the query. This is intentionally stubbed for PoC and will be replaced by a real implementation later.
Empty file added backend/__init__.py
Empty file.
Empty file added backend/controllers/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions backend/controllers/facets_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from fastapi import APIRouter

from backend.services.facets_service import compute_facets_from_query
from backend.services.models import FacetsResponse

from backend.controllers.models import FacetsRequest

router = APIRouter(prefix="/api/v0/facets", tags=["facets"])


@router.post("")
def get_facets(payload: FacetsRequest) -> FacetsResponse:
"""
Return a stubbed query-to-facets response for PoC purposes.
"""
return compute_facets_from_query()
11 changes: 11 additions & 0 deletions backend/controllers/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from pydantic import BaseModel


class FacetsRequest(BaseModel):
"""
Request model for the /api/v0/facets endpoint.
Attributes:
query (str): The search query string to retrieve facets for.
"""

query: str
9 changes: 9 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from fastapi import FastAPI

from backend.controllers.facets_controller import router as facets_router


app = FastAPI(title="Findable API")

# Register controller(s)
app.include_router(facets_router)
4 changes: 4 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fastapi==0.115.0
uvicorn[standard]==0.30.0
pydantic==2.9.0
black==24.10.0
Empty file added backend/services/__init__.py
Empty file.
68 changes: 68 additions & 0 deletions backend/services/facets_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from __future__ import annotations

from backend.services.models import FacetsResponse


def compute_facets_from_query() -> FacetsResponse:
"""Return a stubbed query-to-facets response for PoC purposes.

This function will later be replaced by a real implementation that uses
LLM-backed intent parsing and facet resolution.
"""

return FacetsResponse.model_validate(
{
"query": "public bam files for latino foobar patients with diabetes or foobaz",
"facets": [
{
"facet": "Access",
"selectedValues": [
{
"term": "Granted",
"mention": "public",
}
],
},
{
"facet": "Diagnosis",
"selectedValues": [
{
"term": "MONDO:0005015",
"mention": "diabetes", # Mention resolved but value does not exist in dev
},
{
"term": "unknown",
"mention": "foobaz", # Mention resolved to a disease but does not match a known term
},
],
},
{
"facet": "File Format",
"selectedValues": [
{
"term": ".bam",
"mention": "bam",
}
],
},
{
"facet": "Reported Ethnicity",
"selectedValues": [
{
"term": "Hispanic or Latino",
"mention": "latino",
},
],
},
{
"facet": "unknown",
"selectedValues": [
{
"term": "unknown",
"mention": "foobar", # Mention not resolved to a facet or facet term
}
],
},
],
}
)
43 changes: 43 additions & 0 deletions backend/services/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from pydantic import BaseModel


# Model of a selected facet value and its mention (that is, the original query text that resolved to the value).
class SelectedValue(BaseModel):
"""
Represents a selected facet value and its mention in the original query text.

Attributes:
term (str): The resolved facet value.
mention (str): The original query text that resolved to the value.
"""

term: str
mention: str


# Model of a selected facet and its resolved values.
class FacetSelection(BaseModel):
"""
Represents a selected facet and its resolved values.

Attributes:
facet (str): The name of the facet.
selectedValues (list[SelectedValue]): The list of selected values for this facet.
"""

facet: str
selectedValues: list[SelectedValue]


# Model of the selected facets for a given query, resolved and normalized via LLM.
class FacetsResponse(BaseModel):
"""
Represents the selected facets for a given query, resolved and normalized via LLM.

Attributes:
query (str): The original query string.
facets (list[FacetSelection]): The list of selected facets and their values.
"""

query: str
facets: list[FacetSelection]