This commit is contained in:
douboer
2025-09-06 16:48:23 +08:00
parent 893cd06c2c
commit 5e1788884f
2 changed files with 46 additions and 4 deletions

View File

@@ -51,6 +51,7 @@ class BookListManager:
notes = annotations.get(bk_id, {}) notes = annotations.get(bk_id, {})
day_notes = {} day_notes = {}
# 收集每本书所有笔记的创建时间,按天分组 # 收集每本书所有笔记的创建时间,按天分组
# day_notes: {date对象: [datetime对象, ...]},便于后续统计每天的阅读行为
for uuid, note in notes.items(): for uuid, note in notes.items():
raw_date = note.get('creationdate') raw_date = note.get('creationdate')
try: try:
@@ -64,58 +65,78 @@ class BookListManager:
# 获取该书的打开时间戳ZLASTOPENDATE用于判断无笔记时是否有打开过书籍 # 获取该书的打开时间戳ZLASTOPENDATE用于判断无笔记时是否有打开过书籍
open_info = books_open.get(bk_id, {}) open_info = books_open.get(bk_id, {})
last_open_ts = open_info.get('last_open') last_open_ts = open_info.get('last_open')
# 生成最近30天的阅读时长列表 # 生成最近30天的阅读时长列表readtime30d索引0为今天索引29为30天前
readtime30d = [] readtime30d = []
for i in range(30): for i in range(30):
day = today - datetime.timedelta(days=i) day = today - datetime.timedelta(days=i)
times = day_notes.get(day, []) times = day_notes.get(day, [])
# 统计当天阅读时长
if not times: if not times:
# 没有笔记,判断当天是否有打开过书籍
opened = False opened = False
if last_open_ts: if last_open_ts:
open_dt = datetime.datetime(2001, 1, 1) + datetime.timedelta(seconds=last_open_ts) open_dt = datetime.datetime(2001, 1, 1) + datetime.timedelta(seconds=last_open_ts)
if open_dt.date() == day: if open_dt.date() == day:
opened = True opened = True
# 无笔记但当天有打开书籍阅读时间设为READ_TIME_OPEN_DAY
readtime = READ_TIME_OPEN_DAY if opened else 0 readtime = READ_TIME_OPEN_DAY if opened else 0
elif len(times) == 1: elif len(times) == 1:
# 只有一条笔记,设为最小阅读时长
readtime = READ_TIME_DAY readtime = READ_TIME_DAY
else: else:
# 多条笔记统计相邻笔记时间差仅累加小于3小时的部分单位分钟
times_sorted = sorted(times) times_sorted = sorted(times)
total_minutes = 0 total_minutes = 0
for idx in range(1, len(times_sorted)): for idx in range(1, len(times_sorted)):
delta = (times_sorted[idx] - times_sorted[idx-1]).total_seconds() / 60 delta = (times_sorted[idx] - times_sorted[idx-1]).total_seconds() / 60
# 只统计相邻笔记间隔小于等于180分钟的部分
if 0 < delta <= 180: if 0 < delta <= 180:
total_minutes += int(delta) total_minutes += int(delta)
# 如果没有有效时间差,则用最小阅读时长
readtime = total_minutes if total_minutes > 0 else READ_TIME_DAY readtime = total_minutes if total_minutes > 0 else READ_TIME_DAY
readtime30d.append(readtime) readtime30d.append(readtime)
# 保存每本书的30天阅读时长列表
booksinfo[bk_id]['readtime30d'] = readtime30d booksinfo[bk_id]['readtime30d'] = readtime30d
# 新增:统计今年每个月的阅读时长和年总阅读时长(遍历今年每一天) # 新增:统计今年每月和全年阅读时长(遍历今年每一天,更精确
readtime12m = [0] * 12 # 今年每月阅读时长 # readtime12m: 今年每月阅读时长列表索引0为1月索引11为12月
readtime_year = 0 # 今年总阅读时长 # readtime_year: 今年总阅读时长(分钟)
readtime12m = [0] * 12
readtime_year = 0
first_day = datetime.date(this_year, 1, 1) first_day = datetime.date(this_year, 1, 1)
days_in_year = (today - first_day).days + 1 days_in_year = (today - first_day).days + 1
for i in range(days_in_year): for i in range(days_in_year):
day = first_day + datetime.timedelta(days=i) day = first_day + datetime.timedelta(days=i)
times = day_notes.get(day, []) times = day_notes.get(day, [])
# 统计当天阅读时长逻辑与readtime30d一致
if not times: if not times:
# 无笔记,判断当天是否有打开过书籍
opened = False opened = False
if last_open_ts: if last_open_ts:
open_dt = datetime.datetime(2001, 1, 1) + datetime.timedelta(seconds=last_open_ts) open_dt = datetime.datetime(2001, 1, 1) + datetime.timedelta(seconds=last_open_ts)
if open_dt.date() == day: if open_dt.date() == day:
opened = True opened = True
# 无笔记但当天有打开书籍阅读时间设为READ_TIME_OPEN_DAY
readtime = READ_TIME_OPEN_DAY if opened else 0 readtime = READ_TIME_OPEN_DAY if opened else 0
elif len(times) == 1: elif len(times) == 1:
# 只有一条笔记,设为最小阅读时长
readtime = READ_TIME_DAY readtime = READ_TIME_DAY
else: else:
# 多条笔记统计相邻笔记时间差仅累加小于3小时的部分单位分钟
times_sorted = sorted(times) times_sorted = sorted(times)
total_minutes = 0 total_minutes = 0
for idx in range(1, len(times_sorted)): for idx in range(1, len(times_sorted)):
delta = (times_sorted[idx] - times_sorted[idx-1]).total_seconds() / 60 delta = (times_sorted[idx] - times_sorted[idx-1]).total_seconds() / 60
# 只统计相邻笔记间隔小于等于180分钟的部分
if 0 < delta <= 180: if 0 < delta <= 180:
total_minutes += int(delta) total_minutes += int(delta)
# 如果没有有效时间差,则用最小阅读时长
readtime = total_minutes if total_minutes > 0 else READ_TIME_DAY readtime = total_minutes if total_minutes > 0 else READ_TIME_DAY
# 按月累计到readtime12m
readtime12m[day.month-1] += readtime readtime12m[day.month-1] += readtime
# 全年累计到readtime_year
readtime_year += readtime readtime_year += readtime
# 保存到booksinfo
booksinfo[bk_id]['readtime12m'] = readtime12m booksinfo[bk_id]['readtime12m'] = readtime12m
booksinfo[bk_id]['readtime_year'] = readtime_year booksinfo[bk_id]['readtime_year'] = readtime_year
except Exception as e: except Exception as e:

View File

@@ -1,3 +1,24 @@
# 2025年阅读统计功能设计补充
## 书籍阅读时长统计
1. `readtime30d`每本书最近30天每天的阅读时长分钟索引0为今天索引29为30天前。
2. `readtime12m`每本书今年每月的累计阅读时长分钟索引0为1月索引11为12月。统计逻辑为遍历今年每一天按月累计。
3. `readtime_year`:每本书今年总阅读时长(分钟),为`readtime12m`各月之和。
4. 支持无笔记但当天有打开书籍时,阅读时长设为`READ_TIME_OPEN_DAY`config.py配置默认30分钟
5. 多条笔记时统计相邻笔记时间差仅累加小于3小时的部分更真实反映实际阅读行为。
## 全局统计函数
1. `get_total_readtime_year()`:返回全年所有书的累计阅读时间(分钟)。
2. `get_total_readtime12m()`返回全年所有书的月度累计阅读时间长度12的列表单位分钟
3. `get_total_readtime(days=30)`返回最近days天每天所有书籍的总阅读时间分钟索引0为今天。
## 设计说明
- 所有统计均以“分钟”为单位,便于可视化和分析。
- 年度统计遍历今年每一天,保证月度和年度数据完整。
- 统计逻辑与实际阅读行为高度贴合,支持无笔记但有打开书籍的场景。
# iBooks 笔记导出工具 详细设计文档 # iBooks 笔记导出工具 详细设计文档
## 1. 概述 ## 1. 概述