refactor: вынес сервисы и ui-компоненты
- вынес token/chat/update логику в services - вынес диалог и текст инструкции в ui - добавил и обновил тесты для нового слоя
This commit is contained in:
172
main.py
172
main.py
@@ -14,7 +14,18 @@ import tempfile
|
||||
import urllib.request
|
||||
import zipfile
|
||||
from app_version import APP_VERSION
|
||||
from services import UpdateChecker, VkService, detect_update_repository_url
|
||||
from services import (
|
||||
AutoUpdateService,
|
||||
UpdateChecker,
|
||||
VkService,
|
||||
detect_update_repository_url,
|
||||
load_chat_conversations,
|
||||
load_token as token_store_load_token,
|
||||
resolve_user_ids as chat_resolve_user_ids,
|
||||
save_token as token_store_save_token,
|
||||
)
|
||||
from ui.dialogs import MultiLinkDialog as UIMultiLinkDialog
|
||||
from ui.main_window import instructions_text
|
||||
from PySide6.QtWidgets import (QApplication, QMainWindow, QLabel, QLineEdit,
|
||||
QPushButton, QVBoxLayout, QWidget, QMessageBox,
|
||||
QTextBrowser, QScrollArea, QCheckBox, QHBoxLayout,
|
||||
@@ -22,7 +33,7 @@ from PySide6.QtWidgets import (QApplication, QMainWindow, QLabel, QLineEdit,
|
||||
QProgressBar)
|
||||
from PySide6.QtCore import Qt, QUrl, QDateTime, QTimer
|
||||
from PySide6.QtGui import QIcon, QAction, QDesktopServices
|
||||
from urllib.parse import urlparse, parse_qs, unquote
|
||||
from urllib.parse import parse_qs, unquote
|
||||
from vk_api.exceptions import VkApiError
|
||||
from PySide6.QtCore import QStandardPaths
|
||||
from PySide6.QtCore import QProcess
|
||||
@@ -272,6 +283,7 @@ class VkChatManager(QMainWindow):
|
||||
"5. Нажмите 'ИСКЛЮЧИТЬ' или 'ПРИГЛАСИТЬ'."
|
||||
)
|
||||
self.instructions.setFixedHeight(120)
|
||||
self.instructions.setPlainText(instructions_text())
|
||||
layout.addWidget(self.instructions)
|
||||
|
||||
layout.addWidget(QLabel("Access Token VK:"))
|
||||
@@ -359,7 +371,7 @@ class VkChatManager(QMainWindow):
|
||||
self.resolve_timer.start()
|
||||
|
||||
def open_multi_link_dialog(self):
|
||||
dialog = MultiLinkDialog(self)
|
||||
dialog = UIMultiLinkDialog(self)
|
||||
if dialog.exec():
|
||||
links = dialog.get_links()
|
||||
if links:
|
||||
@@ -931,7 +943,7 @@ class VkChatManager(QMainWindow):
|
||||
raise last_error
|
||||
|
||||
def load_saved_token_on_startup(self):
|
||||
loaded_token, expiration_time = load_token()
|
||||
loaded_token, expiration_time = token_store_load_token(TOKEN_FILE)
|
||||
if loaded_token:
|
||||
self.handle_auth_token_on_load(loaded_token, expiration_time)
|
||||
else:
|
||||
@@ -1057,7 +1069,12 @@ class VkChatManager(QMainWindow):
|
||||
|
||||
self.token = token
|
||||
# Сохраняем и получаем корректный expiration_time (0 или будущее время)
|
||||
self.token_expiration_time = save_token(self.token, expires_in)
|
||||
self.token_expiration_time = token_store_save_token(
|
||||
self.token,
|
||||
TOKEN_FILE,
|
||||
APP_DATA_DIR,
|
||||
expires_in=expires_in,
|
||||
)
|
||||
|
||||
self.token_input.setText(self.token[:50] + "...")
|
||||
self.status_label.setText("Статус: авторизован")
|
||||
@@ -1432,6 +1449,151 @@ class VkChatManager(QMainWindow):
|
||||
self.user_ids_to_process.clear()
|
||||
self.set_ui_state(self.token is not None)
|
||||
|
||||
# Refactor overrides: keep logic in service modules and thin UI orchestration here.
|
||||
def _process_links_list(self, links_list):
|
||||
if not self.vk:
|
||||
QMessageBox.warning(self, "Ошибка", "Сначала авторизуйтесь.")
|
||||
return
|
||||
|
||||
self.user_ids_to_process.clear()
|
||||
resolved_ids = []
|
||||
failed_links = []
|
||||
|
||||
self._set_busy(True, "Статус: Определяю ID...")
|
||||
try:
|
||||
resolved_ids, failed_items = chat_resolve_user_ids(
|
||||
self._vk_call_with_retry,
|
||||
self.vk,
|
||||
links_list,
|
||||
)
|
||||
for failed_link, failed_exc in failed_items:
|
||||
if isinstance(failed_exc, VkApiError):
|
||||
if self._handle_vk_api_error("resolveScreenName", failed_exc, action_name="получения ID пользователей"):
|
||||
return
|
||||
failed_links.append(f"{failed_link} ({self._format_vk_error(failed_exc)})")
|
||||
else:
|
||||
failed_links.append(failed_link)
|
||||
finally:
|
||||
self._set_busy(False)
|
||||
|
||||
self.user_ids_to_process = resolved_ids
|
||||
status_message = f"Статус: Готово к работе с {len(resolved_ids)} пользователем(ем/ями)."
|
||||
if len(links_list) > 1:
|
||||
self._set_vk_url_input_text(f"Загружено {len(resolved_ids)}/{len(links_list)} из списка")
|
||||
|
||||
if failed_links:
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"Ошибка получения ID",
|
||||
"Не удалось получить ID для следующих ссылок:\n" + "\n".join(failed_links),
|
||||
)
|
||||
|
||||
self.status_label.setText(status_message)
|
||||
self.set_ui_state(self.token is not None)
|
||||
|
||||
def load_chats(self):
|
||||
self._clear_chat_tabs()
|
||||
|
||||
layouts = [
|
||||
self.office_tab.findChild(QWidget).findChild(QVBoxLayout),
|
||||
self.retail_tab.findChild(QWidget).findChild(QVBoxLayout),
|
||||
self.warehouse_tab.findChild(QWidget).findChild(QVBoxLayout),
|
||||
self.coffee_tab.findChild(QWidget).findChild(QVBoxLayout),
|
||||
self.other_tab.findChild(QWidget).findChild(QVBoxLayout),
|
||||
]
|
||||
|
||||
try:
|
||||
self._set_busy(True, "Статус: загрузка чатов...")
|
||||
conversations = load_chat_conversations(self._vk_call_with_retry, self.vk)
|
||||
for conv in conversations:
|
||||
if conv["conversation"]["peer"]["type"] != "chat":
|
||||
continue
|
||||
|
||||
chat_id = conv["conversation"]["peer"]["local_id"]
|
||||
title = conv["conversation"]["chat_settings"]["title"]
|
||||
self.chats.append({"id": chat_id, "title": title})
|
||||
checkbox = QCheckBox(f"{title} (id: {chat_id})")
|
||||
checkbox.setProperty("chat_id", chat_id)
|
||||
|
||||
if "AG офис" in title:
|
||||
layouts[0].insertWidget(layouts[0].count() - 1, checkbox)
|
||||
self.office_chat_checkboxes.append(checkbox)
|
||||
elif "AG розница" in title:
|
||||
layouts[1].insertWidget(layouts[1].count() - 1, checkbox)
|
||||
self.retail_chat_checkboxes.append(checkbox)
|
||||
elif "AG склад" in title:
|
||||
layouts[2].insertWidget(layouts[2].count() - 1, checkbox)
|
||||
self.warehouse_chat_checkboxes.append(checkbox)
|
||||
elif "AG кофейни" in title:
|
||||
layouts[3].insertWidget(layouts[3].count() - 1, checkbox)
|
||||
self.coffee_chat_checkboxes.append(checkbox)
|
||||
else:
|
||||
layouts[4].insertWidget(layouts[4].count() - 1, checkbox)
|
||||
self.other_chat_checkboxes.append(checkbox)
|
||||
|
||||
self.chat_tabs.setTabText(0, f"AG Офис ({len(self.office_chat_checkboxes)})")
|
||||
self.chat_tabs.setTabText(1, f"AG Розница ({len(self.retail_chat_checkboxes)})")
|
||||
self.chat_tabs.setTabText(2, f"AG Склад ({len(self.warehouse_chat_checkboxes)})")
|
||||
self.chat_tabs.setTabText(3, f"AG Кофейни ({len(self.coffee_chat_checkboxes)})")
|
||||
self.chat_tabs.setTabText(4, f"Прочие ({len(self.other_chat_checkboxes)})")
|
||||
except VkApiError as e:
|
||||
if self._handle_vk_api_error("load_chats", e, action_name="загрузки чатов"):
|
||||
return
|
||||
QMessageBox.critical(self, "Ошибка", f"Не удалось загрузить чаты: {self._format_vk_error(e)}")
|
||||
self.set_ui_state(False)
|
||||
finally:
|
||||
self._set_busy(False)
|
||||
|
||||
def _start_auto_update(self, download_url, latest_version, checksum_url="", download_name=""):
|
||||
if os.name != "nt":
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"Автообновление",
|
||||
"Автообновление пока поддерживается только в Windows-сборке.",
|
||||
)
|
||||
return False
|
||||
if not getattr(sys, "frozen", False):
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"Автообновление",
|
||||
"Автообновление доступно в собранной версии приложения (.exe).",
|
||||
)
|
||||
return False
|
||||
if not download_url:
|
||||
QMessageBox.warning(self, "Автообновление", "В релизе нет ссылки на файл для обновления.")
|
||||
return False
|
||||
|
||||
self.status_label.setText(f"Статус: загрузка обновления {latest_version}...")
|
||||
self._set_busy(True)
|
||||
try:
|
||||
work_dir, source_dir = AutoUpdateService.prepare_update(
|
||||
download_url=download_url,
|
||||
checksum_url=checksum_url,
|
||||
download_name=download_name,
|
||||
)
|
||||
app_exe = sys.executable
|
||||
script_path = AutoUpdateService.build_update_script(
|
||||
app_dir=os.path.dirname(app_exe),
|
||||
source_dir=source_dir,
|
||||
exe_name=os.path.basename(app_exe),
|
||||
target_pid=os.getpid(),
|
||||
)
|
||||
AutoUpdateService.launch_update_script(script_path, work_dir)
|
||||
self._log_event("auto_update", f"Update {latest_version} started from {download_url}")
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"Обновление запущено",
|
||||
"Обновление скачано. Приложение будет перезапущено.",
|
||||
)
|
||||
QApplication.instance().quit()
|
||||
return True
|
||||
except Exception as e:
|
||||
self._log_event("auto_update_failed", str(e), level="ERROR")
|
||||
QMessageBox.warning(self, "Автообновление", f"Не удалось выполнить автообновление: {e}")
|
||||
return False
|
||||
finally:
|
||||
self._set_busy(False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "--auth" in sys.argv:
|
||||
|
||||
Reference in New Issue
Block a user