支持自定义选择导出聊天记录的日期

This commit is contained in:
shuaikangzhou 2024-01-09 23:55:39 +08:00
parent 63a03858d2
commit fe4e719012
12 changed files with 264 additions and 53 deletions

View File

@ -7,6 +7,7 @@ body:
attributes:
label: '👌 是否检查过没有类似issue'
options:
- 就不检查
-
-
validations:

View File

@ -13,7 +13,7 @@ class CSVExporter(ExporterBase):
columns = ['localId', 'TalkerId', 'Type', 'SubType',
'IsSender', 'CreateTime', 'Status', 'StrContent',
'StrTime', 'Remark', 'NickName', 'Sender']
messages = msg_db.get_messages(self.contact.wxid)
messages = msg_db.get_messages(self.contact.wxid, time_range=self.time_range)
# 写入CSV文件
with open(filename, mode='w', newline='', encoding='utf-8-sig') as file:
writer = csv.writer(file)

View File

@ -288,7 +288,7 @@ class DocxExporter(ExporterBase):
doc = docx.Document()
doc.styles['Normal'].font.name = u'Cambria'
doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
messages = msg_db.get_messages(self.contact.wxid)
messages = msg_db.get_messages(self.contact.wxid, time_range=self.time_range)
Me().save_avatar(os.path.join(f"{origin_docx_path}/avatar/{Me().wxid}.png"))
if self.contact.is_chatroom:
for message in messages:

View File

@ -275,7 +275,7 @@ class HtmlExporter(ExporterBase):
)
def export(self):
messages = msg_db.get_messages(self.contact.wxid)
messages = msg_db.get_messages(self.contact.wxid, time_range=self.time_range)
filename = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}/{self.contact.remark}.html"
file_path = './app/resources/data/template.html'
if not os.path.exists(file_path):

View File

@ -113,7 +113,7 @@ class TxtExporter(ExporterBase):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
os.makedirs(origin_docx_path, exist_ok=True)
filename = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}/{self.contact.remark}.txt"
messages = msg_db.get_messages(self.contact.wxid)
messages = msg_db.get_messages(self.contact.wxid, time_range=self.time_range)
total_steps = len(messages)
with open(filename, mode='w', newline='', encoding='utf-8') as f:
for index, message in enumerate(messages):

View File

@ -139,7 +139,7 @@ class Msg:
new_messages.append(new_message)
return new_messages
def get_messages(self, username_):
def get_messages(self, username_, time_range=None):
"""
return list
a[0]: localId,
@ -157,10 +157,13 @@ class Msg:
"""
if not self.open_flag:
return None
sql = '''
if time_range:
start_time, end_time = time_range
sql = f'''
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent
from MSG
where StrTalker=?
{'AND CreateTime>' + str(start_time) + ' AND CreateTime<' + str(end_time) if time_range else ''}
order by CreateTime
'''
try:
@ -230,14 +233,17 @@ class Msg:
# result.sort(key=lambda x: x[5])
return parser_chatroom_message(result) if username_.__contains__('@chatroom') else result
def get_messages_by_type(self, username_, type_, year_='all'):
def get_messages_by_type(self, username_, type_, year_='all',time_range=None):
if not self.open_flag:
return None
if time_range:
start_time, end_time = time_range
if year_ == 'all':
sql = '''
sql = f'''
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent
from MSG
where StrTalker=? and Type=?
{'AND CreateTime>' + str(start_time) + ' AND CreateTime<' + str(end_time) if time_range else ''}
order by CreateTime
'''
try:

View File

@ -3,29 +3,12 @@ import html
import os
import shutil
import sys
import time
import traceback
import filecmp
from re import findall
import docx
from PyQt5.QtCore import pyqtSignal, QThread
from PyQt5.QtWidgets import QFileDialog
from docx import shared
from docx.enum.table import WD_ALIGN_VERTICAL
from docx.enum.text import WD_COLOR_INDEX, WD_PARAGRAPH_ALIGNMENT
from docx.oxml.ns import qn
from .package_msg import PackageMsg
from ..DataBase import media_msg_db, hard_link_db, micro_msg_db, msg_db
from ..log import logger
from ..person import Me, Contact
from ..util import path
from ..util.compress_content import parser_reply, music_share, share_card
from ..util.emoji import get_emoji_url
from ..util.file import get_file
from ..util.music import get_music_path
from ..util.image import get_image_path, get_image, get_image_abs_path
os.makedirs('./data/聊天记录', exist_ok=True)
@ -107,7 +90,7 @@ class ExporterBase(QThread):
CONTACT_CSV = 4
TXT = 5
def __init__(self, contact, type_=DOCX, message_types={}, parent=None):
def __init__(self, contact, type_=DOCX, message_types={},time_range=None, parent=None):
super().__init__(parent)
self.message_types = message_types # 导出的消息类型
self.contact: Contact = contact # 联系人
@ -115,6 +98,7 @@ class ExporterBase(QThread):
self.total_num = 1 # 总的消息数量
self.num = 0 # 当前处理的消息数量
self.last_timestamp = 0
self.time_range = time_range
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
makedirs(origin_docx_path)
def run(self):

View File

@ -39,11 +39,12 @@ class Output(QThread):
TXT = 5
Batch = 10086
def __init__(self, contact, type_=DOCX, message_types={}, sub_type=[], parent=None):
def __init__(self, contact, type_=DOCX, message_types={}, sub_type=[], time_range=None,parent=None):
super().__init__(parent)
self.children = []
self.last_timestamp = 0
self.sub_type = sub_type
self.time_range = time_range
self.message_types = message_types
self.sec = 2 # 默认1000秒
self.contact = contact
@ -161,7 +162,7 @@ class Output(QThread):
self.okSignal.emit(1)
def to_docx(self, contact, message_types, is_batch=False):
Child = DocxExporter(contact, type_=self.DOCX, message_types=message_types)
Child = DocxExporter(contact, type_=self.DOCX, message_types=message_types,time_range=self.time_range)
self.children.append(Child)
Child.progressSignal.connect(self.progress)
if not is_batch:
@ -170,7 +171,7 @@ class Output(QThread):
Child.start()
def to_txt(self, contact, message_types, is_batch=False):
Child = TxtExporter(contact, type_=self.TXT, message_types=message_types)
Child = TxtExporter(contact, type_=self.TXT, message_types=message_types,time_range=self.time_range)
self.children.append(Child)
Child.progressSignal.connect(self.progress)
if not is_batch:
@ -179,7 +180,7 @@ class Output(QThread):
Child.start()
def to_html(self, contact, message_types, is_batch=False):
Child = HtmlExporter(contact, type_=self.output_type, message_types=message_types)
Child = HtmlExporter(contact, type_=self.output_type, message_types=message_types,time_range=self.time_range)
self.children.append(Child)
Child.progressSignal.connect(self.progress)
if not is_batch:
@ -190,7 +191,7 @@ class Output(QThread):
if message_types.get(34):
# 语音消息单独的线程
self.total_num += 1
output_media = OutputMedia(contact)
output_media = OutputMedia(contact,time_range=self.time_range)
self.children.append(output_media)
output_media.okSingal.connect(self.count_finish_num)
output_media.progressSignal.connect(self.progressSignal)
@ -198,7 +199,7 @@ class Output(QThread):
if message_types.get(47):
# emoji消息单独的线程
self.total_num += 1
output_emoji = OutputEmoji(contact)
output_emoji = OutputEmoji(contact,time_range=self.time_range)
self.children.append(output_emoji)
output_emoji.okSingal.connect(self.count_finish_num)
output_emoji.progressSignal.connect(self.progressSignal)
@ -206,14 +207,14 @@ class Output(QThread):
if message_types.get(3):
# 图片消息单独的线程
self.total_num += 1
output_image = OutputImage(contact)
output_image = OutputImage(contact,time_range=self.time_range)
self.children.append(output_image)
output_image.okSingal.connect(self.count_finish_num)
output_image.progressSignal.connect(self.progressSignal)
output_image.start()
def to_csv(self, contact, message_types, is_batch=False):
Child = CSVExporter(contact, type_=self.CSV, message_types=message_types)
Child = CSVExporter(contact, type_=self.CSV, message_types=message_types,time_range=self.time_range)
self.children.append(Child)
Child.progressSignal.connect(self.progress)
if not is_batch:
@ -263,13 +264,14 @@ class OutputMedia(QThread):
okSingal = pyqtSignal(int)
progressSignal = pyqtSignal(int)
def __init__(self, contact):
def __init__(self, contact,time_range=None):
super().__init__()
self.contact = contact
self.time_range = time_range
def run(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
messages = msg_db.get_messages_by_type(self.contact.wxid, 34)
messages = msg_db.get_messages_by_type(self.contact.wxid, 34,time_range=self.time_range)
for message in messages:
is_send = message[4]
msgSvrId = message[9]
@ -289,13 +291,14 @@ class OutputEmoji(QThread):
okSingal = pyqtSignal(int)
progressSignal = pyqtSignal(int)
def __init__(self, contact):
def __init__(self, contact,time_range=None):
super().__init__()
self.contact = contact
self.time_range = time_range
def run(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
messages = msg_db.get_messages_by_type(self.contact.wxid, 47)
messages = msg_db.get_messages_by_type(self.contact.wxid, 47,time_range=self.time_range)
for message in messages:
str_content = message[7]
try:
@ -315,10 +318,11 @@ class OutputImage(QThread):
okSingal = pyqtSignal(int)
progressSignal = pyqtSignal(int)
def __init__(self, contact):
def __init__(self, contact,time_range):
super().__init__()
self.contact = contact
self.child_thread_num = 2
self.time_range =time_range
self.child_threads = [0] * (self.child_thread_num + 1)
self.num = 0
@ -331,7 +335,7 @@ class OutputImage(QThread):
def run(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
messages = msg_db.get_messages_by_type(self.contact.wxid, 3)
messages = msg_db.get_messages_by_type(self.contact.wxid, 3,time_range=self.time_range)
for message in messages:
str_content = message[7]
BytesExtra = message[10]
@ -359,10 +363,11 @@ class OutputImageChild(QThread):
okSingal = pyqtSignal(int)
progressSignal = pyqtSignal(int)
def __init__(self, contact, messages):
def __init__(self, contact, messages,time_range):
super().__init__()
self.contact = contact
self.messages = messages
self.time_range = time_range
def run(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"

View File

@ -0,0 +1,46 @@
import time
from PyQt5 import QtWidgets
from PyQt5.QtCore import QTimer, QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QDialog, QCheckBox, QMessageBox, QCalendarWidget, QWidget, QVBoxLayout
class CalendarDialog(QDialog):
selected_date_signal = pyqtSignal(int)
def __init__(self, date_range=None, parent=None):
"""
@param date_range: tuple[Union[QDate, datetime.date],Union[QDate, datetime.date]]
@param parent:
"""
super().__init__(parent)
self.calendar = QCalendarWidget(self)
self.calendar.clicked.connect(self.onDateChanged)
if date_range:
self.calendar.setDateRange(*date_range)
layout = QVBoxLayout(self)
layout.addWidget(self.calendar)
self.setLayout(layout)
def onDateChanged(self):
# 获取选择的日期
selected_date = self.calendar.selectedDate()
s_t = time.strptime(selected_date.toString("yyyy-MM-dd"), "%Y-%m-%d") # 返回元祖
mkt = int(time.mktime(s_t))
timestamp = mkt
self.selected_date_signal.emit(timestamp)
print("Selected Date:", selected_date.toString("yyyy-MM-dd"),timestamp)
self.close()
if __name__ == '__main__':
import sys
from datetime import datetime
app = QApplication(sys.argv)
# 设置日期范围
start_date = datetime(2023, 12, 11)
end_date = datetime(2024, 1, 9)
date_range = (start_date.date(),end_date.date())
ex = CalendarDialog(date_range=date_range)
ex.show()
sys.exit(app.exec_())

View File

@ -3,14 +3,16 @@ from typing import List
from PyQt5 import QtWidgets
from PyQt5.QtCore import QTimer, QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QDialog, QCheckBox, QMessageBox
from PyQt5.QtWidgets import QApplication, QDialog, QCheckBox, QMessageBox, QCalendarWidget
from app.DataBase import micro_msg_db, misc_db
from app.DataBase.output_pc import Output
from app.components import ScrollBar
from app.components.calendar_dialog import CalendarDialog
from app.components.export_contact_item import ContactQListWidgetItem
from app.person import Contact
from app.ui.menu.exportUi import Ui_Dialog
from app.ui.menu.export_time_range import TimeRangeDialog
types = {
'文本': 1,
@ -77,22 +79,46 @@ class ExportDialog(QDialog, Ui_Dialog):
self.listWidget.itemClicked.connect(self.setCurrentIndex)
self.visited = set()
self.now_index = 0
self.time_range = None
def set_export_date(self):
date_range = self.comboBox_time.currentText()
if date_range == '全部时间':
pass
elif date_range == '最近三个月':
QMessageBox.warning(self,
"别急别急",
"马上就实现该功能"
)
elif date_range == '自定义时间':
QMessageBox.warning(self,
"别急别急",
"马上就实现该功能"
)
from datetime import datetime, timedelta
# 获取今天的日期和时间
today = datetime.now()
# 获取今天的日期
today_date = today.date()
# 获取今天的24:00:00的时间戳
today_midnight = datetime.combine(today_date, datetime.min.time()) + timedelta(days=1)
today_midnight_timestamp = int(today_midnight.timestamp())
# 获取三个月前的日期
three_months_ago = today - timedelta(days=90)
# 获取三个月前的00:00:00的时间戳
three_months_ago_date = three_months_ago.date()
three_months_ago_midnight = datetime.combine(three_months_ago_date, datetime.min.time())
three_months_ago_midnight_timestamp = int(three_months_ago_midnight.timestamp())
self.time_range = (three_months_ago_midnight_timestamp,today_midnight_timestamp)
elif date_range == '自定义时间':
self.time_range_view = TimeRangeDialog(parent=self)
self.time_range_view.date_range_signal.connect(self.set_time_range)
self.time_range_view.show()
self.comboBox_time.setCurrentIndex(0)
# QMessageBox.warning(self,
# "别急别急",
# "马上就实现该功能"
# )
def set_time_range(self,time_range):
self.time_range = time_range
self.comboBox_time.setCurrentIndex(2)
def export_data(self):
self.btn_start.setEnabled(False)
# 在这里获取用户选择的导出数据类型
@ -115,7 +141,7 @@ class ExportDialog(QDialog, Ui_Dialog):
select_contacts.append(self.contacts[i])
# 在这里根据用户选择的数据类型执行导出操作
print("选择的文件格式:", file_types)
self.worker = Output(select_contacts, type_=Output.Batch, message_types=selected_types, sub_type=file_types)
self.worker = Output(select_contacts, type_=Output.Batch, message_types=selected_types, sub_type=file_types,time_range=self.time_range)
# self.worker.progressSignal.connect(self.update_progress)
self.worker.okSignal.connect(self.export_finished)
self.worker.rangeSignal.connect(self.set_total_msg_num)

View File

@ -0,0 +1,72 @@
from datetime import datetime
from PyQt5 import QtWidgets
from PyQt5.QtCore import QTimer, QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QDialog, QCheckBox, QMessageBox, QCalendarWidget, QWidget, QVBoxLayout, QLabel
from app.components.calendar_dialog import CalendarDialog
from .time_range import Ui_Dialog
Stylesheet = '''
QToolButton{
color:#000000;
}
'''
class TimeRangeDialog(QDialog, Ui_Dialog):
date_range_signal = pyqtSignal(tuple)
def __init__(self, date_range=None, parent=None):
"""
@param date_range: tuple[Union[QDate, datetime.date],Union[QDate, datetime.date]]
@param parent:
"""
super().__init__(parent)
self.calendar = None
self.setupUi(self)
self.setStyleSheet(Stylesheet)
self.toolButton_start_time.clicked.connect(self.select_date_start)
self.toolButton_end_time.clicked.connect(self.select_date_end)
self.calendar = CalendarDialog(date_range=date_range, parent=self)
self.calendar.selected_date_signal.connect(self.set_date)
self.btn_ok.clicked.connect(self.ok)
self.btn_cancel.clicked.connect(lambda x:self.close())
self.start_time_flag = True
self.start_timestamp = 0
self.end_timestamp = 0
def set_date(self, timestamp):
if self.start_time_flag:
self.start_timestamp = timestamp
date_object = datetime.fromtimestamp(timestamp)
str_start =date_object.strftime("%Y-%m-%d")
self.toolButton_start_time.setText(str_start)
else:
date_object = datetime.fromtimestamp(timestamp)
str_start = date_object.strftime("%Y-%m-%d")
self.end_timestamp = timestamp + 86399
self.toolButton_end_time.setText(str_start)
def ok(self):
date_range = (self.start_timestamp,self.end_timestamp)
self.date_range_signal.emit(date_range)
self.close()
def select_date_start(self):
self.start_time_flag = True
self.calendar.show()
def select_date_end(self):
self.start_time_flag = False
self.calendar.show()
if __name__ == '__main__':
import sys
from datetime import datetime
app = QApplication(sys.argv)
# 设置日期范围
start_date = datetime(2023, 12, 11)
end_date = datetime(2024, 1, 9)
date_range = (start_date.date(), end_date.date())
ex = CalendarDialog(date_range=date_range)
ex.show()
sys.exit(app.exec_())

71
app/ui/menu/time_range.py Normal file
View File

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'time_range.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(280, 200)
Dialog.setMinimumSize(QtCore.QSize(280, 200))
Dialog.setMaximumSize(QtCore.QSize(280, 200))
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout")
self.label = QtWidgets.QLabel(Dialog)
self.label.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
self.verticalLayout.addWidget(self.label)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_2 = QtWidgets.QLabel(Dialog)
self.label_2.setObjectName("label_2")
self.horizontalLayout_2.addWidget(self.label_2)
self.toolButton_start_time = QtWidgets.QToolButton(Dialog)
self.toolButton_start_time.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.toolButton_start_time.setArrowType(QtCore.Qt.DownArrow)
self.toolButton_start_time.setObjectName("toolButton_start_time")
self.horizontalLayout_2.addWidget(self.toolButton_start_time)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.label_3 = QtWidgets.QLabel(Dialog)
self.label_3.setObjectName("label_3")
self.horizontalLayout.addWidget(self.label_3)
self.toolButton_end_time = QtWidgets.QToolButton(Dialog)
self.toolButton_end_time.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.toolButton_end_time.setArrowType(QtCore.Qt.DownArrow)
self.toolButton_end_time.setObjectName("toolButton_end_time")
self.horizontalLayout.addWidget(self.toolButton_end_time)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.btn_ok = QtWidgets.QPushButton(Dialog)
self.btn_ok.setObjectName("btn_ok")
self.horizontalLayout_3.addWidget(self.btn_ok)
self.btn_cancel = QtWidgets.QPushButton(Dialog)
self.btn_cancel.setObjectName("btn_cancel")
self.horizontalLayout_3.addWidget(self.btn_cancel)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.label.setText(_translate("Dialog", "自定义时间"))
self.label_2.setText(_translate("Dialog", "开始日期"))
self.toolButton_start_time.setText(_translate("Dialog", "请选择时间"))
self.label_3.setText(_translate("Dialog", "结束日期"))
self.toolButton_end_time.setText(_translate("Dialog", "请选择时间"))
self.btn_ok.setText(_translate("Dialog", "确定"))
self.btn_cancel.setText(_translate("Dialog", "取消"))