Skip to content

Commit 3b616a3

Browse files
committed
Merge branch 'release-2025.5.0' of https://github.com/VariantEffect/mavedb-api into feature/bencap/518/generic-score-range-entries
2 parents a04ccb0 + 2ae432c commit 3b616a3

38 files changed

+2865
-697
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# python-base
33
# Set up shared environment variables
44
################################
5-
FROM --platform=amd64 python:3.11 AS python-base
5+
FROM python:3.11 AS python-base
66

77
# Poetry
88
# https://python-poetry.org/docs/configuration/#using-environment-variables

docker-compose-dev.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ services:
4848
- redis
4949

5050
dcd-mapping:
51+
build: ../dcd_mapping
5152
image: dcd-mapping:dev
5253
command: bash -c "uvicorn api.server_main:app --host 0.0.0.0 --port 8000 --reload"
5354
depends_on:
@@ -61,6 +62,7 @@ services:
6162
- mavedb-seqrepo-dev:/usr/local/share/seqrepo
6263

6364
cdot-rest:
65+
build: ../cdot_rest
6466
image: cdot-rest:dev
6567
command: bash -c "gunicorn cdot_rest.wsgi:application --bind 0.0.0.0:8000"
6668
env_file:

poetry.lock

Lines changed: 368 additions & 61 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,20 @@ ga4gh-va-spec = "~0.4.2"
4444
alembic = { version = "~1.14.0", optional = true }
4545
alembic-utils = { version = "0.8.1", optional = true }
4646
arq = { version = "~0.25.0", optional = true }
47-
authlib = { version = "~1.3.1", optional = true }
47+
authlib = { version = "~1.6.5", optional = true }
4848
boto3 = { version = "~1.34.97", optional = true }
4949
biocommons = { version = "~0.0.0", optional = true }
5050
cryptography = { version = "~44.0.1", optional = true }
5151
cdot = { version = "~0.2.21", optional = true }
52-
fastapi = { version = "~0.115.0", optional = true }
52+
fastapi = { version = "~0.121.0", optional = true }
5353
hgvs = { version = "~1.5.4", optional = true }
5454
orcid = { version = "~1.0.3", optional = true }
5555
pyathena = { version = "~3.14.1", optional = true }
5656
psycopg2 = { version = "~2.9.3", optional = true }
57-
python-jose = { extras = ["cryptography"], version = "~3.4.0", optional = true }
57+
python-jose = { extras = ["cryptography"], version = "~3.5.0", optional = true }
5858
python-multipart = { version = "~0.0.5", optional = true }
5959
requests = { version = "~2.32.2", optional = true }
60-
starlette = { version = "~0.41.0", optional = true }
60+
starlette = { version = "~0.49.0", optional = true }
6161
starlette-context = { version = "^0.3.6", optional = true }
6262
slack-sdk = { version = "~3.21.3", optional = true }
6363
uvicorn = { extras = ["standard"], version = "*", optional = true }

settings/.env.template

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,10 @@ DCD_MAPPING_URL=http://dcd-mapping:8000
6767
####################################################################################################
6868

6969
CDOT_URL=http://cdot-rest:8000
70-
REDIS_HOST=localhost
70+
REDIS_HOST=redis
71+
REDIS_IP=redis
7172
REDIS_PORT=6379
73+
REDIS_SSL=false
7274

7375
####################################################################################################
7476
# Environment variables for ClinGen

src/mavedb/db/view.py

Lines changed: 123 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
import sqlalchemy as sa
88
from sqlalchemy.ext import compiler
9-
from sqlalchemy.schema import DDLElement, MetaData
109
from sqlalchemy.orm import Session
10+
from sqlalchemy.schema import DDLElement, MetaData
1111

1212
from mavedb.db.base import Base
1313

@@ -32,7 +32,53 @@ class MaterializedView(Base):
3232

3333
@classmethod
3434
def refresh(cls, connection, concurrently=True):
35-
"""Refresh this materialized view."""
35+
"""
36+
Refresh the underlying materialized view for this ORM-mapped class.
37+
38+
This class method delegates to `refresh_mat_view` to issue a database
39+
REFRESH MATERIALIZED VIEW (optionally CONCURRENTLY) statement for the
40+
materialized view backing the current model (`cls.__table__.fullname`).
41+
42+
Parameters
43+
---------
44+
connection : sqlalchemy.engine.Connection | sqlalchemy.orm.Session
45+
An active SQLAlchemy connection or session bound to the target database.
46+
concurrently : bool, default True
47+
If True, performs a concurrent refresh (REFRESH MATERIALIZED VIEW CONCURRENTLY),
48+
allowing reads during the refresh when the database backend supports it.
49+
If False, performs a blocking refresh.
50+
51+
Returns
52+
-------
53+
None
54+
55+
Raises
56+
------
57+
sqlalchemy.exc.DBAPIError
58+
If the database reports an error while refreshing the materialized view.
59+
sqlalchemy.exc.OperationalError
60+
For operational issues such as locks or insufficient privileges.
61+
ValueError
62+
If the connection provided is not a valid SQLAlchemy connection/session.
63+
64+
Notes
65+
-----
66+
- A concurrent refresh typically requires the materialized view to have a unique
67+
index matching all rows; otherwise the database may reject the operation.
68+
- This operation does not return a value; it is executed for its side effect.
69+
- Ensure the connection/session is in a clean transactional state if you rely on
70+
consistent snapshot semantics.
71+
- This function commits no changes; it is the caller's responsibility to
72+
commit the session if needed.
73+
74+
Examples
75+
--------
76+
# Refresh with concurrent mode (default)
77+
MyMaterializedView.refresh(connection)
78+
79+
# Perform a blocking refresh
80+
MyMaterializedView.refresh(connection, concurrently=False)
81+
"""
3682
refresh_mat_view(connection, cls.__table__.fullname, concurrently)
3783

3884

@@ -123,19 +169,91 @@ class MyView(Base):
123169

124170
def refresh_mat_view(session: Session, name: str, concurrently=True):
125171
"""
126-
Refreshes a single materialized view, given by `name`.
172+
Refresh a PostgreSQL materialized view within the current SQLAlchemy session.
173+
174+
This helper issues a REFRESH MATERIALIZED VIEW statement for the specified
175+
materialized view name. It first explicitly flushes the session because
176+
session.execute() bypasses SQLAlchemy's autoflush mechanism; without the flush,
177+
pending changes (e.g., newly inserted/updated rows that the view depends on)
178+
might not be reflected in the refreshed view.
179+
180+
Parameters
181+
----------
182+
session : sqlalchemy.orm.Session
183+
An active SQLAlchemy session bound to a PostgreSQL database.
184+
name : str
185+
The exact name (optionally schema-qualified) of the materialized view to refresh.
186+
concurrently : bool, default True
187+
If True, uses REFRESH MATERIALIZED VIEW CONCURRENTLY allowing reads during
188+
the refresh and requiring a unique index on the materialized view. If False,
189+
performs a blocking refresh.
190+
191+
Raises
192+
------
193+
sqlalchemy.exc.SQLAlchemyError
194+
Propagates any database errors encountered during execution (e.g.,
195+
insufficient privileges, missing view, lack of required unique index for
196+
CONCURRENTLY).
197+
198+
Notes
199+
-----
200+
- Using CONCURRENTLY requires the materialized view to have at least one
201+
unique index; otherwise PostgreSQL will raise an error.
202+
- The operation does not return a value; it is executed for its side effect.
203+
- Ensure the session is in a clean transactional state if you rely on
204+
consistent snapshot semantics.
205+
- This function commits no changes; it is the caller's responsibility to
206+
commit the session if needed.
207+
208+
Examples
209+
--------
210+
refresh_mat_view(session, "public.my_materialized_view")
211+
refresh_mat_view(session, "reports.daily_stats", concurrently=False)
127212
"""
128213
# since session.execute() bypasses autoflush, must manually flush in order
129214
# to include newly-created/modified objects in the refresh
130215
session.flush()
216+
131217
_con = "CONCURRENTLY " if concurrently else ""
132218
session.execute(sa.text("REFRESH MATERIALIZED VIEW " + _con + name))
133219

134220

135221
def refresh_all_mat_views(session: Session, concurrently=True):
136222
"""
137-
Refreshes all materialized views. Views are refreshed in non-deterministic order,
138-
so view definitions can't depend on each other.
223+
Refreshes all PostgreSQL materialized views visible to the given SQLAlchemy session.
224+
225+
The function inspects the current database connection for registered materialized
226+
views and issues a REFRESH MATERIALIZED VIEW command for each one using the helper
227+
function `refresh_mat_view`. After all refresh operations complete, the session
228+
is committed to persist any transactional side effects of the refresh statements.
229+
230+
Parameters
231+
----------
232+
session : sqlalchemy.orm.Session
233+
An active SQLAlchemy session bound to a PostgreSQL connection.
234+
concurrently : bool, default True
235+
If True, each materialized view is refreshed using the CONCURRENTLY option
236+
(only supported when the view has a unique index that satisfies PostgreSQL
237+
requirements). If False, a standard blocking refresh is performed.
238+
239+
Behavior
240+
--------
241+
- If inspection of the connection fails or returns no inspector, the function
242+
exits without performing any work.
243+
- Each materialized view name returned by the inspector is passed to
244+
`refresh_mat_view(session, name, concurrently)`.
245+
246+
Notes
247+
-----
248+
- Using CONCURRENTLY allows reads during refresh at the cost of requiring an
249+
appropriate unique index and potentially being slower.
250+
- Exceptions raised during individual refresh operations will propagate unless
251+
`refresh_mat_view` handles them internally; in such a case the commit will
252+
not be reached.
253+
- Ensure the session is in a clean transactional state if you rely on
254+
consistent snapshot semantics.
255+
- This function commits no changes; it is the caller's responsibility to
256+
commit the session if needed.
139257
"""
140258
inspector = sa.inspect(session.connection())
141259

0 commit comments

Comments
 (0)