Skip to content

Commit 4156a47

Browse files
committed
Alert LCs of upcoming sessions
1 parent c341c95 commit 4156a47

File tree

20 files changed

+346
-87
lines changed

20 files changed

+346
-87
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ WORKDIR /project
1919

2020
# make the wheel outside of the venv so 'build' does not dirty requirements.txt
2121
RUN pip install --upgrade pip build && \
22-
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) && \
22+
#export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) && \
2323
python -m build && \
2424
touch requirements.txt
2525

MANIFEST.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ include src/scaup/assets/cut-here.png
22
include src/scaup/assets/diamond-logo.jpg
33
include src/scaup/assets/this-side-up.png
44
include src/scaup/assets/DejaVuSans-Bold.ttf
5-
include src/scaup/assets/DejaVuSans.ttf
5+
include src/scaup/assets/DejaVuSans.ttf
6+
include src/scaup/assets/logo-light.png

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
"backend_url": "https://sample-shipping-staging.diamond.ac.uk",
1010
"frontend_url": "https://sample-shipping-staging.diamond.ac.uk"
1111
},
12+
"alerts": {
13+
"contact_email": "pato@diamond.ac.uk",
14+
"smtp_port": 26,
15+
"smtp_server": "smtp.ac.uk",
16+
"local_contacts": {
17+
"Dr John Doe": "john@diamond.ac.uk"
18+
}
19+
},
1220
"db": { "pool": 3, "overflow": 6 },
1321
"ispyb_api": "http://127.0.0.1:8060/api",
1422
"frontend_url": "http://localtest.diamond.ac.uk:9000"

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ description = "Scaup's backend"
1515

1616
dependencies = [
1717
"SQLAlchemy~=2.0.36",
18+
"APScheduler~=3.11.0",
1819
"fastapi~=0.115.6",
1920
"psycopg[binary,pool]~=3.2.3",
2021
"pydantic~=2.10.5",
@@ -78,7 +79,8 @@ addopts = """
7879
# https://iscinumpy.gitlab.io/post/bound-version-constraints/#watch-for-warnings
7980
filterwarnings = "error"
8081
markers = [
81-
"noregister: do not register HTTP mock responses"
82+
"noregister: do not register HTTP mock responses",
83+
"no_sample_response: do not register sample creation HTTP mock responses"
8284
]
8385

8486
[tool.coverage.run]

src/scaup/assets/logo-light.png

25.1 KB
Loading

src/scaup/assets/paths.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
THIS_SIDE_UP = os.path.join(ASSETS_PATH, "this-side-up.png")
88
DEJAVU_SANS = os.path.join(ASSETS_PATH, "DejaVuSans.ttf")
99
DEJAVU_SANS_BOLD = os.path.join(ASSETS_PATH, "DejaVuSans-Bold.ttf")
10+
COMPANY_LOGO_LIGHT = os.path.join(ASSETS_PATH, "logo-light.png")

src/scaup/crud/pre_sessions.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from fastapi.security import HTTPAuthorizationCredentials
12
from lims_utils.auth import GenericUser
23
from sqlalchemy import select
34
from sqlalchemy.dialects.postgresql import insert
@@ -22,12 +23,12 @@ def create_pre_session_info(shipmentId: int, params: PreSessionIn):
2223
return pre_session
2324

2425

25-
def get_pre_session_info(shipment_id: int, user: GenericUser):
26+
def get_pre_session_info(shipment_id: int, user: GenericUser, token: HTTPAuthorizationCredentials):
2627
pre_session_info = inner_db.session.scalar(select(PreSession).filter(PreSession.shipmentId == shipment_id))
2728

2829
validated_model = PreSessionOut(
2930
details=None if pre_session_info is None else pre_session_info.details,
30-
isLocked=check_session_locked(shipment_id, user),
31+
isLocked=check_session_locked(shipment_id, user, token=token),
3132
)
3233

3334
return validated_model

src/scaup/crud/samples.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import re
2+
from typing import List
23

34
from fastapi import HTTPException, status
45
from lims_utils.logging import app_logger
56
from lims_utils.models import Paged, ProposalReference
6-
from sqlalchemy import and_, insert, select
7+
from sqlalchemy import and_, insert, select, update
78

89
from ..models.inner_db.tables import Container, Sample, SampleParentChild, Shipment
910
from ..models.samples import OptionalSample, SampleIn, SampleOut
@@ -80,8 +81,18 @@ def create_sample(shipmentId: int, params: SampleIn, token: str):
8081
],
8182
).all()
8283

84+
full_samples: List[Sample] = []
85+
8386
for sample in samples:
84-
Expeye.upsert(token, sample, None)
87+
expeye_sample = Expeye.upsert(token, sample, None)
88+
89+
full_sample = inner_db.session.scalar(
90+
update(Sample)
91+
.returning(Sample)
92+
.filter(Sample.id == sample.id)
93+
.values({"externalId": expeye_sample["externalId"]})
94+
)
95+
full_samples.append(full_sample)
8596

8697
if params.parents:
8798
inner_db.session.execute(
@@ -90,7 +101,7 @@ def create_sample(shipmentId: int, params: SampleIn, token: str):
90101
)
91102

92103
inner_db.session.commit()
93-
return Paged(items=samples, total=params.copies, page=0, limit=params.copies)
104+
return Paged(items=full_samples, total=params.copies, page=0, limit=params.copies)
94105

95106

96107
def edit_sample(sampleId: int, params: OptionalSample, token: str):

src/scaup/main.py

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import os
2+
from contextlib import asynccontextmanager
23

34
from fastapi import FastAPI, HTTPException, Request, Response
45
from fastapi.middleware.cors import CORSMiddleware
56
from lims_utils.database import get_session
67
from lims_utils.logging import app_logger, log_exception_handler, register_loggers
7-
from sqlalchemy import create_engine
8-
from sqlalchemy.orm import sessionmaker
98

109
from . import __version__
1110
from .routes import (
@@ -16,9 +15,20 @@
1615
shipments,
1716
top_level_containers,
1817
)
18+
from .utils.alerts import session_alerts_scheduler
1919
from .utils.config import Config
20+
from .utils.database import inner_session
2021

21-
app = FastAPI(version=__version__, title="Scaup API")
22+
23+
@asynccontextmanager
24+
async def lifespan(app: FastAPI):
25+
register_loggers()
26+
session_alerts_scheduler.start()
27+
yield
28+
session_alerts_scheduler.shutdown()
29+
30+
31+
app = FastAPI(version=__version__, title="Scaup API", lifespan=lifespan)
2232

2333
api = FastAPI()
2434

@@ -32,24 +42,6 @@
3242
)
3343

3444

35-
inner_engine = create_engine(
36-
url=os.environ.get(
37-
"SQL_DATABASE_URL",
38-
"postgresql+psycopg://sample_handling:sample_root@127.0.0.1:5432/sample_handling",
39-
),
40-
pool_pre_ping=True,
41-
pool_recycle=3600,
42-
pool_size=Config.db.pool,
43-
max_overflow=Config.db.overflow,
44-
)
45-
46-
47-
inner_session = sessionmaker(autocommit=False, autoflush=False, bind=inner_engine)
48-
49-
50-
register_loggers()
51-
52-
5345
@app.middleware("http")
5446
async def get_session_as_middleware(request, call_next):
5547
with get_session(inner_session):

src/scaup/models/alerts.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# ruff: noqa: E501
2+
3+
EMAIL_HEADER = """
4+
<html>
5+
<body>
6+
<div class="wrapper" style="padding: 0.5%; text-align:center; border-radius: 5px; border: 1px solid #efefef">
7+
<div class="header" style="background: #001d55; text-align: center; padding-top: 15px; padding-bottom: 15px; border-top-left-radius: 5px; border-top-right-radius: 5px;">
8+
<img src="cid:logo-light.png" height="45"/>
9+
</div>
10+
<div>
11+
"""
12+
13+
EMAIL_FOOTER = """
14+
<p style="border-top: 1px solid #001d55; background-color: #1040A1; padding: 10px; color: white;">© 2025, Diamond Light Source</p></div>
15+
"""

0 commit comments

Comments
 (0)