Skip to content
Open
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
4 changes: 4 additions & 0 deletions .devcontainer/config/.genologicsrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[genologics]
BASEURI=http://localhost:8443
USERNAME=fake_user
PASSWORD=fake_password
4 changes: 4 additions & 0 deletions .devcontainer/config/lims_backend_cred.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Fake LIMS backend credentials for local development
baseuri: http://localhost:8443
username: fake_user
password: fake_password
73 changes: 73 additions & 0 deletions .devcontainer/config/settings.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Local development settings for genomics-status
# Uses the CouchDB container from docker-compose

couch_server: http://admin:admin@couchdb:5984
username: admin
password: admin
port: 9761
redirect_uri: http://localhost:9761/login
cookie_secret: dev-cookie-secret-not-for-production

# CouchDB URL - use 'couchdb' hostname for docker network
couch_url: http://couchdb:5984

# Font Awesome - using free version CDN for dev
font_awesome_url: https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js

# LIMS backend credentials location
lims_backend_credential_location: /workspace/.devcontainer/config/lims_backend_cred.yaml

# Order portal - not needed for local dev
order_portal_credential_location: /workspace/.devcontainer/config/orderportal_cred.yaml

# Google OAuth - disabled in testing_mode
google_oauth:
key: fake-google-oauth-key
secret: fake-google-oauth-secret

# Zendesk - not needed for local dev
zendesk:
url: https://example.zendesk.com
username: fake@example.com
token: fake-zendesk-token

contact_person: dev@localhost

# Instruments - empty for local dev
instruments:
HiSeq:
DEV001: Dev-HiSeq
MiSeq:
DEV002: Dev-MiSeq

# Slack - not needed for local dev
slack:
token: fake-slack-token

# Jira - not needed for local dev
jira:
url: https://example.atlassian.net
user: fake@example.com
api_token: fake-jira-token
project_key: DEV

# Suggestion box log
sb_log: suggestion_box.log

# PSUL log - not needed for local dev
psul_log: /tmp/lims2db_projects.log

# Charon - not needed for local dev
charon:
url: https://example.com
api_token: fake-charon-token

# LIMS Dashboard
lims_dashboard_url: http://localhost:8080

# Server status - empty for local dev
server_status:
instruments: {}

# Reports path
reports_path: /tmp/reports
46 changes: 27 additions & 19 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile
{
"name": "Genomics Status",
"build": {
// Sets the run context to one level up instead of the .devcontainer folder.
"context": "..",
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
"dockerfile": "../Dockerfile"
},
"dockerComposeFile": "docker-compose.yml",
"service": "genomics-status",
"workspaceMounts": [
"source=${localWorkspaceFolder},target=/workspace,type=bind",
"source=${localWorkspaceFolder}/../StatusDB_seed_data,target=/StatusDB_seed_data,type=bind"
],
"workspaceFolder": "/workspace",

"initializeCommand": "bash -c 'if [ ! -d ../StatusDB_seed_data ]; then echo \"Cloning StatusDB_seed_data...\"; git clone https://github.com/SciLifeLab/StatusDB_seed_data.git ../StatusDB_seed_data; else echo \"StatusDB_seed_data already exists\"; fi'",
"features": {},

"customizations": {
"vscode": {
"extensions": [
"charliermarsh.ruff",
"ms-python.python",
"Tobermory.es6-string-html"
"Tobermory.es6-string-html"
]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [9761],

"forwardPorts": [9761, 5984],
"portsAttributes": {
"9761": {
"label": "Genomics Status",
"onAutoForward": "notify"
},
"5984": {
"label": "CouchDB",
"onAutoForward": "silent"
}
},

"postCreateCommand": "./.devcontainer/postCreateCommand.sh",
"postStartCommand": "cd run_dir && python ../status_app.py --develop --testing_mode",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
"remoteUser": "mambauser",
"mounts": [
"source=${localEnv:HOME}/conf,target=/home/mambauser/conf,type=bind,consistency=cached"
]
}

"remoteUser": "mambauser"
}
46 changes: 46 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
services:
couchdb:
# For local development, build from sibling repo:
build:
context: ../../StatusDB_seed_data
dockerfile: Dockerfile
# For production, use published image:
# image: ghcr.io/scilifelab/statusdb_seed_data:latest
environment:
- COUCHDB_USER=admin
- COUCHDB_PASSWORD=admin
volumes:
- couchdb-data:/opt/couchdb/data
ports:
- "5984:5984"
networks:
- genomics-status-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5984/_up"]
interval: 5s
timeout: 5s
retries: 10
start_period: 10s

genomics-status:
build:
context: ..
dockerfile: Dockerfile
volumes:
- ..:/workspace:cached
- ../../StatusDB_seed_data:/StatusDB_seed_data:cached
ports:
- "9761:9761"
networks:
- genomics-status-network
depends_on:
couchdb:
condition: service_healthy
working_dir: /workspace/run_dir
command: sleep infinity

volumes:
couchdb-data:

networks:
genomics-status-network:
29 changes: 27 additions & 2 deletions .devcontainer/postCreateCommand.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,31 @@

echo $PATH

# Install the package in development mode
cd /workspace
python -m pip install -e .
ln -s /home/mambauser/conf/.genologicsrc /home/mambauser/.genologicsrc
ln -s /home/mambauser/conf/genosqlrc.yaml /home/mambauser/.genosqlrc.yaml

# Set up config files for local development
# Link settings.yaml to run_dir
ln -sf /workspace/.devcontainer/config/settings.yaml /workspace/run_dir/settings.yaml

# Link .genologicsrc to home directory (where genologics library looks for it)
ln -sf /workspace/.devcontainer/config/.genologicsrc /home/mambauser/.genologicsrc

# Link genosqlrc.yaml to home directory (where genologics_sql library looks for it)
ln -sf /workspace/.devcontainer/config/.genosqlrc.yaml /home/mambauser/.genosqlrc.yaml

# If user has their own conf directory mounted, prefer those configs
if [ -d /home/mambauser/conf ]; then
[ -f /home/mambauser/conf/.genologicsrc ] && ln -sf /home/mambauser/conf/.genologicsrc /home/mambauser/.genologicsrc
[ -f /home/mambauser/conf/genosqlrc.yaml ] && ln -sf /home/mambauser/conf/genosqlrc.yaml /home/mambauser/.genosqlrc.yaml
[ -f /home/mambauser/conf/settings.yaml ] && ln -sf /home/mambauser/conf/settings.yaml /workspace/run_dir/settings.yaml
fi

echo ""
echo "=== Development Environment Ready ==="
echo "CouchDB: http://localhost:5984"
echo "CouchDB UI: http://localhost:5984/_utils"
echo "CouchDB creds: admin / admin"
echo "Genomics Status: http://localhost:9761 (after starting)"
echo "====================================="
123 changes: 123 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

Genomics Status is a Tornado web application for visualizing NGI Stockholm operations at SciLifeLab. It interfaces with StatusDB (CouchDB) for metadata storage and Genologics LIMS for laboratory information.

## Development Commands

### Running the Application

```bash
# From the run_dir directory
cd run_dir
python ../status_app.py --testing_mode

# With auto-reload for development
python ../status_app.py --develop --testing_mode

# Custom port
python ../status_app.py --testing_mode --port=9762
```

The `--testing_mode` flag disables Google OAuth authentication. The app runs on port 9761 by default.

### Dev Container

The project supports VS Code Dev Containers. When using Dev Containers:
- Ports 9761 (app) and 5984 (CouchDB) are forwarded
- The app auto-starts with `--develop --testing_mode`

### Linting

```bash
ruff check . # Check for issues
ruff check --fix . # Auto-fix issues
ruff format . # Format code
```

Ruff is configured in `pyproject.toml` with rules for pycodestyle, Pyflakes, isort, and pyupgrade.

### Running Tests

Tests use nose and require a running server:
```bash
# Start the server first, then in another terminal:
cd tests
nosetests test_integration.py
```

Tests are in the `tests/` directory and require `tests/test_items.yaml` for test data.

## Architecture

### Request Flow

1. Browser requests a page (e.g., `/projects/all`)
2. Tornado routes to the appropriate Handler class
3. Handler renders a template from `run_dir/design/` with server-side data
4. Template makes JavaScript calls to the REST API (`/api/v1/...`)
5. API handlers query CouchDB and return JSON
6. Client-side JavaScript builds the UI

### Code Structure

- `status_app.py` - Main application entry point, defines all URL routes and initializes the Tornado Application
- `status/` - Handler modules organized by feature:
- `util.py` - Base handler classes (`SafeHandler`, `UnsafeHandler`, `BaseHandler`)
- `projects.py`, `flowcells.py`, etc. - Feature-specific handlers
- `run_dir/` - Runtime directory (must run app from here):
- `design/` - Tornado HTML templates
- `static/` - CSS, JavaScript, images
- `settings.yaml` - Configuration (not in repo)

### Handler Inheritance

- `BaseHandler` - Base class with authentication, error handling
- `SafeHandler(BaseHandler)` - Requires authentication (use `@tornado.web.authenticated`)
- `UnsafeHandler(BaseHandler)` - No authentication required
- `SafeSocketHandler` - WebSocket with authentication

### Database Access

The app uses IBM Cloudant SDK to connect to CouchDB:
```python
# Available via self.application.cloudant in handlers
self.application.cloudant.post_view(db="...", ddoc="...", view="...", ...)
self.application.cloudant.get_document(db="...", doc_id="...")
```

Key databases: `projects`, `flowcells`, `worksets`, `gs_users`, `gs_configs`, `server_status`

### API Pattern

API handlers typically:
1. Inherit from `SafeHandler` or `UnsafeHandler`
2. Implement `get()`, `post()`, or `put()` methods
3. Set `Content-type: application/json` header
4. Return JSON via `self.write(json.dumps(data))`

### Templates

Templates use Tornado's templating with embedded Python:
- `{% ... %}` for control structures
- `{{ ... }}` for expressions
- Templates access `gs_globals`, `user`, and handler-passed variables

## Configuration

Required config files in `run_dir/`:
- `settings.yaml` - Main config (CouchDB, OAuth, Zendesk, Jira, Slack)
- `.genologicsrc` - LIMS API credentials
- `orderportal_cred.yaml` - Order portal API token

## User Roles

Defined in `status/util.py`:
- `admin` - Full admin access
- `pricing_admin` - Manage pricing
- `sample_requirements_admin` - Manage sample requirements
- `proj_coord` - Project coordinator
- `api_user` - JWT-authenticated API access
2 changes: 1 addition & 1 deletion run_dir/static/js/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ function load_presets() {
if (!jQuery.isEmptyObject(user_presets)) {
for (var preset in user_presets) {
userDefPresetsDropdown+='<li><a href="#" class="clickDropdownGetValue dropdown-item" style="cursor:pointer;" data-value="'+preset+'"> '+preset+'</a></li>';
allPresetsDropdownMod+='<li><a href="#" class="clickDropdownGetValue dropdown-item" style="cursor:pointer;" data-value="'+preset+'" data-origin="userdefined"">'+preset+'</a></li>';
allPresetsDropdownMod+='<li><a href="#" class="clickDropdownGetValue dropdown-item" style="cursor:pointer;" data-value="'+preset+'" data-origin="userdefined">'+preset+'</a></li>';
}
}
userDefPresetsDropdown+='</ul>';
Expand Down
Loading