Mirri 5.1.2

This commit is contained in:
2023-06-10 14:49:33 +01:00
parent e2278fd509
commit 322ed203d8
102 changed files with 1978 additions and 3356 deletions
View File
+45
View File
@@ -0,0 +1,45 @@
class FrozenClass(object):
__isfrozen = False
def __setattr__(self, key, value):
# print(dir(self))
if self.__isfrozen and not hasattr(self, key):
msg = f"Can not add {key} to {self.__class__.__name__}. It is not one of its attributes"
raise TypeError(msg)
object.__setattr__(self, key, value)
def _freeze(self):
self.__isfrozen = True
class _FieldBasedClass(FrozenClass):
_fields = []
def __init__(self, data=None, freeze=True):
self._data = {}
if data is None:
data = {}
for field in self._fields:
value = data.get(field["label"], None)
setattr(self, field["attribute"], value)
if freeze:
self._freeze()
def __eq__(self, o: object) -> bool:
for field in self._fields:
val1 = getattr(self, field["attribute"], None)
val2 = getattr(o, field["attribute"], None)
if val1 != val2:
return False
return True
def __bool__(self):
return bool(self.dict())
def dict(self):
data = {}
for field in self._fields:
value = getattr(self, field["attribute"])
if value is not None:
data[field["label"]] = value
return data
+87
View File
@@ -0,0 +1,87 @@
from calendar import monthrange
from collections import OrderedDict
from copy import copy
from datetime import date
class DateRange:
def __init__(self, year=None, month=None, day=None):
self._year = year
if month is not None and (month < 1 or month > 12):
raise ValueError("Month must be between 1 and 12")
self._month = month
if day is not None and (day < 1 or day > 31):
raise ValueError("Day must be between 1 and 31")
self._day = day
self._start = None
self._end = None
if year or month or day:
self._create_range()
def __str__(self):
_strdate = self.strfdate
if _strdate is None:
return ""
return _strdate
def __bool__(self):
return bool(self._year or self._month or self._day)
def _create_range(self):
year = self._year
month = self._month
day = self._day
if year and month and day:
start_date = date(year=year, month=month, day=day)
end_date = date(year=year, month=month, day=day)
elif month is None:
start_date = date(year=year, month=1, day=1)
end_date = date(year=year, month=12, day=31)
elif day is None:
month_last_day = monthrange(year, month)[1]
start_date = date(year=year, month=month, day=1)
end_date = date(year=year, month=month, day=month_last_day)
self._start = start_date
self._end = end_date
def strpdate(self, date_str: str):
date_str = str(date_str)
orig_date = copy(date_str)
date_str = date_str.replace("/", "").replace("-", "")
if len(date_str) > 8:
msg = f"Malformed date, Mora caracters than expected: {orig_date}"
raise ValueError(msg)
month = None
day = None
if len(date_str) >= 4:
year = int(date_str[:4])
if len(date_str) >= 6:
month = int(date_str[4:6])
if month < 1 or month > 12:
raise ValueError("Month must be between 1 and 12")
if len(date_str) >= 8:
day = int(date_str[6:8])
if day is not None and (day < 1 or day > 31):
raise ValueError("Day must be between 1 and 31")
self._year = year
self._month = month
self._day = day
self._create_range()
return self
@property
def strfdate(self):
year = "----" if self._year is None else f"{self._start.year:04}"
month = "--" if self._month is None else f"{self._start.month:02}"
day = "--" if self._day is None else f"{self._start.day:02}"
_date = str(f"{year}{month}{day}")
if _date == "--------":
return None
return _date
@property
def range(self):
return OrderedDict([("start", self._start), ("end", self._end)])
+47
View File
@@ -0,0 +1,47 @@
class GrowthMedium:
fields = ['record_id', 'record_name', 'acronym', 'full_description',
'ingredients', 'description', 'other_name', 'ph',
'sterilization_conditions']
def __init__(self, **kwargs):
self._data = {}
for field in self.fields:
if field in kwargs and kwargs['field'] is not None:
value = kwargs['field']
setattr(self, field, value)
def __setattr__(self, attr, value):
if attr == '_data':
super().__setattr__(attr, value)
return
if attr not in self.fields:
raise TypeError(f'{attr} not an allowed attribute')
self._data[attr] = value
def __getattr__(self, attr):
if attr == '_data':
return super
if attr not in self.fields and attr != '_data':
raise TypeError(f'{attr} not an allowed attribute')
return self._data.get(attr, None)
def dict(self):
return self._data
def update(self, growth_media, include_fields=None):
for field in self.fields:
if include_fields and field in include_fields:
new_value = getattr(growth_media, field, None)
actual_value = getattr(self, field, None)
if new_value is not None and new_value != actual_value:
setattr(self, field, new_value)
def is_equal(self, other, exclude_fields=[]):
for field in self.fields:
if field in exclude_fields:
continue
value_of_other = getattr(other, field, None)
value_of_self = getattr(self, field, None)
if value_of_self is not None and value_of_self != value_of_other:
return False
return True
+170
View File
@@ -0,0 +1,170 @@
from __future__ import annotations
import hashlib
from typing import Union
from mirri.entities._private_classes import _FieldBasedClass
from mirri.settings import (
ALTITUDE,
COORD_SPATIAL_REFERENCE,
COORDUNCERTAINTY,
COUNTRY,
GEOREF_METHOD,
ISLAND,
LATITUDE,
LONGITUDE,
MUNICIPALITY,
OTHER,
PROVINCE,
SITE,
STATE,
)
import pycountry
class Location(_FieldBasedClass):
_fields = [
{"attribute": "country", "label": COUNTRY},
{"attribute": "state", "label": STATE},
{"attribute": "province", "label": PROVINCE},
{"attribute": "municipality", "label": MUNICIPALITY},
{"attribute": "site", "label": SITE},
{"attribute": "other", "label": OTHER},
{"attribute": "island", "label": ISLAND},
{"attribute": "longitude", "label": LONGITUDE},
{"attribute": "latitude", "label": LATITUDE},
{"attribute": "altitude", "label": ALTITUDE},
{"attribute": "coord_spatial_reference", "label": COORD_SPATIAL_REFERENCE},
{"attribute": "coord_uncertainty", "label": COORDUNCERTAINTY},
{"attribute": "georef_method", "label": GEOREF_METHOD},
]
def __str__(self):
_site = []
if self.country:
_site.append(self.country)
if self.province:
_site.append(self.province)
if self.site:
_site.append(self.site)
if self.municipality:
_site.append(self.municipality)
return ": ".join(_site)
def __hash__(self):
hash_str = ''
for field in self._fields:
value = str(getattr(self, field, None))
hash_str += value
# hash_str = str(self.country) + str(self.province) + str(self.municipality) + str(self.site)
return int(hashlib.sha1(hash_str.encode("utf-8")).hexdigest(), 16) % (10 ** 8)
@property
def country(self) -> Union[str, None]:
return self._data.get(COUNTRY, None)
@country.setter
def country(self, code3: str):
if code3 is not None:
_country = pycountry.countries.get(alpha_3=code3)
if _country is None:
_country = pycountry.historic_countries.get(alpha_3=code3)
if _country is None and code3 != 'INW':
raise ValueError(f'{code3}, not a valid 3 letter country name')
self._data[COUNTRY] = code3
@property
def province(self) -> Union[str, None]:
return self._data.get(PROVINCE, None)
@province.setter
def province(self, code3: str):
self._data[PROVINCE] = code3
@property
def municipality(self) -> Union[str, None]:
return self._data.get(MUNICIPALITY, None)
@municipality.setter
def municipality(self, name: str):
self._data[MUNICIPALITY] = name
@property
def site(self) -> Union[str, None]:
return self._data.get(SITE, None)
@site.setter
def site(self, name: str):
self._data[SITE] = name
@property
def latitude(self):
return self._data.get(LATITUDE, None)
@latitude.setter
def latitude(self, latitude: float):
self._data[LATITUDE] = latitude
@property
def longitude(self) -> Union[float, None]:
return self._data.get(LONGITUDE, None)
@longitude.setter
def longitude(self, longitude: float):
self._data[LONGITUDE] = longitude
@property
def altitude(self) -> Union[int, float, None]:
return self._data.get(ALTITUDE, None)
@altitude.setter
def altitude(self, altitude: Union[int, float]):
self._data[ALTITUDE] = altitude
@property
def georef_method(self) -> Union[str, None]:
return self._data.get(GEOREF_METHOD, None)
@georef_method.setter
def georef_method(self, georef_method: str):
self._data[GEOREF_METHOD] = georef_method
@property
def coord_uncertainty(self) -> Union[str, None]:
return self._data.get(COORDUNCERTAINTY, None)
@coord_uncertainty.setter
def coord_uncertainty(self, coord_uncertainty: str):
self._data[COORDUNCERTAINTY] = coord_uncertainty
@property
def coord_spatial_reference(self) -> Union[str, None]:
return self._data.get(COORD_SPATIAL_REFERENCE, None)
@coord_spatial_reference.setter
def coord_spatial_reference(self, coord_spatial_reference: str):
self._data[COORD_SPATIAL_REFERENCE] = coord_spatial_reference
@property
def state(self) -> Union[str, None]:
return self._data.get(STATE, None)
@state.setter
def state(self, state):
self._data[STATE] = state
@property
def island(self) -> Union[str, None]:
return self._data.get(ISLAND, None)
@island.setter
def island(self, island):
self._data[ISLAND] = island
@property
def other(self) -> Union[str, None]:
return self._data.get(OTHER, None)
@other.setter
def other(self, other):
self._data[OTHER] = other
+202
View File
@@ -0,0 +1,202 @@
from mirri.settings import (BOOK_EDITOR, BOOK_PUBLISHER, BOOK_TITLE,
PUB_AUTHORS, PUB_DOI, PUB_FIRST_PAGE, PUB_ID,
PUB_ISSUE, PUB_JOURNAL, PUB_LAST_PAGE,
PUB_PUBMED_ID, PUB_TITLE, PUB_VOLUME)
# Maybe we could implement some crossref calls to fill all field data
# and get DOI where ever is possible
RECORD_ID = 'RecordId'
RECORD_NAME = 'RecordName'
class Publication:
def __init__(self, data=None):
self._data = {}
if data:
self.record_id = data.get('RecordId', None)
self.record_name = data.get('RecordName', None)
self.pubmed_id = data.get(PUB_PUBMED_ID, None)
self.doi = data.get(PUB_DOI, None)
self.title = data.get(PUB_TITLE, None)
self.authors = data.get(PUB_AUTHORS, None)
self.journal = data.get(PUB_JOURNAL, None)
self.volume = data.get(PUB_VOLUME, None)
self.issue = data.get(PUB_ISSUE, None)
self.first_page = data.get(PUB_FIRST_PAGE, None)
self.last_page = data.get(PUB_LAST_PAGE, None)
self.editor = data.get(BOOK_EDITOR, None)
self.publisher = data.get(BOOK_PUBLISHER, None)
self.book_title = data.get(BOOK_TITLE, None)
self.isbn = data.get('ISBN', None)
self.issn = data.get('ISSN', None)
self.year = data.get('Year', None)
def __bool__(self):
return bool(self._data)
def dict(self):
return self._data
@property
def id(self) -> int:
return self._data.get(PUB_ID, None)
@id.setter
def id(self, value: int):
if value is not None:
self._data[PUB_ID] = value
@property
def record_id(self) -> int:
return self._data.get(RECORD_ID, None)
@record_id.setter
def record_id(self, value: int):
if value is not None:
self._data[RECORD_ID] = value
@property
def record_name(self) -> int:
return self._data.get(RECORD_NAME, None)
@record_name.setter
def record_name(self, value: int):
if value is not None:
self._data[RECORD_NAME] = value
@property
def pubmed_id(self):
return self._data.get(PUB_PUBMED_ID, None)
@pubmed_id.setter
def pubmed_id(self, value: str):
if value is not None:
self._data[PUB_PUBMED_ID] = value
@property
def isbn(self):
return self._data.get('ISBN', None)
@isbn.setter
def isbn(self, value: str):
if value is not None:
self._data['ISBN'] = value
@property
def issn(self):
return self._data.get('ISSN', None)
@issn.setter
def issn(self, value: str):
if value is not None:
self._data['ISSN'] = value
@property
def doi(self):
return self._data.get(PUB_DOI, None)
@doi.setter
def doi(self, value: str):
if value is not None:
self._data[PUB_DOI] = value
@property
def title(self):
return self._data.get(PUB_TITLE, None)
@title.setter
def title(self, value: str):
if value is not None:
self._data[PUB_TITLE] = value
self._data[RECORD_NAME] = value
@property
def authors(self):
return self._data.get(PUB_AUTHORS, None)
@authors.setter
def authors(self, value: str):
if value is not None:
self._data[PUB_AUTHORS] = value
@property
def journal(self):
return self._data.get(PUB_JOURNAL, None)
@journal.setter
def journal(self, value: str):
if value is not None:
self._data[PUB_JOURNAL] = value
@property
def volume(self):
return self._data.get(PUB_VOLUME, None)
@volume.setter
def volume(self, value: str):
if value is not None:
self._data[PUB_VOLUME] = value
@property
def issue(self):
return self._data.get(PUB_ISSUE, None)
@issue.setter
def issue(self, value: str):
if value is not None:
self._data[PUB_ISSUE] = value
@property
def first_page(self):
return self._data.get(PUB_FIRST_PAGE, None)
@first_page.setter
def first_page(self, value: str):
if value is not None:
self._data[PUB_FIRST_PAGE] = value
@property
def last_page(self):
return self._data.get(PUB_LAST_PAGE, None)
@last_page.setter
def last_page(self, value: str):
if value is not None:
self._data[PUB_LAST_PAGE] = value
@property
def book_title(self):
return self._data.get(BOOK_TITLE, None)
@book_title.setter
def book_title(self, value: str):
if value is not None:
self._data[BOOK_TITLE] = value
@property
def editors(self):
return self._data.get(BOOK_EDITOR, None)
@editors.setter
def editors(self, value: str):
if value is not None:
self._data[BOOK_EDITOR] = value
@property
def publisher(self):
return self._data.get(BOOK_PUBLISHER, None)
@publisher.setter
def publisher(self, value: str):
if value is not None:
self._data[BOOK_PUBLISHER] = value
@property
def year(self) -> int:
return self._data.get('Year', None)
@year.setter
def year(self, value: int):
if value is not None:
self._data['Year'] = value
+45
View File
@@ -0,0 +1,45 @@
from mirri.entities._private_classes import _FieldBasedClass
from mirri.settings import (
ALLOWED_MARKER_TYPES,
MARKER_INSDC,
MARKER_SEQ,
MARKER_TYPE)
from mirri import ValidationError
class GenomicSequence(_FieldBasedClass):
_fields = [
{"attribute": "marker_type", "label": MARKER_TYPE},
{"attribute": "marker_id", "label": MARKER_INSDC},
{"attribute": "marker_seq", "label": MARKER_SEQ},
]
@property
def marker_type(self):
return self._data.get(MARKER_TYPE, None)
@marker_type.setter
def marker_type(self, value: str):
if value is not None:
types = " ".join([m["acronym"] for m in ALLOWED_MARKER_TYPES])
if value not in types:
msg = f"{value} not in allowed marker types: {types}"
raise ValidationError(msg)
self._data[MARKER_TYPE] = value
@property
def marker_id(self) -> str:
return self._data.get(MARKER_INSDC, None)
@marker_id.setter
def marker_id(self, value: str):
self._data[MARKER_INSDC] = value
@property
def marker_seq(self) -> str:
return self._data.get(MARKER_SEQ, None)
@marker_seq.setter
def marker_seq(self, value: str):
self._data[MARKER_SEQ] = value
+1243
View File
File diff suppressed because it is too large Load Diff