forked from MIRRI/mirri_utils
Mirri 5.1.2
This commit is contained in:
@@ -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
|
||||
@@ -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)])
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user