A full-stack web application for monitoring, analysing, and visualising directed evolution experiments.
Built as an MSc Bioinformatics Group Project (2026).
For a help guide of the web portal see HELP_GUIDE.md.
- Features
- Tech Stack
- Prerequisites
- Installation
- Running the Application
- Project Structure
- API Overview
- Environment Variables
- Experiment management — create experiments linked to a UniProt protein accession and a plasmid FASTA sequence, with automatic FASTA validation and invalid-character checks
- Plasmid validation — 6-frame ORF detection with exact, fuzzy-window, and Smith–Waterman fallback matching; failure messages now include the best fuzzy near-miss frame, identity score, and first mismatching amino-acid position
- Data upload — parse TSV/JSON files of variant activity scores; column synonym mapping with optional manual override; duplicate-row detection blocks ingestion before any processing; normalise and store per-generation
- Column mapping preview —
/preview-mappingendpoint returns the auto-detected header mapping before committing data, allowing the user to confirm or correct it - Sequence analysis — Needleman–Wunsch global alignment with circular-plasmid rotation correction to detect synonymous and non-synonymous mutations at every variant position
- Analysis dashboard with four visualisation tabs:
- Overview — matplotlib violin plot of activity score distribution per generation (server-rendered) + summary stats table
- Top Performers — ranked table of the 10 highest-activity variants with their mutation lists
- Mutation Fingerprint — Plotly 3-D residue heatmap showing mutation frequency across the protein structure (AlphaFold PDB or linear fallback)
- Activity Landscape — UMAP/PCA/t-SNE embedding of variant sequences coloured by activity score
- Mutation export — download all detected mutations for an experiment as a CSV file
- Sequence analysis button — triggers background job; button changes to Analysing… → Analysis Complete! → Re-analyse; elapsed timer and auto-polling banner update every 5 s without page refresh
- UniProt integration — automatic fetch and caching of protein features (domains, active sites, etc.) and raw FASTA sequence
- Authentication — bcrypt-hashed user accounts with server-side Flask-Session cookies
| Layer | Technology |
|---|---|
| Frontend | Next.js 14 (App Router), TypeScript, Tailwind CSS, shadcn/ui, react-plotly.js |
| Backend | Flask 3, Python 3.13, SQLAlchemy 2, matplotlib, numpy, pandas |
| Database | PostgreSQL via Neon (serverless, no local install required) |
| Auth | bcrypt + Flask-Session (filesystem) |
| Package manager | pnpm (frontend), pip (backend), npm (root dev runner) |
- Python 3.10+ — python.org
- Node.js 18+ — nodejs.org
- pnpm —
npm install -g pnpm - Git — git-scm.com
A Neon free-tier account is required for the PostgreSQL database.
No local database installation is needed.
Note: Each email address can only be associated with one account.
git clone <repository-url>
cd Directed-Evolution-Portalcd backend
# Create and activate a virtual environment
python -m venv .venv
# Windows:
.venv\Scripts\activate
# macOS / Linux:
source .venv/bin/activate
# Install Python dependencies
pip install -r requirements.txtcd ../frontend
pnpm installSECRET_KEY=change-me-to-a-long-random-string
DATABASE_URL=postgresql://user:pass@host/dbname?sslmode=require
FRONTEND_URL=http://localhost:3000NEXT_PUBLIC_BACKEND_URL=http://localhost:8000From the project root:
npm install # first time only
npm run devThis starts:
- Flask API on http://localhost:8000
- Next.js dev server on http://localhost:3000
# Terminal 1 — backend
cd backend
# activate venv first
python run.py
# Terminal 2 — frontend
cd frontend
pnpm devDirected-Evolution-Portal/
├── backend/
│ ├── models/ SQLAlchemy ORM models
│ │ ├── user.py User account
│ │ └── experiment.py Experiment, VariantData, Mutation
│ ├── routes/
│ │ ├── auth.py POST /api/auth/register|login|logout, GET /api/auth/session
│ │ ├── experiments/ Experiment routes (Blueprint package)
│ │ │ ├── core.py CRUD: POST|GET /api/experiments, GET|PATCH|DELETE /<id>
│ │ │ ├── upload.py POST /<id>/preview-mapping, POST /<id>/upload-data
│ │ │ ├── variants.py GET /<id>/variants, GET /<id>/top-performers
│ │ │ ├── analysis.py POST /<id>/analyze-sequences
│ │ │ ├── fingerprint.py GET /<id>/fingerprint/<vid>, fingerprint3d, fingerprint_linear
│ │ │ └── export.py GET /<id>/mutations/export, GET /<id>/plots/activity-distribution
│ │ ├── uniprot.py GET /api/uniprot/<accession>, GET /api/uniprot/<accession>/fasta
│ │ └── landscape.py GET /api/experiments/<id>/landscape
│ ├── services/
│ │ ├── experiment_service.py High-level experiment CRUD and orchestration
│ │ ├── user_service.py User creation and lookup
│ │ ├── sequence_analyzer.py NW alignment, rotation offset, mutation detection
│ │ ├── sequence_tools.py Low-level sequence utilities
│ │ ├── plasmid_validation.py ORF detection, 6-frame translation, fuzzy/SW fallback QC
│ │ ├── activity_calculator.py Score normalisation
│ │ ├── experimental_data_parser.py TSV/JSON → VariantData (duplicate-row guard)
│ │ ├── fingerprint_plot.py PDB structure + mutation frequency heatmap
│ │ ├── landscape_service.py UMAP / PCA embedding
│ │ ├── uniprot_client.py UniProt REST client with disk cache
│ │ ├── errors.py Shared application error types
│ │ └── staging.py Staging/preview helpers
│ ├── config.py
│ ├── database.py
│ ├── run.py
│ └── requirements.txt
│
├── frontend/
│ ├── app/
│ │ ├── dashboard/
│ │ │ ├── analysis/page.tsx Analysis dashboard (4 tabs)
│ │ │ ├── experiments/ Experiment list + detail
│ │ │ └── new-experiment/ Creation wizard
│ │ └── page.tsx Landing / auth gate
│ ├── components/
│ │ ├── analysis/
│ │ │ ├── activity-distribution-chart.tsx
│ │ │ ├── top-performers-table.tsx
│ │ │ ├── mutation-fingerprint.tsx
│ │ │ └── activity-landscape.tsx
│ │ ├── dashboard-nav.tsx Sidebar navigation
│ │ └── ui/ shadcn/ui primitives
│ ├── hooks/
│ ├── lib/types.ts Shared TypeScript types
│ └── package.json
│
├── HELP_GUIDE.md
├── package.json Root dev runner (concurrently)
└── README.md
| Method | Path | Description |
|---|---|---|
| POST | /api/auth/register |
Create account |
| POST | /api/auth/login |
Log in (sets session cookie) |
| POST | /api/auth/logout |
Destroy session |
| GET | /api/auth/session |
Current user |
| GET | /api/experiments |
List experiments for logged-in user |
| POST | /api/experiments |
Create experiment (accession + plasmid FASTA) |
| GET | /api/experiments/<id> |
Experiment detail + variants |
| PATCH | /api/experiments/<id> |
Update name / metadata |
| DELETE | /api/experiments/<id> |
Delete experiment and all variants |
| GET | /api/experiments/<id>/variants |
Paginated variant list |
| GET | /api/experiments/<id>/top-performers |
Top N variants by activity score |
| POST | /api/experiments/<id>/preview-mapping |
Preview auto-detected column mapping before upload |
| POST | /api/experiments/<id>/upload-data |
Upload TSV/JSON of variant data (duplicate-row guard) |
| POST | /api/experiments/<id>/analyze-sequences |
Run mutation analysis (NW alignment) |
| GET | /api/experiments/<id>/mutations/export |
Download mutations as CSV |
| GET | /api/experiments/<id>/plots/activity-distribution |
PNG violin plot (matplotlib) |
| GET | /api/experiments/<id>/fingerprint/<variant_id> |
Mutation fingerprint data |
| GET | /api/experiments/<id>/fingerprint3d/<variant_id> |
3-D residue heatmap (PDB-mapped) |
| GET | /api/experiments/<id>/fingerprint_linear/<variant_id> |
Linear (no-PDB) mutation heatmap fallback |
| GET | /api/experiments/<id>/landscape |
UMAP/PCA embedding for the experiment |
| GET | /api/uniprot/<accession> |
Fetch + cache UniProt protein features |
| GET | /api/uniprot/<accession>/fasta |
Fetch raw FASTA sequence from UniProt |
All endpoints require a valid session cookie except /api/auth/register and /api/auth/login.
| Variable | Required | Description |
|---|---|---|
SECRET_KEY |
✅ | Flask session signing key — use a long random string in production |
DATABASE_URL |
✅ | Full Neon PostgreSQL connection string with ?sslmode=require |
FRONTEND_URL |
✅ | Allowed CORS origin (e.g. http://localhost:3000) |
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_BACKEND_URL |
✅ | Flask API base URL (e.g. http://localhost:8000) |