一、为什么要搞
之前我感觉腰子剧痛,医生诊断尿道有结石,还不到超声碎石的程度,需要坚持运动和吃药,可很多时候药吃完了,却忘记了买药。
于是乎,我尝试使用Python实现买药提醒并且统计买药数据
二、准备如何搞
买药的数据记录在excel文件中:
tablib读取最近买药数据,计算出下次买药日期
tinydb记录下次购买日期JSON数据
zmail发送买药提醒邮件
matplotlib绘制买药数据统计图表
三、说搞咱就搞
计算
读取excel,获取最后购买时间和数量,根据每日用量3袋计算出下次购买时间,并将其记录至medicine_purchase_notice.json文件
import datetime
import math
from tablib import Dataset
from tinydb import TinyDB
# 计算
def medicine_cant_stop(file):
"""
计算下次买药日期
"""
with open(file, 'rb') as f: # 读取excel到数据集
data = Dataset().load(f)
last_purchase_time = data[-1][0] # 最后购买时间
last_purchase_num = data[-1][1] # 最后购买数量
use_num = 3 # 每日用量--3小袋
gap_days = math.trunc(last_purchase_num / use_num) # 购买的数量还能吃多少天--取整
next_purchase_time = datetime.timedelta(days=gap_days) + last_purchase_time # 预计下次购买时间
with TinyDB('medicine_purchase_notice.json') as db: # 创建db
db.insert({'notice_date': str(next_purchase_time.date())}) # 插入数据
return next_purchase_time.date()
if __name__ == "__main__":
file_path = 'medicine_record.xlsx'
print(medicine_cant_stop(file_path))
运行结果:
提醒
读取medicine_purchase_notice.json文件最后一条数据,若与当前日期相同,则发送买药提醒邮件
import datetime
import traceback
import zmail
from tinydb import TinyDB
# 提醒
def medicine_purchase_notice():
"""
发送买药提醒邮件
"""
with TinyDB('medicine_purchase_notice.json') as db: # 从json文件获取所有数据
data = db.all()
notice_date = datetime.datetime.strptime(data[-1]['notice_date'], '%Y-%m-%d').date() # 将最后一条数据转换为日期格式
today = datetime.date.today() # 今天
if today == notice_date: # 提醒日期=今天
# 发送邮件
try:
server = zmail.server('********@163.com', 'L************V') # 发件人邮箱地址和授权码
mail_content = {
'subject': '买药提醒', # 主题
'content_text': '今天要买药了哦!' # 文本内容
}
server.send_mail('********@163.com', mail_content) # 收件人地址和邮件内容
except Exception as e:
info = f"邮件发送失败!\n{repr(e)}\n{traceback.format_exc()}"
print(info)
else:
print('邮件发送成功!')
if __name__ == "__main__":
medicine_purchase_notice()
运行结果:
统计
读取excel中的数据,绘制时间-数量折线图和时间-花费柱状图
import os
import mplcyberpunk
from matplotlib import pyplot as plt
from tablib import Dataset
# 统计
def medicine_purchase_report(file):
"""
历史买药数据统计
"""
with open(file, 'rb') as f: # 读取excel到数据集
data = Dataset().load(f)
plt.style.use(os.path.dirname(os.path.abspath(__file__)) + os.sep + 'cyberpunk.mplstyle') # 设置主题样式
plt.rcParams['font.sans-serif'] = ['STSong'] # Simhei(黑体) Kaiti(楷体) Lisu(隶书) STSong(宋体)--设置后可正常显示中文
x = [d.date() for d in data.get_col(0)] # X轴
y_num = data.get_col(1) # Y轴--数量(小袋)
y_cost = data.get_col(2) # Y轴--花费
fig, ax_num = plt.subplots() # 创建图表和数量坐标轴
ax_num.plot(x, y_num, label=data.headers[1], color='b') # 数量折线图--蓝色
ax_num.set_ylabel(data.headers[1], color='b') # 数量折线图Y轴标签--蓝色
ax_num.tick_params('y', colors='b') # 设置数量折线图Y轴刻度和标签颜色--蓝色
ax_num.set_ylim([0, max(y_num)]) # 设置Y轴刻度范围
ax_num.set_xticks(x, labels=x, rotation=35) # 设置X轴标签和角度
ax_cost = ax_num.twinx() # 花费坐标轴--共享数量坐标轴的X轴
bars = ax_cost.bar(x, y_cost, label=data.headers[2], color='r') # 花费柱状图--红色
ax_cost.set_xlabel(data.headers[0]) # X轴标题
ax_cost.set_ylabel(data.headers[2], color='r') # 花费柱状图Y轴标签--红色
for index, val in enumerate(y_cost): # 设置Y轴数据
plt.text(x[index], val, val, ha='center', va='bottom')
mplcyberpunk.add_bar_gradient(bars=bars) # 柱状图颜色渐变
ax_cost.tick_params('y', colors='r') # 设置花费柱状图Y轴刻度和标签颜色--红色
ax_cost.grid(linestyle='--') # 设置网格线样式
plt.show() # 显示图像
if __name__ == "__main__":
file_path = 'medicine_record.xlsx'
medicine_purchase_report(file_path)
运行结果:
运行
既然是提醒,当然得是自动的才会有意义,故将上面包含medicine_cant_stop和medicine_purchase_notice两个方法的py文件(medicine_cant_stop.py)放入Linux云服务器或者装有python环境的Windows电脑中。
Case1:若是Linux云服务器,则设置crontab定时任务每天执行medicine_cant_stop.py即可
Case2:若是Windows电脑,则添加计划任务每天执行medicine_cant_stop.py
当然,基础买药数据excel文件也得放入与medicine_cant_stop.py相同的文件目录下,若有新买药,也需要手动更新数据。