두번째커밋

This commit is contained in:
R5600U_PC 2024-05-09 16:13:15 +09:00
parent cc08c1135c
commit 3b4e61f451
17 changed files with 345 additions and 0 deletions

Binary file not shown.

View File

View File

View File

View File

22
main.py
View File

@ -0,0 +1,22 @@
import sys
from PyQt5.QtWidgets import QApplication
from src.chat_ui import MainWindow
from src.database import *
def main():
mongo_config = MongoConfig() # MongoConfig 인스턴스 생성
mongo_uri = mongo_config.get_mongo_uri() # MongoDB URI 가져오기
db_manager = DatabaseManager(mongo_uri) # URI 전달
if db_manager.connect():
print("Database connected successfully.")
else:
print("Failed to connect to the database.")
app = QApplication(sys.argv)
mainWindow = MainWindow(db_manager) # db_manager 전달
mainWindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

53
src/chat_parser.py Normal file
View File

@ -0,0 +1,53 @@
import re
from datetime import datetime
# import locale
# 로케일을 한국어로 설정
# locale.setlocale(locale.LC_TIME, 'ko_KR.utf8')
def parse_chat_log(contents):
# 날짜에서 요일 정보를 제외하고 파싱
date_pattern = re.compile(r"--------------- (\d{4}\d{1,2}월 \d{1,2}일) [가-힣]+ ---------------")
dialogue_pattern = re.compile(r"\[(.*?)\] \[(.*?)\] (.*)")
chatroom_name_pattern = re.compile(r"^(.*?) 님과 카카오톡 대화")
lines = contents.split('\n')
chatroom_name_match = chatroom_name_pattern.match(lines[0])
chatroom_name = chatroom_name_match.group(1) if chatroom_name_match else "Unknown Chatroom"
parsed_data = []
current_date = None
for line in lines[3:]:
if date_match := date_pattern.match(line):
# 날짜 파싱 및 datetime 객체 생성
# current_date = datetime.strptime(date_match.group(1), "%Y년 %m월 %d일 %A")
current_date_str = date_match.group(1)
current_date = datetime.strptime(current_date_str, "%Y년 %m월 %d")
continue
if dialogue_match := dialogue_pattern.match(line):
user, time_str, message = dialogue_match.groups()
time_str = time_str.replace("오전", "AM").replace("오후", "PM")
# 시간 파싱 및 datetime 객체에 시간 추가
time = datetime.strptime(time_str, "%p %I:%M").time()
full_datetime = datetime.combine(current_date, time)
parsed_data.append({
'chatroom_name': chatroom_name,
'datetime': {
'year': full_datetime.year,
'month': full_datetime.month,
'day': full_datetime.day,
'weekday': full_datetime.strftime('%A'),
'hour': full_datetime.hour,
'minute': full_datetime.minute
},
'user': user,
'message': message
})
elif line.strip() and not line.startswith('['): # 연속된 메시지 처리
parsed_data[-1]['message'] += "\n" + line # 마지막 메시지에 추가
return parsed_data

112
src/chat_ui.py Normal file
View File

@ -0,0 +1,112 @@
from PyQt5.QtWidgets import *
from src.chat_parser import *
from src.database import *
from PyQt5.QtCore import QDate
class MainWindow(QMainWindow):
def __init__(self, db_manager):
super().__init__()
self.db_manager = db_manager # 데이터베이스 매니저 인스턴스 저장
self.initUI()
def initUI(self):
self.setWindowTitle('KakaoTalk Chat Parser')
self.setGeometry(300, 300, 800, 600)
layout = QVBoxLayout()
# 캘린더 및 날짜 선택
self.startDateCalendar = QCalendarWidget()
self.startDateCalendar.setSelectedDate(QDate(2024, 1, 1))
self.endDateCalendar = QCalendarWidget()
self.endDateCalendar.setSelectedDate(QDate.currentDate())
# 채팅 데이터 가져오기 버튼
self.loadChatDataButton = QPushButton('채팅 데이터 가져오기')
self.loadChatDataButton.clicked.connect(self.load_chat_data)
# 채팅 파일 입력 버튼
self.loadChatFileButton = QPushButton('채팅 파일 입력')
self.loadChatFileButton.clicked.connect(self.load_chat_log)
# 사용자 선택 콤보 박스
self.userComboBox = QComboBox()
self.userComboBox.addItem("전체 유저 선택")
# 검색 필드
self.searchField = QLineEdit()
self.searchButton = QPushButton('검색')
self.searchButton.clicked.connect(self.search_chat_data)
# 배치
calendarLayout = QHBoxLayout()
calendarLayout.addWidget(self.startDateCalendar)
calendarLayout.addWidget(self.endDateCalendar)
layout.addLayout(calendarLayout)
layout.addWidget(self.loadChatDataButton)
layout.addWidget(self.userComboBox)
layout.addWidget(self.searchField)
layout.addWidget(self.searchButton)
layout.addWidget(self.loadChatFileButton)
self.textEdit = QTextEdit()
layout.addWidget(self.textEdit)
central_widget = QWidget()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
def load_chat_data(self):
start_date = self.startDateCalendar.selectedDate().toPyDate()
end_date = self.endDateCalendar.selectedDate().toPyDate()
collection_name = "YourCollectionNameHere" # 적절한 컬렉션 이름 사용
documents = self.db_manager.get_documents_by_date_range(collection_name, start_date, end_date)
self.textEdit.clear()
for doc in documents:
self.textEdit.append(f"{doc['datetime']} - {doc['user']}: {doc['message']}")
self.textEdit.append("Chat logs loaded successfully from the database.")
def search_chat_data(self):
text = self.searchField.text()
collection_name = "YourCollectionNameHere" # 적절한 컬렉션 이름 사용
if text.strip():
documents = self.db_manager.search_documents_by_text(collection_name, text)
self.textEdit.clear()
for doc in documents:
self.textEdit.append(f"{doc['datetime']} - {doc['user']}: {doc['message']}")
self.textEdit.append(f"Search results for '{text}':")
else:
self.textEdit.append("Please enter text to search.")
def load_chat_log(self):
options = QFileDialog.Options()
filename, _ = QFileDialog.getOpenFileName(self, "채팅파일 입력", "", "Text Files (*.txt);;All Files (*)", options=options)
if filename:
try:
with open(filename, 'r', encoding='utf-8') as file:
chat_log_contents = file.read()
# Parse the chat log contents
parsed_data = parse_chat_log(chat_log_contents)
if self.db_manager.db is not None: # 데이터베이스 연결 확인
for data in parsed_data:
chatroom_name = data.get('chatroom_name')
if self.db_manager.insert_if_not_exists(chatroom_name, data):
self.textEdit.append(f"{data.get('user')} - {data.get('message')}.")
else:
self.textEdit.append(f"Failed to insert data for chatroom {chatroom_name}.")
self.textEdit.append("All chat logs loaded and parsed successfully.")
else:
self.textEdit.append("Database connection not established.")
except Exception as e:
self.textEdit.append(f"An error occurred: {e}")

3
src/config.ini Normal file
View File

@ -0,0 +1,3 @@
[MongoDB]
uri=mongodb://root:1234@cckb9998.synology.me:27017/
db_name=KakaoChatData

126
src/database.py Normal file
View File

@ -0,0 +1,126 @@
from pymongo import MongoClient
from configparser import ConfigParser
import os
import sys
import logging
# 로깅 설정
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('default_logger')
class MongoConfig:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(MongoConfig, cls).__new__(cls)
cls._instance.init_config()
return cls._instance
def get_mongo_uri(self):
# MongoDB URI를 반환하는 메서드
return self.mongo_uri
def get_config_path(self):
if getattr(sys, 'frozen', False):
application_path = sys._MEIPASS
else:
application_path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(application_path, 'config.ini')
def init_config(self):
config_path = self.get_config_path()
self.config = ConfigParser()
self.config.read(config_path)
logger.debug(f"Config loaded from {config_path}")
self.client = None
self.load_config()
def load_config(self):
if not self.config.has_section('MongoDB'):
logger.error('Config section for MongoDB is missing.')
raise Exception('Config section for MongoDB is missing.')
try:
self.mongo_uri = self.config.get('MongoDB', 'uri')
self.db_name = self.config.get('MongoDB', 'db_name')
except Exception as e:
logger.error(f"Missing MongoDB configuration option: {e}")
raise Exception(f"Missing configuration option: {e}")
def try_connect(self):
try:
self.client = MongoClient(self.mongo_uri)
self.db = self.client[self.db_name]
logger.debug("MongoDB connection established.")
return True
except Exception as e:
logger.error(f"Failed to connect to MongoDB: {e}")
return False
class DatabaseManager:
def __init__(self, mongo_uri):
self.mongo_uri = mongo_uri
self.client = None
self.db = None
self.connect()
def connect(self):
try:
self.client = MongoClient(self.mongo_uri)
self.db = self.client['KakaoChatData'] # 고정된 데이터베이스 이름
return True
except Exception as e:
print(f"Failed to connect to MongoDB: {e}")
return False
def insert_documents(self, collection_name, documents):
if self.db is not None: # 수정된 조건문
collection = self.db[collection_name]
collection.insert_many(documents)
print(f"Inserted documents into {collection_name}")
return True
print("Database not connected.")
return False
def insert_if_not_exists(self, collection_name, document):
query = {
'datetime': document['datetime'],
'user': document['user'],
'message': document['message'] # 대화 내용
}
update = {
'$setOnInsert': document
}
collection = self.db[collection_name] # 채팅방 이름을 컬렉션 이름으로 사용
result = collection.update_one(query, update, upsert=True)
if result.upserted_id is not None:
print(f"Inserted new document with ID: {result.upserted_id}")
else:
print("Document already exists, no insertion made.")
def query_documents(self, collection_name, query):
if self.db is not None: # 수정된 조건문
collection = self.db[collection_name]
results = list(collection.find(query))
return results
print("Database not connected.")
return []
def get_documents_by_date_range(self, collection_name, start_date, end_date):
query = {
'datetime': {
'$gte': start_date,
'$lte': end_date
}
}
return list(self.db[collection_name].find(query))
def search_documents_by_text(self, collection_name, text):
query = {
'message': {'$regex': text, '$options': 'i'} # 대소문자 구분 없이 검색
}
return list(self.db[collection_name].find(query))

View File

@ -0,0 +1,16 @@
import pytest
from resources.chat_parser import parse_chat_log
def test_parse_chat_log():
test_input = """[Test Room]
--------------- 2024 5 9 목요일 ---------------
[User1] [09:00] 안녕하세요
[User2] [09:01] 반갑습니다"""
expected_output = [
{'date': '2024-05-09', 'user': 'User1', 'time': '09:00', 'message': '안녕하세요'},
{'date': '2024-05-09', 'user': 'User2', 'time': '09:01', 'message': '반갑습니다'}
]
assert parse_chat_log(test_input) == expected_output
def test_empty_input():
assert parse_chat_log("") == []

13
tests/test_database.py Normal file
View File

@ -0,0 +1,13 @@
from src.database import DatabaseManager
def test_insert_chat_data(mocker):
db_manager = DatabaseManager()
mocker.patch.object(db_manager, 'insert_chat_data')
db_manager.insert_chat_data("chatroom1", [{"message": "test"}])
db_manager.insert_chat_data.assert_called_once()
def test_get_messages_by_user(mocker):
db_manager = DatabaseManager()
mocker.patch.object(db_manager.db.chatrooms, 'find')
db_manager.get_messages_by_user("chatroom1", "User1")
db_manager.db.chatrooms.find.assert_called_with({"_id": "chatroom1", "messages.user": "User1"}, {"messages.$": 1})

View File