summaryrefslogblamecommitdiffstats
path: root/glucometerutils/drivers/accuchek_reports.py
blob: c4d7527c23fad885d1536c4eaf6f28c033b6b2e2 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
                       

                              

                                                         





                                                                        




                                                            




               
                                              
                                               

             

                                 

 








                                                       
                                                         
                                             
 

                         
 
                                                         
 

                                           


                                                   


                                                                             
 
                                                                    
                                              

                                              

                                                                   









                                          

                                                                                     

                      
                                                                                   





                                
                                               
                                                   

                                                


                                     
                                                                                   





                                                                                
                                              






                                                        
                                 
 
                                         
                                 

                       
                                 
 
                                                                       

                                                                         
                                                                                

                                                                          
                                                                   
                                                                        
                                                                                        












                                                 



                                                     



                                                
# -*- coding: utf-8 -*-
#
# SPDX-License-Identifier: MIT
"""Driver for Accu-Chek Mobile devices with reports mode.

Supported features:
    - get readings, including comments;
    - use the glucose unit preset on the device by default;
    - get serial number.

Expected device path: /mnt/ACCUCHEK, the mountpoint of the block device.

The Accu-Chek Mobile meters should be set to "Reports" mode.

"""

import csv
import datetime
import glob
import os

from glucometerutils import common, exceptions
from glucometerutils.support import driver_base

_UNIT_MAP = {
    "mmol/l": common.Unit.MMOL_L,
    "mg/dl": common.Unit.MG_DL,
}

_DATE_CSV_KEY = "Date"
_TIME_CSV_KEY = "Time"
_RESULT_CSV_KEY = "Result"
_UNIT_CSV_KEY = "Unit"
_TEMPWARNING_CSV_KEY = "Temperature warning"  # ignored
_OUTRANGE_CSV_KEY = "Out of target range"  # ignored
_OTHER_CSV_KEY = "Other"  # ignored
_BEFORE_MEAL_CSV_KEY = "Before meal"
_AFTER_MEAL_CSV_KEY = "After meal"
# Control test has extra whitespace which is not ignored.
_CONTROL_CSV_KEY = "Control test" + " " * 197

_DATE_FORMAT = "%d.%m.%Y"
_TIME_FORMAT = "%H:%M"

_DATETIME_FORMAT = " ".join((_DATE_FORMAT, _TIME_FORMAT))


class Device(driver_base.GlucometerDriver):
    def __init__(self, device):
        if not device or not os.path.isdir(device):
            raise exceptions.CommandLineError(
                "--device parameter is required, should point to mount path "
                "for the meter."
            )

        reports_path = os.path.join(device, "*", "Reports", "*.csv")
        report_files = glob.glob(reports_path)
        if not report_files:
            raise exceptions.ConnectionFailed(
                'No report file found in path "%s".' % reports_path
            )

        self.report_file = report_files[0]

    def _get_records_reader(self):
        self.report.seek(0)
        # Skip the first two lines
        next(self.report)
        next(self.report)

        return csv.DictReader(
            self.report, delimiter=";", skipinitialspace=True, quoting=csv.QUOTE_NONE
        )

    def connect(self):
        self.report = open(self.report_file, "r", newline="\r\n", encoding="utf-8")

    def disconnect(self):
        self.report.close()

    def get_meter_info(self):
        return common.MeterInfo(
            "%s glucometer" % self.get_model(),
            serial_number=self.get_serial_number(),
            native_unit=self.get_glucose_unit(),
        )

    def get_model(self):
        # $device/MODEL/Reports/*.csv
        return os.path.basename(os.path.dirname(os.path.dirname(self.report_file)))

    def get_serial_number(self):
        self.report.seek(0)
        # ignore the first line.
        next(self.report)
        # The second line of the CSV is serial-no;report-date;report-time;;;;;;;
        return next(self.report).split(";")[0]

    def get_glucose_unit(self):
        # Get the first record available and parse that.
        record = next(self._get_records_reader())
        return _UNIT_MAP[record[_UNIT_CSV_KEY]]

    def get_datetime(self):
        raise NotImplementedError

    def _set_device_datetime(self, date):
        raise NotImplementedError

    def zero_log(self):
        raise NotImplementedError

    def _extract_datetime(self, record):  # pylint: disable=no-self-use
        # Date and time are in separate column, but we want to parse them
        # together.
        date_and_time = " ".join((record[_DATE_CSV_KEY], record[_TIME_CSV_KEY]))
        return datetime.datetime.strptime(date_and_time, _DATETIME_FORMAT)

    def _extract_meal(self, record):  # pylint: disable=no-self-use
        if record[_AFTER_MEAL_CSV_KEY] and record[_BEFORE_MEAL_CSV_KEY]:
            raise exceptions.InvalidResponse("Reading cannot be before and after meal.")
        elif record[_AFTER_MEAL_CSV_KEY]:
            return common.Meal.AFTER
        elif record[_BEFORE_MEAL_CSV_KEY]:
            return common.Meal.BEFORE
        else:
            return common.Meal.NONE

    def get_readings(self):
        for record in self._get_records_reader():
            if record[_RESULT_CSV_KEY] is None:
                continue

            yield common.GlucoseReading(
                self._extract_datetime(record),
                common.convert_glucose_unit(
                    float(record[_RESULT_CSV_KEY]),
                    _UNIT_MAP[record[_UNIT_CSV_KEY]],
                    common.Unit.MG_DL,
                ),
                meal=self._extract_meal(record),
            )