diff --git a/production_tests.sh b/production_tests.sh index 299c9328ee510c1e5d4b2b78b114a6a9197e876f..24abf25e9f3e3427db6019e07465a033dfd8f59f 100755 --- a/production_tests.sh +++ b/production_tests.sh @@ -26,9 +26,9 @@ curl http://127.0.0.1:8000/stationmeta/ curl http://127.0.0.1:8000/stationmeta_core/DEUB001 curl http://127.0.0.1:8000/stationmeta/DEUB001 # station upload without nested fields -curl -X POST -H "Content-Type:application/json" -d '{"stationmeta": {"codes":["China11"],"name":"Mount Tai","coordinates":{"lat":36.256,"lng":17.106,"alt":1534.0},"country":"China","state":"Shandong Sheng","coordinate_validation_status":"NotChecked","coordinate_validation_date":"2020-03-11T12:22:18.047974+01:00","type_of_environment":"Background","type_of_area":"Rural","category":"","timezone":"Asia/Shanghai", "coordinate_validator_id": 1, "additional_metadata":"{}"}}' "http://127.0.0.1:8000/stationmeta/" +curl -X POST -H "Content-Type:application/json" -d '{"stationmeta": {"codes":["China11"],"name":"Mount Tai","coordinates":{"lat":36.256,"lng":17.106,"alt":1534.0},"country":"China","state":"Shandong Sheng","type_of_environment":"Background","type_of_area":"Rural","category":"","timezone":"Asia/Shanghai","additional_metadata":"{}"}}' "http://127.0.0.1:8000/stationmeta/" # (nested) upload including 'global' (not defining all metadata -- getting the rest from default values) with human readable fields -curl -X POST -H "Content-Type:application/json" -d '{"stationmeta": {"codes":["CHHKG015"],"name":"Shatin","coordinates":{"lat":22.3765,"lng":114.1847,"alt":25},"country":"China","state":"New Territories","coordinate_validation_status":"NotChecked","coordinate_validation_date":"2020-03-11T12:22:18.047974+01:00","type_of_environment":"Unknown","type_of_area":"Suburban","category":"","timezone":"Asia/Hong_Kong", "coordinate_validator_id": 1, "additional_metadata":"{}", "roles": [{"role": "PointOfContact", "contact_id": 43, "status": "active"},{"role": "PrincipalInvestigator", "contact_id": 41, "status": "active"}], "globalmeta": {"climatic_zone": "WarmTemperateMoist"}}}' http://127.0.0.1:8000/stationmeta/ +curl -X POST -H "Content-Type:application/json" -d '{"stationmeta": {"codes":["CHHKG015"],"name":"Shatin","coordinates":{"lat":22.3765,"lng":114.1847,"alt":25},"country":"China","state":"New Territories","type_of_environment":"Unknown","type_of_area":"Suburban","category":"","timezone":"Asia/Hong_Kong","additional_metadata":"{}", "roles": [{"role": "PointOfContact", "contact_id": 43, "status": "active"},{"role": "PrincipalInvestigator", "contact_id": 41, "status": "active"}], "globalmeta": {"climatic_zone": "WarmTemperateMoist"}}}' http://127.0.0.1:8000/stationmeta/ curl http://127.0.0.1:8000/timeseries/ curl http://127.0.0.1:8000/timeseries/97 diff --git a/toardb/generic/models.py b/toardb/generic/models.py index 42beb57373cfb496042303a71344b37a9733c629..ad6af6b31157305fc1981556dba295a99663ef66 100644 --- a/toardb/generic/models.py +++ b/toardb/generic/models.py @@ -38,3 +38,23 @@ RC_enum = ( Enumdict(5, 'ResourceProvider', 'resource provider') ) +# Changelog: Type of Change +CL_enum_table = Table("cl_vocabulary", + Base.metadata, + Column("enum_val", Integer, primary_key=True), + Column("enum_str", String), + Column("enum_display_str", String) + ) +# The following code is just a workaround (see stationmeta/models.py): +from collections import namedtuple +Enumdict=namedtuple("Dict",["value","string","display_str"]) +CL_enum = ( + Enumdict(0, 'Created', 'created'), + Enumdict(1, 'SingleValue', 'single value correction in metadata'), + Enumdict(2, 'Comprehensive', 'comprehensive metadata revision'), + Enumdict(3, 'Typo', 'typographic correction of metadata'), + Enumdict(4, 'UnspecifiedData', 'unspecified data value corrections'), + Enumdict(5, 'Replaced', 'replaced data with a new version'), + Enumdict(6, 'Flagging', 'data value flagging') + ) + diff --git a/toardb/stationmeta/models.py b/toardb/stationmeta/models.py index a0b8409923a1d3341c457f1671be8fb6b9762800..bceee936402944a0d5a921c8cd8c33460524516c 100644 --- a/toardb/stationmeta/models.py +++ b/toardb/stationmeta/models.py @@ -6,6 +6,7 @@ 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 +from .models_changelog import StationmetaChangelog from toardb.base import Base diff --git a/toardb/stationmeta/models_changelog.py b/toardb/stationmeta/models_changelog.py new file mode 100644 index 0000000000000000000000000000000000000000..329837057995fda916bcb11f3921d4924344ba73 --- /dev/null +++ b/toardb/stationmeta/models_changelog.py @@ -0,0 +1,61 @@ +# coding: utf-8 +""" +class StationmetaChangelog(Base) +================================ +""" +from sqlalchemy import Column, DateTime, BigInteger, ForeignKey, String, \ + Text, CheckConstraint, Table, Sequence +from sqlalchemy.orm import relationship +from .models_core import StationmetaCore +from toardb.auth_user.models import AuthUser +from toardb.base import Base + +STATIONMETA_CHANGELOG_ID_SEQ = Sequence('stationmeta_changelog_id_seq') # define sequence explicitly +class StationmetaChangelog(Base): + """ Table "public.stationmeta_changelog" + + +----------------+--------------------------+-----------+----------+--------------------------------------------------------+ + | Column | Type | Collation | Nullable | Default | + +================+==========================+===========+==========+========================================================+ + | id | bigint | | not null | nextval('stationmeta_changelog_id_seq'::regclass) | + +----------------+--------------------------+-----------+----------+--------------------------------------------------------+ + | datetime | timestamp with time zone | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------------+ + | station_id | bigint | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------------+ + | type_of_change | integer | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------------+ + | description | text | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------------+ + | old_value | character varying(256) | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------------+ + | new_value | character varying(256) | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------------+ + | author_id | integer | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------------+ + Indexes: + "stationmeta_changelog_pkey" PRIMARY KEY, btree (id) + Foreign-key constraints: + "stationmeta_changelog_author_id_fk_auth_user_id" FOREIGN KEY (author_id) REFERENCES auth_user(id) + "stationmeta_changelog_station_id_fk_stationmeta_core_id" FOREIGN KEY (station_id) REFERENCES stationmeta_core(id) + "stationmeta_changelog_type_of_change_fk_cl_vocabulary_enum_val" FOREIGN KEY (type_of_change) REFERENCES cl_vocabulary(enum_val) + """ + __tablename__ = 'stationmeta_changelog' + + id = Column(BigInteger, STATIONMETA_CHANGELOG_ID_SEQ, primary_key=True, server_default=STATIONMETA_CHANGELOG_ID_SEQ.next_value()) + datetime = Column(DateTime(True), nullable=False) + description = Column(Text, nullable=False) + old_value = Column(String(256), nullable=False) + new_value = Column(String(256), 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 + station_id = Column(ForeignKey(StationmetaCore.id), nullable=False) + author_id = Column(ForeignKey(AuthUser.id), nullable=False) +# still to check for some pytest solution for controlled vocabulary + type_of_change = Column(ForeignKey('cl_Vocabulary.enum_val'), nullable=False) + + author = relationship('AuthUser') + station = relationship('StationmetaCore') + +# cl_vocabulary = relationship('ClVocabulary') diff --git a/toardb/stationmeta/models_core.py b/toardb/stationmeta/models_core.py index 1f0b0eaa2ade3198b4b3e34eb4fb754cc38783dd..7e263307fa7112cabdefec76dcc2b2254508880a 100644 --- a/toardb/stationmeta/models_core.py +++ b/toardb/stationmeta/models_core.py @@ -35,10 +35,6 @@ class StationmetaCore_WithoutCoords(Base): +------------------------------+--------------------------+-----------+----------+----------------------------------------------+ | state | character varying(128) | | not null | | +------------------------------+--------------------------+-----------+----------+----------------------------------------------+ - | coordinate_validation_status | integer | | not null | | - +------------------------------+--------------------------+-----------+----------+----------------------------------------------+ - | coordinate_validation_date | timestamp with time zone | | not null | | - +------------------------------+--------------------------+-----------+----------+----------------------------------------------+ | type_of_environment | integer | | not null | | +------------------------------+--------------------------+-----------+----------+----------------------------------------------+ | type_of_area | integer | | not null | | @@ -47,23 +43,17 @@ class StationmetaCore_WithoutCoords(Base): +------------------------------+--------------------------+-----------+----------+----------------------------------------------+ | additional_metadata | jsonb | | not null | | +------------------------------+--------------------------+-----------+----------+----------------------------------------------+ - | coordinate_validator_id | integer | | not null | | - +------------------------------+--------------------------+-----------+----------+----------------------------------------------+ Indexes: "stationmeta_core_pkey" PRIMARY KEY, btree (id) - "stationmeta_core_coordinate_validator_id_38c0ef8d" btree (coordinate_validator_id) "stationmeta_core_coordinates_id" gist (coordinates gist_geometry_ops_nd) "stationmeta_core_country_fa755dde" btree (country) "stationmeta_core_country_fa755dde_like" btree (country varchar_pattern_ops) "stationmeta_core_state_85025a96" btree (state) "stationmeta_core_state_85025a96_like" btree (state varchar_pattern_ops) Check constraints: - "stationmeta_core_coordinate_validation_status_check" CHECK (coordinate_validation_status >= 0) "stationmeta_core_type_of_area_check" CHECK (type_of_area >= 0) "stationmeta_core_type_of_environment_check" CHECK (type_of_environment >= 0) Foreign-key constraints: - "stationmeta_core_coordinate_validator_38c0ef8d_fk_auth_user" FOREIGN KEY (coordinate_validator_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED - "stationmeta_core_coord_valid_status_fk_cv_vocabulary_enum_val" FOREIGN KEY (coordinate_validation_status) REFERENCES cv_vocabulary(enum_val) "stationmeta_core_type_of_area_fk_ta_vocabulary_enum_val" FOREIGN KEY (type_of_area) REFERENCES ta_vocabulary(enum_val) "stationmeta_core_type_of_environment_fk_st_vocabulary_enum_val" FOREIGN KEY (type_of_environment) REFERENCES st_vocabulary(enum_val) Referenced by: @@ -78,7 +68,6 @@ class StationmetaCore_WithoutCoords(Base): __tablename__ = 'stationmeta_core' __table_args__ = ( - CheckConstraint('coordinate_validation_status >= 0'), CheckConstraint('type_of_area >= 0'), CheckConstraint('type_of_environment >= 0') ) @@ -88,15 +77,10 @@ class StationmetaCore_WithoutCoords(Base): name = Column(String(128), nullable=False) country = Column(String(128), nullable=False, index=True) state = Column(String(128), nullable=False, index=True) - coordinate_validation_status = Column(ForeignKey('cv_vocabulary.enum_val'), nullable=False) - coordinate_validation_date = Column(DateTime(True), nullable=False) type_of_environment = Column(ForeignKey('st_vocabulary.enum_val'), nullable=False) type_of_area = Column(ForeignKey('ta_vocabulary.enum_val'), nullable=False) timezone = Column(String(64), nullable=False) additional_metadata = Column(JSONB(astext_type=Text()), nullable=True) - coordinate_validator_id = Column(ForeignKey(AuthUser.id, deferrable=True, initially='DEFERRED'), nullable=False, index=True) - - coordinate_validator = relationship('AuthUser') aux_images = relationship('StationmetaAuxImage', back_populates='station') aux_docs = relationship('StationmetaAuxDoc', back_populates='station') diff --git a/toardb/stationmeta/pydantic_toar_testing.py b/toardb/stationmeta/pydantic_toar_testing.py index 4585aa257366ecd1f3e81e0625e50cd7751090d2..ab49149671138e4f4faf5b86375780f57951c99d 100644 --- a/toardb/stationmeta/pydantic_toar_testing.py +++ b/toardb/stationmeta/pydantic_toar_testing.py @@ -26,13 +26,10 @@ class StationmetaCoreBase(BaseModel): coordinates: Coordinates country: str state: str - coordinate_validation_status: int - coordinate_validation_date: dt.datetime type_of_environment: int type_of_area: int timezone: str additional_metadata: Json - coordinate_validator_id: int class Config(BaseConfig): arbitrary_types_allowed = True @@ -55,12 +52,9 @@ external_data_toar = { "coordinates":{"lat":36.256,"lng":17.106,"alt":1534.0}, "country":"China", "state":"Shandong Sheng", - "coordinate_validation_status":0, - "coordinate_validation_date":"2020-03-11T12:22:18.047974+01:00", "type_of_environment":0, "type_of_area":0, "timezone":"", - "coordinate_validator_id": 1, "additional_metadata": {} } diff --git a/toardb/stationmeta/test_stationmeta.py b/toardb/stationmeta/test_stationmeta.py index 6d44513d3dc2e06e82aea954aaa967f77e819198..c1ef93abaebce26b89c525022dba699fe2a81208 100644 --- a/toardb/stationmeta/test_stationmeta.py +++ b/toardb/stationmeta/test_stationmeta.py @@ -100,21 +100,18 @@ class TestApps: ## expected_resp = [{'id': 1, 'codes': ['China11'], 'name': 'Mount Tai', ## 'coordinates': {'lat': 36.256, 'lng': 117.106, 'alt': 1534.0}, ## 'country': 'China', 'state': 'Shandong Sheng', -## 'coordinate_validation_status': 0, 'coordinate_validation_date': '2020-02-28T12:27:03.746260+01:00', ## 'type_of_environment': 0, 'type_of_area': 0, 'timezone': '', -## 'additional_metadata': {}, 'coordinate_validator_id': 1}, +## 'additional_metadata': {}}, ## {'id': 2, 'codes': ['SDZ54421'], 'name': 'Shangdianzi', ## 'coordinates': {'lat': 40.65, 'lng': 117.106, 'alt': 293.9}, ## 'country': 'China', 'state': 'Beijing Shi', -## 'coordinate_validation_status': 0, 'coordinate_validation_date': '2020-02-28T12:27:03.746260+01:00', ## 'type_of_environment': 0, 'type_of_area': 0, 'timezone': '', -## 'additional_metadata': {}, 'coordinate_validator_id': 1}, +## 'additional_metadata': {}}, ## {'id': 3, 'codes': ['China_test8'], 'name': 'Test_China', ## 'coordinates': {'lat': 36.256, 'lng': 117.106, 'alt': 1534.0}, ## 'country': 'China', 'state': 'Shandong Sheng', -## 'coordinate_validation_status': 0, 'coordinate_validation_date': '2020-03-11T12:22:18.047974+01:00', ## 'type_of_environment': 0, 'type_of_area': 0, 'timezone': '', -## 'additional_metadata': {}, 'coordinate_validator_id': 1}] +## 'additional_metadata': {}}] ## assert response.json() == expected_resp @@ -124,8 +121,6 @@ class TestApps: # assert response.status_code == expected_status_code # expected_resp = [OrderedDict([('codes', ['China11']), ('name', 'Mount Tai'), ('country', 'China'), # ('state', 'Shandong Sheng'), ('coordinates', 'SRID=4326;POINT Z (17.106 36.256 1534)'), -# ('coordinate_validation_status', 'not checked'), ('coordinate_validator', 'sschroeder'), -# ('coordinate_validation_date', '2020-03-11T11:22:18.047000Z'), # ('roles', [OrderedDict([('rolecode', 'PointOfContact'), ('person', 'Sabine Schröder <s.schroeder@fz-juelich.de>'), ('status', 'active')])]), # ('meta_global', None), ('additional_metadata', {}), # ('stationmetaauxurl', []), ('stationmetaauxdoc', []), ('stationmetaauximage', []), ('annotations', [])])] @@ -138,10 +133,10 @@ class TestApps: assert response.status_code == expected_status_code expected_resp = {'id': 3, 'codes': ['China_test8'], 'name': 'Test_China', 'coordinates': {'lat': 36.256, 'lng': 117.106, 'alt': 1534.0}, - 'country': 'China', 'state': 'Shandong Sheng', 'coordinate_validation_status': 'NotChecked', - 'coordinate_validation_date': '2020-03-11T12:22:18.047974+01:00', 'type_of_environment': 'Unknown', + 'country': 'China', 'state': 'Shandong Sheng', + 'type_of_environment': 'Unknown', 'type_of_area': 'Unknown', 'timezone': '', - 'additional_metadata': {}, 'coordinate_validator_id': 1} + 'additional_metadata': {}} assert response.json() == expected_resp @@ -151,10 +146,10 @@ class TestApps: assert response.status_code == expected_status_code expected_resp = {'id': 3, 'codes': ['China_test8'], 'name': 'Test_China', 'coordinates': {'lat': 36.256, 'lng': 117.106, 'alt': 1534.0}, - 'country': 'China', 'state': 'Shandong Sheng', 'coordinate_validation_status': 'NotChecked', - 'coordinate_validation_date': '2020-03-11T12:22:18.047974+01:00', 'type_of_environment': 'Unknown', + 'country': 'China', 'state': 'Shandong Sheng', + 'type_of_environment': 'Unknown', 'type_of_area': 'Unknown', 'timezone': '', - 'additional_metadata': {}, 'coordinate_validator_id': 1, + 'additional_metadata': {}, 'roles': [], 'annotations': [], 'aux_images': [], 'aux_docs': [], 'aux_urls': [], 'globalmeta': None, 'globalservice': None} assert response.json() == expected_resp @@ -182,20 +177,19 @@ class TestApps: json={"stationmeta": {"codes":["ttt3","ttt4"], "name":"Test_China","coordinates":{"lat":36.256,"lng":117.106,"alt":1534.0}, - "country":"China","state":"Shandong Sheng","coordinate_validation_status":"NotChecked", - "coordinate_validation_date":"2020-03-11T12:22:18.047974+01:00", + "country":"China","state":"Shandong Sheng", "type_of_environment":"Unknown","type_of_area":"Unknown","timezone":"", - "coordinate_validator_id": 1, "additional_metadata":"{}"} + "additional_metadata":"{}"} } ) expected_status_code = 200 assert response.status_code == expected_status_code expected_resp = {'id': 4, 'codes': ['ttt3','ttt4'], 'name': 'Test_China', 'coordinates': {'lat': 36.256, 'lng': 117.106, 'alt': 1534.0}, - 'country': 'China', 'state': 'Shandong Sheng', 'coordinate_validation_status': 'NotChecked', - 'coordinate_validation_date': '2020-03-11T12:22:18.047974+01:00', 'type_of_environment': 'Unknown', + 'country': 'China', 'state': 'Shandong Sheng' + 'type_of_environment': 'Unknown', 'type_of_area': 'Unknown', 'timezone': '', - 'additional_metadata': {}, 'coordinate_validator_id': 1, + 'additional_metadata': {} 'roles': [], 'annotations': [], 'aux_images': [], 'aux_docs': [], 'aux_urls': [], 'globalmeta': None, 'globalservice': None} assert response.json() == expected_resp @@ -206,10 +200,9 @@ class TestApps: json={"stationmeta": {"codes":["China11"], "name":"Test_China","coordinates":{"lat":36.256,"lng":117.106,"alt":1534.0}, - "country":"China","state":"Shandong Sheng","coordinate_validation_status":"NotChecked", - "coordinate_validation_date":"2020-03-11T12:22:18.047974+01:00", + "country":"China","state":"Shandong Sheng", "type_of_environment":"Unknown","type_of_area":"Unknown","timezone":"", - "coordinate_validator_id": 1, "additional_metadata":"{}"} + "additional_metadata":"{}"} } ) expected_status_code = 400 diff --git a/toardb/test_toardb.py b/toardb/test_toardb.py index 48b9f3b4838a1cf36f2556c613c40cf07475c360..be76f07681437f4a705014ad1efbcfc06108381c 100644 --- a/toardb/test_toardb.py +++ b/toardb/test_toardb.py @@ -146,6 +146,13 @@ class TestApps: [12,"NotCheckedPreliminary","not checked preliminary"], [13,"Changed","changed"], [14,"Estimated","estimated"], - [15,"MissingValue","missing value"]]} + [15,"MissingValue","missing value"]], + "CL_vocabulary":[[0,"Created","created"], + [1,"SingleValue","single value correction in metadata"], + [2,"Comprehensive","comprehensive metadata revision"], + [3,"Typo","typographic correction of metadata"], + [4,"UnspecifiedData","unspecified data value corrections"], + [5,"Replaced","replaced data with a new version"], + [6,"Flagging", "data value flagging"]]} assert response.json() == expected_resp diff --git a/toardb/timeseries/models.py b/toardb/timeseries/models.py index 4f2616371ebf8fb3fac2a8b733ab10ad10049810..19cbb5214132047867ebf1793827cfe42d580e40 100644 --- a/toardb/timeseries/models.py +++ b/toardb/timeseries/models.py @@ -2,6 +2,7 @@ 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 +from .models_changelog import TimeseriesChangelog from toardb.base import Base from sqlalchemy import Table, Column, Integer, String diff --git a/toardb/timeseries/models_changelog.py b/toardb/timeseries/models_changelog.py new file mode 100644 index 0000000000000000000000000000000000000000..c4ac170754c34ec827c7f8dcceaae3ee7a5fe38e --- /dev/null +++ b/toardb/timeseries/models_changelog.py @@ -0,0 +1,71 @@ +# coding: utf-8 +""" +class TimeseriesChangelog(Base) +=============================== +""" +from sqlalchemy import Column, DateTime, ForeignKey, BigInteger, String, CHAR, \ + Text, Table, Sequence +from sqlalchemy.orm import relationship +from .models_core import Timeseries +from toardb.auth_user.models import AuthUser +from toardb.base import Base + +TIMESERIES_CHANGELOG_ID_SEQ = Sequence('timeseries_changelog_id_seq') # define sequence explicitly +class TimeseriesChangelog(Base): + """ Table "public.timeseries_changelog" + + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | Column | Type | Collation | Nullable | Default | + +================+==========================+===========+==========+==================================================+ + | id | bigint | | not null | nextval('timeseries_changelog_id_seq'::regclass) | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | datetime | timestamp with time zone | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | timeseries_id | bigint | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | type_of_change | integer | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | description | text | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | old_value | character varying(256) | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | new_value | character varying(256) | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | period_start | timestamp with time zone | | | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | period_end | timestamp with time zone | | | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | version | character(28) | | | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + | author_id | integer | | not null | | + +----------------+--------------------------+-----------+----------+--------------------------------------------------+ + Indexes: + "timeseries_changelog_pkey" PRIMARY KEY, btree (id) + Foreign-key constraints: + "timeseries_changelog_author_id_fk_auth_user_id" FOREIGN KEY (author_id) REFERENCES auth_user(id) + "timeseries_changelog_timeseries_id_fk_timeseries_id" FOREIGN KEY (timeseries_id) REFERENCES timeseries(id) + "timeseries_changelog_type_of_change_fk_cl_vocabulary_enum_val" FOREIGN KEY (type_of_change) REFERENCES cl_vocabulary(enum_val) + """ + + __tablename__ = 'timeseries_changelog' + + id = Column(BigInteger, TIMESERIES_CHANGELOG_ID_SEQ, primary_key=True, server_default=TIMESERIES_CHANGELOG_ID_SEQ.next_value()) + datetime = Column(DateTime(True), nullable=False) + description = Column(Text, nullable=False) + old_value = Column(String(256), nullable=False) + new_value = Column(String(256), nullable=False) + period_start = Column(DateTime(True)) + period_end = Column(DateTime(True)) + version = Column(CHAR(28)) +# 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 + timeseries_id = Column(ForeignKey(Timeseries.id), nullable=False) + author_id = Column(ForeignKey(AuthUser.id), nullable=False) +# still to check for some pytest solution for controlled vocabulary + type_of_change = Column(ForeignKey('cl_Vocabulary.enum_val'), nullable=False) + + author = relationship('AuthUser') + timeseries = relationship('Timeseries') + +# cl_vocabulary = relationship('ClVocabulary') diff --git a/toardb/timeseries/test_timeseries.py b/toardb/timeseries/test_timeseries.py index 0fad17edc96ba71b20028f2613072a28ae3ef014..796ea1cbd94cce2ebf67d9bbaeda9f72ddc1a99f 100644 --- a/toardb/timeseries/test_timeseries.py +++ b/toardb/timeseries/test_timeseries.py @@ -154,8 +154,8 @@ class TestApps: 'cf_standardname': 'mole_fraction_of_toluene_in_air', 'units': 'nmol mol-1', 'chemical_formula': 'C7H8', 'id': 7}, 'station': {'additional_metadata': '{}', 'type_of_area': 0, - 'coordinate_validation_date': '2020-02-28T12:27:03.746260+01:00', 'country': 'China', - 'codes': ['SDZ54421'], 'coordinate_validation_status': 0, 'coordinate_validator_id': 1, + 'country': 'China', + 'codes': ['SDZ54421'], 'timezone': '', 'type_of_environment': 0, 'state': 'Beijing Shi', 'name': 'Shangdianzi', 'id': 2}, 'programme': {'id': 0, 'name': '', 'longname': '', 'homepage': '', 'description': ''}}] assert response.json() == expected_resp @@ -183,8 +183,8 @@ class TestApps: 'cf_standardname': 'mole_fraction_of_toluene_in_air', 'units': 'nmol mol-1', 'chemical_formula': 'C7H8', 'id': 7}, 'station': {'additional_metadata': '{}', 'type_of_area': 0, - 'coordinate_validation_date': '2020-02-28T12:27:03.746260+01:00', 'country': 'China', - 'codes': ['SDZ54421'], 'coordinate_validation_status': 0, 'coordinate_validator_id': 1, + 'country': 'China', + 'codes': ['SDZ54421'], 'timezone': '', 'type_of_environment': 0, 'state': 'Beijing Shi', 'name': 'Shangdianzi', 'id': 2}, 'programme': {'id': 0, 'name': '', 'longname': '', 'homepage': '', 'description': ''}} assert response.json() == expected_resp @@ -234,9 +234,8 @@ class TestApps: 'cf_standardname': 'mole_fraction_of_toluene_in_air', 'units': 'nmol mol-1', 'chemical_formula': 'C7H8', 'id': 7}, 'station': {'additional_metadata': '{}', 'type_of_area': 0, - 'coordinate_validation_date': '2020-02-28T12:27:03.746260+01:00', - 'country': 'China', 'codes': ['SDZ54421'], 'coordinate_validation_status': 0, - 'coordinate_validator_id': 1, 'timezone': '', 'type_of_environment': 0, + 'country': 'China', 'codes': ['SDZ54421'], + 'timezone': '', 'type_of_environment': 0, 'state': 'Beijing Shi', 'name': 'Shangdianzi', 'id': 2}, 'programme': {'id': 0, 'name': '', 'longname': '', 'homepage': '', 'description': ''}} assert response.json() == expected_resp @@ -273,9 +272,8 @@ class TestApps: 'cf_standardname': 'mole_fraction_of_toluene_in_air', 'units': 'nmol mol-1', 'chemical_formula': 'C7H8', 'id': 7}, 'station': {'additional_metadata': '{}', 'type_of_area': 0, - 'coordinate_validation_date': '2020-02-28T12:27:03.746260+01:00', - 'country': 'China', 'codes': ['SDZ54421'], 'coordinate_validation_status': 0, - 'coordinate_validator_id': 1, 'timezone': '', 'type_of_environment': 0, + 'country': 'China', 'codes': ['SDZ54421'], + 'timezone': '', 'type_of_environment': 0, 'state': 'Beijing Shi', 'name': 'Shangdianzi', 'id': 2}, 'programme': {'id': 0, 'name': '', 'longname': '', 'homepage': '', 'description': ''}} assert response.json() == expected_resp diff --git a/toardb/toardb.py b/toardb/toardb.py index 01a94469b740a677c8f7395e251d80961fa3a573..ca457c4a707f6c6c5d53edef98edb432579e323f 100644 --- a/toardb/toardb.py +++ b/toardb/toardb.py @@ -39,6 +39,7 @@ async def info(): "Station Dominant Landcover Types": settings.DL_vocab, "Result Types": settings.RT_vocab, "Data Flags": settings.DF_vocab, + "Type of Change": settings.CL_vocab, } return controlled_vocabulary @@ -62,6 +63,7 @@ async def info(name: str): "Station Dominant Landcover Types": settings.DL_vocab, "Result Types": settings.RT_vocab, "Data Flags": settings.DF_vocab, + "Type of Change": settings.CL_vocab, } return controlled_vocabulary[name] @@ -132,6 +134,7 @@ async def startup_event(): DL_vocabulary = __get_enum_dict(fake_cur, "dl_vocabulary") RT_vocabulary = __get_enum_dict(fake_cur, "rt_vocabulary") DF_vocabulary = __get_enum_dict(fake_cur, "df_vocabulary") + CL_vocabulary = __get_enum_dict(fake_cur, "cl_vocabulary") Enumdict=namedtuple("Dict",["value","string","display_str"]) @@ -154,6 +157,7 @@ TR_vocabulary = __get_enum_dict(fake_cur, "tr_vocabulary") DL_vocabulary = __get_enum_dict(fake_cur, "dl_vocabulary") RT_vocabulary = __get_enum_dict(fake_cur, "rt_vocabulary") DF_vocabulary = __get_enum_dict(fake_cur, "df_vocabulary") +CL_vocabulary = __get_enum_dict(fake_cur, "cl_vocabulary") class Settings(BaseSettings): @@ -175,6 +179,7 @@ class Settings(BaseSettings): DL_vocab = DL_vocabulary RT_vocab = RT_vocabulary DF_vocab = DF_vocabulary + CL_vocab = CL_vocabulary settings = Settings()