个人年度报告新增性别和地区分布

This commit is contained in:
SiYuan 2024-02-13 22:34:45 +08:00
parent fac0226edb
commit 040077dc7f
5 changed files with 128 additions and 16 deletions

View File

@ -2,12 +2,16 @@ import os
from collections import Counter from collections import Counter
import sys import sys
from datetime import datetime from datetime import datetime
from typing import List
import jieba import jieba
from app.DataBase import msg_db, MsgType from app.DataBase import msg_db, MsgType
from pyecharts import options as opts from pyecharts import options as opts
from pyecharts.charts import WordCloud, Calendar, Bar, Line, Pie from pyecharts.charts import WordCloud, Calendar, Bar, Line, Pie, Map
from app.person import Contact
from app.util.region_conversion import conversion_province_to_chinese
os.makedirs('./data/聊天统计/', exist_ok=True) os.makedirs('./data/聊天统计/', exist_ok=True)
@ -415,6 +419,58 @@ def sender(wxid, time_range, my_name='', ta_name=''):
} }
def contacts_analysis(contacts):
man_contact_num = 0
woman_contact_num = 0
province_dict = {
'北京': '北京市',
'上海': '上海市',
'天津': '天津市',
'重庆': '重庆市',
'新疆': '新疆维吾尔族自治区',
'广西': '广西壮族自治区',
'内蒙古': '内蒙古自治区',
'宁夏': '宁夏回族自治区',
'西藏': '西藏自治区'
}
provinces = []
for contact, num, text_length in contacts:
if contact.detail.get('gender') == 1:
man_contact_num += 1
elif contact.detail.get('gender') == 2:
woman_contact_num += 1
province_py = contact.detail.get('region')
if province_py:
province = province_py[1]
province = conversion_province_to_chinese(province)
if province:
if province in province_dict:
province = province_dict[province]
else:
province += ''
provinces.append(province)
print(province, contact.detail)
data = Counter(provinces)
data = [[k, v] for k, v in data.items()]
print(data)
max_ = max(list(map(lambda x:x[1],data)))
c = (
Map()
.add("分布", data, "china")
.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
.set_global_opts(
title_opts=opts.TitleOpts(title="地区分布"),
visualmap_opts=opts.VisualMapOpts(max_=max_, is_piecewise=True),
legend_opts=opts.LegendOpts(is_show=False),
)
)
return {
'woman_contact_num': woman_contact_num,
'man_contact_num': man_contact_num,
'contact_region_map': c.dump_options_with_quotes(),
}
def my_message_counter(time_range, my_name=''): def my_message_counter(time_range, my_name=''):
msg_data = msg_db.get_messages_all(time_range=time_range) msg_data = msg_db.get_messages_all(time_range=time_range)
types_count = {} types_count = {}

View File

@ -25,6 +25,7 @@ class Person:
self.avatar_path = None self.avatar_path = None
self.avatar = None self.avatar = None
self.avatar_path_qt = Icon.Default_avatar_path self.avatar_path_qt = Icon.Default_avatar_path
self.detail = {}
def set_avatar(self, img_bytes): def set_avatar(self, img_bytes):
if not img_bytes: if not img_bytes:
@ -55,6 +56,7 @@ class Person:
@singleton @singleton
class Me(Person): class Me(Person):
def __init__(self): def __init__(self):
super().__init__()
self.avatar = QPixmap(Icon.Default_avatar_path) self.avatar = QPixmap(Icon.Default_avatar_path)
self.avatar_path = ':/icons/icons/default_avatar.svg' self.avatar_path = ':/icons/icons/default_avatar.svg'
self.wxid = '' self.wxid = ''
@ -68,6 +70,7 @@ class Me(Person):
class Contact(Person): class Contact(Person):
def __init__(self, contact_info: Dict): def __init__(self, contact_info: Dict):
super().__init__()
self.wxid = contact_info.get('UserName') self.wxid = contact_info.get('UserName')
self.remark = contact_info.get('Remark') self.remark = contact_info.get('Remark')
# Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl,bigHeadImgUrl # Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl,bigHeadImgUrl
@ -81,7 +84,7 @@ class Contact(Person):
self.avatar = QPixmap() self.avatar = QPixmap()
self.avatar_path = Icon.Default_avatar_path self.avatar_path = Icon.Default_avatar_path
self.is_chatroom = self.wxid.__contains__('@chatroom') self.is_chatroom = self.wxid.__contains__('@chatroom')
self.detail = contact_info.get('detail') self.detail:Dict = contact_info.get('detail')
self.label_name = contact_info.get('label_name') # 联系人的标签分类 self.label_name = contact_info.get('label_name') # 联系人的标签分类
""" """
@ -97,6 +100,7 @@ class Contact(Person):
class ContactDefault(Person): class ContactDefault(Person):
def __init__(self, wxid=""): def __init__(self, wxid=""):
super().__init__()
self.avatar = QPixmap(Icon.Default_avatar_path) self.avatar = QPixmap(Icon.Default_avatar_path)
self.avatar_path = ':/icons/icons/default_avatar.svg' self.avatar_path = ':/icons/icons/default_avatar.svg'
self.wxid = wxid self.wxid = wxid
@ -106,6 +110,7 @@ class ContactDefault(Person):
self.smallHeadImgUrl = "" self.smallHeadImgUrl = ""
self.smallHeadImgBLOG = b'' self.smallHeadImgBLOG = b''
self.is_chatroom = False self.is_chatroom = False
self.detail = {}
class Contacts: class Contacts:

View File

@ -40,6 +40,7 @@ province_mapping = {
country_mapping = { country_mapping = {
'CN': '中国大陆', 'CN': '中国大陆',
'TW': '中国台湾', 'TW': '中国台湾',
'GB': "英国",
} }
city_mapping = { city_mapping = {
"Beijing": "北京", "Beijing": "北京",
@ -326,12 +327,22 @@ city_mapping = {
"Jingdezhen": "景德镇", "Jingdezhen": "景德镇",
"Xinyu": "新余", "Xinyu": "新余",
"Yichun": "宜春", "Yichun": "宜春",
"Fuzhou": "抚州" "Fuzhou": "抚州",
"Tin Shui": "天水"
} }
def conversion_province_to_chinese(province):
area = ''
if province in province_mapping:
area = f'{province_mapping[province]}'
return area
def conversion_region_to_chinese(region: tuple): def conversion_region_to_chinese(region: tuple):
area = '' area = ''
if not region:
return area
if region[2]: if region[2]:
if region[2] in city_mapping: if region[2] in city_mapping:
area = city_mapping[region[2]] area = city_mapping[region[2]]

View File

@ -8,6 +8,7 @@
<link rel="stylesheet" href="https://memotrace.lc044.love/static/css/fullpage.min.css" /> <link rel="stylesheet" href="https://memotrace.lc044.love/static/css/fullpage.min.css" />
<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>
<script type="text/javascript" src="https://assets.pyecharts.org/assets/v5/maps/china.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.4.min.js"></script> <script type="text/javascript" src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script type="text/javascript" src="https://memotrace.lc044.love/static/js/fullpage.min.js"></script> <script type="text/javascript" src="https://memotrace.lc044.love/static/js/fullpage.min.js"></script>
<script type="text/javascript" src="https://davidshimjs.github.io/qrcodejs/qrcode.min.js"></script> <script type="text/javascript" src="https://davidshimjs.github.io/qrcodejs/qrcode.min.js"></script>
@ -31,6 +32,13 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.slide{
margin: 0px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#snow { #snow {
position: fixed; position: fixed;
top: 0; top: 0;
@ -252,7 +260,7 @@ p {
} }
.chart{ .chart{
width: 800px; width: 800px;
height: 300px; height: 500px;
} }
.chart-container{ .chart-container{
display: flex; display: flex;
@ -264,7 +272,7 @@ p {
@media screen and (max-width:480px){ @media screen and (max-width:480px){
.chart{ .chart{
width: 300px; width: 300px;
height: 250px; height: 400px;
} }
.chart-container{ .chart-container{
display:flex; display:flex;
@ -310,10 +318,18 @@ p {
<div class="section cover"> <div class="section cover">
<h1>2023年</h1> <h1>2023年</h1>
<p>你一共给<span class="number">{{contact_num}}</span>个联系人<br>发送了<span class="number">{{send_msg_num}}</span>条消息<br>收到了<span class="number">{{receive_msg_num}}</span>条消息<br>总计<span class="number">{{total_text_num}}</span></p> <p>你一共给<span class="number">{{contact_num}}</span>个联系人<br>发送了<span class="number">{{send_msg_num}}</span>条消息<br>收到了<span class="number">{{receive_msg_num}}</span>条消息<br>总计<span class="number">{{total_text_num}}</span></p>
<div class="chart-container"> <p><span class="number">{{man_contact_num}}</span>人 女<span class="number">{{woman_contact_num}}</span></p>
<div class="slide" data-anchor="slide1">
<div id="contact_region_map" class="chart" ></div>
</div>
<div class="slide" data-anchor="slide1">
<div id="types-chart" class="chart"></div> <div id="types-chart" class="chart"></div>
</div>
<div class="slide" data-anchor="slide1">
<div id="sender-chart" class="chart" ></div> <div id="sender-chart" class="chart" ></div>
</div> </div>
</div> </div>
<div class="section cover"> <div class="section cover">
<h1>年度关键词</h1> <h1>年度关键词</h1>
@ -390,10 +406,17 @@ p {
document.getElementById('types-chart'), 'white', {renderer: 'canvas'}); document.getElementById('types-chart'), 'white', {renderer: 'canvas'});
var result = {{chart_data_types|safe}}; var result = {{chart_data_types|safe}};
chart_51ebd4312946429e9c32b2b55b96a479.setOption(result); chart_51ebd4312946429e9c32b2b55b96a479.setOption(result);
var chart_51ebd4312946429e9c32b2b55b96a479 = echarts.init(
document.getElementById('contact_region_map'), 'white', {renderer: 'canvas'});
var result = {{contact_region_map|safe}};
chart_51ebd4312946429e9c32b2b55b96a479.setOption(result);
var chart_51ebd4312946429e9c32b2b55b96a4 = echarts.init( var chart_51ebd4312946429e9c32b2b55b96a4 = echarts.init(
document.getElementById('sender-chart'), 'white', {renderer: 'canvas'}); document.getElementById('sender-chart'), 'white', {renderer: 'canvas'});
var result = {{chart_data_sender|safe}}; var result = {{chart_data_sender|safe}};
chart_51ebd4312946429e9c32b2b55b96a4.setOption(result); chart_51ebd4312946429e9c32b2b55b96a4.setOption(result);
var chart_51ebd4312946429e9c32b2b55b96 = echarts.init( var chart_51ebd4312946429e9c32b2b55b96 = echarts.init(
document.getElementById('word_cloud'), 'white', {renderer: 'canvas'}); document.getElementById('word_cloud'), 'white', {renderer: 'canvas'});
var result = {{chart_data_wordcloud|safe}}; var result = {{chart_data_wordcloud|safe}};

View File

@ -7,9 +7,11 @@ from flask import Flask, render_template, send_file, jsonify, make_response, req
from pyecharts.charts import Bar from pyecharts.charts import Bar
from app.DataBase import msg_db, micro_msg_db from app.DataBase import msg_db, micro_msg_db
from app.DataBase.hard_link import decodeExtraBuf
from app.analysis import analysis from app.analysis import analysis
from app.person import Contact, Me, ContactDefault from app.person import Contact, Me, ContactDefault
from app.util.emoji import get_most_emoji from app.util.emoji import get_most_emoji
from app.util.region_conversion import conversion_region_to_chinese
app = Flask(__name__) app = Flask(__name__)
@ -23,10 +25,12 @@ html: str = ''
api_url = 'http://api.lc044.love/upload' api_url = 'http://api.lc044.love/upload'
def get_contact(wxid): def get_contact(wxid) -> ContactDefault | Contact:
contact_info_list = micro_msg_db.get_contact_by_username(wxid) contact_info_list = micro_msg_db.get_contact_by_username(wxid)
if not contact_info_list: if not contact_info_list:
return ContactDefault('') return ContactDefault('')
detail = decodeExtraBuf(contact_info_list[9])
# detail = {}
contact_info = { contact_info = {
'UserName': contact_info_list[0], 'UserName': contact_info_list[0],
'Alias': contact_info_list[1], 'Alias': contact_info_list[1],
@ -35,31 +39,44 @@ def get_contact(wxid):
'NickName': contact_info_list[4], 'NickName': contact_info_list[4],
'smallHeadImgUrl': contact_info_list[7], 'smallHeadImgUrl': contact_info_list[7],
'label_name': contact_info_list[10], 'label_name': contact_info_list[10],
'detail': detail,
} }
contact = Contact(contact_info) contact =Contact(contact_info)
print(detail)
# region = contact.detail.get('region')
# area = conversion_region_to_chinese(region)
# print(area)
return contact return contact
@app.route("/") @app.route("/")
def index(): def index():
contact_topN_num = msg_db.get_chatted_top_contacts(time_range=time_range, top_n=9999999,contain_chatroom=True) contact_topN_num = msg_db.get_chatted_top_contacts(time_range=time_range, top_n=9999999, contain_chatroom=True)
total_msg_num = sum(list(map(lambda x:x[1],contact_topN_num))) total_msg_num = sum(list(map(lambda x: x[1], contact_topN_num)))
contact_topN = []
for wxid, num in contact_topN_num:
contact = get_contact(wxid)
text_length = 0
contact_topN.append([contact, num, text_length])
contacts_data = analysis.contacts_analysis(contact_topN)
contact_topN = [] contact_topN = []
send_msg_num = msg_db.get_send_messages_number_sum(time_range) send_msg_num = msg_db.get_send_messages_number_sum(time_range)
contact_topN_num = msg_db.get_chatted_top_contacts(time_range=time_range, top_n=9999999, contain_chatroom=False) contact_topN_num = msg_db.get_chatted_top_contacts(time_range=time_range, top_n=9999999, contain_chatroom=False)
for wxid, num in contact_topN_num[:6]: for wxid, num in contact_topN_num[:6]:
contact = get_contact(wxid) contact = get_contact(wxid)
text_length = msg_db.get_message_length(wxid,time_range) text_length = msg_db.get_message_length(wxid, time_range)
contact_topN.append([contact, num,text_length]) contact_topN.append([contact, num, text_length])
my_message_counter_data = analysis.my_message_counter(time_range=time_range) my_message_counter_data = analysis.my_message_counter(time_range=time_range)
data = { data = {
'avatar': Me().smallHeadImgUrl, 'avatar': Me().smallHeadImgUrl,
'contact_topN': contact_topN, 'contact_topN': contact_topN,
'contact_num':len(contact_topN_num), 'contact_num': len(contact_topN_num),
'send_msg_num':send_msg_num , 'send_msg_num': send_msg_num,
'receive_msg_num':total_msg_num-send_msg_num, 'receive_msg_num': total_msg_num - send_msg_num,
} }
return render_template('index.html', **data,**my_message_counter_data) return render_template('index.html', **data,**contacts_data, **my_message_counter_data)
@app.route("/christmas/<wxid>") @app.route("/christmas/<wxid>")