Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,9 @@ sleepy_util_steam_enabled = false
sleepy_util_steam_key = ""
# Steam 用户 ID
sleepy_util_steam_ids = ""
# 是否储存使用数据到数据库 (可填写 "sqlite" 或 "mysql", 空串为不储存)
sleepy_util_save_to_db = ""
# sqlite 数据库文件名
sleepy_util_sqlite_name = "usage"
# 是否开启 ManicTime 时间线读取 (基于数据库)
sleepy_util_manictime_load = false
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ client/Win_Simple/build/
client/Win_Simple/*.spec
.env
client/Win_Simple/dist/config.ini
*.db
160 changes: 160 additions & 0 deletions data.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
# coding: utf-8

import os
import random
import pytz
import json
import threading
from time import sleep
from datetime import datetime

from orm import ColorGroupIndex, Event
import orm
import utils as u
import env as env
from setting import metrics_list

import sqlite3

class data:
'''
Expand Down Expand Up @@ -40,6 +44,8 @@ def __init__(self):
self.save()
except Exception as e:
u.exception(f'Create data.json failed: {e}')
self.init_db()


# --- Storage functions

Expand Down Expand Up @@ -259,5 +265,159 @@ def timer_check(self):
except Exception as e:
u.warning(f'[timer_check] Error: {e}, retrying.')

def init_db(self):
'''
初始化数据库

'''
if env.util.save_to_db == 'sqlite':
db_path = f'{env.util.sqlite_name}.db'
if not os.path.exists(db_path):
u.info('No existing sqlite database founded, will create one.')
try:
db = sqlite3.connect(db_path)
cursor = db.cursor()
except Exception as e:
u.warning(f'Error when connecting sqlite {env.util.sqlite_name}: {e}')
cursor.execute("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='Events'")
if cursor.fetchone()[0] == 0:
u.info('No existing "Events" table, will create one.')
try:
for command in u.get_sql(r'sql/sqlite_init.sql'):
cursor.execute(command)
db.commit()
except Exception as e:
db.rollback()
u.warning(f'Failed to execute SQL file: {str(e)}')
cursor.close()
db.close()


def save_db(self, device_id:str = None):
'''
将设备使用数据存储至数据库

支持只存储某个设备的数据

:param id: 选择存储的设备
'''
db_path = f'{env.util.sqlite_name}.db'
u.debug(f'[save_db] started, saving data to {env.util.save_to_db}.')
if env.util.save_to_db == 'sqlite':
ds_dict:dict = self.data["device_status"]
device_dict:dict = ds_dict.get(device_id)
if device_dict == None:
u.warning(f'[save_db] Status of this device not detected, will not save.')
return

sql_script = u.get_sql(r'sql/save.sql')[0]
if device_id != None:
db = orm.get_db()
cursor = db.cursor()
try:
cursor.execute(sql_script,(
device_id,
device_dict.get('show_name'),
device_dict.get('app_name'),
device_dict.get('using'),
datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
db.commit()
u.debug(f'[save_db] Successfully saving data to {env.util.save_to_db}.')
except Exception as e:
db.rollback()
u.warning(f'[save_db] Error while inserting value into database: {e}')


if orm.get_color_orm().find_group_color(device_dict["app_name"]) == None:

new_color_row = {
'group_name': device_dict["app_name"],
'color_hex': f'#{random.randint(0, 0xFFFFFF):06X}',
'[set]': 0
}

orm.get_color_orm().append_row(new_color_row)
cursor.close()

def db_to_xml(self, device_id:str ,table:str='Events', start_from:datetime=None, end_to:datetime=None, ignore_sec=2) -> str:
"""将数据库中设备使用的信息转换为可被ManicTime接收的xml文件

Args:
device_id (str): 设备标识符
start_from (datetime): 开始时间
end_to (datetime): 结束时间
"""
# ignore_sec (int): 忽略小于等于此时间秒数的事件
# 注:table参数和ignore_sec参数相关逻辑未完成,暂时留置
import xml.etree.ElementTree as ET
from xml.dom import minidom

# 定义 XML 结构
timeline = ET.Element("Timeline")
color = ET.SubElement(timeline, "Color")
color.text = "#bacf9c" # #bacf9c
activities = ET.SubElement(timeline, "Activities")
groups_elem = ET.SubElement(timeline,"Groups")

events = orm.get_orm().query(Event,
"SELECT * FROM Events WHERE device_id = ? AND start_time BETWEEN ? AND ?",
(device_id,
start_from.strftime('%Y-%m-%d %H:%M:%S'),
end_to.strftime('%Y-%m-%d %H:%M:%S'))
)

events = sorted(events, key=lambda e: e.start_time, reverse=False) # False:升序,由旧到新

if events:
last_event = orm.get_orm().query(Event, '''SELECT * FROM Events WHERE device_id = ? AND id = ?''', (device_id, events[0].id - 1))
if last_event is not None:
events.insert(0, last_event[0])

color_groups = orm.get_color_orm().find_matching_color_groups(events)
color_groups_index = ColorGroupIndex(color_groups)

for index, event in enumerate(events):

color_group = color_groups_index.get_group_by_name(event.app_name)
if color_group is None:
u.warning(f'[db_to_xml] Generating group id: event exists but not found in group, will pass event:{event.app_name}')
continue

group_id = color_group.id

activity_elem = ET.SubElement(activities, "Activity")

group_id_elem = ET.SubElement(activity_elem, "GroupId")
group_id_elem.text = str(group_id)

display_name_elem = ET.SubElement(activity_elem, "DisplayName")
display_name_elem.text = event.app_name

start_time_elem = ET.SubElement(activity_elem, "StartTime")
start_time_elem.text = event.start_time.strftime("%Y-%m-%dT%H:%M:%S+08:00")

end_time_elem = ET.SubElement(activity_elem, "EndTime")
if index == len(events) - 1:
end_time_elem.text = datetime.now().strftime("%Y-%m-%dT%H:%M:%S+08:00")
else:
end_time_elem.text = events[index + 1].start_time.strftime("%Y-%m-%dT%H:%M:%S+08:00")

for color_group in color_groups:

group_elem = ET.SubElement(groups_elem, "Group")

group_id_elem = ET.SubElement(group_elem, "GroupId")
group_id_elem.text = str(color_group.id)

color_elem = ET.SubElement(group_elem,"Color")
color_elem.text = color_group.color_hex

display_name_elem_g = ET.SubElement(group_elem, "DisplayName")
display_name_elem_g.text = color_group.group_name

xmlstr = minidom.parseString(ET.tostring(timeline)).toprettyxml(indent=" ")

return xmlstr

# --- check device heartbeat
# TODO
3 changes: 3 additions & 0 deletions env.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ class _util:
steam_enabled: bool = getenv('sleepy_util_steam_enabled', False, bool)
steam_key: str = getenv('sleepy_util_steam_key', '', str)
steam_ids: str = getenv('sleepy_util_steam_ids', '', str)
save_to_db: str = getenv('sleepy_util_save_to_db', '', str)
sqlite_name: str = getenv('sleepy_util_sqlite_name', 'usage', str)
manictime_load: str = getenv('sleepy_util_manictime_load', False, bool)


main = _main()
Expand Down
Loading