[Tutor] Feedback on coding style
Dennis Lee Bieber
wlfraed at ix.netcom.com
Mon May 9 14:52:06 EDT 2022
o/~ Talking to myself in public o/~
On Mon, 09 May 2022 11:24:24 -0400, Dennis Lee Bieber
<wlfraed at ix.netcom.com> declaimed the following:
For some reason I felt challenged, and spent three hours to produce
this variation... WARNING: news client may be adding undesirable line
wrapping -- and should be viewed with fixed width font!
NOTE: I should have created a helper function to do the formatting rather
than repeating all those print blocks.
I'm actually surprised the DML_MAX_* statements worked first time.
Whereas I must have spent over half an hour tweaking the report format.
-=-=-
#!/usr/bin/env python3
"""
name: bp_tracker.py
version: 0.0.1-a
date: 20220509
author: Leam Hall
desc: Track and report on blood pressure numbers
revisions:
20220509 Dennis Lee Bieber
rewritten as an exercise to use SQLite3 for the
data store
TODO:
ADD TRY/EXCEPT BLOCKS TO HANDLE ERRORS IN DATABASE OPERATIONS
TODO:
add SQLite3 aggregate function for std. deviations
TODO:
Add report options for time-of-day
(early, mid-morning, afternoon, evening)
TODO:
Add current distance from goal (requires input of target)
"""
import argparse
import datetime
import os.path as op
import sqlite3 as db
#TIMESTAMP is the name used by the Python API for
#conversion as SQLite3 does not have a native
#date/datetime data type
#I always create an integer primary key, even if not
#needed by the application
DDL = """CREATE TABLE IF NOT EXISTS reading
(
ID INTEGER PRIMARY KEY,
reading_date TIMESTAMP NOT NULL,
systolic INTEGER NOT NULL,
diastolic INTEGER NOT NULL,
pulse INTEGER NOT NULL
)
"""
DML_ADD = """INSERT INTO reading
(reading_date, systolic, diastolic, pulse)
VALUES (?, ?, ?, ?)
"""
DML_MAX_SYSTOLIC = """ SELECT
r.ID, r.reading_date, r.systolic, r.diastolic, r.pulse
FROM reading AS r
INNER JOIN (SELECT max(m.systolic) AS mx
FROM reading AS m)
ON r.systolic = mx
ORDER BY r.reading_date
"""
DML_MAX_DIASTOLIC = """ SELECT
r.ID, r.reading_date, r.systolic, r.diastolic, r.pulse
FROM reading AS r
INNER JOIN (SELECT max(m.diastolic) AS mx
FROM reading AS m)
ON r.diastolic = mx
ORDER BY r.reading_date
"""
DML_MAX_PULSE = """ SELECT
r.ID, r.reading_date, r.systolic, r.diastolic, r.pulse
FROM reading AS r
INNER JOIN (SELECT max(m.pulse) AS mx
FROM reading AS m)
ON r.pulse = mx
ORDER BY r.reading_date
"""
DML_MAX_DATE = """ SELECT
r.ID, r.reading_date, r.systolic, r.diastolic, r.pulse
FROM reading AS r
INNER JOIN (SELECT max(m.reading_date) AS mx
FROM reading AS m)
ON r.reading_date = mx
"""
DML_SUMMARY = """ SELECT
count(*) AS Count,
max(systolic) AS Max_Systolic,
min(systolic) AS Min_Systolic,
avg(systolic) AS Avg_Systolic,
max(diastolic) AS Max_Diastolic,
min(diastolic) AS min_Diastolic,
avg(diastolic) AS Avg_Diastolic,
max(pulse) AS Max_Pulse,
min(pulse) AS Min_Pulse,
avg(pulse) AS Avg_Pulse
FROM reading
"""
DEFAULT_DATABASE = "bp_numbers.sqlite"
def open_database(db_name):
"""
Opens the specified database file.write
Creates table schema (use of IF NOT EXISTS
should mean this is safe for pre-existing
database, yet will initialize a new database.
CAVEAT: it also means if some random database
file is provided a new table will be created
within that database!)
"""
#specify that datatypes should be parsed -- used to
#associate datetime converters
con = db.connect(db_name, detect_types=db.PARSE_DECLTYPES)
cur = con.cursor()
cur.execute(DDL)
con.commit()
cur.close()
return con
def add_reading(connection, datestamp, systolic, diastolic, pulse):
"""
Using the provided database connection, adds a new
record with provided datestamp, systolic, diastolic, pulse
"""
cur = connection.cursor()
cur.execute(DML_ADD, (datestamp, systolic, diastolic, pulse))
connection.commit()
cur.close()
def generate_report(connection):
"""
Produce a summary report showing the record(s) with maximum
systolic, diastolic, pulse, datestamp.
Also list overall maximum and minimum, means
(SQLite3 does not have a built-in standard deviation function;
one can be defined in Python and registered to the SQLite3
API so it can be called in SQL statements -- this is left
for the future)
"""
cur = connection.cursor()
#obtain summary max/min values
cur.execute(DML_SUMMARY)
summary = cur.fetchone() #there is only one record
#should use a dictionary cursor for this, so later formatting
#can reference by column name, rather than having to know
#index
#collect the full records for each maximum category
cur.execute(DML_MAX_SYSTOLIC)
systolic = cur.fetchall()
cur.execute(DML_MAX_DIASTOLIC)
diastolic = cur.fetchall()
cur.execute(DML_MAX_PULSE)
pulse = cur.fetchall()
cur.execute(DML_MAX_DATE)
date = cur.fetchall()[0]
print("\n\n\n%s\n" % "*************************************")
print("\tSummary for %s readings\n" % summary[0])
print(" %-9s %-9s %-9s" %
("Systolic", "Diastolic", " Pulse"))
print(" %-9s %-9s %-9s" %
("=========", "=========", "========="))
print("%-9s %9s %9s %9s" %
("MAX", summary[1], summary[4], summary[7]))
print("%-9s %9s %9s %9s" %
("MIN", summary[2], summary[5], summary[8]))
print("%-9s %9.9s %9.9s %9.9s" %
("AVERAGE", summary[3], summary[6], summary[9]))
print("\n\t******\n")
print("\tMaximum %s event(s)" % "SYSTOLIC")
print("%-9s %-30s %-9s %-9s %-9s" %
(" ID", " Datestamp", "Systolic", "Diastolic", "
Pulse"))
print("%-9s %-30s %-9s %-9s %-9s" %
("=========", "=============================",
"=========", "=========", "========="))
for record in systolic:
print("%9s %-30s %9s %9s %9s" %
(record[0], record[1], record[2], record[3], record[4]))
print()
print("\tMaximum %s event(s)" % "DIASTOLIC")
print("%-9s %-30s %-9s %-9s %-9s" %
(" ID", " Datestamp", "Systolic", "Diastolic", "
Pulse"))
print("%-9s %-30s %-9s %-9s %-9s" %
("=========", "=============================",
"=========", "=========", "========="))
for record in diastolic:
print("%9s %-30s %9s %9s %9s" %
(record[0], record[1], record[2], record[3], record[4]))
print()
print("\tMaximum %s event(s)" % "PULSE")
print("%-9s %-30s %-9s %-9s %-9s" %
(" ID", " Datestamp", "Systolic", "Diastolic", "
Pulse"))
print("%-9s %-30s %-9s %-9s %-9s" %
("=========", "=============================",
"=========", "=========", "========="))
for record in pulse:
print("%9s %-30s %9s %9s %9s" %
(record[0], record[1], record[2], record[3], record[4]))
print()
print("\tMaximum %s event" % "DATESTAMP")
print("%-9s %-30s %-9s %-9s %-9s" %
(" ID", " Datestamp", "Systolic", "Diastolic", "
Pulse"))
print("%-9s %-30s %-9s %-9s %-9s" %
("=========", "=============================",
"=========", "=========", "========="))
if date:
print("%9s %-30s %9s %9s %9s" %
(date[0], date[1], date[2], date[3], date[4]))
print("\n\n\n%s\n" % "*************************************")
if __name__ == "__main__":
#create argument/command line parser
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--add", nargs=3,
help = "Add in the order of systolic, diastolic,
pulse. "
"Date/Time is assumed")
parser.add_argument("-f", "--file", help = "Database file",
default = DEFAULT_DATABASE)
args = parser.parse_args()
if args.file:
database = args.file
#open/initialize database
con = open_database(database)
if args.add:
#generate timestamp
datestamp = datetime.datetime.now()
add_reading(connection=con,
datestamp=datestamp,
systolic=args.add[0],
diastolic=args.add[1],
pulse=args.add[2])
#produce summary report regardless of any other actions
generate_report(connection=con)
#close database
con.close()
-=-=-
Designed as an IMPORTable module so other programs could import it,
call the database open, add entry, and report generation functions (though
the latter should be configured to take an output destination so the report
could be sent to a file, not just blatted out on the console).
-=-=-
C:\Users\Wulfraed\Documents\_Hg-Repositories\Python
Progs\LH_BP_tracker>bp_tracker.py -a 123 98 69
*************************************
Summary for 5 readings
Systolic Diastolic Pulse
========= ========= =========
MAX 136 98 70
MIN 115 65 65
AVERAGE 126.8 86.4 68.4
******
Maximum SYSTOLIC event(s)
ID Datestamp Systolic Diastolic Pulse
========= ============================= ========= =========
=========
1 2022-05-09 14:14:37.494260 136 92 70
2 2022-05-09 14:20:40.620195 136 88 68
Maximum DIASTOLIC event(s)
ID Datestamp Systolic Diastolic Pulse
========= ============================= ========= =========
=========
5 2022-05-09 14:34:50.873347 123 98 69
Maximum PULSE event(s)
ID Datestamp Systolic Diastolic Pulse
========= ============================= ========= =========
=========
1 2022-05-09 14:14:37.494260 136 92 70
3 2022-05-09 14:21:19.428237 124 89 70
Maximum DATESTAMP event
ID Datestamp Systolic Diastolic Pulse
========= ============================= ========= =========
=========
5 2022-05-09 14:34:50.873347 123 98 69
*************************************
-=-=-
C:\Users\Wulfraed\Documents\_Hg-Repositories\Python
Progs\LH_BP_tracker>sqlite3 bp_numbers.sqlite
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .mode box
sqlite> select * from reading;
+----------------------------------------------------------------+
¦ ID ¦ reading_date ¦ systolic ¦ diastolic ¦ pulse ¦
+----+----------------------------+----------+-----------+-------¦
¦ 1 ¦ 2022-05-09 14:14:37.494260 ¦ 136 ¦ 92 ¦ 70 ¦
¦ 2 ¦ 2022-05-09 14:20:40.620195 ¦ 136 ¦ 88 ¦ 68 ¦
¦ 3 ¦ 2022-05-09 14:21:19.428237 ¦ 124 ¦ 89 ¦ 70 ¦
¦ 4 ¦ 2022-05-09 14:32:31.471217 ¦ 115 ¦ 65 ¦ 65 ¦
¦ 5 ¦ 2022-05-09 14:34:50.873347 ¦ 123 ¦ 98 ¦ 69 ¦
+----------------------------------------------------------------+
sqlite>
-=-=-
--
Wulfraed Dennis Lee Bieber AF6VN
wlfraed at ix.netcom.com http://wlfraed.microdiversity.freeddns.org/
More information about the Tutor
mailing list