Skip to content

Commit

Permalink
LtBio V.2
Browse files Browse the repository at this point in the history
  • Loading branch information
jomy-kk committed Mar 11, 2024
2 parents b6082dc + 9d97dc6 commit 0d7aba8
Show file tree
Hide file tree
Showing 42 changed files with 2,855 additions and 195 deletions.
27 changes: 26 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@ bokeh==2.4.3
boltons==21.0.0
certifi==2021.10.8
cffi==1.15.0
cftime==1.6.3
chardet==4.0.0
charset-normalizer==2.0.12
click==8.1.3
click-spinner==0.1.10
colorlog==6.7.0
colorspacious==1.1.2
commonmark==0.9.1
cramjam==2.5.0
cycler==0.11.0
Cython==0.29.32
dacite==1.6.0
darkdetect==0.8.0
datacommons==1.4.3
datacommons-pandas==0.0.3
datapane==0.15.3
DateTime==5.1
DateTimeRange==1.2.0
debugpy==1.6.0
decorator==5.1.1
Expand Down Expand Up @@ -83,6 +87,8 @@ jupyterlab-widgets==1.1.1
keyring==23.7.0
kiwisolver==1.4.2
lazy-object-proxy==1.7.1
lazy_loader==0.3
littleutils==0.2.2
lxml==4.9.1
markdown-it-py==2.1.0
MarkupSafe==2.1.1
Expand All @@ -94,28 +100,36 @@ mdit-py-plugins==0.3.3
mdurl==0.1.2
micawber==0.5.4
mistune==0.8.4
mne==1.0.2
mne
mne-connectivity
mne-qt-browser==0.6.1
monotonic==1.6
multimethod==1.9.1
multipledispatch==0.6.0
munch==2.5.0
myst-parser==0.18.1
nbclient==0.6.4
nbconvert==6.5.0
nbformat==5.4.0
neo==0.10.2
nest-asyncio==1.5.5
netCDF4==1.6.5
notebook==6.4.12
numexpr==2.8.3
numpy==1.22.3
opencv-python==4.5.5.64
orderedmultidict==1.0.1
outdated==0.2.2
packaging==21.3
pandas==1.4.2
pandas-flavor==0.6.0
pandocfilters==1.5.0
parso==0.8.3
patsy==0.5.2
pexpect==4.8.0
pickleshare==0.7.5
Pillow==9.1.0
pingouin==0.5.3
pkginfo==1.8.3
platformdirs==2.5.2
plotly==5.10.0
Expand All @@ -139,14 +153,21 @@ pyEDFlib==0.1.30
pyflakes==2.5.0
Pygments==2.12.0
pylint==2.13.9
pymatreader==0.0.32
Pympler==1.0.1
PyOpenGL==3.1.7
pyparsing==3.0.8
PyQt5
PyQt5-Qt5
PyQt5-sip
pyqtgraph
pyrsistent==0.18.1
pytest==7.1.2
python-dateutil==2.8.2
pytz==2022.1
PyYAML==6.0
pyzmq==23.2.0
QDarkStyle==3.2.3
qtconsole==5.3.1
QtPy==2.1.0
quantities==0.13.0
Expand All @@ -157,6 +178,7 @@ rfc3986==2.0.0
rich==12.5.1
scikit-learn==1.1.0
scipy==1.8.0
scooby==0.9.2
seaborn==0.11.2
Send2Trash==1.8.0
shortuuid==1.0.9
Expand Down Expand Up @@ -208,4 +230,7 @@ wfdb==3.4.1
wget==3.2
widgetsnbextension==3.6.1
wrapt==1.14.1
xarray==2023.12.0
xmltodict==0.13.0
zipp==3.8.1
zope.interface==6.0
11 changes: 11 additions & 0 deletions resources/Sense_CSV_tests/sense_defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@
"4": "BodyLocation.INDEX_L"
},
"location": "BodyLocation.ARM_L"
},

"rafa":
{
"modalities": {
"ECG": [1]
},
"labels": {
"1": "ecg"
},
"location": "BodyLocation.V1"
}
}

2 changes: 1 addition & 1 deletion resources/config.ini
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[DEFAULT]
Sense = /Users/saraiva/Desktop/LongTermBiosignals/resources/Sense_CSV_tests/sense_defaults.json
Sense = /Users/saraiva/PycharmProjects/IT-LongTermBiosignals/resources/Sense_CSV_tests/sense_defaults.json
12 changes: 12 additions & 0 deletions src/ltbio/_core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# -- encoding: utf-8 --
# ===================================
# ScientISST LTBio | Long-Term Biosignals

# Package:
# Module:
# Description:

# Contributors: João Saraiva
# Created:
# Last Updated:
# ===================================
118 changes: 118 additions & 0 deletions src/ltbio/_core/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# -- encoding: utf-8 --
# ===================================
# ScientISST LTBio | Long-Term Biosignals
from datetimerange import DateTimeRange

from ltbio.biosignals.timeseries import Timeline


#from ltbio.biosignals._Timeline import Timeline
#from ltbio.biosignals._Timeseries import Timeseries
#from ltbio.biosignals.units import Unit, Frequency
#from ltbio.clinical import BodyLocation


# Package:
# Module:
# Description:

# Contributors: João Saraiva
# Created:
# Last Updated:
# ===================================


class IncompatibleTimeseriesError(Exception):
def __init__(self, why: str):
super().__init__(f"These Timeseries are incompatible because {why}")


class DifferentSamplingFrequenciesError(IncompatibleTimeseriesError):
def __init__(self, *frequencies):
super().__init__(f"these different sampling frequencies were found: {','.join(frequencies)}. "
f"Try to resample first.")


class DifferentUnitsError(IncompatibleTimeseriesError):
def __init__(self, *units):
super().__init__(f"these different units were found: {','.join(units)}. "
f"Try to convert first.")


class DifferentDomainsError(IncompatibleTimeseriesError):
def __init__(self, *timelines):
note = "they have different domains: "
note += '; '.join([f"({i+1}): {domain}" for i, domain in enumerate(timelines)])
super().__init__(note)


class IncompatibleBiosignalsError(Exception):
def __init__(self, why: str):
super().__init__(f"These Biosignals are incompatible because {why}")


class DifferentPatientsError(IncompatibleTimeseriesError):
def __init__(self, first, second):
super().__init__(f"at least two different patients were found: {first} and {second}. "
f"Try to drop the patients first.")

class IncompatibleSegmentsError(Exception):
def __init__(self, why: str):
super().__init__(f"These Segments are incompatible because {why}")

class DifferentLengthsError(Exception):
def __init__(self, first: int, second: int):
super().__init__(f"the first has length {first} and the second has length {second}.")


class TimeError(Exception):
...


class ChannelsWithDifferentStartTimepointsError(TimeError):
def __init__(self, first_name, first_start, second_name, second_start, additional: str = ''):
super().__init__(f"{first_name} starts at {first_start} and {second_name} starts at {second_start}. " + additional)

class ChannelsWithDifferentDomainsError(TimeError):
def __init__(self, domains_by_channel_name: dict[str, Timeline], additional: str = ''):
super().__init__(f"The channels of this Biosignal do not have the same domain. "
+ additional + "\n"
+ "\n".join([f"{channel_name}: {domain}" for channel_name, domain in domains_by_channel_name.items()]))


class OverlappingError(TimeError):
def __init__(self, what: str):
super().__init__(f"There is an overlap between {what}")


class TimeseriesOverlappingError(OverlappingError):
def __init__(self, first, second, *overlap: DateTimeRange):
super().__init__(f"Timeseries {first} and Timeseries {second}" + f" on {overlap}." if overlap else ".")


class OperationError(Exception):
...


class UndoableOperationError(OperationError):
def __init__(self, operation, by_nature: bool):
note = f"Operation {operation} is undoable"
if by_nature:
note += " by nature, i.e. there is no mathematical way of reversing it or, at least, it's not implemented."
else:
note += ", most likely because this operation was what created this object."
super().__init__(note)


class BiosignalError(Exception):
...


class ChannelNotFoundError(BiosignalError, IndexError, AttributeError):
def __init__(self, name):
super().__init__(f"There is no channel named '{name}'.")


class EventNotFoundError(BiosignalError, IndexError, AttributeError):
def __init__(self, name: str):
super().__init__(f"There is no event named '{name}'.")
108 changes: 108 additions & 0 deletions src/ltbio/_core/operations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# -- encoding: utf-8 --
# ===================================
# ScientISST LTBio | Long-Term Biosignals
from abc import ABC, abstractmethod
from datetime import datetime
from typing import Any, ClassVar

from .exceptions import UndoableOperationError


# Package:
# Module:
# Description:

# Contributors: João Saraiva
# Created:
# Last Updated:
# ===================================


class Operator(ABC):
NAME: str
DESCRIPTION: str
SHORT: str

def __init__(self, **parameters):
...

def __call__(self, *args, **kwargs) -> Any:
result, _ = self._apply(*args, **kwargs)
return

@abstractmethod
def _apply(self, *args, **kwargs) -> (Any, 'Operation'):
pass

@abstractmethod
def _undo(self, *args, **kwargs):
raise NotImplementedError()

@property
def is_reversible(self) -> bool:
# Check if "_undo" method is implemented, not if it is callable
try:
x = self._undo
return True
except NotImplementedError:
return False


class Operation:
def __init__(self, operator: Operator, when: datetime, allow_undo: bool, *objects, **kwargs):
self.__operator = operator
self.__when = when
self.__objects = objects
self.__kwargs = kwargs
self.__allow_undo = allow_undo

# Getters
@property
def operator(self) -> Operator:
return self.__operator

@property
def when(self) -> datetime:
return self.__when

@property
def objects(self):
return self.__objects

@property
def is_undoable(self) -> bool:
return self.__allow_undo and hasattr(self.__operator, "undo")

def undo(self):
if not self.__allow_undo:
raise UndoableOperationError(self, by_nature=False)
if not hasattr(self, "undo"):
raise UndoableOperationError(self, by_nature=True)
return self.__operator.undo(*self.__objects, **self.__kwargs)

def __str__(self):
return str(self.__operator)

def __repr__(self):
return repr(self.__operator) + " performed in " + str(self.__when)


class ArithmeticOperator(Operator, ABC): ...


class BinaryOperator(Operator, ABC): ...


class UnaryOperator(Operator, ABC): ...


class Addition(ArithmeticOperator, BinaryOperator):
NAME = "Add"
DESCRIPTION = "Adds two Biosignals, Timeseries or Segments, sample by sample."
SHORT = "+"

def _apply(self, first, second):
return first + second

def _undo(self, first, second):
return first - second
Loading

0 comments on commit 0d7aba8

Please sign in to comment.