Parsing Radiology Exam Data with Python Class

I’ve had a really clunky program to track what I do.  It lets me know how much volume I’m reading calculated each 30 min based a logfile.

I’ve decided to get a little more sophisticated with the program because it has grown to the point where it’s painful for me to edit, and if I wrote it, it must be completely abstruse to someone else.

I have never used a class in python but it solves many issues for me:

  • Cleans up the code
  • Moves functions to a module for importing and repurposing
  • Teaches me how to construct a class properly
  • Allows parsing of text with a structure that makes sense
    • Class instance attributes now make inherent sense
  • Eliminates having to bring global var into functions for modification
    • The class instance can access global counters
    • The class output can eliminate the need for global counters
  • Allows me to expand to the class to perform other manipulation
    • calculating age of patient
    • SQL archival
  • Works in python 2 and 3
  • Uses the Regex from hell
import re

class Parser:
    A class to parse data strings into components
    from parser_fx import *
    result = Parser()     ## Instantiate class
    result.parse(study)   ## Pass the instance a study string
    result.XXXXXXX        ## Attributes of result now available

    def __init__(self):
        """ Simply establishes the regex expression"""
        self.regex = re.compile(r'([\'A-Z\s-]+)(\[.+||\s])\s*'
                                '(CURRENT STUDY:)([A-Z\s/\[\]]+)'

    def parse(self, study_string=('SIMPSON HOMER J[Prelim  report]CURRENT STUDY:'
        'CT HEAD 2016-01-01 23:59:59 [DOB: 1111/1/11 ] [ID: 1234567]')):
        """Parser with failure options"""
        self.study_string = study_string
            self.matchobj = self.regex.match(self.study_string)
            ## Attributes: name, prelim/final, study, date, time dob, ID 
            self.type =[1:7].rstrip()
            self.study_name =
            self.time =
            self.DOB =
            self.ID =[1:-1]
            print("REGEX Match Failed")

    """  Leaving here as an example to follow 
    def ID(self):
        ''' returns study  '''
        self.ID =
        return self.ID   

study= ('SIMPSON HOMER J[Prelim  report]CURRENT STUDY:'
        'CT HEAD 2016-01-01 23:59:59 [DOB: 1111/1/11 ] [ID: 1234567]')
result = Parser()
print("%s | %s | %s | %s | %s | %s" %(result.type,,
        result.study_name,, result.time, result.ID))


After testing for a week, I’ll report back on success or failure.


Python 3.6 Install on DietPi

Debian repos don’t have python 3.6 ready to go at this time but thanks to the awesome dudes at HA forums, the guide to replacing your venv with 3.6 works to install also.

Step 1.

sudo apt-get install build-essential tk-dev libncurses5-dev libncursesw5-dev libreadline6-dev libdb5.3-dev libgdbm-dev libsqlite3-dev libssl-dev libbz2-dev libexpat1-dev liblzma-dev zlib1g-dev


wget tar
xzvf Python-3.6.0.tgz cd Python-3.6.0/
sudo make install

DietPi HomeAssistant Aliases


(I’m just too lazy to type sudo every time).

This is an update to the list of aliases included in the main post. I have set up a dev server on RPi and my instructions did were not previously complete.

alias config='nano ~/.homeassistant/configuration.yaml'
alias checkconfig='venv && hass --script check_config && cd ~'
alias venv='cd ~/homeassistant && source bin/activate'
alias hasslog='nano ~/.homeassistant/home-assistant.log'
alias customize='nano ~/.homeassistant/customize.yaml'
alias restart='sudo systemctl restart home-assistant@root'
alias status='sudo systemctl status home-assistant@root'
alias stop='sudo systemctl stop home-assistant@root'
alias start='sudo systemctl start home-assistant@root'
alias secrets='nano ~/.homeassistant/secrets.yaml'
alias ms='mosquitto_sub -h "" -t "#" -v'

The most used are:

  • restart
  • config
  • checkconfig
  • status

Using Python GSpread to Fill Spreadsheet Values

import gspread
from oauth2client.service_account import ServiceAccountCredentials


#Gspread authorization
scope = ['']
credentials = ServiceAccountCredentials.from_json_keyfile_name('creds.json', scope)

gc = gspread.authorize(credentials)


## Critical to share spreadsheet with service account
## If you don't you'll just end up with a not found error.


# Open a worksheet from spreadsheet with one shot
wks = gc.open_by_key("<<>>").sheet1

# Generate list with alphabet
alphabet = map(chr, range(65, 91)) ## Gives capital letters A-Z
print(alphabet)  ## Just printing to make sure it's okay 
for letter in alphabet:
    for x in range(1,10):
        wks.update_acell((letter + str(x)), ("Cell ") + letter +str(x))


Excellent help from here:

The Uncanny Valley of Home Automation

I just added some more Sonoff Basics to my home system with the Oh-so-awesome Sonoff Tasmota Firmware.  For about 7 bucks, you get a MQTT Home Assistant Connected Wi-Fi Smart Switch.

I have a two modules that are not ready for immediate use.  As I consider the items I want automate, I realize that automation won’t work on several appliances because the appliances are already “too smart”.

My humidifier is too smart for its own good.  It has a sensor right by the output which of course gives it a false reading from the local environment.  This leads to it cycling on and off at about 1 cycle per minute (CPM).  To solve this I thought, “I’ll just put it on a Sonoff to run full at night and cycle at intervals during the day based on the humidity sensors I have.

Doesn’t work.  The controls automatically reset to off when current is removed.

I found the same issues with the fan I use to cool off in my gym, Black and Decker coffee maker logitech Z906 speaker system.

It led me to think that it is much like the phenomenon of the Uncanny Valley.


Similarly, Home automation follows this curve:

desmos-graph (1)

Where the most analog (“dumbest”) of appliances become the most useful.  Smart appliances are already just that, so there isn’t really much to do to enhance them.


Although the graph crossing zero is an artifact, it’s useful to remember that some appliances when automated can inflict harm.






remove retained MQTT message

You can remove retained messages by publishing a zero length retained message to the topic you wish to clear. For example, you could do it with mosquitto_pub as follows:

mosquitto_pub -t -r -n


From the Docs:

-r, --retain

If retain is given, the message will be retained as a “last known good” value on the broker. See mqtt(7) for more information.

-n, --null-message

Send a null (zero length) message.