diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 6eba5b8..8197bfe 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -16,7 +16,7 @@ jobs: services: mysql: - image: mysql:8.0.35 + image: mysql:8.4 ports: - 3306:3306 env: diff --git a/CHANGELOG b/CHANGELOG index dbfe136..18709fd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,15 +3,32 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## Unreleased + +## [1.0.0] - 2026-04-24 + +### Changed + +* Upgraded MySQL version for GitHub CI to 8.4 +* Updated the ORM to reflect [changes](https://github.com/wtsi-npg/npg_tracking/pull/969) + to the production database. +* Regenerated the ORM with the current (4.0.3) version of `sqlacodegen`. +* Renamed `tests/test_schema.py` to `tests/test_retrieval.py` to reflect the + module name that is being tested. + +### Added + +* `validate_runfolder` function to npgtracking.db.retrieval + ## [0.2.0] - 2025-12-12 ### Added -* get_run_by_id function added to retrieval module +* `get_run_by_id` function added to retrieval module ### Fixed -* run.batch_id is now a varchar +* `run.batch_id` is now a varchar ## [0.1.1] - 2025-12-11 diff --git a/README.md b/README.md index fb1be41..fdf7a24 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The code in this package was tested for read-only operations. Business logic for `create` and `update` operation for different database tables is implemented in the Perl package. We advise against performing `write` operations using this ORM. -This ORM has been auto-generated with [`sqlacodegen 3.1.1`](https://pypi.org/project/sqlacodegen/3.1.1/) +This ORM has been auto-generated with [`sqlacodegen 4.0.3`](https://pypi.org/project/sqlacodegen/4.0.3/) ``` sqlacodegen --generator declarative mysql+pymysql://user:pass@host:port/dbname > src/npgtracking/db/schema.py diff --git a/src/npgtracking/db/retrieval.py b/src/npgtracking/db/retrieval.py index e424122..0ab8cdb 100644 --- a/src/npgtracking/db/retrieval.py +++ b/src/npgtracking/db/retrieval.py @@ -107,3 +107,25 @@ def get_run_by_id( raise ValueError("Can't get one run without an argument") result = session.execute(statement).scalar_one_or_none() return result + + +def validate_runfolder(session: Session, id_run: int, runfolder_name: str) -> bool: + """Validates the runfolder name against the run ID. + + Errors if run with the given ID does not exist. + + Args: + session : + Database session + id_run : + Tracking run ID + runfolder_name : + Runfolder name + Returns: + `True` if the runfolder name and run id belong to the same run, `False` + otherwise. + """ + run = session.execute(select(Run).where(Run.id_run == id_run)).scalar() + if not run: + raise ValueError(f"Run with ID {id_run} does not exist.") + return run.folder_name == runfolder_name diff --git a/src/npgtracking/db/schema.py b/src/npgtracking/db/schema.py index 2ad3c04..a9314c0 100644 --- a/src/npgtracking/db/schema.py +++ b/src/npgtracking/db/schema.py @@ -32,7 +32,7 @@ from sqlalchemy.dialects.mysql import BIGINT, CHAR, INTEGER, LONGBLOB, SMALLINT, TINYINT from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship -"""ORM for the tracking database schema. +"""ORM for the NPG tracking database schema. These classes were generated by sqlacodegen to provide access to the tables of the existing sequencing run tracking database schema. @@ -50,7 +50,9 @@ class Base(DeclarativeBase): class Designation(Base): __tablename__ = "designation" - id_designation: Mapped[int] = mapped_column(INTEGER, primary_key=True) + id_designation: Mapped[int] = mapped_column( + INTEGER(unsigned=True), primary_key=True + ) description: Mapped[str] = mapped_column(String(64), nullable=False) instrument_designation: Mapped[list["InstrumentDesignation"]] = relationship( @@ -62,12 +64,12 @@ class EntityType(Base): __tablename__ = "entity_type" __table_args__ = (Index("iscurrent", "iscurrent"),) - id_entity_type: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_entity_type: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) description: Mapped[str] = mapped_column( CHAR(64), nullable=False, server_default=text("''") ) iscurrent: Mapped[int] = mapped_column( - TINYINT, nullable=False, server_default=text("'0'") + TINYINT(unsigned=True), nullable=False, server_default=text("'0'") ) event_type: Mapped[list["EventType"]] = relationship( @@ -81,7 +83,9 @@ class EntityType(Base): class InstrumentModDict(Base): __tablename__ = "instrument_mod_dict" - id_instrument_mod_dict: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_instrument_mod_dict: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) description: Mapped[str] = mapped_column( CHAR(128), nullable=False, server_default=text("''") ) @@ -98,12 +102,14 @@ class InstrumentStatusDict(Base): Index("unique_instrdict_description", "description", unique=True), ) - id_instrument_status_dict: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_instrument_status_dict: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) description: Mapped[str] = mapped_column( CHAR(64), nullable=False, server_default=text("''") ) iscurrent: Mapped[int] = mapped_column( - TINYINT, nullable=False, server_default=text("'1'") + TINYINT(unsigned=True), nullable=False, server_default=text("'1'") ) instrument_status: Mapped[list["InstrumentStatus"]] = relationship( @@ -115,8 +121,12 @@ class Manufacturer(Base): __tablename__ = "manufacturer" __table_args__ = (Index("name", "name", unique=True),) - id_manufacturer: Mapped[int] = mapped_column(BIGINT, primary_key=True) - name: Mapped[Optional[str]] = mapped_column(CHAR(128)) + id_manufacturer: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) + name: Mapped[Optional[str]] = mapped_column( + CHAR(128, charset="latin1", collation="latin1_swedish_ci") + ) instrument_format: Mapped[list["InstrumentFormat"]] = relationship( "InstrumentFormat", back_populates="manufacturer" @@ -127,9 +137,11 @@ class RunLaneStatusDict(Base): __tablename__ = "run_lane_status_dict" __table_args__ = (Index("unique_rlstdict_description", "description", unique=True),) - id_run_lane_status_dict: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_run_lane_status_dict: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) description: Mapped[str] = mapped_column( - String(64), primary_key=True, server_default=text("''") + String(64), nullable=False, server_default=text("''") ) run_lane_status: Mapped[list["RunLaneStatus"]] = relationship( @@ -149,9 +161,9 @@ class RunStatusDict(Base): String(64), nullable=False, server_default=text("''") ) iscurrent: Mapped[int] = mapped_column( - TINYINT, nullable=False, server_default=text("'1'") + TINYINT(unsigned=True), nullable=False, server_default=text("'1'") ) - temporal_index: Mapped[int] = mapped_column(SMALLINT, nullable=False) + temporal_index: Mapped[int] = mapped_column(SMALLINT(unsigned=True), nullable=False) run_status: Mapped[list["RunStatus"]] = relationship( "RunStatus", back_populates="run_status_dict" @@ -162,7 +174,7 @@ class Tag(Base): __tablename__ = "tag" __table_args__ = (Index("u_tag", "tag", unique=True),) - id_tag: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_tag: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) tag: Mapped[str] = mapped_column( CHAR(32), nullable=False, server_default=text("''") ) @@ -184,7 +196,7 @@ class User(Base): Index("uidx_username", "username", unique=True), ) - id_user: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_user: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) iscurrent: Mapped[int] = mapped_column( TINYINT(1), nullable=False, server_default=text("'1'") ) @@ -222,7 +234,7 @@ class Usergroup(Base): groupname: Mapped[str] = mapped_column( String(32), nullable=False, server_default=text("''") ) - id_usergroup: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_usergroup: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) is_public: Mapped[int] = mapped_column( TINYINT(1), nullable=False, server_default=text("'0'") ) @@ -245,8 +257,8 @@ class Annotation(Base): Index("ann_idu", "id_user"), ) - id_annotation: Mapped[int] = mapped_column(BIGINT, primary_key=True) - id_user: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_annotation: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) + id_user: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) date: Mapped[datetime.datetime] = mapped_column( DateTime, nullable=False, server_default=text("'0000-00-00 00:00:00'") ) @@ -278,12 +290,12 @@ class EventType(Base): Index("evt_entityt", "id_entity_type"), ) - id_event_type: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_event_type: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) description: Mapped[str] = mapped_column( CHAR(64), nullable=False, server_default=text("''") ) id_entity_type: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) entity_type: Mapped["EntityType"] = relationship( @@ -301,22 +313,24 @@ class InstrumentFormat(Base): Index("id_mfctr", "id_manufacturer"), ) - id_instrument_format: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_instrument_format: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) id_manufacturer: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) iscurrent: Mapped[int] = mapped_column( TINYINT(1), nullable=False, server_default=text("'0'") ) default_tiles: Mapped[int] = mapped_column( - INTEGER, nullable=False, server_default=text("'0'") + INTEGER(unsigned=True), nullable=False, server_default=text("'0'") ) default_columns: Mapped[int] = mapped_column( - INTEGER, nullable=False, server_default=text("'0'") + INTEGER(unsigned=True), nullable=False, server_default=text("'0'") ) model: Mapped[Optional[str]] = mapped_column(CHAR(64)) - days_between_washes: Mapped[Optional[int]] = mapped_column(INTEGER) - runs_between_washes: Mapped[Optional[int]] = mapped_column(INTEGER) + days_between_washes: Mapped[Optional[int]] = mapped_column(INTEGER(unsigned=True)) + runs_between_washes: Mapped[Optional[int]] = mapped_column(INTEGER(unsigned=True)) manufacturer: Mapped["Manufacturer"] = relationship( "Manufacturer", back_populates="instrument_format" @@ -338,15 +352,17 @@ class TagFrequency(Base): Index("tf_id_tag", "id_tag"), ) - id_tag_frequency: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_tag_frequency: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) id_tag: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) id_entity_type: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) frequency: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'1'") + BIGINT(unsigned=True), nullable=False, server_default=text("'1'") ) entity_type: Mapped["EntityType"] = relationship( @@ -366,12 +382,14 @@ class User2usergroup(Base): Index("id_usergroup", "id_usergroup"), ) - id_user_usergroup: Mapped[int] = mapped_column(BIGINT, primary_key=True) - id_user: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_user_usergroup: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) + id_user: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) id_usergroup: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) - level: Mapped[Optional[int]] = mapped_column(TINYINT) + level: Mapped[Optional[int]] = mapped_column(TINYINT(unsigned=True)) user: Mapped["User"] = relationship("User", back_populates="user2usergroup") usergroup: Mapped["Usergroup"] = relationship( @@ -390,20 +408,20 @@ class Event(Base): Index("id_event_type", "id_event_type"), ) - id_event: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_event: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) id_event_type: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) date: Mapped[datetime.datetime] = mapped_column( DateTime, nullable=False, server_default=text("'0000-00-00 00:00:00'") ) entity_id: Mapped[int] = mapped_column( - BIGINT, + BIGINT(unsigned=True), nullable=False, server_default=text("'0'"), comment="the id of the entity having id_event_type.id_entity_type", ) - id_user: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_user: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) notification_sent: Mapped[datetime.datetime] = mapped_column( TIMESTAMP, nullable=False, server_default=text("'0000-00-00 00:00:00'") ) @@ -425,12 +443,14 @@ class Instrument(Base): Index("name", "name", unique=True), ) - id_instrument: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_instrument: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) name: Mapped[str] = mapped_column( - CHAR(32), primary_key=True, server_default=text("''") + CHAR(32, charset="ascii", collation="ascii_general_ci"), + nullable=False, + server_default=text("''"), ) id_instrument_format: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) external_name: Mapped[str] = mapped_column( CHAR(32), nullable=False, server_default=text("''") @@ -446,7 +466,7 @@ class Instrument(Base): mirroring_host: Mapped[Optional[str]] = mapped_column(String(16)) staging_dir: Mapped[Optional[str]] = mapped_column(String(128)) latest_contact: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime) - percent_complete: Mapped[Optional[int]] = mapped_column(TINYINT) + percent_complete: Mapped[Optional[int]] = mapped_column(TINYINT(unsigned=True)) lab: Mapped[Optional[str]] = mapped_column(String(10)) instrument_format: Mapped["InstrumentFormat"] = relationship( @@ -480,12 +500,14 @@ class InstrumentAnnotation(Base): Index("ia_idinstrument", "id_instrument"), ) - id_instrument_annotation: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_instrument_annotation: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) id_instrument: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) id_annotation: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) annotation: Mapped["Annotation"] = relationship( @@ -509,9 +531,11 @@ class InstrumentDesignation(Base): Index("id_ii", "id_instrument"), ) - id_instrument_designation: Mapped[int] = mapped_column(BIGINT, primary_key=True) - id_instrument: Mapped[int] = mapped_column(BIGINT, nullable=False) - id_designation: Mapped[int] = mapped_column(INTEGER, nullable=False) + id_instrument_designation: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) + id_instrument: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) + id_designation: Mapped[int] = mapped_column(INTEGER(unsigned=True), nullable=False) designation: Mapped["Designation"] = relationship( "Designation", back_populates="instrument_designation" @@ -538,17 +562,19 @@ class InstrumentMod(Base): Index("im_id_user", "id_user"), ) - id_instrument_mod: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_instrument_mod: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) id_instrument: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) id_instrument_mod_dict: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) date_added: Mapped[datetime.datetime] = mapped_column(DateTime, nullable=False) - id_user: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_user: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) iscurrent: Mapped[int] = mapped_column( - TINYINT, nullable=False, server_default=text("'0'") + TINYINT(unsigned=True), nullable=False, server_default=text("'0'") ) date_removed: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime) @@ -579,19 +605,21 @@ class InstrumentStatus(Base): Index("is_idu", "id_user"), ) - id_instrument_status: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_instrument_status: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) id_instrument: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) id_instrument_status_dict: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) date: Mapped[datetime.datetime] = mapped_column( DateTime, nullable=False, server_default=text("'0000-00-00 00:00:00'") ) - id_user: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_user: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) iscurrent: Mapped[int] = mapped_column( - TINYINT, nullable=False, server_default=text("'0'") + TINYINT(unsigned=True), nullable=False, server_default=text("'0'") ) comment: Mapped[Optional[str]] = mapped_column(Text) @@ -618,27 +646,32 @@ class Run(Base): ["instrument_format.id_instrument_format"], name="r_idif", ), + Index("batch_id", "batch_id"), Index("id_instrument", "id_instrument"), Index("r_id_run_pair", "id_run_pair"), Index("r_idif", "id_instrument_format"), ) - id_run: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_run: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) id_instrument: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) priority: Mapped[int] = mapped_column( - TINYINT, nullable=False, server_default=text("'0'") + TINYINT(unsigned=True), nullable=False, server_default=text("'0'") ) is_paired: Mapped[int] = mapped_column( - TINYINT, nullable=False, server_default=text("'0'") + TINYINT(unsigned=True), nullable=False, server_default=text("'0'") + ) + id_instrument_format: Mapped[int] = mapped_column( + BIGINT(unsigned=True), nullable=False ) - id_instrument_format: Mapped[int] = mapped_column(BIGINT, nullable=False) team: Mapped[str] = mapped_column(CHAR(10), nullable=False) - actual_cycle_count: Mapped[Optional[int]] = mapped_column(BIGINT) - expected_cycle_count: Mapped[Optional[int]] = mapped_column(BIGINT) - id_run_pair: Mapped[Optional[int]] = mapped_column(BIGINT) - batch_id: Mapped[Optional[int]] = mapped_column(String(64)) + actual_cycle_count: Mapped[Optional[int]] = mapped_column(BIGINT(unsigned=True)) + expected_cycle_count: Mapped[Optional[int]] = mapped_column(BIGINT(unsigned=True)) + id_run_pair: Mapped[Optional[int]] = mapped_column(BIGINT(unsigned=True)) + batch_id: Mapped[Optional[str]] = mapped_column( + String(255), comment="LIMS-specific or user-defined identifier for run data" + ) flowcell_id: Mapped[Optional[str]] = mapped_column(String(64)) folder_name: Mapped[Optional[str]] = mapped_column(String(64)) folder_path_glob: Mapped[Optional[str]] = mapped_column(String(256)) @@ -677,10 +710,12 @@ class InstrumentStatusAnnotation(Base): ) id_instrument_status_annotation: Mapped[int] = mapped_column( - BIGINT, primary_key=True + BIGINT(unsigned=True), primary_key=True + ) + id_instrument_status: Mapped[int] = mapped_column( + BIGINT(unsigned=True), nullable=False ) - id_instrument_status: Mapped[int] = mapped_column(BIGINT, nullable=False) - id_annotation: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_annotation: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) annotation: Mapped["Annotation"] = relationship( "Annotation", back_populates="instrument_status_annotation" @@ -701,15 +736,17 @@ class RunAnnotation(Base): Index("ra_idrun", "id_run"), ) - id_run_annotation: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_run_annotation: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) id_run: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) id_annotation: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) - run_current_ok: Mapped[Optional[int]] = mapped_column(TINYINT) - current_cycle: Mapped[Optional[int]] = mapped_column(BIGINT) + run_current_ok: Mapped[Optional[int]] = mapped_column(TINYINT(unsigned=True)) + current_cycle: Mapped[Optional[int]] = mapped_column(BIGINT(unsigned=True)) annotation: Mapped["Annotation"] = relationship( "Annotation", back_populates="run_annotation" @@ -725,17 +762,22 @@ class RunLane(Base): Index("uq_id_run_position", "id_run", "position", unique=True), ) - id_run_lane: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_run_lane: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) id_run: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) tile_count: Mapped[int] = mapped_column( - INTEGER, nullable=False, server_default=text("'0'") + INTEGER(unsigned=True), nullable=False, server_default=text("'0'") ) tracks: Mapped[int] = mapped_column( - INTEGER, nullable=False, server_default=text("'0'"), comment="Double=2" + INTEGER(unsigned=True), + nullable=False, + server_default=text("'0'"), + comment="Double=2", + ) + position: Mapped[Optional[int]] = mapped_column( + INTEGER(unsigned=True), server_default=text("'0'") ) - position: Mapped[Optional[int]] = mapped_column(INTEGER, server_default=text("'0'")) run: Mapped["Run"] = relationship("Run", back_populates="run_lane") run_lane_annotation: Mapped[list["RunLaneAnnotation"]] = relationship( @@ -763,17 +805,17 @@ class RunRead(Base): Index("uq_id_run_order", "id_run", "read_order", unique=True), ) - id_run_read: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_run_read: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) id_run: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) intervention: Mapped[int] = mapped_column( TINYINT(1), nullable=False, server_default=text("'0'") ) read_order: Mapped[Optional[int]] = mapped_column( - INTEGER, server_default=text("'0'") + INTEGER(unsigned=True), server_default=text("'0'") ) - expected_cycle_count: Mapped[Optional[int]] = mapped_column(BIGINT) + expected_cycle_count: Mapped[Optional[int]] = mapped_column(BIGINT(unsigned=True)) run: Mapped["Run"] = relationship("Run", back_populates="run_read") @@ -796,7 +838,7 @@ class RunStatus(Base): id_run_status: Mapped[int] = mapped_column(Integer, primary_key=True) id_run: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) date: Mapped[datetime.datetime] = mapped_column( DateTime, nullable=False, server_default=text("'0000-00-00 00:00:00'") @@ -804,7 +846,7 @@ class RunStatus(Base): id_run_status_dict: Mapped[int] = mapped_column( Integer, nullable=False, server_default=text("'0'") ) - id_user: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_user: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) iscurrent: Mapped[int] = mapped_column( TINYINT(1), nullable=False, server_default=text("'0'") ) @@ -824,9 +866,9 @@ class StCache(Base): Index("type", "type"), ) - id_cache: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_cache: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) id_run: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) type: Mapped[str] = mapped_column( CHAR(64), nullable=False, server_default=text("''") @@ -850,14 +892,14 @@ class TagRun(Base): Index("u_idrun_idtag", "id_run", "id_tag", unique=True), ) - id_tag_run: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_tag_run: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) id_run: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) id_tag: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) - id_user: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_user: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) date: Mapped[datetime.date] = mapped_column( Date, nullable=False, server_default=text("'0000-00-00'") ) @@ -880,9 +922,11 @@ class RunLaneAnnotation(Base): Index("rla_idrunlane", "id_run_lane"), ) - id_run_lane_annotation: Mapped[int] = mapped_column(BIGINT, primary_key=True) - id_run_lane: Mapped[int] = mapped_column(BIGINT, nullable=False) - id_annotation: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_run_lane_annotation: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) + id_run_lane: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) + id_annotation: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) annotation: Mapped["Annotation"] = relationship( "Annotation", back_populates="run_lane_annotation" @@ -907,19 +951,21 @@ class RunLaneStatus(Base): Index("rls_user", "id_user"), ) - id_run_lane_status: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_run_lane_status: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) id_run_lane: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) date: Mapped[datetime.datetime] = mapped_column( DateTime, nullable=False, server_default=text("'0000-00-00 00:00:00'") ) - id_user: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_user: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) iscurrent: Mapped[int] = mapped_column( TINYINT(1), nullable=False, server_default=text("'0'") ) id_run_lane_status_dict: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) run_lane: Mapped["RunLane"] = relationship( @@ -944,14 +990,16 @@ class TagRunLane(Base): Index("trl_id_user", "id_user"), ) - id_tag_run_lane: Mapped[int] = mapped_column(BIGINT, primary_key=True) + id_tag_run_lane: Mapped[int] = mapped_column( + BIGINT(unsigned=True), primary_key=True + ) id_run_lane: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) id_tag: Mapped[int] = mapped_column( - BIGINT, nullable=False, server_default=text("'0'") + BIGINT(unsigned=True), nullable=False, server_default=text("'0'") ) - id_user: Mapped[int] = mapped_column(BIGINT, nullable=False) + id_user: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) date: Mapped[datetime.date] = mapped_column( Date, nullable=False, server_default=text("'0000-00-00'") ) diff --git a/tests/test_schema.py b/tests/test_retrieval.py similarity index 83% rename from tests/test_schema.py rename to tests/test_retrieval.py index 3ed450e..52d01f6 100644 --- a/tests/test_schema.py +++ b/tests/test_retrieval.py @@ -21,10 +21,11 @@ from npgtracking.db.retrieval import ( get_run_by_id, get_runs_by_currentstatus, + validate_runfolder, ) -@m.describe("SchemaModel") +@m.describe("Data retrieval from the tracking database") class TestSchemaModel(object): @m.context("When retrieving run records from tracking DB") @m.context("When there are no runs having a current status and manufacturer name") @@ -102,3 +103,19 @@ def test_run_by_id(self, tracking_session): run = get_run_by_id(session=tracking_session, id_run=12345) assert run is None + + @m.context("When runfolder name and run ID are correct") + @m.it("Validator returns True, otherwise False") + def test_validate_runfolder(self, tracking_session): + assert ( + validate_runfolder(tracking_session, 51533, "430591-20251204_1628") is True + ) + assert ( + validate_runfolder(tracking_session, 51533, "430591-20251204_XXXX") is False + ) + + @m.context("When run ID is invalid") + @m.it("An error is raised") + def test_validate_runfolder_error(self, tracking_session): + with raises(ValueError, match="Run with ID 1 does not exist"): + validate_runfolder(tracking_session, 1, "430591-20251204_1628")