diff --git a/Pipfile b/Pipfile index 921318cd0001bcf6253b419eb8fa30d2c50a375f..0b6c797572887983771813753c4b8c71b29d143c 100644 --- a/Pipfile +++ b/Pipfile @@ -15,18 +15,16 @@ numpy = "*" scipy = "*" obspy = ">1.2.0" pycurl = "*" - authlib = "*" cryptography = "*" -databases = {extras = ["postgresql", "sqlite"],version = "*"} +databases = {extras = ["sqlite"],version = "*"} fastapi = "*" httpx = "==0.11.1" openpyxl = "*" -orm = "==0.1.5" pydantic = "==1.4" sqlalchemy = "*" +sqlalchemy-utc = "*" uvicorn = "*" -typesystem = "==0.2.4" [pipenv] allow_prereleases = true diff --git a/Pipfile.lock b/Pipfile.lock index 6f8dbed1be00f10269a18049ff3541d018dacfcf..c91debcbc9a19b055515563ea50d802f293a4e37 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "9d11130b39f605d10cd4d32c8b721e21b38cdbae4b5eaf19f794d6181184a08c" + "sha256": "feb945aefc3117570ac55b98510d96243c861469466ee798fa8b49416693f89d" }, "pipfile-spec": 6, "requires": {}, @@ -201,10 +201,10 @@ }, "hstspreload": { "hashes": [ - "sha256:3d2a71e189aa9216e92130865d907a4be4fd5db6df79bfa6e843e72f86a05aef", - "sha256:3e952e761ea9c43ccf1c61c2b1962da63c66c85469b18608228f95fcae6535ee" + "sha256:acbf4d6fd362b363ce567db56a8667c4f0e43073001add9c4e43f449c11e1d81", + "sha256:ca0af58f803598a737d5f325212e488b24de1ac9f0cf6fa12da1a3f4651914e6" ], - "version": "==2020.4.7" + "version": "==2020.4.14" }, "httptools": { "hashes": [ @@ -376,13 +376,6 @@ "index": "pypi", "version": "==3.0.3" }, - "orm": { - "hashes": [ - "sha256:37cb4757b670c1713f4e0d65874c5afe819acbd712abb9743c97e1d4b00d511c" - ], - "index": "pypi", - "version": "==0.1.5" - }, "psycopg2-binary": { "hashes": [ "sha256:008da3ab51adc70a5f1cfbbe5db3a22607ab030eb44bcecf517ad11a0c2b3cac", @@ -531,11 +524,37 @@ }, "sqlalchemy": { "hashes": [ - "sha256:7224e126c00b8178dfd227bc337ba5e754b197a3867d33b9f30dc0208f773d70" + "sha256:083e383a1dca8384d0ea6378bd182d83c600ed4ff4ec8247d3b2442cf70db1ad", + "sha256:0a690a6486658d03cc6a73536d46e796b6570ac1f8a7ec133f9e28c448b69828", + "sha256:114b6ace30001f056e944cebd46daef38fdb41ebb98f5e5940241a03ed6cad43", + "sha256:128f6179325f7597a46403dde0bf148478f868df44841348dfc8d158e00db1f9", + "sha256:13d48cd8b925b6893a4e59b2dfb3e59a5204fd8c98289aad353af78bd214db49", + "sha256:211a1ce7e825f7142121144bac76f53ac28b12172716a710f4bf3eab477e730b", + "sha256:2dc57ee80b76813759cccd1a7affedf9c4dbe5b065a91fb6092c9d8151d66078", + "sha256:3e625e283eecc15aee5b1ef77203bfb542563fa4a9aa622c7643c7b55438ff49", + "sha256:43078c7ec0457387c79b8d52fff90a7ad352ca4c7aa841c366238c3e2cf52fdf", + "sha256:5b1bf3c2c2dca738235ce08079783ef04f1a7fc5b21cf24adaae77f2da4e73c3", + "sha256:6056b671aeda3fc451382e52ab8a753c0d5f66ef2a5ccc8fa5ba7abd20988b4d", + "sha256:68d78cf4a9dfade2e6cf57c4be19f7b82ed66e67dacf93b32bb390c9bed12749", + "sha256:7025c639ce7e170db845e94006cf5f404e243e6fc00d6c86fa19e8ad8d411880", + "sha256:7224e126c00b8178dfd227bc337ba5e754b197a3867d33b9f30dc0208f773d70", + "sha256:7d98e0785c4cd7ae30b4a451416db71f5724a1839025544b4edbd92e00b91f0f", + "sha256:8d8c21e9d4efef01351bf28513648ceb988031be4159745a7ad1b3e28c8ff68a", + "sha256:bbb545da054e6297242a1bb1ba88e7a8ffb679f518258d66798ec712b82e4e07", + "sha256:d00b393f05dbd4ecd65c989b7f5a81110eae4baea7a6a4cdd94c20a908d1456e", + "sha256:e18752cecaef61031252ca72031d4d6247b3212ebb84748fc5d1a0d2029c23ea" ], "index": "pypi", "version": "==1.3.16" }, + "sqlalchemy-utc": { + "hashes": [ + "sha256:ec5395dfa4d237239c162a1b83283d88c8e2a94219512708634c55329c900278", + "sha256:fed53af37d250168b99eba8f9908a50e34e10dab3c32d38df3e65601ac951baf" + ], + "index": "pypi", + "version": "==0.10.0" + }, "starlette": { "hashes": [ "sha256:6169ee78ded501095d1dda7b141a1dc9f9934d37ad23196e180150ace2c6449b", @@ -543,13 +562,6 @@ ], "version": "==0.13.2" }, - "typesystem": { - "hashes": [ - "sha256:ba2bd10f1c5844d08dd8841e777bdee55bfca569bf21cb96cd0f91e0a4f66cd8" - ], - "index": "pypi", - "version": "==0.2.4" - }, "urllib3": { "hashes": [ "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", @@ -655,39 +667,39 @@ }, "coverage": { "hashes": [ - "sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0", - "sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30", - "sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b", - "sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0", - "sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823", - "sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe", - "sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037", - "sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6", - "sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31", - "sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd", - "sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892", - "sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1", - "sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78", - "sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac", - "sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006", - "sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014", - "sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2", - "sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7", - "sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8", - "sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7", - "sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9", - "sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1", - "sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307", - "sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a", - "sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435", - "sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0", - "sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5", - "sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441", - "sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732", - "sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de", - "sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1" - ], - "version": "==5.0.4" + "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a", + "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355", + "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65", + "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7", + "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9", + "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1", + "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0", + "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55", + "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c", + "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6", + "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef", + "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019", + "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e", + "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0", + "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf", + "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24", + "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2", + "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c", + "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4", + "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0", + "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd", + "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04", + "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e", + "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730", + "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2", + "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768", + "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796", + "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7", + "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a", + "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489", + "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052" + ], + "version": "==5.1" }, "distlib": { "hashes": [ diff --git a/geomagio/api/db/create.py b/geomagio/api/db/create.py index dbabda419503e88df557422a6c09b54d65da3db2..b2dc79313aa132b8bc510a87cc5900e1e60d6d36 100644 --- a/geomagio/api/db/create.py +++ b/geomagio/api/db/create.py @@ -3,7 +3,8 @@ import sqlalchemy from .common import database, sqlalchemy_metadata # register models with sqlalchemy_metadata by importing -from .session import Session +from .metadata import metadata +from .session import session def create_db(): diff --git a/geomagio/api/db/metadata.py b/geomagio/api/db/metadata.py index 384d970e0a7ccf5d8bb7c89e7cdd39e0cdc943cf..79d4b9a35e2e15b421300c102401b8b8611478ad 100644 --- a/geomagio/api/db/metadata.py +++ b/geomagio/api/db/metadata.py @@ -1,93 +1,112 @@ import datetime import enum -import orm - +from obspy import UTCDateTime +from sqlalchemy import or_, Boolean, Column, Index, Integer, String, Table, Text +import sqlalchemy_utc +from ...metadata import Metadata, MetadataCategory from .common import database, sqlalchemy_metadata -# known category values as enumeration -class MetadataCategory(str, enum.Enum): - ADJUSTED_MATRIX = "adjusted-matrix" - FLAG = "flag" - READING = "reading" - - -class Metadata(orm.Model): - """Metadata database model. - - This class is used for Data flagging and other Metadata. - - Flag example: - ``` - automatic_flag = Metadata( - created_by = 'algorithm/version', - start_time = UTCDateTime('2020-01-02T00:17:00.1Z'), - end_time = UTCDateTime('2020-01-02T00:17:00.1Z'), - network = 'NT', - station = 'BOU', - channel = 'BEU', - category = CATEGORY_FLAG, - comment = "spike detected", - priority = 1, - data_valid = False) - ``` - - Adjusted Matrix example: - ``` - adjusted_matrix = Metadata( - created_by = 'algorithm/version', - start_time = UTCDateTime('2020-01-02T00:17:00Z'), - end_time = None, - network = 'NT', - station = 'BOU', - category = CATEGORY_ADJUSTED_MATRIX, - comment = 'automatic adjusted matrix', - priority = 1, - value = { - 'parameters': {'x': 1, 'y': 2, 'z': 3} - 'matrix': [ ... ] - } - ) - ``` - """ - - __tablename__ = "metadata" - __database__ = database - __metadata__ = sqlalchemy_metadata - - id = orm.Integer(primary_key=True) +"""Metadata database model. +See pydantic model geomagio.metadata.Metadata +""" +metadata = Table( + "metadata", + sqlalchemy_metadata, + ## COLUMNS + Column("id", Integer, primary_key=True), # author - created_by = orm.Text(index=True) - created_time = orm.DateTime(default=datetime.datetime.utcnow, index=True) + Column("created_by", String(length=255), index=True), + Column( + "created_time", + sqlalchemy_utc.UtcDateTime, + default=sqlalchemy_utc.now, + index=True, + ), # reviewer - reviewed_by = orm.Text(allow_null=True, index=True) - reviewed_time = orm.DateTime(allow_null=True, index=True) - + Column("reviewed_by", String(length=255), index=True, nullable=True), + Column("reviewed_time", sqlalchemy_utc.UtcDateTime, index=True, nullable=True), # time range - starttime = orm.DateTime(allow_null=True, index=True) - endtime = orm.DateTime(allow_null=True, index=True) - # what metadata applies to - # channel/location allow_null for wildcard - network = orm.String(index=True, max_length=255) - station = orm.String(index=True, max_length=255) - channel = orm.String(allow_null=True, index=True, max_length=255) - location = orm.String(allow_null=True, index=True, max_length=255) - + Column("starttime", sqlalchemy_utc.UtcDateTime, index=True, nullable=True), + Column("endtime", sqlalchemy_utc.UtcDateTime, index=True, nullable=True), + # what data metadata references, null for wildcard + Column("network", String(length=255), nullable=True), # indexed below + Column("station", String(length=255), nullable=True), # indexed below + Column("channel", String(length=255), nullable=True), # indexed below + Column("location", String(length=255), nullable=True), # indexed below # category (flag, matrix, etc) - category = orm.String(index=True, max_length=255) + Column("category", String(length=255)), # indexed below # higher priority overrides lower priority - priority = orm.Integer(default=1, index=True) + Column("priority", Integer, default=1), # whether data is valid (primarily for flags) - data_valid = orm.Boolean(default=True, index=True) - # value - metadata = orm.JSON(allow_null=True) + Column("data_valid", Boolean, default=True, index=True), # whether metadata is valid (based on review) - metadata_valid = orm.Boolean(default=True, index=True) - + Column("metadata_valid", Boolean, default=True, index=True), + # metadata json blob + Column("metadata", Text, nullable=True), # general comment - comment = orm.Text(allow_null=True) + Column("comment", Text, nullable=True), # review specific comment - review_comment = orm.Text(allow_null=True) + Column("review_comment", Text, nullable=True), + ## INDICES + Index( + "index_station_metadata", + # sncl + "network", + "station", + "channel", + "location", + # type + "category", + # date + "starttime", + "endtime", + # valid + "metadata_valid", + "data_valid", + ), + Index( + "index_category_time", + # type + "category", + # date + "starttime", + "endtime", + ), +) + + +async def get_metadata( + *, # make all params keyword + network: str, + station: str, + channel: str = None, + location: str = None, + category: MetadataCategory = None, + starttime: UTCDateTime = None, + endtime: UTCDateTime = None, + data_valid: bool = None, + metadata_valid: bool = None, +): + query = ( + metadata.select() + .where(metadata.c.network.like(network or "%")) + .where(metadata.c.station.like(station or "%")) + .where(metadata.c.channel.like(channel or "%")) + .where(metadata.c.location.like(location or "%")) + ) + if starttime: + query = query.where( + or_(metadata.c.endtime == None, metadata.c.endtime > starttime.datetime) + ) + if endtime: + query = query.where( + or_(metadata.c.starttime == None, metadata.c.starttime < endtime.datetime) + ) + if data_valid is not None: + query = query.where(metadata.c.data_valid == data_valid) + if metadata_valid is not None: + query = query.where(metadata.c.metadata_valid == metadata_valid) diff --git a/geomagio/api/db/session.py b/geomagio/api/db/session.py index f5a94fcaea153df3f40d109695975236bdd9d215..f5a3f89e4045b56b8dc7673c64f4014b04a77847 100644 --- a/geomagio/api/db/session.py +++ b/geomagio/api/db/session.py @@ -1,51 +1,52 @@ -import datetime +from datetime import datetime, timedelta, timezone import json from typing import Dict, Optional +import sqlalchemy +import sqlalchemy_utc -import orm from .common import database, sqlalchemy_metadata -class Session(orm.Model): - """Model for database sessions. - """ +session = sqlalchemy.Table( + "session", + sqlalchemy_metadata, + sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True), + sqlalchemy.Column("session_id", sqlalchemy.String(length=100), index=True), + sqlalchemy.Column("data", sqlalchemy.Text), + sqlalchemy.Column("updated", sqlalchemy_utc.UtcDateTime, index=True), +) - __tablename__ = "session" - __database__ = database - __metadata__ = sqlalchemy_metadata - id = orm.Integer(primary_key=True) - session_id = orm.String(index=True, max_length=100) - data = orm.Text() - updated = orm.DateTime(index=True) - - -async def delete_session(session_id: str): - try: - session = await Session.objects.get(session_id=session_id) - await session.delete() - except orm.exceptions.NoMatch: - return {} +async def delete_session(session_id: str) -> None: + query = session.delete().where(session.c.session_id == session_id) + await database.execute(query) async def get_session(session_id: str) -> str: - try: - session = await Session.objects.get(session_id=session_id) - return session.data - except orm.exceptions.NoMatch: - return {} - - -async def remove_expired_sessions(max_age: datetime.timedelta): - now = datetime.datetime.now(tz=datetime.timezone.utc) - await Session.objects.delete(updated__lt=(now - max_age)) - - -async def save_session(session_id: str, data: str): - updated = datetime.datetime.now(tz=datetime.timezone.utc) - try: - session = await Session.objects.get(session_id=session_id) - await session.update(data=data, updated=updated) - except orm.exceptions.NoMatch: - await Session.objects.create(session_id=session_id, data=data, updated=updated) + query = session.select().where(session.c.session_id == session_id) + row = await database.fetch_one(query) + return row.data + + +async def remove_expired_sessions(max_age: timedelta) -> None: + threshold = datetime.now(tz=timezone.utc) - max_age + query = session.delete().where(session.c.updated < threshold) + await database.execute(query) + + +async def save_session(session_id: str, data: str) -> None: + updated = datetime.now(tz=timezone.utc) + # try update first + query = ( + session.update() + .where(session.c.session_id == session_id) + .values(data=data, updated=updated) + ) + count = await database.execute(query) + if count == 0: + # no matching session, insert + query = session.insert().values( + session_id=session_id, data=data, updated=updated + ) + await database.execute(query) diff --git a/geomagio/metadata/Metadata.py b/geomagio/metadata/Metadata.py new file mode 100644 index 0000000000000000000000000000000000000000..b1962fdd243562debc2d844d340d6375799aeb59 --- /dev/null +++ b/geomagio/metadata/Metadata.py @@ -0,0 +1,77 @@ +from typing import Dict + +from obspy import UTCDateTime +from pydantic import BaseModel + +from .. import pydantic_utcdatetime +from .MetadataCategory import MetadataCategory + + +class Metadata(BaseModel): + """ + This class is used for Data flagging and other Metadata. + + Flag example: + ``` + automatic_flag = Metadata( + created_by = 'algorithm/version', + start_time = UTCDateTime('2020-01-02T00:17:00.1Z'), + end_time = UTCDateTime('2020-01-02T00:17:00.1Z'), + network = 'NT', + station = 'BOU', + channel = 'BEU', + category = MetadataCategory.FLAG, + comment = "spike detected", + priority = 1, + data_valid = False) + ``` + + Adjusted Matrix example: + ``` + adjusted_matrix = Metadata( + created_by = 'algorithm/version', + start_time = UTCDateTime('2020-01-02T00:17:00Z'), + end_time = None, + network = 'NT', + station = 'BOU', + category = MetadataCategory.ADJUSTED_MATRIX, + comment = 'automatic adjusted matrix', + priority = 1, + value = { + 'parameters': {'x': 1, 'y': 2, 'z': 3} + 'matrix': [ ... ] + } + ) + ``` + """ + + # database id + id: int = None + # author + created_by: str = None + created_time: UTCDateTime = None + # reviewer + reviewed_by: str = None + reviewed_time: UTCDateTime = None + # time range + starttime: UTCDateTime = None + endtime: UTCDateTime = None + # what data metadata references, null for wildcard + network: str = None + station: str = None + channel: str = None + location: str = None + # category (flag, matrix, etc) + category: MetadataCategory = None + # higher priority overrides lower priority + priority: int = 1 + # whether data is valid (primarily for flags) + data_valid: bool = True + # whether metadata is valid (based on review) + metadata_valid: bool = True + # metadata json blob + metadata: Dict = None + # general comment + comment: str = None + # review specific comment + review_comment: str = None diff --git a/geomagio/metadata/MetadataCategory.py b/geomagio/metadata/MetadataCategory.py new file mode 100644 index 0000000000000000000000000000000000000000..d70092f842e83753b5121c7ea093cabcc2cfd36b --- /dev/null +++ b/geomagio/metadata/MetadataCategory.py @@ -0,0 +1,8 @@ +from enum import Enum + +# known category values as enumeration +class MetadataCategory(str, Enum): + ADJUSTED_MATRIX = "adjusted-matrix" + FLAG = "flag" + INSTRUMENT = "instrument" + READING = "reading"