统计信息新增日历图和逐月统计图

This commit is contained in:
shuaikangzhou 2024-01-27 17:32:23 +08:00
parent 9795ac9921
commit 8f16ef5e60
4 changed files with 148 additions and 142 deletions

View File

@ -379,77 +379,57 @@ class Msg:
lock.release() lock.release()
return [date[0] for date in result] return [date[0] for date in result]
def get_messages_by_days(self, username_, is_Annual_report_=False, year_='2023'): def get_messages_by_days(self, username_, time_range=None):
if is_Annual_report_: result = None
sql = ''' if not self.open_flag:
SELECT strftime('%Y-%m-%d',CreateTime,'unixepoch','localtime') as days,count(MsgSvrID) return None
from ( if time_range:
SELECT MsgSvrID, CreateTime start_time, end_time = time_range
FROM MSG sql = f'''
WHERE StrTalker = ? AND strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ? SELECT strftime('%Y-%m-%d',CreateTime,'unixepoch','localtime') as days,count(MsgSvrID)
) from (
group by days SELECT MsgSvrID, CreateTime
''' FROM MSG
else: WHERE StrTalker = ?
sql = ''' {'AND CreateTime>' + str(start_time) + ' AND CreateTime<' + str(end_time) if time_range else ''}
SELECT strftime('%Y-%m-%d',CreateTime,'unixepoch','localtime') as days,count(MsgSvrID) )
from ( group by days
SELECT MsgSvrID, CreateTime '''
FROM MSG
WHERE StrTalker = ?
)
group by days
'''
result = None result = None
if not self.open_flag: if not self.open_flag:
return None return None
try: try:
lock.acquire(True) lock.acquire(True)
if is_Annual_report_: self.cursor.execute(sql, [username_])
self.cursor.execute(sql, [username_, year_])
else:
self.cursor.execute(sql, [username_])
result = self.cursor.fetchall() result = self.cursor.fetchall()
finally: finally:
lock.release() lock.release()
return result return result
def get_messages_by_month(self, username_, is_Annual_report_=False, year_='2023'): def get_messages_by_month(self, username_, time_range=None):
if is_Annual_report_:
sql = '''
SELECT strftime('%Y-%m',CreateTime,'unixepoch','localtime') as days,count(MsgSvrID)
from (
SELECT MsgSvrID, CreateTime
FROM MSG
WHERE StrTalker = ? AND strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?
)
group by days
'''
else:
sql = '''
SELECT strftime('%Y-%m',CreateTime,'unixepoch','localtime') as days,count(MsgSvrID)
from (
SELECT MsgSvrID, CreateTime
FROM MSG
WHERE StrTalker = ?
)
group by days
'''
result = None result = None
if not self.open_flag: if not self.open_flag:
return None return None
if time_range:
start_time, end_time = time_range
sql = f'''
SELECT strftime('%Y-%m',CreateTime,'unixepoch','localtime') as days,count(MsgSvrID)
from (
SELECT MsgSvrID, CreateTime
FROM MSG
WHERE StrTalker = ?
{'AND CreateTime>' + str(start_time) + ' AND CreateTime<' + str(end_time) if time_range else ''}
)
group by days
'''
try: try:
lock.acquire(True) lock.acquire(True)
if is_Annual_report_: self.cursor.execute(sql, [username_])
self.cursor.execute(sql, [username_, year_])
else:
self.cursor.execute(sql, [username_])
result = self.cursor.fetchall() result = self.cursor.fetchall()
except sqlite3.DatabaseError: except sqlite3.DatabaseError:
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试') logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
finally: finally:
lock.release() lock.release()
# result.sort(key=lambda x: x[5])
return result return result
def get_messages_by_hour(self, username_, year_='all'): def get_messages_by_hour(self, username_, year_='all'):
@ -572,12 +552,14 @@ class Msg:
lock.release() lock.release()
return result return result
def get_messages_number(self, username_, year_="all") -> int: def get_messages_number(self, username_, time_range=None) -> int:
if time_range:
start_time, end_time = time_range
sql = f""" sql = f"""
SELECT Count(MsgSvrID) SELECT Count(MsgSvrID)
from MSG from MSG
where StrTalker = ? where StrTalker = ?
{"and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?" if year_ != "all" else ""} {'AND CreateTime>' + str(start_time) + ' AND CreateTime<' + str(end_time) if time_range else ''}
group by type, subtype group by type, subtype
order by Count(MsgSvrID) desc order by Count(MsgSvrID) desc
""" """
@ -586,7 +568,7 @@ class Msg:
return None return None
try: try:
lock.acquire(True) lock.acquire(True)
self.cursor.execute(sql, [username_, year_] if year_ != "all" else [username_]) self.cursor.execute(sql, [username_])
result = self.cursor.fetchone() result = self.cursor.fetchone()
except sqlite3.DatabaseError: except sqlite3.DatabaseError:
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试') logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')

View File

@ -73,7 +73,7 @@ def wordcloud(wxid, is_Annual_report=False, year='2023', who='1'):
} }
def wordcloud_(wxid, is_Annual_report=False, time_range=None): def wordcloud_(wxid, time_range=None):
import jieba import jieba
txt_messages = msg_db.get_messages_by_type(wxid, MsgType.TEXT, time_range=time_range) txt_messages = msg_db.get_messages_by_type(wxid, MsgType.TEXT, time_range=time_range)
if not txt_messages: if not txt_messages:
@ -183,30 +183,15 @@ def wordcloud_christmas(wxid, year='2023'):
} }
def calendar_chart(wxid, is_Annual_report=False, year='2023'): def calendar_chart(wxid, time_range=None):
try: calendar_data = msg_db.get_messages_by_days(wxid,time_range)
calendar_data = msg_db.get_messages_by_days(wxid, is_Annual_report, year)
except:
return {
'calendar_chart_data': None,
'chat_days': 0,
}
if not calendar_data:
return {
'calendar_chart_data': None,
'chat_days': 0,
}
min_ = min(map(lambda x: x[1], calendar_data)) min_ = min(map(lambda x: x[1], calendar_data))
max_ = max(map(lambda x: x[1], calendar_data)) max_ = max(map(lambda x: x[1], calendar_data))
start_date_ = calendar_data[0][0] start_date_ = calendar_data[0][0]
end_date_ = calendar_data[-1][0] end_date_ = calendar_data[-1][0]
print(start_date_, '---->', end_date_) print(start_date_, '---->', end_date_)
if is_Annual_report: calendar_days = (start_date_, end_date_)
calendar_days = year calendar_title = '和Ta的聊天情况'
calendar_title = f'{year}年聊天情况'
else:
calendar_days = (start_date_, end_date_)
calendar_title = '和Ta的聊天情况'
c = ( c = (
Calendar() Calendar()
.add( .add(
@ -226,43 +211,50 @@ def calendar_chart(wxid, is_Annual_report=False, year='2023'):
) )
) )
return { return {
'calendar_chart_data': c.dump_options_with_quotes(), 'chart_data': c.dump_options_with_quotes(),
'chat_days': len(calendar_data), 'chat_days': len(calendar_data),
# 'chart':c,
} }
def month_count(wxid, is_Annual_report=False, year='2023'): def month_count(wxid, time_range=None):
""" """
每月聊天条数 每月聊天条数
""" """
msg_data = msg_db.get_messages_by_month(wxid, is_Annual_report, year) msg_data = msg_db.get_messages_by_month(wxid, time_range)
y_data = list(map(lambda x: x[1], msg_data)) y_data = list(map(lambda x: x[1], msg_data))
x_axis = list(map(lambda x: x[0], msg_data)) x_axis = list(map(lambda x: x[0], msg_data))
m = ( m = (
Bar(init_opts=opts.InitOpts(width=f"{charts_width}px", height=f"{charts_height}px")) Bar(init_opts=opts.InitOpts())
.add_xaxis(x_axis) .add_xaxis(x_axis)
.add_yaxis("消息数量", y_data, .add_yaxis("消息数量", y_data,
label_opts=opts.LabelOpts(is_show=False), label_opts=opts.LabelOpts(is_show=True),
itemstyle_opts=opts.ItemStyleOpts(color="skyblue"), itemstyle_opts=opts.ItemStyleOpts(color="#ffae80"),
) )
.set_global_opts( .set_global_opts(
title_opts=opts.TitleOpts(title="逐月统计", subtitle=None), title_opts=opts.TitleOpts(title="逐月统计", subtitle=None),
datazoom_opts=opts.DataZoomOpts(), datazoom_opts=opts.DataZoomOpts(),
toolbox_opts=opts.ToolboxOpts(), toolbox_opts=opts.ToolboxOpts(),
yaxis_opts=opts.AxisOpts(
name="消息数",
type_="value",
axistick_opts=opts.AxisTickOpts(is_show=True),
splitline_opts=opts.SplitLineOpts(is_show=True),
),
visualmap_opts=opts.VisualMapOpts( visualmap_opts=opts.VisualMapOpts(
min_=min(y_data), min_=min(y_data),
max_=max(y_data), max_=max(y_data),
dimension=1, # 根据第2个维度y 轴)进行映射 dimension=1, # 根据第2个维度y 轴)进行映射
is_piecewise=False, # 是否分段显示 is_piecewise=False, # 是否分段显示
range_color=["#66ccff", "#003366"], # 设置颜色范围 range_color=["#ffbe7a", "#fa7f6f"], # 设置颜色范围
type_="color", type_="color",
pos_right="0%", pos_right="0%",
), ),
) )
) )
return { return {
'chart_data': m 'chart_data': m.dump_options_with_quotes(),
# 'chart': m,
} }
@ -314,13 +306,11 @@ class Analysis:
if __name__ == '__main__': if __name__ == '__main__':
msg_db.init_database(path='../DataBase/Msg/MSG.db') msg_db.init_database(path='../DataBase/Msg/MSG.db')
# w = wordcloud('wxid_0o18ef858vnu22') # w = wordcloud('wxid_0o18ef858vnu22')
w_data = wordcloud('wxid_27hqbq7vx5hf22', True, '2023') # w_data = wordcloud('wxid_27hqbq7vx5hf22', True, '2023')
# print(w_data) # # print(w_data)
# w['chart_data'].render("./data/聊天统计/wordcloud.html") # w_data['chart_data'].render("./data/聊天统计/wordcloud.html")
c = calendar_chart('wxid_27hqbq7vx5hf22', False, '2023') wxid = 'wxid_0o18ef858vnu22'
c['chart_data'].render("./data/聊天统计/calendar.html") # data = month_count(wxid, time_range=None)
# print('c:::', c) # data['chart'].render("./data/聊天统计/month_count.html")
m = month_count('wxid_27hqbq7vx5hf22', False, '2023') data = calendar_chart(wxid, time_range=None)
m['chart_data'].render("./data/聊天统计/month_num.html") data['chart'].render("./data/聊天统计/calendar_chart.html")
h = hour_count('wxid_27hqbq7vx5hf22')
h['chart_data'].render("./data/聊天统计/hour_count.html")

View File

@ -6,24 +6,59 @@
<title>微信年度聊天报告</title> <title>微信年度聊天报告</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@latest/dist/echarts.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@latest/dist/echarts.min.js"></script>
<script type="text/javascript" src="https://assets.pyecharts.org/assets/v5/echarts-wordcloud.min.js"></script> <script type="text/javascript" src="https://assets.pyecharts.org/assets/v5/echarts-wordcloud.min.js"></script>
<style>
body{
display: flex;
flex-direction: column;
margin: 0;
align-items: center;
}
</style>
</head> </head>
<body> <body>
<h1>{{my_nickname}}——{{ta_nickname}}</h1> <h1>{{my_nickname}}——{{ta_nickname}}</h1>
<div id="echarts-container" style="width: 600px; height: 400px;"></div> <div>
<p class="mt-3">我们第一次聊天在</p>
<p id="first_time" class="first-time">{{first_time}}</p>
<p class="mt-3">距今已有</p>
<div class="mt-3">
<span id="t_d"></span>
<span id="t_h"></span>
<span id="t_m"></span>
<span id="t_s"></span>
</div>
</div>
<div id="echarts-month_count" style="width: 800px; height: 600px;"></div>
<div id="echarts-wordcloud" style="width: 800px; height: 800px;"></div> <div id="echarts-wordcloud" style="width: 800px; height: 800px;"></div>
<div id="echarts-calendar" style="width: 800px; height: 300px;"></div>
<script type="text/javascript"> function getRTime() {
var tt = document.getElementById("first_time").innerText;
var EndTime = new Date(tt);
var NowTime = new Date();
var t = NowTime.getTime()-EndTime.getTime();
var d = Math.floor(t / 1000 / 60 / 60 / 24);
var h = Math.floor(t / 1000 / 60 / 60 % 24);
var m = Math.floor(t / 1000 / 60 % 60);
var s = Math.floor(t / 1000 % 60);
document.getElementById("t_d").innerHTML = d + " 天";
document.getElementById("t_h").innerHTML = h + " 时";
document.getElementById("t_m").innerHTML = m + " 分";
document.getElementById("t_s").innerHTML = s + " 秒";
}
setInterval(getRTime, 1000);
</script>
<script> <script>
// 使用 AJAX 请求获取 Pyecharts 生成的图表配置项数据 fetch('/month_count')
fetch('/get_chart_options')
.then(response => response.json()) .then(response => response.json())
.then(chartOptions => { .then(chartOptions => {
// 在这里使用 ECharts 渲染图表 // 在这里使用 ECharts 渲染图表
var myChart = echarts.init(document.getElementById('echarts-container')); var myChart = echarts.init(document.getElementById('echarts-month_count'));
var option = chartOptions.chart_data; var option = chartOptions.chart_data;
myChart.setOption(JSON.parse(option)); myChart.setOption(JSON.parse(option));
}); });
</script> </script>
<script> <script>
// 使用 AJAX 请求获取 Pyecharts 生成的图表配置项数据
fetch('/wordcloud') fetch('/wordcloud')
.then(response => response.json()) .then(response => response.json())
.then(chartOptions => { .then(chartOptions => {
@ -33,5 +68,15 @@
myChart.setOption(JSON.parse(option)); myChart.setOption(JSON.parse(option));
}); });
</script> </script>
<script>
fetch('/calendar')
.then(response => response.json())
.then(chartOptions => {
// 在这里使用 ECharts 渲染图表
var myChart = echarts.init(document.getElementById('echarts-calendar'));
var option = chartOptions.chart_data;
myChart.setOption(JSON.parse(option));
});
</script>
</body> </body>
</html> </html>

View File

@ -28,6 +28,13 @@ def index():
@app.route("/christmas") @app.route("/christmas")
def christmas(): def christmas():
t = '2023-1-01 00:00:00'
s_t = time.strptime(t, "%Y-%m-%d %H:%M:%S") # 返回元祖
start_time = int(time.mktime(s_t))
t = '2023-12-31 23:59:59'
s_t = time.strptime(t, "%Y-%m-%d %H:%M:%S") # 返回元祖
end_time = int(time.mktime(s_t))
time_range = (start_time, end_time)
# 渲染模板,并传递图表的 HTML 到模板中 # 渲染模板,并传递图表的 HTML 到模板中
try: try:
first_message, first_time = msg_db.get_first_time_of_message(contact.wxid) first_message, first_time = msg_db.get_first_time_of_message(contact.wxid)
@ -63,7 +70,7 @@ def christmas():
'chat_time': chat_time, 'chat_time': chat_time,
'chat_time_num': num, 'chat_time_num': num,
} }
month_data = msg_db.get_messages_by_month(contact.wxid, True, year_='2023') month_data = msg_db.get_messages_by_month(contact.wxid,time_range=time_range)
if month_data: if month_data:
month_data.sort(key=lambda x: x[1]) month_data.sort(key=lambda x: x[1])
@ -74,16 +81,18 @@ def christmas():
else: else:
max_month, max_num = '月份', 0 max_month, max_num = '月份', 0
min_month, min_num = '月份', 0 min_month, min_num = '月份', 0
month_data = { month_data = {
'year': '2023', 'year': '2023',
'total_msg_num': msg_db.get_messages_number(contact.wxid, '2023'), 'total_msg_num': msg_db.get_messages_number(contact.wxid,time_range=time_range),
'max_month': max_month, 'max_month': max_month,
'min_month': min_month, 'min_month': min_month,
'max_month_num': max_num, 'max_month_num': max_num,
'min_month_num': min_num, 'min_month_num': min_num,
} }
calendar_data = analysis.calendar_chart(contact.wxid, True, year='2023')
emoji_msgs = msg_db.get_messages_by_type(contact.wxid, 47, year_='2023') calendar_data = analysis.calendar_chart(contact.wxid, time_range)
emoji_msgs = msg_db.get_messages_by_type(contact.wxid, 47, time_range=time_range)
url, num = get_most_emoji(emoji_msgs) url, num = get_most_emoji(emoji_msgs)
emoji_data = { emoji_data = {
'emoji_total_num': len(emoji_msgs), 'emoji_total_num': len(emoji_msgs),
@ -96,21 +105,6 @@ def christmas():
return html return html
@app.route('/home')
def home():
try:
first_message, first_time = msg_db.get_first_time_of_message(contact.wxid)
except TypeError:
return set_text('咱就是说,一次都没聊过就别分析了')
data = {
'sub_title': '二零二三年度报告',
'avatar_path': contact.avatar_path,
'nickname': contact.remark,
'first_time': first_time,
}
return render_template('home.html', **data)
@app.route('/upload') @app.route('/upload')
def upload(): def upload():
@ -129,20 +123,6 @@ def upload():
response.headers.add('Content-Type', 'application/json') response.headers.add('Content-Type', 'application/json')
return response return response
@app.route('/wordcloud/<who>/')
def one(who):
wxid = contact.wxid
# wxid = 'wxid_lltzaezg38so22'
# print('wxid:'+wxid)
world_cloud_data = analysis.wordcloud(wxid, who=who) # 获取与Ta的对话数据
# print(world_cloud_data)
who = "" if who == '1' else "TA"
with open('wordcloud.html', 'w', encoding='utf-8') as f:
f.write(render_template('wordcloud.html', **world_cloud_data))
return render_template('wordcloud.html', **world_cloud_data, who=who)
def set_text(text): def set_text(text):
html = ''' html = '''
<!DOCTYPE html> <!DOCTYPE html>
@ -217,32 +197,41 @@ def generate_chart():
return bar.dump_options_with_quotes() return bar.dump_options_with_quotes()
@app.route('/get_chart_options') @app.route('/month_count')
def get_chart_options(): def get_chart_options():
chart_options = generate_chart() time_range = (0, time.time())
data = { data = analysis.month_count(contact.wxid, time_range=time_range)
'chart_data': chart_options
}
return jsonify(data) return jsonify(data)
@app.route('/wordcloud') @app.route('/wordcloud')
def get_wordcloud(): def get_wordcloud():
time_range = (0, time.time()) time_range = (0, time.time())
print(time_range) print(time_range,contact.wxid)
world_cloud_data = analysis.wordcloud_(contact.wxid, time_range=time_range) world_cloud_data = analysis.wordcloud_(contact.wxid, time_range=time_range)
return jsonify(world_cloud_data) return jsonify(world_cloud_data)
@app.route('/charts') @app.route('/charts')
def charts(): def charts():
# 渲染模板,并传递图表的 HTML 到模板中
try:
first_message, first_time = msg_db.get_first_time_of_message(contact.wxid)
except TypeError:
first_time = '2023-01-01 00:00:00'
data = { data = {
'my_nickname':Me().name, 'my_nickname': Me().name,
'ta_nickname':contact.remark, 'ta_nickname': contact.remark,
'first_time': first_time
} }
return render_template('charts.html',**data) return render_template('charts.html', **data)
@app.route('/calendar')
def get_calendar():
time_range = (0, time.time())
world_cloud_data = analysis.calendar_chart(contact.wxid, time_range=time_range)
return jsonify(world_cloud_data)
if __name__ == "__main__": if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0') app.run(debug=True, host='0.0.0.0')