diff --git a/toardb/auth_user/models.py b/toardb/auth_user/models.py index f595e0c7e9c7ea32847f49a00fbe3f0730d84435..5f644ed4c381c523e00a4352bd14c9e94dd2ece2 100644 --- a/toardb/auth_user/models.py +++ b/toardb/auth_user/models.py @@ -3,10 +3,8 @@ from sqlalchemy import ARRAY, BigInteger, Boolean, CheckConstraint, Column, Date from sqlalchemy.orm import relationship from geoalchemy2.types import Geometry from sqlalchemy.dialects.postgresql import JSONB -from sqlalchemy.ext.declarative import declarative_base +from toardb.base import Base -Base = declarative_base() -metadata = Base.metadata class AuthUser(Base): __tablename__ = 'auth_user' diff --git a/toardb/base.py b/toardb/base.py new file mode 100644 index 0000000000000000000000000000000000000000..531e94bff4f9baa3766cdbf1cbd87bece716803a --- /dev/null +++ b/toardb/base.py @@ -0,0 +1,6 @@ +# coding: utf-8 +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() +metadata = Base.metadata + diff --git a/toardb/contacts/models.py b/toardb/contacts/models.py index a39c643d21663d66d46568fba0e3ebaebbc902b4..f863c1a218f89603eeec066d5b186a58b16ce35c 100644 --- a/toardb/contacts/models.py +++ b/toardb/contacts/models.py @@ -1,18 +1,6 @@ -#taken from: https://stackoverflow.com/questions/29954815/merge-multiple-declarative-bases-in-sqlalchemy -from sqlalchemy import MetaData -from sqlalchemy.ext.declarative import declarative_base -from .models_person import ( - Person, - Base as PersonBase) -from .models_organisation import ( - Organisation, - Base as OrganisationBase) +# coding: utf-8 +from toardb.base import Base -Base = declarative_base() -metadata = MetaData() +from .models_person import Person +from .models_organisation import Organisation -for declarative_base in [PersonBase, OrganisationBase]: - for (table_name, table) in declarative_base.metadata.tables.items(): - metadata._add_table(table_name, table.schema, table) - -Base.metadata = metadata diff --git a/toardb/contacts/models_organisation.py b/toardb/contacts/models_organisation.py index c7f23c3d01c11e7842d8db51dfb71b37bfac6ff6..dcf8f9c7502b77a28149d72880b91481e889f18d 100644 --- a/toardb/contacts/models_organisation.py +++ b/toardb/contacts/models_organisation.py @@ -4,10 +4,7 @@ class Organisation (Base) ========================= """ from sqlalchemy import CheckConstraint, Column, Integer, String, text -from sqlalchemy.ext.declarative import declarative_base - -Base = declarative_base() -metadata = Base.metadata +from toardb.base import Base class Organisation(Base): diff --git a/toardb/contacts/models_person.py b/toardb/contacts/models_person.py index d8fe29aadff6a27faedba2d34192249c2f0376f6..aa31af4c9c1658cc968c552eb3352ee262c2ca7e 100644 --- a/toardb/contacts/models_person.py +++ b/toardb/contacts/models_person.py @@ -4,10 +4,7 @@ class Person (Base) =================== """ from sqlalchemy import Boolean, Column, Integer, String, text -from sqlalchemy.ext.declarative import declarative_base - -Base = declarative_base() -metadata = Base.metadata +from toardb.base import Base class Person(Base): """ Table "public.persons" diff --git a/toardb/data/models.py b/toardb/data/models.py index a8d9b14fbd965eefbf9446a8dcbad1b90bc273c0..3d08600a1efbc86e14b0719a0ec18eee42f799b7 100644 --- a/toardb/data/models.py +++ b/toardb/data/models.py @@ -3,12 +3,9 @@ from sqlalchemy import PrimaryKeyConstraint, Column, DateTime, Float, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.sql.sqltypes import NullType from sqlalchemy.dialects.postgresql import JSONB -from sqlalchemy.ext.declarative import declarative_base from toardb.timeseries.models import Timeseries - -Base = declarative_base() -metadata = Base.metadata +from toardb.base import Base class Data(Base): diff --git a/toardb/stationmeta/models.py b/toardb/stationmeta/models.py index 04c9641729b6a4105c5f06fdcf757234f7acf2d5..debb835d0b35857740194fd667e28abe493a3055 100644 --- a/toardb/stationmeta/models.py +++ b/toardb/stationmeta/models.py @@ -1,31 +1,9 @@ -#taken from: https://stackoverflow.com/questions/29954815/merge-multiple-declarative-bases-in-sqlalchemy -from sqlalchemy import MetaData from sqlalchemy.ext.declarative import declarative_base -from .models_core import ( - StationmetaCore, - Base as StationmetaCoreBase) -from .models_global import ( - StationmetaGlobal, - Base as StationmetaGlobalBase) -from .models_global_services import ( - StationmetaGlobalService, - Base as StationmetaGlobalServiceBase) -from .models_role import ( - StationmetaRole, - Base as StationmetaRoleBase) -from .models_annotation import ( - StationmetaAnnotation, - Base as StationmetaAnnotationBase) -from .models_aux import ( - StationmetaAuxDoc, StationmetaAuxImage, StationmetaAuxUrl, - Base as StationmetaAuxBase) +from .models_core import StationmetaCore +from .models_global import StationmetaGlobal +from .models_global_services import StationmetaGlobalService +from .models_role import StationmetaRole, stationmeta_core_stationmeta_roles_table +from .models_annotation import StationmetaAnnotation, stationmeta_core_stationmeta_annotations_table +from .models_aux import StationmetaAuxDoc, StationmetaAuxImage, StationmetaAuxUrl -Base = declarative_base() -metadata = MetaData() - -for declarative_base in [StationmetaCoreBase, StationmetaGlobalBase, StationmetaGlobalServiceBase, - StationmetaRoleBase, StationmetaAnnotationBase, StationmetaAuxBase]: - for (table_name, table) in declarative_base.metadata.tables.items(): - metadata._add_table(table_name, table.schema, table) - -Base.metadata = metadata +from toardb.base import Base diff --git a/toardb/stationmeta/models_annotation.py b/toardb/stationmeta/models_annotation.py index 49e8d15801632324faa03de7a21e7a0dff1e8d16..fd31faf860b1d78bc16fb4bc327d1d440b88764b 100644 --- a/toardb/stationmeta/models_annotation.py +++ b/toardb/stationmeta/models_annotation.py @@ -4,14 +4,18 @@ class StationmetaAnnotation (Base) ================================== """ from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, text, String, \ - Text, Boolean, CheckConstraint -#from sqlalchemy.orm import relationship -from sqlalchemy.ext.declarative import declarative_base + Text, Boolean, CheckConstraint, Table +from sqlalchemy.orm import relationship from toardb.auth_user.models import AuthUser -from .models_core import StationmetaCore +from toardb.base import Base + + +# many-to-many relationships +stationmeta_core_stationmeta_annotations_table = Table('stationmeta_core_stationmeta_annotations', Base.metadata, + Column('station_id', Integer, ForeignKey('stationmeta_core.id')), + Column('annotation_id', Integer, ForeignKey('stationmeta_annotations.id')) +) -Base = declarative_base() -metadata = Base.metadata class StationmetaAnnotation(Base): """ Table "public.stationmeta_annotations" @@ -31,16 +35,12 @@ class StationmetaAnnotation(Base): +----------------+--------------------------+-----------+----------+----------------------------------------------------+ | contributor_id | integer | | not null | | +----------------+--------------------------+-----------+----------+----------------------------------------------------+ - | station_id | integer | | not null | | - +----------------+--------------------------+-----------+----------+----------------------------------------------------+ Indexes: "stationmeta_annotations_pkey" PRIMARY KEY, btree (id) "stationmeta_annotations_contributor_id_a5009820" btree (contributor_id) - "stationmeta_annotations_station_id_9d3fe3d0" btree (station_id) Check constraints: "stationmeta_annotations_kind_check" CHECK (kind >= 0) Foreign-key constraints: - "stationmeta_annotati_station_id_9d3fe3d0_fk_stationme" FOREIGN KEY (station_id) REFERENCES stationmeta_core(id) DEFERRABLE INITIALLY DEFERRED "stationmeta_annotations_contributor_id_a5009820_fk_auth_user_id" FOREIGN KEY (contributor_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED """ __tablename__ = 'stationmeta_annotations' @@ -57,7 +57,10 @@ class StationmetaAnnotation(Base): # use the explicit class name here, # see: https://groups.google.com/forum/#!topic/sqlalchemy/YjGhE4d6K4U contributor_id = Column(ForeignKey(AuthUser.id, deferrable=True, initially='DEFERRED'), nullable=False, index=True) - station_id = Column(ForeignKey(StationmetaCore.id, deferrable=True, initially='DEFERRED'), nullable=False, index=True) # how to reactivate the following two lines?! # contributor = relationship('AuthUser') -# station = relationship('StationmetaCore') + + stationmeta_core = relationship("StationmetaCore", + secondary=stationmeta_core_stationmeta_annotations_table, + backref="annotations") + diff --git a/toardb/stationmeta/models_aux.py b/toardb/stationmeta/models_aux.py index c42686392bb860d762857a002a8bc0e7870943e9..3f754c7e413bcb83515b07c6da4af46540a349c6 100644 --- a/toardb/stationmeta/models_aux.py +++ b/toardb/stationmeta/models_aux.py @@ -14,11 +14,9 @@ class StationMetaAuxImage (Base) """ from sqlalchemy import CheckConstraint, Column, DateTime, ForeignKey, Integer, String, Text, text #from sqlalchemy.orm import relationship -from sqlalchemy.ext.declarative import declarative_base from .models_core import StationmetaCore +from toardb.base import Base -Base = declarative_base() -metadata = Base.metadata class StationmetaAuxDoc(Base): """ Table "public.stationmeta_aux_doc" diff --git a/toardb/stationmeta/models_core.py b/toardb/stationmeta/models_core.py index 36b2d39ac162145cf13823697ebad32f45ece099..e4498c74d968ccd7035cdea8b7427b02b0cb25d3 100644 --- a/toardb/stationmeta/models_core.py +++ b/toardb/stationmeta/models_core.py @@ -8,12 +8,9 @@ from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, text, Strin from geoalchemy2.types import Geometry from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import JSONB, ARRAY -from sqlalchemy.ext.declarative import declarative_base from shapely import wkt from toardb.auth_user.models import AuthUser - -Base = declarative_base() -metadata = Base.metadata +from toardb.base import Base class StationmetaCore(Base): diff --git a/toardb/stationmeta/models_global.py b/toardb/stationmeta/models_global.py index 9f3040c97db63811a7c7098c4d6e469aab141b0d..a4960507b6eb70f0cab0700edbb2ecd9d9d017ba 100644 --- a/toardb/stationmeta/models_global.py +++ b/toardb/stationmeta/models_global.py @@ -8,12 +8,8 @@ from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, text, Strin from geoalchemy2.types import Geometry from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import JSONB, ARRAY -from sqlalchemy.ext.declarative import declarative_base -from shapely import wkt from .models_core import StationmetaCore - -Base = declarative_base() -metadata = Base.metadata +from toardb.base import Base class StationmetaGlobal(Base): diff --git a/toardb/stationmeta/models_global_services.py b/toardb/stationmeta/models_global_services.py index 7b63f479103555f5693d322e2c56134e0f41e3ab..a0c99b7c661c4c3ec5a82d9e9a578da684e1ffc1 100644 --- a/toardb/stationmeta/models_global_services.py +++ b/toardb/stationmeta/models_global_services.py @@ -4,10 +4,7 @@ class StationmetaGlobalService (Base) ===================================== """ from sqlalchemy import CheckConstraint, Column, Integer, String, text -from sqlalchemy.ext.declarative import declarative_base - -Base = declarative_base() -metadata = Base.metadata +from toardb.base import Base class StationmetaGlobalService(Base): """ Table "public.stationmeta_global_services" diff --git a/toardb/stationmeta/models_role.py b/toardb/stationmeta/models_role.py index 29e6d49393ea42ba0d13ba32e1628d216df1ad37..4d6b6602ef54d7324ee6c4a4fb166b4426eabe11 100644 --- a/toardb/stationmeta/models_role.py +++ b/toardb/stationmeta/models_role.py @@ -4,14 +4,18 @@ class StationmetaRole (Base) ============================ """ from sqlalchemy import Column, ForeignKey, Integer, text, \ - CheckConstraint, UniqueConstraint -#from sqlalchemy.orm import relationship -from sqlalchemy.ext.declarative import declarative_base + CheckConstraint, UniqueConstraint, Table +from sqlalchemy.orm import relationship from toardb.contacts.models import Person -from .models_core import StationmetaCore +from toardb.base import Base + + +# many-to-many relationships +stationmeta_core_stationmeta_roles_table = Table('stationmeta_core_timeseries_roles', Base.metadata, + Column('station_id', Integer, ForeignKey('stationmeta_core.id')), + Column('role_id', Integer, ForeignKey('stationmeta_roles.id')) +) -Base = declarative_base() -metadata = Base.metadata class StationmetaRole(Base): """ Table "public.stationmeta_roles" @@ -27,26 +31,20 @@ class StationmetaRole(Base): +------------+---------+-----------+----------+-------------------------------------------+ | person_id | integer | | not null | | +------------+---------+-----------+----------+-------------------------------------------+ - | station_id | integer | | not null | | - +------------+---------+-----------+----------+-------------------------------------------+ Indexes: "stationmeta_roles_pkey" PRIMARY KEY, btree (id) - "stationmeta_roles_station_id_role_person_id_29a832af_uniq" UNIQUE CONSTRAINT, btree (station_id, role, person_id) "stationmeta_roles_person_id_3bd9c160" btree (person_id) - "stationmeta_roles_station_id_f31f80fc" btree (station_id) Check constraints: "stationmeta_roles_role_check" CHECK (role >= 0) "stationmeta_roles_status_check" CHECK (status >= 0) Foreign-key constraints: "stationmeta_roles_person_id_3bd9c160_fk_persons_id" FOREIGN KEY (person_id) REFERENCES persons(id) DEFERRABLE INITIALLY DEFERRED - "stationmeta_roles_station_id_f31f80fc_fk_stationmeta_core_id" FOREIGN KEY (station_id) REFERENCES stationmeta_core(id) DEFERRABLE INITIALLY DEFERRED """ __tablename__ = 'stationmeta_roles' __table_args__ = ( CheckConstraint('role >= 0'), - CheckConstraint('status >= 0'), - UniqueConstraint('station_id', 'role', 'person_id') + CheckConstraint('status >= 0') ) id = Column(Integer, primary_key=True, server_default=text("nextval('stationmeta_roles_id_seq'::regclass)")) @@ -56,8 +54,10 @@ class StationmetaRole(Base): # use the explicit class name here, # see: https://groups.google.com/forum/#!topic/sqlalchemy/YjGhE4d6K4U person_id = Column(ForeignKey(Person.id, deferrable=True, initially='DEFERRED'), nullable=False, index=True) - station_id = Column(ForeignKey(StationmetaCore.id, deferrable=True, initially='DEFERRED'), nullable=False, index=True) # how to reactivate the following two lines?! # person = relationship('Person') -# station = relationship('Station') + + stationmeta_core = relationship("StationmetaCore", + secondary=stationmeta_core_stationmeta_roles_table, + backref="roles") diff --git a/toardb/stationmeta/test_base.py b/toardb/stationmeta/test_base.py index bd411c787b28c7cf12c811f968238262ceb5a698..55575696b5b2021a36e1776537329e6dfb80ad59 100644 --- a/toardb/stationmeta/test_base.py +++ b/toardb/stationmeta/test_base.py @@ -9,8 +9,6 @@ from sqlalchemy_utils import database_exists, create_database, drop_database from toardb.utils.database import DATABASE_URL from .models import Base -from toardb.auth_user.models import Base as AuthUserBase -from toardb.contacts.models import Base as ContactBase from toardb.toardb import app from toardb.stationmeta.stationmeta import get_db @@ -70,8 +68,6 @@ def create_test_database(): fake_conn.commit() fake_cur.execute("CREATE SEQUENCE public.organisations_id_seq AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1") fake_conn.commit() - AuthUserBase.metadata.create_all(_db_conn) # Create the tables. - ContactBase.metadata.create_all(_db_conn) # Create the tables. Base.metadata.create_all(_db_conn) # Create the tables. #try with the basics app.dependency_overrides[get_db] = get_test_db # Mock the Database Dependency @@ -87,7 +83,7 @@ def test_db_session(): yield session # Drop all data after each test - for tbl in reversed(ContactBase.metadata.sorted_tables + AuthUserBase.metadata.sorted_tables + Base.metadata.sorted_tables): + for tbl in reversed(Base.metadata.sorted_tables): _db_conn.execute(tbl.delete()) # put back the connection to the connection pool session.close() diff --git a/toardb/timeseries/crud.py b/toardb/timeseries/crud.py index 0988f9ab33c19f579f43c34a846c11a0fab25aae..d0f4364b23b4c73620e1b089bc10350185857065 100644 --- a/toardb/timeseries/crud.py +++ b/toardb/timeseries/crud.py @@ -55,6 +55,13 @@ def get_unique_timeseries_role(db: Session, role: int, person_id: int, status: i .first() return db_object +# is this internal, or should this also go to public REST api? +def get_unique_timeseries_programme(db: Session, name: str, homepage: str): + db_object = db.query(models.TimeseriesProgramme).filter(models.TimeseriesProgramme.name == name) \ + .filter(models.TimeseriesProgramme.homepage == homepage) \ + .first() + return db_object + def __get_status_enum(db: Session): return db.query(RS_enum).all() @@ -69,13 +76,15 @@ def create_timeseries(db: Session, timeseries: TimeseriesCreate): enum_RC = __get_role_enum(db) timeseries_dict = timeseries.dict() roles_data = timeseries_dict.pop('roles', None) + annotations_data = timeseries_dict.pop('annotations', None) + programmes_data = timeseries_dict.pop('programmes', None) db_timeseries = models.Timeseries(**timeseries_dict) db.add(db_timeseries) result = db.commit() db.refresh(db_timeseries) # get timeseries_id timeseries_id = db_timeseries.id - # store roles and update associaton table + # store roles and update association table if roles_data: for r in roles_data: db_role = models.TimeseriesRole(**r) @@ -90,6 +99,33 @@ def create_timeseries(db: Session, timeseries: TimeseriesCreate): role_id = db_role.id db.execute(insert(timeseries_timeseries_roles_table).values(timeseries_id=timeseries_id, role_id=role_id)) db.commit() + # store annotations and update association table + if annotations_data: + for a in annotations_data: + db_annotation = models.TimeseriesAnnotation(**a) + db.add(db_annotation) + db.commit() + db.refresh(db_annotation) + annotation_id = db_annotation.id + db.execute(insert(timeseries_timeseries_annotations_table).values(timeseries_id=timeseries_id, annotation_id=annotation_id)) + db.commit() + # store programmes and update association table + if programmes_data: + for r in programmes_data: + db_programme = models.TimeseriesProgramme(**r) + # check whether programme is already present in database + db_object = get_unique_timeseries_programme(db, db_programme.name, db_programme.homepage) + if db_object: + programme_id = db_object.id + else: + db.add(db_programme) + db.commit() + db.refresh(db_programme) + programme_id = db_programme.id + db.execute(insert(timeseries_timeseries_programmes_table).values(timeseries_id=timeseries_id, programme_id=programme_id)) + db.commit() + # there is a mismatch with additional_metadata + # there is a mismatch with additional_metadata # there is a mismatch with additional_metadata # in upload command, we have now: "additional_metadata": "{}" # but return from this method gives (=database): "additional_metadata": {} diff --git a/toardb/timeseries/models.py b/toardb/timeseries/models.py index 21a4dc4dc3fc33d5d22b7133ad9723f06f0c1349..4314698276028f5c6703ebfb1b9fed037f5d1cbe 100644 --- a/toardb/timeseries/models.py +++ b/toardb/timeseries/models.py @@ -1,43 +1,21 @@ -#taken from: https://stackoverflow.com/questions/29954815/merge-multiple-declarative-bases-in-sqlalchemy -from sqlalchemy import MetaData -from sqlalchemy.ext.declarative import declarative_base -from .models_core import ( - Timeseries, - TimeseriesRole, - timeseries_timeseries_roles_table, - Base as TimeseriesBase) -from .models_annotation import ( - TimeseriesAnnotation, - Base as TimeseriesAnnotationBase) -from .models_programme import ( - TimeseriesProgramme, - Base as TimeseriesProgrammBase) - -Base = declarative_base() -metadata = MetaData() +from .models_core import Timeseries +from .models_role import TimeseriesRole, timeseries_timeseries_roles_table +from .models_annotation import TimeseriesAnnotation, timeseries_timeseries_annotations_table +from .models_programme import TimeseriesProgramme, timeseries_timeseries_programmes_table +from toardb.base import Base from sqlalchemy import Table, String, Column, ForeignKey, Integer -from sqlalchemy.ext.declarative import declarative_base - -Base = declarative_base() -metadata = Base.metadata - -for declarative_base in [TimeseriesBase, TimeseriesAnnotationBase, TimeseriesProgrammBase]: - for (table_name, table) in declarative_base.metadata.tables.items(): - metadata._add_table(table_name, table.schema, table) - -Base.metadata = metadata # controlled vocabulary RS_enum = Table("rs_vocabulary", - metadata, + Base.metadata, Column("enum_val", Integer, primary_key=True), Column("enum_str", String), Column("enum_display_str", String) ) RC_enum = Table("rc_vocabulary", - metadata, + Base.metadata, Column("enum_val", Integer, primary_key=True), Column("enum_str", String), Column("enum_display_str", String) diff --git a/toardb/timeseries/models_annotation.py b/toardb/timeseries/models_annotation.py index dfc781433e9b4062527ab1c2a1332c4e579878fe..43b0c3ef5c72985afa130af42e378fded4276383 100644 --- a/toardb/timeseries/models_annotation.py +++ b/toardb/timeseries/models_annotation.py @@ -4,14 +4,17 @@ class TimeseriesAnnotation(Base) ================================ """ from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, text, String, \ - Text, Boolean, CheckConstraint -#from sqlalchemy.orm import relationship -from sqlalchemy.ext.declarative import declarative_base + Text, Boolean, CheckConstraint, Table +from sqlalchemy.orm import relationship from toardb.auth_user.models import AuthUser -from .models_core import Timeseries +from toardb.base import Base + +# many-to-many relationships +timeseries_timeseries_annotations_table = Table('timeseries_timeseries_annotations', Base.metadata, + Column('timeseries_id', Integer, ForeignKey('timeseries.id')), + Column('annotation_id', Integer, ForeignKey('timeseries_annotations.id')) +) -Base = declarative_base() -metadata = Base.metadata class TimeseriesAnnotation(Base): """ Table "public.timeseries_annotations" @@ -31,17 +34,13 @@ class TimeseriesAnnotation(Base): +----------------+--------------------------+-----------+----------+----------------------------------------------------+ | contributor_id | integer | | not null | | +----------------+--------------------------+-----------+----------+----------------------------------------------------+ - | timeseries_id | integer | | not null | | - +----------------+--------------------------+-----------+----------+----------------------------------------------------+ Indexes: "timeseries_annotations_pkey" PRIMARY KEY, btree (id) "timeseries_annotations_contributor_id_7a1758f6" btree (contributor_id) - "timeseries_annotations_timeseries_id_02014000" btree (timeseries_id) Check constraints: "timeseries_annotations_kind_check" CHECK (kind >= 0) Foreign-key constraints: "timeseries_annotations_contributor_id_7a1758f6_fk_auth_user_id" FOREIGN KEY (contributor_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED - "timeseries_annotations_timeseries_id_02014000_fk_timeseries_id" FOREIGN KEY (timeseries_id) REFERENCES timeseries(id) DEFERRABLE INITIALLY DEFERRED """ __tablename__ = 'timeseries_annotations' __table_args__ = ( @@ -57,7 +56,11 @@ class TimeseriesAnnotation(Base): # use the explicit class name here, # see: https://groups.google.com/forum/#!topic/sqlalchemy/YjGhE4d6K4U contributor_id = Column(ForeignKey(AuthUser.id, deferrable=True, initially='DEFERRED'), nullable=False, index=True) - timeseries_id = Column(ForeignKey(Timeseries.id, deferrable=True, initially='DEFERRED'), nullable=False, index=True) # how to reactivate the following two lines?! # contributor = relationship('AuthUser') # timeseries = relationship('Timeseries') + + timeseries = relationship("Timeseries", + secondary=timeseries_timeseries_annotations_table, + backref="annotations") + diff --git a/toardb/timeseries/models_core.py b/toardb/timeseries/models_core.py index 04360b5839f540e1901dd3a30c8a5edad0d80524..81df7e15fe71d574410cfae85ad235550dd6b35a 100644 --- a/toardb/timeseries/models_core.py +++ b/toardb/timeseries/models_core.py @@ -4,70 +4,11 @@ class Timeseries (Base) ======================= """ from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, text, String, \ - Text, CheckConstraint, UniqueConstraint, PrimaryKeyConstraint, \ - Table -from sqlalchemy.orm import relationship + Text, CheckConstraint, UniqueConstraint, PrimaryKeyConstraint from sqlalchemy.dialects.postgresql import JSONB -from sqlalchemy.ext.declarative import declarative_base from toardb.stationmeta.models import StationmetaCore from toardb.variables.models import Variable -from toardb.contacts.models import Person - -Base = declarative_base() -metadata = Base.metadata - -# many-to-many relationships -timeseries_timeseries_roles_table = Table('timeseries_timeseries_roles', Base.metadata, - Column('timeseries_id', Integer, ForeignKey('timeseries.id')), - Column('role_id', Integer, ForeignKey('timeseries_roles.id')) -) - - -class TimeseriesRole(Base): - """ Table "public.timeseries_roles" - - +---------------+---------+-----------+----------+----------------------------------------------+ - | Column | Type | Collation | Nullable | Default | - +===============+=========+===========+==========+==============================================+ - | id | integer | | not null | nextval('timeseries_roles_id_seq'::regclass) | - +---------------+---------+-----------+----------+----------------------------------------------+ - | role | integer | | not null | | - +---------------+---------+-----------+----------+----------------------------------------------+ - | status | integer | | not null | | - +---------------+---------+-----------+----------+----------------------------------------------+ - | person_id | integer | | not null | | - +---------------+---------+-----------+----------+----------------------------------------------+ - Indexes: - "timeseries_roles_pkey" PRIMARY KEY, btree (id) - "timeseries_roles_role_person_id_uniq" UNIQUE CONSTRAINT, btree (role, person_id) - "timeseries_roles_person_id" btree (person_id) - Check constraints: - "timeseries_roles_role_check" CHECK (role >= 0) - "timeseries_roles_status_check" CHECK (status >= 0) - Foreign-key constraints: - "timeseries_roles_person_id_3e26200e_fk_persons_id" FOREIGN KEY (person_id) REFERENCES persons(id) DEFERRABLE INITIALLY DEFERRED - """ - - __tablename__ = 'timeseries_roles' - __table_args__ = ( - CheckConstraint('role >= 0'), - CheckConstraint('status >= 0'), - UniqueConstraint('role', 'person_id'), - {'extend_existing': True} - ) - - id = Column(Integer, primary_key=True, server_default=text("nextval('timeseries_roles_id_seq'::regclass)")) - role = Column(Integer, nullable=False) - status = Column(Integer, nullable=False) -# do not use string declaration here (not working for pytest) -# use the explicit class name here, -# see: https://groups.google.com/forum/#!topic/sqlalchemy/YjGhE4d6K4U - person_id = Column(ForeignKey(Person.id, deferrable=True, initially='DEFERRED'), nullable=False, index=True) - person = relationship(Person) - - timeseries = relationship("Timeseries", - secondary=timeseries_timeseries_roles_table, - backref="roles") +from toardb.base import Base class Timeseries(Base): diff --git a/toardb/timeseries/models_programme.py b/toardb/timeseries/models_programme.py index ce84b2d961a503bd07d3dada9568d46fd0e85da0..d293905a436ba0a54d97c900137c24cd359672c1 100644 --- a/toardb/timeseries/models_programme.py +++ b/toardb/timeseries/models_programme.py @@ -3,12 +3,15 @@ class TimeseriesProgramme (Base) ================================ """ -from sqlalchemy import Column, Integer, text, String, Text -from sqlalchemy.ext.declarative import declarative_base - -Base = declarative_base() -metadata = Base.metadata +from sqlalchemy import Column, Integer, text, String, Text, ForeignKey, Table +from sqlalchemy.orm import relationship +from toardb.base import Base +# many-to-many relationships +timeseries_timeseries_programmes_table = Table('timeseries_timeseries_programmes', Base.metadata, + Column('timeseries_id', Integer, ForeignKey('timeseries.id')), + Column('programme_id', Integer, ForeignKey('timeseries_programmes.id')) +) # this has at the moment no relation to anything!!! class TimeseriesProgramme(Base): @@ -38,3 +41,7 @@ class TimeseriesProgramme(Base): homepage = Column(String(200), nullable=False) description = Column(Text, nullable=False) + timeseries = relationship("Timeseries", + secondary=timeseries_timeseries_programmes_table, + backref="programmes") + diff --git a/toardb/timeseries/models_role.py b/toardb/timeseries/models_role.py new file mode 100644 index 0000000000000000000000000000000000000000..fbfa87981337cba805b1d0ab18f85d4b786ca6c5 --- /dev/null +++ b/toardb/timeseries/models_role.py @@ -0,0 +1,64 @@ +# coding: utf-8 +""" +class TimeseriesRole (Base) +=========================== +""" +from sqlalchemy import Column, ForeignKey, Integer, text, \ + CheckConstraint, UniqueConstraint, \ + Table +from sqlalchemy.orm import relationship +from toardb.contacts.models import Person +from toardb.base import Base + + +# many-to-many relationships +timeseries_timeseries_roles_table = Table('timeseries_timeseries_roles', Base.metadata, + Column('timeseries_id', Integer, ForeignKey('timeseries.id')), + Column('role_id', Integer, ForeignKey('timeseries_roles.id')) +) + + +class TimeseriesRole(Base): + """ Table "public.timeseries_roles" + + +---------------+---------+-----------+----------+----------------------------------------------+ + | Column | Type | Collation | Nullable | Default | + +===============+=========+===========+==========+==============================================+ + | id | integer | | not null | nextval('timeseries_roles_id_seq'::regclass) | + +---------------+---------+-----------+----------+----------------------------------------------+ + | role | integer | | not null | | + +---------------+---------+-----------+----------+----------------------------------------------+ + | status | integer | | not null | | + +---------------+---------+-----------+----------+----------------------------------------------+ + | person_id | integer | | not null | | + +---------------+---------+-----------+----------+----------------------------------------------+ + Indexes: + "timeseries_roles_pkey" PRIMARY KEY, btree (id) + "timeseries_roles_role_person_id_uniq" UNIQUE CONSTRAINT, btree (role, person_id) + "timeseries_roles_person_id" btree (person_id) + Check constraints: + "timeseries_roles_role_check" CHECK (role >= 0) + "timeseries_roles_status_check" CHECK (status >= 0) + Foreign-key constraints: + "timeseries_roles_person_id_3e26200e_fk_persons_id" FOREIGN KEY (person_id) REFERENCES persons(id) DEFERRABLE INITIALLY DEFERRED + """ + + __tablename__ = 'timeseries_roles' + __table_args__ = ( + CheckConstraint('role >= 0'), + CheckConstraint('status >= 0'), + UniqueConstraint('role', 'person_id') + ) + + id = Column(Integer, primary_key=True, server_default=text("nextval('timeseries_roles_id_seq'::regclass)")) + role = Column(Integer, nullable=False) + status = Column(Integer, nullable=False) +# do not use string declaration here (not working for pytest) +# use the explicit class name here, +# see: https://groups.google.com/forum/#!topic/sqlalchemy/YjGhE4d6K4U + person_id = Column(ForeignKey(Person.id, deferrable=True, initially='DEFERRED'), nullable=False, index=True) + person = relationship(Person) + + timeseries = relationship("Timeseries", + secondary=timeseries_timeseries_roles_table, + backref="roles") diff --git a/toardb/timeseries/schemas.py b/toardb/timeseries/schemas.py index 59aea875e41b4100aa48f56529d39fd842d1f328..0a21ebea5caa4d211732992ecffb8d476ddb4cb4 100644 --- a/toardb/timeseries/schemas.py +++ b/toardb/timeseries/schemas.py @@ -103,12 +103,16 @@ class TimeseriesProgramme(TimeseriesProgrammeBase): class TimeseriesBase(TimeseriesCoreBase): roles: List[TimeseriesRole] = None + annotations: List[TimeseriesAnnotation] = None + programmes: List[TimeseriesProgramme] = None class Config: orm_mode = True class TimeseriesCreate(TimeseriesCoreBase): roles: List[TimeseriesRoleBase] = None + annotations: List[TimeseriesAnnotation] = None + programmes: List[TimeseriesProgramme] = None class Config: orm_mode = True diff --git a/toardb/timeseries/test_base.py b/toardb/timeseries/test_base.py index 259b2b38f75a136f098aaf839e63e3796fad3631..0906f4b5e617430be4ec3d332ba897506af947cd 100644 --- a/toardb/timeseries/test_base.py +++ b/toardb/timeseries/test_base.py @@ -9,10 +9,6 @@ from sqlalchemy_utils import database_exists, create_database, drop_database from toardb.utils.database import DATABASE_URL from .models import Base -from toardb.variables.models import Base as VariableBase -from toardb.stationmeta.models import Base as StationmetaBase -from toardb.auth_user.models import Base as AuthUserBase -from toardb.contacts.models import Base as ContactBase from toardb.toardb import app from toardb.timeseries.timeseries import get_db @@ -82,10 +78,6 @@ def create_test_database(): fake_conn.commit() fake_cur.execute("CREATE SEQUENCE public.timeseries_id_seq AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1") fake_conn.commit() - AuthUserBase.metadata.create_all(_db_conn) # Create the tables. - ContactBase.metadata.create_all(_db_conn) # Create the tables. - VariableBase.metadata.create_all(_db_conn) # Create the tables. - StationmetaBase.metadata.create_all(_db_conn) # Create the tables. # just to show that tables are available now: # fake_cur.execute("SELECT * FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'") # rows = fake_cur.fetchall() @@ -106,11 +98,7 @@ def test_db_session(): yield session # Drop all data after each test - for tbl in reversed(AuthUserBase.metadata.sorted_tables + - ContactBase.metadata.sorted_tables + - VariableBase.metadata.sorted_tables + - StationmetaBase.metadata.sorted_tables + - Base.metadata.sorted_tables): + for tbl in reversed(Base.metadata.sorted_tables): _db_conn.execute(tbl.delete()) # put back the connection to the connection pool session.close() diff --git a/toardb/timeseries/test_timeseries.py b/toardb/timeseries/test_timeseries.py index 18633bf893a275813468ed0ea972baec068f23ec..37f74fe46203162bdff0120099593e36c9802ebf 100644 --- a/toardb/timeseries/test_timeseries.py +++ b/toardb/timeseries/test_timeseries.py @@ -128,7 +128,7 @@ class TestApps: 'measurement_method': 'UV absorption', 'sampling_height': 7.0, 'date_added': '2020-05-15T15:30:00+02:00', 'date_modified': '2020-05-16T09:30:00+02:00', 'station_id': 2, 'variable_id': 7, - 'additional_metadata':{}, 'roles': []}] + 'additional_metadata':{}, 'roles': [], 'annotations': [], 'programmes': []}] assert response.json() == expected_resp @@ -150,7 +150,7 @@ class TestApps: 'measurement_method': 'UV absorption', 'sampling_height': 7.0, 'date_added': '2020-05-15T15:30:00+02:00', 'date_modified': '2020-05-16T09:30:00+02:00', 'station_id': 2, 'variable_id': 7, - 'additional_metadata':{}, 'roles': []} + 'additional_metadata':{}, 'roles': [], 'annotations': [], 'programmes': []} assert response.json() == expected_resp @@ -192,7 +192,7 @@ class TestApps: 'measurement_method': 'UV absorption', 'sampling_height': 7.0, 'date_added': '2020-05-15T15:30:00+02:00', 'date_modified': '2020-05-16T09:30:00+02:00', 'station_id': 2, 'variable_id': 7, - 'additional_metadata':{}, 'roles': []} + 'additional_metadata':{}, 'roles': [], 'annotations': [], 'programmes': []} assert response.json() == expected_resp @@ -210,7 +210,6 @@ class TestApps: "roles": [{"role": 0, "person_id": 3, "status": 0},{"role": 1, "person_id": 3, "status": 0}]} } ) - print("I got:", response.json()) expected_status_code = 200 assert response.status_code == expected_status_code expected_resp = {'id': 2, 'label': 'CMA2', 'order': 1, 'access_rights': 0, @@ -220,7 +219,8 @@ class TestApps: 'date_added': '2020-05-15T15:30:00+02:00', 'date_modified': '2020-05-16T09:30:00+02:00', 'station_id': 2, 'variable_id': 7, 'additional_metadata':{}, - 'roles': [{'id': 1, 'person_id': 3, 'role': 0, 'status': 0}, {'id': 2, 'person_id': 3, 'role': 1, 'status': 0}]} + 'roles': [{'id': 1, 'person_id': 3, 'role': 0, 'status': 0}, {'id': 2, 'person_id': 3, 'role': 1, 'status': 0}], + 'annotations': [], 'programmes': []} assert response.json() == expected_resp diff --git a/toardb/variables/models.py b/toardb/variables/models.py index 4d264d9d60bc84cf65d94a36ea07ea66fdf349bb..06a6511c165a7e825a05fade2d81cde7387746ba 100644 --- a/toardb/variables/models.py +++ b/toardb/variables/models.py @@ -3,10 +3,7 @@ from sqlalchemy import ARRAY, BigInteger, Boolean, CheckConstraint, Column, Date from sqlalchemy.orm import relationship from sqlalchemy.sql.sqltypes import NullType from sqlalchemy.dialects.postgresql import JSONB -from sqlalchemy.ext.declarative import declarative_base - -Base = declarative_base() -metadata = Base.metadata +from toardb.base import Base class Variable(Base):