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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ node_modules
.pnp.js
myenv/
venv/

*.md
Comment thread
Siddharthakhandelwal marked this conversation as resolved.

# testing
coverage
Expand Down
18 changes: 5 additions & 13 deletions PROJECT_DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,21 @@ The frontend is a Single Page Application (SPA) built with React and bundled usi
- **Styling**: Vanilla CSS (`index.css`) emphasizing a dark-mode, premium UI with smooth transitions and glassmorphism elements.
- **Routing/State**: Managed primarily via React local state (`useState`, `useCallback`) within a primary layout orchestrator (`App.jsx`).
- **Key Libraries**:
- `@supabase/supabase-js`: Handles user authentication and session management.
- `3dmol`: Used in `MolViewer.jsx` to render interactive 3D models of protein structures based on PDB data.
- `chart.js` / `react-chartjs-2`: Powers the graphical rendering of pLDDT metrics (per-residue confidence lines/bars).
- `recharts`: Alternate charting library for other analytical visualizations.
- **Testing**: Setup with `vitest` and `@testing-library/react`.

### 2.2 Backend (FastAPI / Python)
The backend is a high-performance RESTful API that handles analytical computations, rate-limiting, authentication checks, and proxies external biological APIs.
The backend is a high-performance RESTful API that handles analytical computations, rate-limiting, and proxies external biological APIs.
- **Core Framework**: FastAPI, served via Uvicorn.
- **Authentication**: Validates Supabase JWTs attached to requests to protect endpoints.
- **Rate Limiting**: Implemented using `slowapi` to prevent abuse (e.g., 10 predictions/min, 30 UniProt searches/min).
- **Key Python Libraries**:
- `biotite`: Parses the PDB structures returned from ESMFold to reliably extract `b_factor` data, which maps to the pLDDT confidence scores.
- `biopython`: Powers the `/api/analyze` endpoint. Uses `Bio.SeqUtils.ProtParam.ProteinAnalysis` to calculate biochemical metrics.
- `requests` / `httpx`: Handles secure outbound HTTP calls to UniProt and ESMAtlas APIs.
- `numpy`: Fast mathematical aggregation and distribution calculations of pLDDT scores.
- `python-jose`: Secured JWT decoding for Supabase authentication.


### 2.3 Auxiliary Components
- **ESMFold Streamlit App (`esmfold-master` directory)**: Contains an alternative, standalone `streamlit_app.py` for interacting with ESMFold structure predictions natively in Python, acting potentially as a prototype or a specialized tool alongside the main stack.
Expand All @@ -52,13 +50,7 @@ The backend is a high-performance RESTful API that handles analytical computatio

## 3. Core Functionality & Implementation

### 3.1 Authentication Flow
- **Implementation**: The `AuthProvider.jsx` context wrapper initializes a Supabase client. Users must log in via `LoginPage.jsx`.
- Upon successful login, Supabase provides a JWT.
- The `App.jsx` constructs an `Authorization: Bearer <token>` header for all outward requests to the FastAPI backend.
- The backend's `authenticate_requests` middleware decodes this token using `SUPABASE_JWT_SECRET`. If invalid or missing, it blocks the request (`401 Unauthorized`).

### 3.2 Feature: Protein Structure Prediction
### 3.1 Feature: Protein Structure Prediction
- **Frontend Flow**: User enters an amino acid sequence in `SequenceInput.jsx`. `App.jsx` issues a POST to `/api/predict`.
- **Backend Flow**:
1. Sequence is validated by Pydantic (`PredictRequest`) against valid 20 standard amino acid characters. Length is constrained (10 to 2000 AAs).
Expand All @@ -73,12 +65,12 @@ The backend is a high-performance RESTful API that handles analytical computatio
- **Very Low**: < 50
- **Visualization**: Data returns to the frontend. `MolViewer.jsx` uses `3dmol` to spin up an interactive 3D WebGL viewer of the PDB output. `ConfidenceBar.jsx` and `PldtMetrics.jsx` visualize the confidence metrics using Chart.js.

### 3.3 Feature: UniProt Discovery & Search
### 3.2 Feature: UniProt Discovery & Search
- **Frontend Flow**: Switching the view to '*Discovery*' opens the `SearchPanel.jsx` and `DiscoveryTable.jsx`.
- **Filters**: Users can toggle 'Reviewed' (Swiss-Prot), Organism specificity, and Max Sequence Length.
- **Backend Flow (`/api/uniprot/search`)**: Proxy endpoint converts UI filters into a complex Solr query string (e.g., `(reviewed:true) AND (organism_id:9606) AND (length:[1 TO 1000])`). Fetches paginated JSON results from UniProt REST API, standardizing fields (Gene Name, Accession, Organism) for the frontend discovery table.

### 3.4 Feature: Lab Readiness Analysis
### 3.3 Feature: Lab Readiness Analysis
- When a user selects a protein from the Discovery Table, the view shifts to '*Analysis*'.
- **Frontend Flow**: Triggers concurrent requests to `/api/predict` (for structure) and `/api/analyze` (for properties).
- **Backend Flow (`/api/analyze`)**:
Expand Down
8 changes: 0 additions & 8 deletions backend/.env
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
VALID_AMINO_ACIDS=A,C,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y

# Supabase Auth Configuration
SUPABASE_URL=https://amimmaueterlwesbeatn.supabase.co
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFtaW1tYXVldGVybHdlc2JlYXRuIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQxMjgxOTgsImV4cCI6MjA4OTcwNDE5OH0.j1rRXxN6Ikzt13WgjCDT9gUcI2LihmzkwhX4NnPVNxU

# JWT Secret — IMPORTANT: Get this from Supabase Dashboard → Project Settings → API → JWT Secret
# Without this, the backend will skip JWT verification (dev mode)
SUPABASE_JWT_SECRET=

# CORS — comma-separated list of allowed origins
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173,http://localhost:3000
6 changes: 0 additions & 6 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,5 @@
# Amino acid whitelist (usually unchanged)
VALID_AMINO_ACIDS=A,C,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y

# Supabase Auth Configuration
# Get these from: https://supabase.com/dashboard → Project Settings → API
SUPABASE_URL=https://your-project-id.supabase.co
SUPABASE_ANON_KEY=your-supabase-anon-public-key
SUPABASE_JWT_SECRET=your-supabase-jwt-secret

# CORS — comma-separated list of allowed frontend origins
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173,http://localhost:3000
3 changes: 0 additions & 3 deletions backend/TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ from fastapi.testclient import TestClient
from unittest.mock import patch
import main

# If testing public functionality and avoiding middleware token checks:
main.SUPABASE_JWT_SECRET = ""

client = TestClient(main.app)

@patch("main.requests.post")
Expand Down
70 changes: 6 additions & 64 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
Protly Backend — FastAPI server for ESMFold protein structure prediction.

Endpoints:
GET /api/health → health check
POST /api/predict → accepts { sequence: str }, returns PDB + pLDDT data
GET /api/health → health check
POST /api/predict → accepts { sequence: str }, returns PDB + pLDDT data
GET /api/uniprot/search → proxy UniProt search
GET /api/uniprot/entry/{id} → fetch full UniProt entry
POST /api/analyze → Biopython lab-readiness metrics
"""

import re
Expand All @@ -14,15 +17,13 @@

from fastapi import FastAPI, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field, field_validator
import requests
import numpy as np
import biotite.structure.io as bsio
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
from jose import jwt, JWTError
from dotenv import load_dotenv

load_dotenv()
Expand All @@ -46,15 +47,6 @@
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

# ---------------------------------------------------------------------------
# Supabase Auth Config
# ---------------------------------------------------------------------------
SUPABASE_URL = os.getenv("SUPABASE_URL", "")
SUPABASE_JWT_SECRET = os.getenv("SUPABASE_JWT_SECRET", "")
SUPABASE_ANON_KEY = os.getenv("SUPABASE_ANON_KEY", "")

if not SUPABASE_JWT_SECRET:
logger.warning("SUPABASE_JWT_SECRET is not set — JWT auth verification will be skipped!")

# ---------------------------------------------------------------------------
# CORS — allow the Vite dev server and common local origins
Expand All @@ -68,7 +60,7 @@
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["GET", "POST", "OPTIONS"],
allow_headers=["Authorization", "Content-Type", "X-Requested-With"],
allow_headers=["Content-Type", "X-Requested-With"],
)


Expand All @@ -88,56 +80,6 @@ async def add_security_headers(request: Request, call_next):
return response


# ---------------------------------------------------------------------------
# JWT Authentication middleware
# ---------------------------------------------------------------------------
# Endpoints that do NOT require authentication
PUBLIC_PATHS = {"/api/health", "/docs", "/openapi.json", "/redoc"}


@app.middleware("http")
async def authenticate_requests(request: Request, call_next):
"""Validate Supabase JWT on protected endpoints."""
path = request.url.path

# Skip auth for public endpoints and CORS preflight
if path in PUBLIC_PATHS or request.method == "OPTIONS":
return await call_next(request)

# If no JWT secret configured, skip validation (dev mode)
if not SUPABASE_JWT_SECRET:
return await call_next(request)

# Extract Bearer token
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer "):
return JSONResponse(
status_code=401,
content={"detail": "Missing or invalid Authorization header. Please sign in."},
)

token = auth_header.split(" ", 1)[1]

try:
payload = jwt.decode(
token,
SUPABASE_JWT_SECRET,
algorithms=["HS256"],
audience="authenticated",
)
# Attach user info to request state for downstream handlers
request.state.user_id = payload.get("sub")
request.state.user_email = payload.get("email", "")
except JWTError as exc:
logger.warning("JWT verification failed: %s", exc)
return JSONResponse(
status_code=401,
content={"detail": "Invalid or expired token. Please sign in again."},
)

return await call_next(request)


# ---------------------------------------------------------------------------
# Request logging middleware
# ---------------------------------------------------------------------------
Expand Down
2 changes: 0 additions & 2 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ biotite
numpy
slowapi
biopython
python-jose[cryptography]
python-dotenv
supabase
pytest
pytest-asyncio
httpx
Expand Down
3 changes: 0 additions & 3 deletions backend/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
from unittest.mock import patch
import main

# Force bypass of JWT auth for testing by removing the secret
main.SUPABASE_JWT_SECRET = ""

client = TestClient(main.app)


Expand Down
3 changes: 0 additions & 3 deletions backend/tests/test_subcellular.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
from fastapi.testclient import TestClient
import main

# Bypass JWT auth for testing
main.SUPABASE_JWT_SECRET = ""

client = TestClient(main.app)


Expand Down
4 changes: 0 additions & 4 deletions frontend/.env
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
VITE_VALID_AMINO_ACIDS=A,C,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y

# Supabase Auth Configuration
VITE_SUPABASE_URL=https://amimmaueterlwesbeatn.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFtaW1tYXVldGVybHdlc2JlYXRuIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQxMjgxOTgsImV4cCI6MjA4OTcwNDE5OH0.j1rRXxN6Ikzt13WgjCDT9gUcI2LihmzkwhX4NnPVNxU

# Backend API base URL
VITE_API_BASE=http://localhost:8000
5 changes: 0 additions & 5 deletions frontend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,5 @@
# Amino acid whitelist (usually unchanged)
VITE_VALID_AMINO_ACIDS=A,C,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y

# Supabase Auth Configuration
# Get these from: https://supabase.com/dashboard → Project Settings → API
VITE_SUPABASE_URL=https://your-project-id.supabase.co
VITE_SUPABASE_ANON_KEY=your-supabase-anon-public-key

# Backend API base URL
VITE_API_BASE=http://localhost:8000
2 changes: 1 addition & 1 deletion frontend/TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,4 @@ describe('SequenceInput Component', () => {
### Best Practices

- **Query by Accessibility**: Prefer `getByRole`, `getByLabelText`, and `getByText`. Avoid querying by CSS classes, as tests should test functionality, not DOM structure.
- **Mock External Hooks**: If your component relies on global states (like `useAuth`), mock the hook using `vi.spyOn(AuthProvider, 'useAuth')` so you can test the component in isolation.
- **Mock External Hooks**: If your component relies on global state or context hooks, mock the hook using `vi.spyOn` so you can test the component in isolation.
Loading
Loading