mrna-design-studio / core /models /worklist.py
offtargeteffect's picture
Deploy mRNA Design Studio (Docker SDK)
99f834c verified
Raw
History Blame Contribute Delete
5.64 kB
"""
Worklist domain model.
A Worklist is the user's active work queue β€” a curated collection of
sequences they want to analyze, score, assemble, or export.
Items can originate from three sources:
- database_import : selected rows from a DB connector
- generated : output of a generative model or genetic algorithm
- cds_optimized : protein sequence β†’ optimized CDS + UTR selection
"""
from __future__ import annotations
import uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any, Dict, Iterator, List, Literal, Optional
from core.models.sequence import mRNASequence
ItemOrigin = Literal["database_import", "generated", "cds_optimized", "manual"]
ItemStatus = Literal["pending", "analyzing", "analyzed", "error"]
@dataclass
class WorklistItem:
"""
A single entry in the worklist.
scores: populated by model runners β€” {model_name: score}
analysis: populated by SequenceAnalyzer β€” {metric_name: value}
"""
sequence: mRNASequence
origin: ItemOrigin
id: str = field(default_factory=lambda: str(uuid.uuid4()))
status: ItemStatus = "pending"
added_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
# Scoring results β€” keyed by model name
scores: Dict[str, float] = field(default_factory=dict)
# Analysis results β€” support multiple analyses
# Structure: {analysis_name: {metric: value, ...}}
# - "base_analysis": core sequence metrics (GC%, CAI, etc.)
# - model/analysis names: custom analysis results
analyses: Dict[str, Dict[str, Any]] = field(default_factory=dict)
# Notes / tags
notes: Optional[str] = None
tags: List[str] = field(default_factory=list)
@property
def name(self) -> str:
return self.sequence.name
@property
def has_scores(self) -> bool:
return bool(self.scores)
@property
def has_analyses(self) -> bool:
return bool(self.analyses)
@property
def base_analysis(self) -> Optional[Dict[str, Any]]:
"""Quick accessor for base sequence analysis results."""
return self.analyses.get("base_analysis")
def to_dict(self) -> Dict[str, Any]:
return {
"id": self.id,
"sequence_id": self.sequence.id,
"sequence_name": self.sequence.name,
"origin": self.origin,
"status": self.status,
"added_at": self.added_at.isoformat(),
"scores": self.scores,
"notes": self.notes,
"tags": self.tags,
}
@dataclass
class Worklist:
"""
The user's active sequence work queue.
Provides list-like access to WorklistItems with helpers for
filtering by origin, status, and tags.
"""
name: str = "Untitled Worklist"
id: str = field(default_factory=lambda: str(uuid.uuid4()))
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
items: List[WorklistItem] = field(default_factory=list)
# ── Mutation ────────────────────────────────────────────────────────────
def add(self, sequence: mRNASequence, origin: ItemOrigin = "manual") -> WorklistItem:
"""Add a sequence and return the new WorklistItem."""
item = WorklistItem(sequence=sequence, origin=origin)
self.items.append(item)
return item
def add_many(
self,
sequences: List[mRNASequence],
origin: ItemOrigin = "manual",
) -> List[WorklistItem]:
return [self.add(seq, origin) for seq in sequences]
def remove(self, item_id: str) -> bool:
"""Remove item by id. Returns True if found and removed."""
before = len(self.items)
self.items = [i for i in self.items if i.id != item_id]
return len(self.items) < before
def clear(self) -> None:
self.items.clear()
# ── Query ────────────────────────────────────────────────────────────────
def get(self, item_id: str) -> Optional[WorklistItem]:
return next((i for i in self.items if i.id == item_id), None)
def by_origin(self, origin: ItemOrigin) -> List[WorklistItem]:
return [i for i in self.items if i.origin == origin]
def by_status(self, status: ItemStatus) -> List[WorklistItem]:
return [i for i in self.items if i.status == status]
def with_tag(self, tag: str) -> List[WorklistItem]:
return [i for i in self.items if tag in i.tags]
def scored(self, model_name: Optional[str] = None) -> List[WorklistItem]:
"""Items that have scores. Filter by model_name if given."""
if model_name:
return [i for i in self.items if model_name in i.scores]
return [i for i in self.items if i.scores]
# ── Properties ──────────────────────────────────────────────────────────
@property
def count(self) -> int:
return len(self.items)
@property
def sequences(self) -> List[mRNASequence]:
return [i.sequence for i in self.items]
def __iter__(self) -> Iterator[WorklistItem]:
return iter(self.items)
def __len__(self) -> int:
return len(self.items)
def __repr__(self) -> str:
return f"Worklist(name={self.name!r}, count={self.count})"