Skip to content

Commit

Permalink
Refactor enumtables hooks (#61)
Browse files Browse the repository at this point in the history
### Description
Enumtables require entries to be put there after the table is created, but there is not a convenient sqlalchemy way to do so.  This dedups the logic that I've been sprinkling all over the codebase to do this work after table creation.  This is now done automatically by `enumtables.EnumTable`.

#### Issue
[ch64743](https://app.clubhouse.io/genepi/stories/space/64743)

### Test plan
Dropped the database and created it from scratch.  Verified that one of the enumtables had the correct values.
  • Loading branch information
Tony Tung authored Feb 10, 2021
1 parent b300400 commit 9959048
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 66 deletions.
26 changes: 1 addition & 25 deletions src/py/aspen/database/models/accessions.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import enum

import enumtables
from sqlalchemy import (
Column,
event,
ForeignKey,
Integer,
String,
text,
UniqueConstraint,
)
from sqlalchemy import Column, ForeignKey, Integer, String, UniqueConstraint
from sqlalchemy.orm import backref, relationship

from .base import base, idbase
Expand All @@ -31,22 +23,6 @@ class PublicRepositoryType(enum.Enum):
)


def _after_publicrepositorytypes_create(target, connection, **kw):
for entitytype in PublicRepositoryType:
connection.execute(
text(
f"INSERT INTO {target.schema}.{target.name} (item_id) VALUES (:name)"
).params(name=entitytype.value)
)


event.listen(
_PublicRepositoryTypeTable.__table__,
"after_create",
_after_publicrepositorytypes_create,
)


class PublicRepository(idbase):
"""A public repository for genetic or epidemiology data."""

Expand Down
14 changes: 1 addition & 13 deletions src/py/aspen/database/models/cansee.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import enum

import enumtables
from sqlalchemy import Column, event, ForeignKey, Integer, text
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.orm import backref, relationship

from .base import base, idbase
Expand All @@ -27,18 +27,6 @@ class DataType(enum.Enum):
)


def _after_datatypes_create(target, connection, **kw):
for datatype in DataType:
connection.execute(
text(
f"INSERT INTO {target.schema}.{target.name} (item_id) VALUES (:name)"
).params(name=datatype.value)
)


event.listen(_DataTypeTable.__table__, "after_create", _after_datatypes_create)


class CanSee(idbase):
"""
Expresses a relationship where users from a group can see some data of another
Expand Down
14 changes: 1 addition & 13 deletions src/py/aspen/database/models/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Mapping, Union

import enumtables
from sqlalchemy import Column, event, ForeignKey, text
from sqlalchemy import Column, ForeignKey

from .base import base, idbase
from .enum import Enum
Expand All @@ -26,18 +26,6 @@ class EntityType(enum.Enum):
)


def _after_entitytypes_create(target, connection, **kw):
for entitytype in EntityType:
connection.execute(
text(
f"INSERT INTO {target.schema}.{target.name} (item_id) VALUES (:name)"
).params(name=entitytype.value)
)


event.listen(_EntityTypeTable.__table__, "after_create", _after_entitytypes_create)


class Entity(idbase):
"""A piece of data in the system. It is represented as a file, though not always
local to the system."""
Expand Down
14 changes: 1 addition & 13 deletions src/py/aspen/database/models/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Mapping, Union

import enumtables
from sqlalchemy import Column, DateTime, event, ForeignKey, JSON, Table, text
from sqlalchemy import Column, DateTime, ForeignKey, JSON, Table
from sqlalchemy.orm import relationship

from .base import base, idbase
Expand All @@ -29,18 +29,6 @@ class WorkflowType(enum.Enum):
)


def _after_workflowtypes_create(target, connection, **kw):
for workflowtype in WorkflowType:
connection.execute(
text(
f"INSERT INTO {target.schema}.{target.name} (item_id) VALUES (:name)"
).params(name=workflowtype.value)
)


event.listen(_WorkflowTypeTable.__table__, "after_create", _after_workflowtypes_create)


_workflow_inputs_table = Table(
"workflow_inputs",
base.metadata,
Expand Down
41 changes: 39 additions & 2 deletions third-party/sqlalchemy-enum-tables/enumtables/enum_table.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
import enum
import re
from typing import Type

import sqlalchemy as sa
import re

__all__ = ["EnumTable"]

def convert_case(name):
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()


def _make_after_table_create(enumtype: Type[enum.Enum], **kw):
"""Tables used for enumtables require pre-seeded values, but the table creation
process does not manage this. To do so, add a hook to the table creation to ensure
that we populate those entries:
event.listen(
MyEnumTypeTable.__table__,
"after_create",
make_after_table_create(MyEnumType),
)
This is automatically done by EnumTable for all classes.
"""

def _after_table_create(target, connection, **kw):
for entitytype in enumtype:
connection.execute(
sa.text(
f"INSERT INTO {target.schema}.{target.name} (item_id) VALUES (:val)"
).params(val=entitytype.value)
)

return _after_table_create


def EnumTable(enum, declBase, name = None, tablename = None, doc = None, **kwargs):
"""
Create a table for a Python enumeration
Expand Down Expand Up @@ -47,4 +75,13 @@ def EnumTable(enum, declBase, name = None, tablename = None, doc = None, **kwarg
}
if doc:
namespace["__doc__"] = doc
return declBase.__class__(typename, (declBase,), namespace)
result = declBase.__class__(typename, (declBase,), namespace)

# register the after_create hook.
sa.event.listen(
result.__table__,
"after_create",
_make_after_table_create(enum)
)

return result

0 comments on commit 9959048

Please sign in to comment.