summaryrefslogblamecommitdiffstats
path: root/glucometerutils/common.py
blob: 24bc0d190d18c3e7f707bf3cfd36f9d777a39c83 (plain) (tree)
1
2
3
4
5
6
7
8
9
                       
 
                                                             
                              

                                              
               
           
               

                                       
 

           
 
                      


                     
 
                                
                      



                          
 
                              
                                   


                                                
 
 
                                                                                
                                                              
 



                                                                   
 




                                                              
 

                            
 

                                     
 
                                 
 
 
                          
                     




                                                                                
                                               
                                                         
     
                                                   
 
                                                   


                                                       
                                                   


                                                                    
                                        

                                                                              






                                      
 
                          
                    


                                



                                                                        
                                                   
 
                                        

                                                                              
 
                                             

                           
                                      


                         
 
                          
                     


                                                
                                                                                        
     
                                                   
 
                                        
                
                                         


                                      


         


                                                                 
                          
                









                                                                                
 




                                                                                        
 
                             
                                          
                             


                                                                   
 
                                      


                                               

                                            
                                                 
           
         
 
                                         
                                                                 

                          
# -*- coding: utf-8 -*-
#
# SPDX-FileCopyrightText: © 2013 The glucometerutils Authors
# SPDX-License-Identifier: MIT
"""Common routines for data in glucometers."""

import datetime
import enum
import textwrap
from collections.abc import Sequence
from typing import Any, Optional, Union

import attr


class Unit(enum.Enum):
    MG_DL = "mg/dL"
    MMOL_L = "mmol/L"


# Constants for meal information
class Meal(enum.Enum):
    NONE = ""
    BEFORE = "Before Meal"
    AFTER = "After Meal"


# Constants for measure method
class MeasurementMethod(enum.Enum):
    BLOOD_SAMPLE = "blood sample"
    CGM = "CGM"  # Continuous Glucose Monitoring
    TIME = "time"


def convert_glucose_unit(value: float, from_unit: Unit, to_unit: Unit) -> float:
    """Convert the given value of glucose level between units.

    Args:
      value: The value of glucose in the current unit
      from_unit: The unit value is currently expressed in
      to_unit: The unit to conver the value to: the other if empty.

    Returns:
      The converted representation of the blood glucose level.
    """
    from_unit = Unit(from_unit)
    to_unit = Unit(to_unit)

    if from_unit == to_unit:
        return value

    if from_unit == Unit.MG_DL:
        return round(value / 18.0, 2)

    return round(value * 18.0, 1)


@attr.s(auto_attribs=True)
class GlucoseReading:
    timestamp: datetime.datetime
    value: float
    meal: Meal = attr.ib(default=Meal.NONE, validator=attr.validators.in_(Meal))
    comment: str = ""
    measure_method: MeasurementMethod = attr.ib(
        default=MeasurementMethod.BLOOD_SAMPLE,
        validator=attr.validators.in_(MeasurementMethod),
    )
    extra_data: dict[str, Any] = attr.Factory(dict)

    def get_value_as(self, to_unit: Unit) -> float:
        """Returns the reading value as the given unit.

        Args:
          to_unit: The unit to return the value to.
        """
        return convert_glucose_unit(self.value, Unit.MG_DL, to_unit)

    def as_csv(self, unit: Unit) -> str:
        """Returns the reading as a formatted comma-separated value string."""
        return '"%s","%.2f","%s","%s","%s"' % (
            self.timestamp,
            self.get_value_as(unit),
            self.meal.value,
            self.measure_method.value,
            self.comment,
        )


@attr.s(auto_attribs=True)
class KetoneReading:
    timestamp: datetime.datetime
    value: float
    comment: str = ""
    measure_method: MeasurementMethod = attr.ib(
        default=MeasurementMethod.BLOOD_SAMPLE,
        validator=attr.validators.in_({MeasurementMethod.BLOOD_SAMPLE}),
    )
    extra_data: dict[str, Any] = attr.Factory(dict)

    def as_csv(self, unit: Unit) -> str:
        """Returns the reading as a formatted comma-separated value string."""
        del unit  # Unused for Ketone readings.

        return '"%s","%.2f","","%s","%s"' % (
            self.timestamp,
            self.value,
            self.measure_method.value,
            self.comment,
        )


@attr.s(auto_attribs=True)
class TimeAdjustment:
    timestamp: datetime.datetime
    old_timestamp: datetime.datetime
    measure_method: MeasurementMethod = attr.ib(
        default=MeasurementMethod.TIME, validator=attr.validators.in_(MeasurementMethod)
    )
    extra_data: dict[str, Any] = attr.Factory(dict)

    def as_csv(self, unit: Unit) -> str:
        del unit
        return '"%s","","","%s","%s"' % (
            self.timestamp,
            self.measure_method.value,
            self.old_timestamp,
        )


AnyReading = Union[GlucoseReading, KetoneReading, TimeAdjustment]


@attr.s(auto_attribs=True)
class MeterInfo:
    """General information about the meter.

    Attributes:
      model: Human readable model name, chosen by the driver.
      serial_number: Serial number identified for the reader (or N/A if not
        available in the protocol.)
      version_info: List of strings with any version information available about
        the device. It can include hardware and software version.
      native_unit: One of the Unit values to identify the meter native unit.
    """

    model: str
    serial_number: str = "N/A"
    version_info: Sequence[str] = ()
    native_unit: Unit = attr.ib(default=Unit.MG_DL, validator=attr.validators.in_(Unit))
    patient_name: Optional[str] = None

    def __str__(self) -> str:
        version_information_string = "N/A"
        if self.version_info:
            version_information_string = "\n                ".join(
                self.version_info
            ).strip()

        base_output = textwrap.dedent(
            f"""\
            {self.model}
            Serial Number: {self.serial_number}
            Version Information:
                {version_information_string}
            Native Unit: {self.native_unit.value}
        """
        )

        if self.patient_name is not None:
            base_output += f"Patient Name: {self.patient_name}\n"

        return base_output