Skip to content

Commit b25d9fd

Browse files
committed
Generate barcodes using old ISPyB standard
1 parent 7e9e15d commit b25d9fd

File tree

7 files changed

+107
-37
lines changed

7 files changed

+107
-37
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Make barcode column string
2+
3+
Revision ID: 730f6f07da68
4+
Revises: dea9c772d734
5+
Create Date: 2025-07-17 15:50:30.406046
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
import sqlalchemy as sa
11+
from alembic import op
12+
13+
# revision identifiers, used by Alembic.
14+
revision: str = '730f6f07da68'
15+
down_revision: Union[str, None] = 'dea9c772d734'
16+
branch_labels: Union[str, Sequence[str], None] = None
17+
depends_on: Union[str, Sequence[str], None] = None
18+
19+
20+
def upgrade() -> None:
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.alter_column('TopLevelContainer', 'barCode',
23+
existing_type=sa.UUID(),
24+
type_=sa.String(length=40),
25+
existing_nullable=True)
26+
# ### end Alembic commands ###
27+
28+
29+
def downgrade() -> None:
30+
# ### commands auto generated by Alembic - please adjust! ###
31+
op.alter_column('TopLevelContainer', 'barCode',
32+
existing_type=sa.String(length=40),
33+
type_=sa.UUID(),
34+
existing_nullable=True)
35+
# ### end Alembic commands ###

database/data.sql

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
-- PostgreSQL database dump
33
--
44

5-
-- Dumped from database version 17.4
5+
-- Dumped from database version 17.5
66
-- Dumped by pg_dump version 17.4
77

88
SET statement_timeout = 0;
@@ -305,7 +305,7 @@ CREATE TABLE public."TopLevelContainer" (
305305
"externalId" integer,
306306
comments character varying(255),
307307
"isInternal" boolean NOT NULL,
308-
"barCode" uuid,
308+
"barCode" character varying(40),
309309
"creationDate" timestamp with time zone DEFAULT now() NOT NULL
310310
);
311311

@@ -498,43 +498,43 @@ COPY public."TopLevelContainer" ("topLevelContainerId", "shipmentId", details, c
498498
--
499499

500500
COPY public.alembic_version (version_num) FROM stdin;
501-
dea9c772d734
501+
730f6f07da68
502502
\.
503503

504504

505505
--
506506
-- Name: Container_containerId_seq; Type: SEQUENCE SET; Schema: public; Owner: sample_handling
507507
--
508508

509-
SELECT pg_catalog.setval('public."Container_containerId_seq"', 2238, true);
509+
SELECT pg_catalog.setval('public."Container_containerId_seq"', 2247, true);
510510

511511

512512
--
513513
-- Name: PreSession_preSessionId_seq; Type: SEQUENCE SET; Schema: public; Owner: sample_handling
514514
--
515515

516-
SELECT pg_catalog.setval('public."PreSession_preSessionId_seq"', 423, true);
516+
SELECT pg_catalog.setval('public."PreSession_preSessionId_seq"', 427, true);
517517

518518

519519
--
520520
-- Name: Sample_sampleId_seq; Type: SEQUENCE SET; Schema: public; Owner: sample_handling
521521
--
522522

523-
SELECT pg_catalog.setval('public."Sample_sampleId_seq"', 2558, true);
523+
SELECT pg_catalog.setval('public."Sample_sampleId_seq"', 2579, true);
524524

525525

526526
--
527527
-- Name: Shipment_shipmentId_seq; Type: SEQUENCE SET; Schema: public; Owner: sample_handling
528528
--
529529

530-
SELECT pg_catalog.setval('public."Shipment_shipmentId_seq"', 307, true);
530+
SELECT pg_catalog.setval('public."Shipment_shipmentId_seq"', 308, true);
531531

532532

533533
--
534534
-- Name: TopLevelContainer_topLevelContainerId_seq; Type: SEQUENCE SET; Schema: public; Owner: sample_handling
535535
--
536536

537-
SELECT pg_catalog.setval('public."TopLevelContainer_topLevelContainerId_seq"', 980, true);
537+
SELECT pg_catalog.setval('public."TopLevelContainer_topLevelContainerId_seq"', 987, true);
538538

539539

540540
--

src/scaup/crud/top_level_containers.py

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from fastapi import HTTPException, status
22
from lims_utils.logging import app_logger
33
from lims_utils.models import Paged
4-
from sqlalchemy import func, insert, select
4+
from sqlalchemy import func, insert, select, update
55

66
from ..models.inner_db.tables import Shipment, TopLevelContainer
77
from ..models.top_level_containers import (
@@ -59,6 +59,17 @@ def _check_fields(
5959
@assert_not_booked
6060
@retry_if_exists
6161
def create_top_level_container(shipmentId: int | None, params: TopLevelContainerIn, token: str, autocreate=True):
62+
proposal = (
63+
None
64+
if shipmentId is None
65+
else inner_db.session.execute(
66+
select(
67+
func.concat(Shipment.proposalCode, Shipment.proposalNumber).label("reference"),
68+
Shipment.visitNumber,
69+
).filter(Shipment.id == shipmentId)
70+
).one()
71+
)
72+
6273
if params.code:
6374
_check_fields(params, token, shipmentId)
6475
elif params.type == "dewar" and autocreate:
@@ -84,28 +95,25 @@ def create_top_level_container(shipmentId: int | None, params: TopLevelContainer
8495

8596
new_code = f"DLS-BI-{dewar_number:04}"
8697

87-
proposal_reference = inner_db.session.scalar(
88-
select(func.concat(Shipment.proposalCode, Shipment.proposalNumber)).filter(Shipment.id == shipmentId)
89-
)
90-
91-
ext_resp = ExternalRequest.request(
92-
Config.ispyb_api.jwt,
93-
method="POST",
94-
url=f"/proposals/{proposal_reference}/dewar-registry",
95-
json={"facilityCode": new_code},
96-
)
97-
98-
if ext_resp.status_code != 201:
99-
app_logger.warning(
100-
"Error from Expeye while creating dewar registry entry with code %s: %s",
101-
new_code,
102-
ext_resp.text,
103-
)
104-
raise HTTPException(
105-
status_code=status.HTTP_424_FAILED_DEPENDENCY,
106-
detail="Invalid response while creating top level container in ISPyB",
98+
if proposal:
99+
ext_resp = ExternalRequest.request(
100+
Config.ispyb_api.jwt,
101+
method="POST",
102+
url=f"/proposals/{proposal.reference}/dewar-registry",
103+
json={"facilityCode": new_code},
107104
)
108105

106+
if ext_resp.status_code != 201:
107+
app_logger.warning(
108+
"Error from Expeye while creating dewar registry entry with code %s: %s",
109+
new_code,
110+
ext_resp.text,
111+
)
112+
raise HTTPException(
113+
status_code=status.HTTP_424_FAILED_DEPENDENCY,
114+
detail="Invalid response while creating top level container in ISPyB",
115+
)
116+
109117
params.code = new_code
110118

111119
if not params.name:
@@ -116,6 +124,17 @@ def create_top_level_container(shipmentId: int | None, params: TopLevelContainer
116124
{"shipmentId": shipmentId, **params.model_dump(exclude_unset=True)},
117125
)
118126

127+
if proposal:
128+
bar_code = f"{proposal.reference}-{proposal.visitNumber}-{container.id:07}"
129+
130+
# This is because some users expect a sequential numeric ID to make tracking how old a dewar is easier,
131+
# and because of historical reasons, some users are used to seeing the proposal/session number on there
132+
# as well, so I have to create barcodes this way.
133+
container = inner_db.session.scalar(
134+
update(TopLevelContainer).returning(TopLevelContainer).filter(TopLevelContainer.id == container.id),
135+
{"barCode": bar_code},
136+
)
137+
119138
inner_db.session.commit()
120139
return container
121140

src/scaup/models/inner_db/tables.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import uuid
21
from datetime import datetime
32
from typing import Any, List, Literal, Optional
43

54
from sqlalchemy import (
65
JSON,
7-
UUID,
86
DateTime,
97
ForeignKey,
108
PrimaryKeyConstraint,
@@ -55,7 +53,7 @@ class TopLevelContainer(Base, BaseColumns):
5553

5654
details: Mapped[dict[str, Any] | None] = mapped_column(JSON)
5755
code: Mapped[str] = mapped_column(String(20))
58-
barCode: Mapped[UUID | None] = mapped_column(UUID(as_uuid=True), default=uuid.uuid4)
56+
barCode: Mapped[str | None] = mapped_column(String(40))
5957
type: Mapped[str] = mapped_column(String(40), server_default="dewar")
6058
isInternal: Mapped[bool] = mapped_column(
6159
default=False,

src/scaup/models/top_level_containers.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import json
2-
import uuid
32
from datetime import datetime
43
from typing import Any, List, Optional
54

@@ -44,14 +43,14 @@ class TopLevelContainerOut(BaseTopLevelContainer):
4443
type: str
4544
model_config = ConfigDict(from_attributes=True, arbitrary_types_allowed=True)
4645
externalId: int | None = None
47-
barCode: uuid.UUID
46+
barCode: str | None = None
4847
history: List[TopLevelContainerHistory] | None = None
4948

5049

5150
class TopLevelContainerExternal(BaseExternal):
5251
comments: str
5352
code: str
54-
barCode: uuid.UUID
53+
barCode: str | None = None
5554
firstExperimentId: int | None = None
5655
weight: float = 18
5756
dewarRegistryId: int | None = None

tests/shipments/top_level_containers/test_create.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,26 @@ def test_create_no_name(client):
4040
)
4141

4242

43+
@responses.activate
44+
def test_create_auto_barcode(client):
45+
"""Should automatically generate barcode if not provided in request"""
46+
47+
resp = client.post(
48+
"/shipments/1/topLevelContainers",
49+
json={
50+
"type": "dewar",
51+
"code": "DLS-EM-0001",
52+
},
53+
)
54+
55+
assert resp.status_code == 201
56+
57+
new_tlc = resp.json()["id"]
58+
assert "cm1-1-" in inner_db.session.scalar(
59+
select(TopLevelContainer.barCode).filter(TopLevelContainer.id == new_tlc)
60+
)
61+
62+
4363
@responses.activate
4464
def test_create_duplicate_name(client):
4565
"""Should not allow creation if name is already present in shipment"""

tests/utils/external/test_external_object.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import copy
22
from datetime import datetime
3-
from uuid import UUID
43

54
import pytest
65
import responses
@@ -16,7 +15,7 @@
1615
shipmentId=1,
1716
type="dewar",
1817
name="Test_Dewar",
19-
barCode=UUID(bytes=b"ffffffffffffffff"),
18+
barCode="barCode",
2019
creationDate=datetime.now(),
2120
)
2221

0 commit comments

Comments
 (0)