diff --git a/nmrpy/data_objects.py b/nmrpy/data_objects.py index 80ac019..ed11e97 100644 --- a/nmrpy/data_objects.py +++ b/nmrpy/data_objects.py @@ -14,6 +14,7 @@ from sdRDM import DataModel from sdRDM.base.importedmodules import ImportedModules from nmrpy.datamodel.core import * +from nmrpy.utils import create_enzymeml class Base: @@ -514,10 +515,24 @@ def deconvoluted_integrals(self): """ if self._deconvoluted_peaks is not None: integrals = [] + i = 0 for peak in self._deconvoluted_peaks: int_gauss = peak[-1] * Fid._f_gauss_int(peak[3], peak[1]) int_lorentz = (1 - peak[-1]) * Fid._f_lorentz_int(peak[3], peak[2]) integrals.append(int_gauss + int_lorentz) + + for peak_identity in self.fid_object.peak_identities: + if peak_identity.name == self.identities[i]: + try: + peak_identity.associated_integrals.append( + float(integrals[i]) + ) + except: + peak_identity.associated_integrals = [] + peak_identity.associated_integrals.append( + float(integrals[i]) + ) + i += 1 return integrals def _get_plots(self): @@ -1509,6 +1524,24 @@ def enzymeml_library(self): del self.__enzymeml_library print("The current EnzymeML library has been deleted.") + @property + def concentrations(self): + """ + An array of the concentration for each FID. + """ + return self.__c + + @concentrations.setter + def concentrations(self, c): + if not isinstance(c, dict): + raise TypeError("c must be a dictionary.") + self.__c = c + + @concentrations.deleter + def concentrations(self): + del self.__c + print("The current concentrations have been deleted.") + def __str__(self): return "FidArray of {} FID(s)".format(len(self.data)) @@ -1568,6 +1601,7 @@ def _get_widgets(self): or isinstance(self.__dict__[id], FidArrayRangeSelector) or isinstance(self.__dict__[id], DataTraceRangeSelector) or isinstance(self.__dict__[id], DataTraceSelector) + or isinstance(self.__dict__[id], IdentityRangeAssigner) ] return widgets @@ -2325,14 +2359,22 @@ def save_to_file(self, filename=None, overwrite=False): self._del_widgets() for fid in self.get_fids(): fid._del_widgets() + # delete EnzymeML library & document (can't be pickled) + try: + del self.enzymeml_library + del self.enzymeml_document + except: + pass with open(filename, "wb") as f: pickle.dump(self, f) # TODO: Will probably create a measurement object for each FID(?) # and add them to the EnzymeML document provided # Issue: How to get species for IdentityAssigner? __init__()? - def add_to_enzymeml(self, enzymeml_document=None) -> None: - ... + def to_enzymeml(self, enzymeml_document: DataModel = None) -> DataModel: + if not enzymeml_document: + enzymeml_document = self.enzymeml_document + return create_enzymeml(self, enzymeml_document) # TODO: Refactor save_data method # possibly make saving to EnzymeML a get_measurements method @@ -2409,6 +2451,12 @@ def clear_identities(self): for fid in self.get_fids(): fid.identities = None + def calculate_concentrations(self): + integrals = self.deconvoluted_integrals.transpose() + self._concentration_widget = ConcentrationCalculator( + fid_array=self, integrals=integrals + ) + class Importer(Base): def __init__(self, *args, **kwargs): diff --git a/nmrpy/datamodel/__init__.py b/nmrpy/datamodel/__init__.py index 08352f9..d866ab2 100644 --- a/nmrpy/datamodel/__init__.py +++ b/nmrpy/datamodel/__init__.py @@ -1,3 +1,3 @@ -__URL__ = "https://github.com/NMRPy/nmrpy" -__COMMIT__ = "dec2cda6676f8d04070715fe079ed786515ea918" +__URL__ = "" +__COMMIT__ = "" diff --git a/nmrpy/datamodel/core/__init__.py b/nmrpy/datamodel/core/__init__.py index 7027d43..f22d8a0 100644 --- a/nmrpy/datamodel/core/__init__.py +++ b/nmrpy/datamodel/core/__init__.py @@ -5,17 +5,12 @@ from .processingsteps import ProcessingSteps from .identity import Identity from .fidarray import FIDArray -from .citation import Citation -from .person import Person -from .publication import Publication from .cv import CV from .term import Term from .fileformats import FileFormats -from .subjects import Subjects -from .publicationtypes import PublicationTypes -from .identifiertypes import IdentifierTypes __doc__ = "" + __all__ = [ "NMRpy", "Experiment", @@ -24,13 +19,7 @@ "ProcessingSteps", "Identity", "FIDArray", - "Citation", - "Person", - "Publication", "CV", "Term", "FileFormats", - "Subjects", - "PublicationTypes", - "IdentifierTypes", ] diff --git a/nmrpy/datamodel/core/abstractspecies.py b/nmrpy/datamodel/core/abstractspecies.py deleted file mode 100644 index 57e50eb..0000000 --- a/nmrpy/datamodel/core/abstractspecies.py +++ /dev/null @@ -1,71 +0,0 @@ -import sdRDM - -from typing import Optional, Union -from pydantic import Field, validator -from sdRDM.base.utils import forge_signature, IDGenerator - -from pydantic import StrictBool - -from .vessel import Vessel - - -@forge_signature -class AbstractSpecies(sdRDM.DataModel): - """This object is used to inherit basic attributes common to all species used in the data model.""" - - id: Optional[str] = Field( - description="Unique identifier of the given object.", - default_factory=IDGenerator("abstractspeciesINDEX"), - xml="@id", - ) - - name: str = Field( - ..., - description="None", - ) - - vessel_id: Union[Vessel, str] = Field( - ..., - reference="Vessel.id", - description="None", - ) - - init_conc: Optional[float] = Field( - default=None, - description="None", - ) - - constant: StrictBool = Field( - ..., - description="None", - ) - - unit: Optional[str] = Field( - default=None, - description="None", - ) - - uri: Optional[str] = Field( - default=None, - description="None", - ) - - creator_id: Optional[str] = Field( - default=None, - description="None", - ) - - @validator("vessel_id") - def get_vessel_id_reference(cls, value): - """Extracts the ID from a given object to create a reference""" - - from .vessel import Vessel - - if isinstance(value, Vessel): - return value.id - elif isinstance(value, str): - return value - else: - raise TypeError( - f"Expected types [Vessel, str] got '{type(value).__name__}' instead." - ) diff --git a/nmrpy/datamodel/core/citation.py b/nmrpy/datamodel/core/citation.py deleted file mode 100644 index c7834f3..0000000 --- a/nmrpy/datamodel/core/citation.py +++ /dev/null @@ -1,216 +0,0 @@ -import sdRDM - -from typing import Any, List, Optional -from pydantic import AnyUrl, Field, PrivateAttr -from sdRDM.base.listplus import ListPlus -from sdRDM.base.utils import forge_signature, IDGenerator -from .identifiertypes import IdentifierTypes -from .person import Person -from .publicationtypes import PublicationTypes -from .subjects import Subjects -from .term import Term -from .publication import Publication - - -@forge_signature -class Citation(sdRDM.DataModel): - """Container for various types of metadata primarily used in the publication and citation of the dataset.""" - - id: Optional[str] = Field( - description="Unique identifier of the given object.", - default_factory=IDGenerator("citationINDEX"), - xml="@id", - ) - - title: Optional[str] = Field( - default=None, - description="Title the dataset should have when published.", - ) - - doi: Optional[AnyUrl] = Field( - default=None, - description="DOI pointing to the published dataset", - ) - - description: Optional[str] = Field( - default=None, - description="Description the dataset should have when published.", - ) - - authors: List[Person] = Field( - description="List of authors for this dataset.", - default_factory=ListPlus, - multiple=True, - ) - - subjects: List[Subjects] = Field( - description="List of subjects this dataset belongs to.", - default_factory=ListPlus, - multiple=True, - ) - - keywords: List[Term] = Field( - description="List of CV-based keywords describing the dataset.", - default_factory=ListPlus, - multiple=True, - ) - - topics: List[Term] = Field( - description="List of CV-based topics the dataset addresses.", - default_factory=ListPlus, - multiple=True, - ) - - related_publications: List[Publication] = Field( - description="List of publications relating to this dataset.", - default_factory=ListPlus, - multiple=True, - ) - - notes: Optional[str] = Field( - default=None, - description="Additional notes about the dataset.", - ) - - funding: List[str] = Field( - description="Funding information for this dataset.", - default_factory=ListPlus, - multiple=True, - ) - - license: Optional[str] = Field( - default="CC BY 4.0", - description="License information for this dataset. Defaults to `CC BY 4.0`.", - ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) - - def add_to_authors( - self, - last_name: str, - first_name: str, - middle_names: List[str] = ListPlus(), - affiliation: Optional[str] = None, - email: Optional[str] = None, - identifier_type: Optional[IdentifierTypes] = None, - identifier_value: Optional[str] = None, - id: Optional[str] = None, - ) -> None: - """ - This method adds an object of type 'Person' to attribute authors - - Args: - id (str): Unique identifier of the 'Person' object. Defaults to 'None'. - last_name (): Family name of the person.. - first_name (): Given name of the person.. - middle_names (): List of middle names of the person.. Defaults to ListPlus() - affiliation (): Institution the Person belongs to.. Defaults to None - email (): Email address of the person.. Defaults to None - identifier_type (): Recognized identifier for the person.. Defaults to None - identifier_value (): Value of the identifier for the person.. Defaults to None - """ - params = { - "last_name": last_name, - "first_name": first_name, - "middle_names": middle_names, - "affiliation": affiliation, - "email": email, - "identifier_type": identifier_type, - "identifier_value": identifier_value, - } - if id is not None: - params["id"] = id - self.authors.append(Person(**params)) - return self.authors[-1] - - def add_to_keywords( - self, - name: str, - accession: str, - term_cv_reference: Optional[str] = None, - value: Optional[Any] = None, - id: Optional[str] = None, - ) -> None: - """ - This method adds an object of type 'Term' to attribute keywords - - Args: - id (str): Unique identifier of the 'Term' object. Defaults to 'None'. - name (): The preferred name of the term associated with the given accession number.. - accession (): Accession number of the term in the controlled vocabulary.. - term_cv_reference (): Reference to the `CV.id` of a controlled vocabulary that has been defined for this dataset.. Defaults to None - value (): Value of the term, if applicable.. Defaults to None - """ - params = { - "name": name, - "accession": accession, - "term_cv_reference": term_cv_reference, - "value": value, - } - if id is not None: - params["id"] = id - self.keywords.append(Term(**params)) - return self.keywords[-1] - - def add_to_topics( - self, - name: str, - accession: str, - term_cv_reference: Optional[str] = None, - value: Optional[Any] = None, - id: Optional[str] = None, - ) -> None: - """ - This method adds an object of type 'Term' to attribute topics - - Args: - id (str): Unique identifier of the 'Term' object. Defaults to 'None'. - name (): The preferred name of the term associated with the given accession number.. - accession (): Accession number of the term in the controlled vocabulary.. - term_cv_reference (): Reference to the `CV.id` of a controlled vocabulary that has been defined for this dataset.. Defaults to None - value (): Value of the term, if applicable.. Defaults to None - """ - params = { - "name": name, - "accession": accession, - "term_cv_reference": term_cv_reference, - "value": value, - } - if id is not None: - params["id"] = id - self.topics.append(Term(**params)) - return self.topics[-1] - - def add_to_related_publications( - self, - type: PublicationTypes, - title: str, - authors: List[Person] = ListPlus(), - year: Optional[int] = None, - doi: Optional[AnyUrl] = None, - id: Optional[str] = None, - ) -> None: - """ - This method adds an object of type 'Publication' to attribute related_publications - - Args: - id (str): Unique identifier of the 'Publication' object. Defaults to 'None'. - type (): Nature of the publication.. - title (): Title of the publication.. - authors (): Authors of the publication.. Defaults to ListPlus() - year (): Year of publication.. Defaults to None - doi (): The DOI pointing to the publication.. Defaults to None - """ - params = { - "type": type, - "title": title, - "authors": authors, - "year": year, - "doi": doi, - } - if id is not None: - params["id"] = id - self.related_publications.append(Publication(**params)) - return self.related_publications[-1] diff --git a/nmrpy/datamodel/core/complexdatapoint.py b/nmrpy/datamodel/core/complexdatapoint.py deleted file mode 100644 index 44ce1c2..0000000 --- a/nmrpy/datamodel/core/complexdatapoint.py +++ /dev/null @@ -1,32 +0,0 @@ -import sdRDM - -from typing import Optional -from pydantic import Field -from sdRDM.base.utils import forge_signature, IDGenerator - - -@forge_signature -class ComplexDataPoint(sdRDM.DataModel): - """Container for a complex number from the Free Induction Decay.""" - - id: Optional[str] = Field( - description="Unique identifier of the given object.", - default_factory=IDGenerator("complexdatapointINDEX"), - xml="@id", - ) - - real_part: Optional[float] = Field( - default=None, - description=( - "Real part of the complex number. Equivalent to `z.real` with `z` being a" - " `complex` number in Python." - ), - ) - - imaginary_part: Optional[float] = Field( - default=None, - description=( - "Imaginary part of the complex number. Equivalent to `z.imag` with `z`" - " being a `complex` number in Python." - ), - ) diff --git a/nmrpy/datamodel/core/cv.py b/nmrpy/datamodel/core/cv.py index ccc6ca5..e7e070a 100644 --- a/nmrpy/datamodel/core/cv.py +++ b/nmrpy/datamodel/core/cv.py @@ -1,9 +1,11 @@ import sdRDM from typing import Optional -from pydantic import AnyUrl, Field, PrivateAttr +from pydantic import Field from sdRDM.base.utils import forge_signature, IDGenerator +from pydantic import AnyUrl + @forge_signature class CV(sdRDM.DataModel): @@ -29,7 +31,3 @@ class CV(sdRDM.DataModel): ..., description="URL pointing to the CV used.", ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) diff --git a/nmrpy/datamodel/core/datatypes.py b/nmrpy/datamodel/core/datatypes.py deleted file mode 100644 index 92b2754..0000000 --- a/nmrpy/datamodel/core/datatypes.py +++ /dev/null @@ -1,10 +0,0 @@ -from enum import Enum - - -class DataTypes(Enum): - CONCENTRATION = "conc" - ABSORPTION = "abs" - FEED = "feed" - BIOMASS = "biomass" - CONVERSION = "conversion" - PEAK_AREA = "peak-area" diff --git a/nmrpy/datamodel/core/experiment.py b/nmrpy/datamodel/core/experiment.py index d698bcf..e379b4f 100644 --- a/nmrpy/datamodel/core/experiment.py +++ b/nmrpy/datamodel/core/experiment.py @@ -1,14 +1,16 @@ import sdRDM from typing import Optional, Union, List -from pydantic import Field, PrivateAttr +from pydantic import Field from sdRDM.base.listplus import ListPlus from sdRDM.base.utils import forge_signature, IDGenerator + + from .fidobject import FIDObject from .processingsteps import ProcessingSteps -from .identity import Identity from .fidarray import FIDArray from .parameters import Parameters +from .identity import Identity @forge_signature @@ -38,10 +40,6 @@ class Experiment(sdRDM.DataModel): default=None, description="Multiple NMR spectra to be processed together.", ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) def add_to_fid( self, @@ -63,6 +61,7 @@ def add_to_fid( processing_steps (): Contains the processing steps performed, as well as the parameters used for them.. Defaults to None peak_identities (): Container holding and mapping integrals resulting from peaks and their ranges to EnzymeML species.. Defaults to ListPlus() """ + params = { "raw_data": raw_data, "processed_data": processed_data, @@ -70,7 +69,10 @@ def add_to_fid( "processing_steps": processing_steps, "peak_identities": peak_identities, } + if id is not None: params["id"] = id + self.fid.append(FIDObject(**params)) + return self.fid[-1] diff --git a/nmrpy/datamodel/core/fidarray.py b/nmrpy/datamodel/core/fidarray.py index fae2599..1400e9c 100644 --- a/nmrpy/datamodel/core/fidarray.py +++ b/nmrpy/datamodel/core/fidarray.py @@ -1,7 +1,7 @@ import sdRDM from typing import List, Optional -from pydantic import Field, PrivateAttr +from pydantic import Field from sdRDM.base.listplus import ListPlus from sdRDM.base.utils import forge_signature, IDGenerator @@ -21,7 +21,3 @@ class FIDArray(sdRDM.DataModel): multiple=True, default_factory=ListPlus, ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) diff --git a/nmrpy/datamodel/core/fidobject.py b/nmrpy/datamodel/core/fidobject.py index 513846b..61e4f00 100644 --- a/nmrpy/datamodel/core/fidobject.py +++ b/nmrpy/datamodel/core/fidobject.py @@ -1,12 +1,15 @@ import sdRDM from typing import Optional, Union, List -from pydantic import Field, PrivateAttr +from pydantic import Field from sdRDM.base.listplus import ListPlus from sdRDM.base.utils import forge_signature, IDGenerator + + from .processingsteps import ProcessingSteps -from .identity import Identity, AssociatedRanges from .parameters import Parameters +from .identity import Identity +from .identity import AssociatedRanges @forge_signature @@ -35,16 +38,16 @@ class FIDObject(sdRDM.DataModel): ) nmr_parameters: Optional[Parameters] = Field( - default=Parameters(), description="Contains commonly-used NMR parameters.", + default_factory=Parameters, ) processing_steps: Optional[ProcessingSteps] = Field( - default=ProcessingSteps(), description=( "Contains the processing steps performed, as well as the parameters used" " for them." ), + default_factory=ProcessingSteps, ) peak_identities: List[Identity] = Field( @@ -55,10 +58,6 @@ class FIDObject(sdRDM.DataModel): default_factory=ListPlus, multiple=True, ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) def add_to_peak_identities( self, @@ -80,6 +79,7 @@ def add_to_peak_identities( associated_ranges (): Sets of ranges belonging to the given peaks. Defaults to ListPlus() associated_integrals (): Integrals resulting from the given peaks and ranges of a species. Defaults to ListPlus() """ + params = { "name": name, "species_id": species_id, @@ -87,7 +87,10 @@ def add_to_peak_identities( "associated_ranges": associated_ranges, "associated_integrals": associated_integrals, } + if id is not None: params["id"] = id + self.peak_identities.append(Identity(**params)) + return self.peak_identities[-1] diff --git a/nmrpy/datamodel/core/identifiertypes.py b/nmrpy/datamodel/core/identifiertypes.py deleted file mode 100644 index f4bf8fe..0000000 --- a/nmrpy/datamodel/core/identifiertypes.py +++ /dev/null @@ -1,5 +0,0 @@ -from enum import Enum - - -class IdentifierTypes(Enum): - ORCID = "ORCID" diff --git a/nmrpy/datamodel/core/identity.py b/nmrpy/datamodel/core/identity.py index 3f28d1a..17c838b 100644 --- a/nmrpy/datamodel/core/identity.py +++ b/nmrpy/datamodel/core/identity.py @@ -1,7 +1,7 @@ import sdRDM from typing import List, Optional -from pydantic import Field, PrivateAttr +from pydantic import Field from sdRDM.base.listplus import ListPlus from sdRDM.base.utils import forge_signature, IDGenerator @@ -15,12 +15,10 @@ class AssociatedRanges(sdRDM.DataModel): default_factory=IDGenerator("associatedrangesINDEX"), xml="@id", ) + start: Optional[float] = Field() + end: Optional[float] = Field() - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) @forge_signature @@ -60,10 +58,6 @@ class Identity(sdRDM.DataModel): default_factory=ListPlus, multiple=True, ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) def add_to_associated_ranges( self, @@ -79,8 +73,15 @@ def add_to_associated_ranges( start (): . Defaults to None end (): . Defaults to None """ - params = {"start": start, "end": end} + + params = { + "start": start, + "end": end, + } + if id is not None: params["id"] = id + self.associated_ranges.append(AssociatedRanges(**params)) + return self.associated_ranges[-1] diff --git a/nmrpy/datamodel/core/nmrpy.py b/nmrpy/datamodel/core/nmrpy.py index 9f68800..ab19951 100644 --- a/nmrpy/datamodel/core/nmrpy.py +++ b/nmrpy/datamodel/core/nmrpy.py @@ -1,10 +1,11 @@ import sdRDM from typing import Optional -from pydantic import Field, PrivateAttr +from pydantic import Field from sdRDM.base.utils import forge_signature, IDGenerator + from datetime import datetime as Datetime -from .citation import Citation + from .experiment import Experiment @@ -32,15 +33,3 @@ class NMRpy(sdRDM.DataModel): default=None, description="List of experiments associated with this dataset.", ) - - citation: Optional[Citation] = Field( - default=Citation(), - description=( - "Relevant information regarding the publication and citation of this" - " dataset." - ), - ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) diff --git a/nmrpy/datamodel/core/parameters.py b/nmrpy/datamodel/core/parameters.py index f2983ef..66f0c37 100644 --- a/nmrpy/datamodel/core/parameters.py +++ b/nmrpy/datamodel/core/parameters.py @@ -1,7 +1,7 @@ import sdRDM from typing import List, Optional -from pydantic import Field, PrivateAttr +from pydantic import Field from sdRDM.base.listplus import ListPlus from sdRDM.base.utils import forge_signature, IDGenerator @@ -67,7 +67,3 @@ class Parameters(sdRDM.DataModel): default=None, description="sw_left", ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) diff --git a/nmrpy/datamodel/core/person.py b/nmrpy/datamodel/core/person.py deleted file mode 100644 index 0fc6d07..0000000 --- a/nmrpy/datamodel/core/person.py +++ /dev/null @@ -1,58 +0,0 @@ -import sdRDM - -from typing import List, Optional -from pydantic import Field, PrivateAttr -from sdRDM.base.listplus import ListPlus -from sdRDM.base.utils import forge_signature, IDGenerator -from .identifiertypes import IdentifierTypes - - -@forge_signature -class Person(sdRDM.DataModel): - """Container for information regarding a person that worked on an experiment.""" - - id: Optional[str] = Field( - description="Unique identifier of the given object.", - default_factory=IDGenerator("personINDEX"), - xml="@id", - ) - - last_name: str = Field( - ..., - description="Family name of the person.", - ) - - first_name: str = Field( - ..., - description="Given name of the person.", - ) - - middle_names: List[str] = Field( - description="List of middle names of the person.", - default_factory=ListPlus, - multiple=True, - ) - - affiliation: Optional[str] = Field( - default=None, - description="Institution the Person belongs to.", - ) - - email: Optional[str] = Field( - default=None, - description="Email address of the person.", - ) - - identifier_type: Optional[IdentifierTypes] = Field( - default=None, - description="Recognized identifier for the person.", - ) - - identifier_value: Optional[str] = Field( - default=None, - description="Value of the identifier for the person.", - ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) diff --git a/nmrpy/datamodel/core/processingsteps.py b/nmrpy/datamodel/core/processingsteps.py index 8fdea85..e4c8830 100644 --- a/nmrpy/datamodel/core/processingsteps.py +++ b/nmrpy/datamodel/core/processingsteps.py @@ -1,7 +1,7 @@ import sdRDM from typing import Optional -from pydantic import Field, PrivateAttr +from pydantic import Field from sdRDM.base.utils import forge_signature, IDGenerator @@ -79,7 +79,3 @@ class ProcessingSteps(sdRDM.DataModel): default=False, description="Whether or not Baseline correction was performed.", ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) diff --git a/nmrpy/datamodel/core/protein.py b/nmrpy/datamodel/core/protein.py deleted file mode 100644 index efcc389..0000000 --- a/nmrpy/datamodel/core/protein.py +++ /dev/null @@ -1,57 +0,0 @@ -import sdRDM - -from typing import Optional -from pydantic import Field -from sdRDM.base.utils import forge_signature, IDGenerator - - -from .sboterm import SBOTerm - - -@forge_signature -class Protein(sdRDM.DataModel): - """This objects describes the proteins that were used or produced in the course of the experiment.""" - - id: Optional[str] = Field( - description="Unique identifier of the given object.", - default_factory=IDGenerator("proteinINDEX"), - xml="@id", - ) - - sequence: str = Field( - ..., - description="Amino acid sequence of the protein", - template_alias="Sequence", - ) - - ecnumber: Optional[str] = Field( - default=None, - description="EC number of the protein.", - regex="(\d+.)(\d+.)(\d+.)(\d+)", - template_alias="EC Number", - ) - - organism: Optional[str] = Field( - default=None, - description="Organism the protein was expressed in.", - template_alias="Source organism", - ) - - organism_tax_id: Optional[str] = Field( - default=None, - description="Taxonomy identifier of the expression host.", - ) - - uniprotid: Optional[str] = Field( - default=None, - description=( - "Unique identifier referencing a protein entry at UniProt. Use this" - " identifier to initialize the object from the UniProt database." - ), - template_alias="UniProt ID", - ) - - ontology: SBOTerm = Field( - description="None", - default=SBOTerm.CATALYST, - ) diff --git a/nmrpy/datamodel/core/publication.py b/nmrpy/datamodel/core/publication.py deleted file mode 100644 index f3f8e50..0000000 --- a/nmrpy/datamodel/core/publication.py +++ /dev/null @@ -1,88 +0,0 @@ -import sdRDM - -from typing import List, Optional -from pydantic import AnyUrl, Field, PrivateAttr -from sdRDM.base.listplus import ListPlus -from sdRDM.base.utils import forge_signature, IDGenerator -from .identifiertypes import IdentifierTypes -from .person import Person -from .publicationtypes import PublicationTypes - - -@forge_signature -class Publication(sdRDM.DataModel): - """Container for citation information of a relevant publication.""" - - id: Optional[str] = Field( - description="Unique identifier of the given object.", - default_factory=IDGenerator("publicationINDEX"), - xml="@id", - ) - - type: PublicationTypes = Field( - ..., - description="Nature of the publication.", - ) - - title: str = Field( - ..., - description="Title of the publication.", - ) - - authors: List[Person] = Field( - description="Authors of the publication.", - multiple=True, - default_factory=ListPlus, - ) - - year: Optional[int] = Field( - default=None, - description="Year of publication.", - ) - - doi: Optional[AnyUrl] = Field( - default=None, - description="The DOI pointing to the publication.", - ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) - - def add_to_authors( - self, - last_name: str, - first_name: str, - middle_names: List[str] = ListPlus(), - affiliation: Optional[str] = None, - email: Optional[str] = None, - identifier_type: Optional[IdentifierTypes] = None, - identifier_value: Optional[str] = None, - id: Optional[str] = None, - ) -> None: - """ - This method adds an object of type 'Person' to attribute authors - - Args: - id (str): Unique identifier of the 'Person' object. Defaults to 'None'. - last_name (): Family name of the person.. - first_name (): Given name of the person.. - middle_names (): List of middle names of the person.. Defaults to ListPlus() - affiliation (): Institution the Person belongs to.. Defaults to None - email (): Email address of the person.. Defaults to None - identifier_type (): Recognized identifier for the person.. Defaults to None - identifier_value (): Value of the identifier for the person.. Defaults to None - """ - params = { - "last_name": last_name, - "first_name": first_name, - "middle_names": middle_names, - "affiliation": affiliation, - "email": email, - "identifier_type": identifier_type, - "identifier_value": identifier_value, - } - if id is not None: - params["id"] = id - self.authors.append(Person(**params)) - return self.authors[-1] diff --git a/nmrpy/datamodel/core/publicationtypes.py b/nmrpy/datamodel/core/publicationtypes.py deleted file mode 100644 index f5974ef..0000000 --- a/nmrpy/datamodel/core/publicationtypes.py +++ /dev/null @@ -1,5 +0,0 @@ -from enum import Enum - - -class PublicationTypes(Enum): - ARTICLE = "Journal article" diff --git a/nmrpy/datamodel/core/reactant.py b/nmrpy/datamodel/core/reactant.py deleted file mode 100644 index faf65c1..0000000 --- a/nmrpy/datamodel/core/reactant.py +++ /dev/null @@ -1,49 +0,0 @@ -import sdRDM - -from typing import Optional -from pydantic import Field -from sdRDM.base.utils import forge_signature, IDGenerator - - -from .sboterm import SBOTerm - - -@forge_signature -class Reactant(sdRDM.DataModel): - """This objects describes the reactants that were used or produced in the course of the experiment.""" - - id: Optional[str] = Field( - description="Unique identifier of the given object.", - default_factory=IDGenerator("reactantINDEX"), - xml="@id", - ) - - smiles: Optional[str] = Field( - default=None, - description=( - "Simplified Molecular Input Line Entry System (SMILES) encoding of the" - " reactant." - ), - template_alias="SMILES", - ) - - inchi: Optional[str] = Field( - default=None, - description=( - "International Chemical Identifier (InChI) encoding of the reactant." - ), - template_alias="InCHI", - ) - - chebi_id: Optional[str] = Field( - default=None, - description=( - "Unique identifier of the CHEBI database. Use this identifier to initialize" - " the object from the CHEBI database." - ), - ) - - ontology: SBOTerm = Field( - description="None", - default=SBOTerm.SMALL_MOLECULE, - ) diff --git a/nmrpy/datamodel/core/sboterm.py b/nmrpy/datamodel/core/sboterm.py deleted file mode 100644 index 74d2eb6..0000000 --- a/nmrpy/datamodel/core/sboterm.py +++ /dev/null @@ -1,35 +0,0 @@ -from enum import Enum - - -class SBOTerm(Enum): - BIOCHEMICAL_REACTION = "SBO:0000176" - ACID_BASE_REACTION = "SBO:0000208" - CONFORMATIONAL_TRANSITION = "SBO:0000181" - CONVERSION = "SBO:0000182" - DEGRADATION = "SBO:0000179" - DISSOCIATION = "SBO:0000180" - IONISATION = "SBO:0000209" - ISOMERISATION = "SBO:0000377" - NON_COVALENT_BINDING = "SBO:0000177" - REDOX_REACTION = "SBO:0000200" - SPONTANEOUS_REACTION = "SBO:0000672" - PROTEIN = "SBO:0000252" - GENE = "SBO:0000251" - SMALL_MOLECULE = "SBO:0000247" - ION = "SBO:0000327" - RADICAL = "SBO:0000328" - INTERACTOR = "SBO:0000336" - SUBSTRATE = "SBO:0000015" - PRODUCT = "SBO:0000011" - CATALYST = "SBO:0000013" - INHIBITOR = "SBO:0000020" - ESSENTIAL_ACTIVATOR = "SBO:0000461" - NON_ESSENTIAL_ACTIVATOR = "SBO:0000462" - POTENTIATOR = "SBO:0000021" - MACROMOLECULAR_COMPLEX = "SBO:0000296" - PROTEIN_COMPLEX = "SBO:0000297" - DIMER = "SBO:0000607" - MICHAELIS_MENTEN = "SBO:0000028" - K_CAT = "SBO:0000025" - K_M = "SBO:0000027" - V_MAX = "SBO:0000186" diff --git a/nmrpy/datamodel/core/subjects.py b/nmrpy/datamodel/core/subjects.py deleted file mode 100644 index d343f01..0000000 --- a/nmrpy/datamodel/core/subjects.py +++ /dev/null @@ -1,8 +0,0 @@ -from enum import Enum - - -class Subjects(Enum): - BIOLOGY = "Biology" - CHEMISTRY = "Chemistry" - IT = "Computer and Information Science" - PHYSICS = "Physics" diff --git a/nmrpy/datamodel/core/term.py b/nmrpy/datamodel/core/term.py index eabbad0..82c67e8 100644 --- a/nmrpy/datamodel/core/term.py +++ b/nmrpy/datamodel/core/term.py @@ -1,9 +1,11 @@ import sdRDM -from typing import Any, Optional -from pydantic import Field, PrivateAttr +from typing import Optional +from pydantic import Field from sdRDM.base.utils import forge_signature, IDGenerator +from typing import Any + @forge_signature class Term(sdRDM.DataModel): @@ -39,7 +41,3 @@ class Term(sdRDM.DataModel): default=None, description="Value of the term, if applicable.", ) - __repo__: Optional[str] = PrivateAttr(default="https://github.com/NMRPy/nmrpy") - __commit__: Optional[str] = PrivateAttr( - default="dec2cda6676f8d04070715fe079ed786515ea918" - ) diff --git a/nmrpy/datamodel/core/vessel.py b/nmrpy/datamodel/core/vessel.py deleted file mode 100644 index 5dc6fb5..0000000 --- a/nmrpy/datamodel/core/vessel.py +++ /dev/null @@ -1,52 +0,0 @@ -import sdRDM - -from typing import Optional -from pydantic import Field -from sdRDM.base.utils import forge_signature, IDGenerator - -from pydantic import StrictBool -from pydantic import PositiveFloat - - -@forge_signature -class Vessel(sdRDM.DataModel): - """This object describes vessels in which the experiment has been carried out. These can include any type of vessel used in biocatalytic experiments.""" - - id: Optional[str] = Field( - description="Unique identifier of the given object.", - default_factory=IDGenerator("vesselINDEX"), - xml="@id", - ) - - name: str = Field( - ..., - description="Name of the used vessel.", - template_alias="Name", - ) - - volume: PositiveFloat = Field( - ..., - description="Volumetric value of the vessel.", - template_alias="Volume value", - ) - - unit: str = Field( - ..., - description="Volumetric unit of the vessel.", - template_alias="Volume unit", - ) - - constant: StrictBool = Field( - description="Whether the volume of the vessel is constant or not.", - default=True, - ) - - uri: Optional[str] = Field( - default=None, - description="URI of the vessel.", - ) - - creator_id: Optional[str] = Field( - default=None, - description="Unique identifier of the author.", - ) diff --git a/nmrpy/datamodel/schemes/datamodel_schema.md b/nmrpy/datamodel/schemes/datamodel_schema.md index 85785fa..0cf4664 100644 --- a/nmrpy/datamodel/schemes/datamodel_schema.md +++ b/nmrpy/datamodel/schemes/datamodel_schema.md @@ -1,25 +1,16 @@ ```mermaid classDiagram NMRpy *-- Experiment - NMRpy *-- Citation Experiment *-- FIDObject Experiment *-- FIDArray FIDObject *-- Parameters FIDObject *-- ProcessingSteps FIDObject *-- Identity - Citation *-- Subjects - Citation *-- Person - Citation *-- Publication - Citation *-- Term - Person *-- IdentifierTypes - Publication *-- PublicationTypes - Publication *-- Person class NMRpy { +datetime datetime_created* +datetime datetime_modified +Experiment experiment - +Citation citation } class Experiment { @@ -77,38 +68,6 @@ classDiagram +string[0..*] fids* } - class Citation { - +string title - +URL doi - +string description - +Person[0..*] authors - +Subjects[0..*] subjects - +Term[0..*] keywords - +Term[0..*] topics - +Publication[0..*] related_publications - +string notes - +string[0..*] funding - +string license - } - - class Person { - +string last_name* - +string first_name* - +string[0..*] middle_names - +string affiliation - +string email - +IdentifierTypes identifier_type - +string identifier_value - } - - class Publication { - +PublicationTypes type* - +string title* - +Person[0..*] authors* - +integer year - +URL doi - } - class CV { +string vocabulary* +string version* @@ -129,22 +88,4 @@ classDiagram +NONE } - class Subjects { - << Enumeration >> - +BIOLOGY - +CHEMISTRY - +IT - +PHYSICS - } - - class PublicationTypes { - << Enumeration >> - +ARTICLE - } - - class IdentifierTypes { - << Enumeration >> - +ORCID - } - ``` \ No newline at end of file diff --git a/nmrpy/plotting.py b/nmrpy/plotting.py index bd470b3..f328381 100644 --- a/nmrpy/plotting.py +++ b/nmrpy/plotting.py @@ -19,11 +19,13 @@ Label, Button, Combobox, + Text, + HTML, ) from IPython.display import display import asyncio -from .utils import get_species_from_enzymeml +from .utils import get_species_from_enzymeml, get_ordered_list_of_species_names class Plot: @@ -185,7 +187,9 @@ def _plot_deconv( ax.text( ppm[numpy.argmax(peak)], label_pad + peak.max(), - str(n), + get_ordered_list_of_species_names(fid)[n] + if fid.fid_object.peak_identities + else str(n), ha="center", ) ax.invert_xaxis() @@ -1458,6 +1462,7 @@ def on_save_button_click(b): } ], ) + self.fid.identities = get_ordered_list_of_species_names(self.fid) self.fid._flags["assigned"] = True reset_button.disabled = False @@ -1480,6 +1485,7 @@ def on_reset_button_click(b): peak_dropdown.options = self.available_peaks peak_dropdown.disabled = False save_button.disabled = False + self.fid.identities = None self.fid._flags["assigned"] = False reset_button.disabled = True @@ -1657,6 +1663,7 @@ def on_save_button_click(b): } ], ) + fid.identities = get_ordered_list_of_species_names(fid) reset_button.disabled = False # Attach the function to the save button's click event @@ -1671,6 +1678,7 @@ def on_reset_button_click(b): print("\nCleared selections!") for fid in self.fids: fid.fid_object.peak_identities = [] + fid.identities = None self.selected_values = {} # Refill the list of available peaks as before, # re-enable the peak dropdown, and disable the reset @@ -2197,5 +2205,113 @@ def assign(self): plt.close(self.span_selector.fig) +class ConcentrationCalculator: + def __init__(self, fid_array, integrals): + self.fid_array = fid_array + self.integrals = integrals + self.fids = fid_array.get_fids() + self.available_species = get_ordered_list_of_species_names( + self.fid_array.get_fid("fid00") + ) + self.equation = "" + + # Create the label widget for the title + title_label = Label( + value="[WORK IN PROGRESS] Calculate concentrations from peak integrals for all FIDs [WORK IN PROGRESS]" + ) + + # Create the dropdown widget for the internal standard + standard_dropdown = Dropdown( + options=self.available_species, + description="Select the internal standard:", + layout={"width": "max-content"}, + style={"description_width": "initial"}, + ) + + # Create a text input widget for the concentration equation + concentration_equation = Text( + value="", + placeholder="Enter the equation for the concentration here", + description="Concentration equation:", + layout={"width": "auto"}, + style={ + "description_width": "initial", + }, + ) + + # Create an HTML widget to display a legend for the concentration equation + legend_html = HTML( + value="'c_s': Concentration of species
'c_n': Concentration of internal standard
'x_s': Peak integral of species
'x_n': Peak integral of internal standard
Example: c_s = c_n * x_s / x_n", + description="Legend:", + ) + + # Create a button to calculate the concentrations + calculate_button = Button( + description="Calculate concentrations", + icon="calculator", + layout={"width": "max-content"}, + disabled=True, + ) + + # Create an output widget to display the calculation progress + output = Output() + + # Define a method to handle the text input widget's change event + def on_text_change(event): + if event["type"] == "change" and event["name"] == "value": + calculate_button.disabled = False + + # Attach the method to the text input widget's change event + concentration_equation.observe(on_text_change) + + # Define a method to handle the calculate button's click event + def on_calculate_button_click(b): + with output: + output.clear_output(wait=True) + # Fetch the values from the standard dropdown and the + # text widget and add them to a dictionary with species as + # keys + print("\nCalculating concentrations...") + if ( + not concentration_equation.value.replace(" ", "") + == "c_s=c_n*x_s/x_n" + ): + raise NotImplementedError( + "Only the example formula is currently supported." + ) + else: + # TODO: Currently hard-coded for the example data + standard_index = self.available_species.index( + standard_dropdown.value + ) + self.fid_array.concentrations = { + species: 5 + * concentration + / self.integrals[standard_index].mean() + for species, concentration in zip( + self.available_species, self.integrals + ) + } + print(f"Done! Get concentrations with `FidArray.concentrations`.") + + # Attach the function to the calculate button's click event + calculate_button.on_click(on_calculate_button_click) + + # Create the container + container = VBox( + [ + title_label, + standard_dropdown, + concentration_equation, + legend_html, + calculate_button, + output, + ] + ) + + # Display the container + display(container) + + if __name__ == "__main__": pass diff --git a/nmrpy/utils.py b/nmrpy/utils.py index 5732fb1..8d206d9 100644 --- a/nmrpy/utils.py +++ b/nmrpy/utils.py @@ -26,3 +26,59 @@ def get_species_from_enzymeml(enzymeml_document: DataModel) -> list: for reactant in enzymeml_document.reactants: available_species.append(reactant) return available_species + + +def get_ordered_list_of_species_names(fid: "Fid") -> list: + """Iterate over the identites in a given FID object and extract a + list of species names ordered by peak index, multiple occurences + thus allowed. + + Args: + fid (Fid): The FID object from which to get the species names. + + Returns: + list: List of species names in desecending order by peak index. + """ + list_of_tuples = [] + # Iterate over the identies and then over their associated peaks of + # a given FID object and append a tuple of the identity's name and + # corresponding peak (one tuple per peak) to a list of tuples. + for identity in fid.fid_object.peak_identities: + for peak in identity.associated_peaks: + list_of_tuples.append((identity.name, peak)) + # Use the `sorted` function with a custom key to sort the list of + # tuples by the second element of each tuple (the peak) from highest + # value to lowest (reverse=True). + list_of_tuples = sorted(list_of_tuples, key=lambda x: x[1], reverse=True) + # Create and return an ordered list of only the species names from + # the sorted list of tuples. + ordered_list_of_species_names = [t[0] for t in list_of_tuples] + return ordered_list_of_species_names + + +def create_enzymeml(fid_array: "FidArray", enzymeml_document: DataModel) -> DataModel: + # Specify EnzymeML version + URL = "https://github.com/EnzymeML/enzymeml-specifications.git" + COMMIT = "5e5f05b9dc76134305b8f9cef65271e35563ac76" + + EnzymeML = DataModel.from_git(URL, COMMIT) + SBOTerm = EnzymeML.enums.SBOTerm + DataTypes = EnzymeML.enums.DataTypes + + measurement = EnzymeML.Measurement( + name=fid_array.data_model.experiment.name, + temperature=enzymeml_document.reactions[0].temperature, + temperature_unit=enzymeml_document.reactions[0].temperature_unit, + ph=enzymeml_document.reactions[0].ph, + global_time=fid_array.t.tolist(), + global_time_unit="min", + ) + + enzymeml_document.measurements.append(measurement) + + return enzymeml_document + + # for species, concentrations in fid_array.concentrations.items(): + # new_species = EnzymeML.MeasurementData( + # init_conc=enzymeml_document.reactants + # ) diff --git a/specifications/nmrpy.md b/specifications/nmrpy.md index 0adcf22..d433e21 100644 --- a/specifications/nmrpy.md +++ b/specifications/nmrpy.md @@ -19,9 +19,6 @@ Root element of the NMRpy data model. - experiment - Type: [Experiment](#experiment) - Description: List of experiments associated with this dataset. -- citation - - Type: [Citation](#citation) - - Description: Relevant information regarding the publication and citation of this dataset. ### Experiment @@ -189,102 +186,6 @@ Container for processing of multiple spectra. Must reference the respective `FID - Multiple: True -### Citation - -Container for various types of metadata primarily used in the publication and citation of the dataset. - -- title - - Type: string - - Description: Title the dataset should have when published. -- doi - - Type: URL - - Description: DOI pointing to the published dataset -- description - - Type: string - - Description: Description the dataset should have when published. -- authors - - Type: [Person](#person) - - Description: List of authors for this dataset. - - Multiple: True -- subjects - - Type: [Subjects](#subjects) - - Description: List of subjects this dataset belongs to. - - Multiple: True -- keywords - - Type: [Term](#term) - - Description: List of CV-based keywords describing the dataset. - - Multiple: True -- topics - - Type: [Term](#term) - - Description: List of CV-based topics the dataset addresses. - - Multiple: True -- related_publications - - Type: [Publication](#publication) - - Description: List of publications relating to this dataset. - - Multiple: True -- notes - - Type: string - - Description: Additional notes about the dataset. -- funding - - Type: string - - Description: Funding information for this dataset. - - Multiple: True -- license - - Type: string - - Description: License information for this dataset. Defaults to `CC BY 4.0`. - - Default: CC BY 4.0 - - -### Person - -Container for information regarding a person that worked on an experiment. - -- __last_name__ - - Type: string - - Description: Family name of the person. -- __first_name__ - - Type: string - - Description: Given name of the person. -- middle_names - - Type: string - - Description: List of middle names of the person. - - Multiple: True -- affiliation - - Type: string - - Description: Institution the Person belongs to. -- email - - Type: string - - Description: Email address of the person. -- identifier_type - - Type: [IdentifierTypes](#identifiertypes) - - Description: Recognized identifier for the person. -- identifier_value - - Type: string - - Description: Value of the identifier for the person. - - -### Publication - -Container for citation information of a relevant publication. - -- __type__ - - Type: [PublicationTypes](#publicationtypes) - - Description: Nature of the publication. -- __title__ - - Type: string - - Description: Title of the publication. -- __authors__ - - Type: [Person](#person) - - Description: Authors of the publication. - - Multiple: True -- year - - Type: integer - - Description: Year of publication. -- doi - - Type: URL - - Description: The DOI pointing to the publication. - - ## Utility objects @@ -334,33 +235,3 @@ VARIAN = "varian" BRUKER = "bruker" NONE = None ``` - - -### Subjects - -Enumeration containing common subjects (research fields) that implement NMR. - -```python -BIOLOGY = "Biology" -CHEMISTRY = "Chemistry" -IT = "Computer and Information Science" -PHYSICS = "Physics" -``` - - -### PublicationTypes - -Enumeration containing accepted types of publication. - -```python -ARTICLE = "Journal article" -``` - - -### IdentifierTypes - -Enumeration containing recognized identifiers for persons. - -```python -ORCID = "ORCID" -```