From ed565c54c3a12ece61510cbde487254e7202b822 Mon Sep 17 00:00:00 2001 From: Admin Date: Sat, 27 Jun 2026 10:35:22 +0400 Subject: [PATCH] WebPanel: Redesign Email Audit section, add stats widgets and dynamic AJAX counters --- docker-compose.yml | 6 + requirements.txt | 9 ++ web/app.py | 108 ++++++++++++-- web/templates/emails.html | 305 +++++++++++++++++++++++++------------- 4 files changed, 311 insertions(+), 117 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 31dd1bf..aac7950 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,9 @@ services: - ./data:/app/data - ../libs/swarmlib:/app/swarmlib:ro - /home/matrixhasyou/domovoy_google_creds.json:/home/matrixhasyou/domovoy_google_creds.json:ro + - /home/matrixhasyou/mutt:/home/matrixhasyou/mutt:rw + - /home/matrixhasyou/domovoy_drive_sync.py:/home/matrixhasyou/domovoy_drive_sync.py:ro + - /home/matrixhasyou/.muttrc:/home/matrixhasyou/.muttrc:ro logging: driver: "json-file" options: @@ -34,6 +37,9 @@ services: - ./data:/app/data - ../libs/swarmlib:/app/swarmlib:ro - /home/matrixhasyou/domovoy_google_creds.json:/home/matrixhasyou/domovoy_google_creds.json:ro + - /home/matrixhasyou/mutt:/home/matrixhasyou/mutt:rw + - /home/matrixhasyou/domovoy_drive_sync.py:/home/matrixhasyou/domovoy_drive_sync.py:ro + - /home/matrixhasyou/.muttrc:/home/matrixhasyou/.muttrc:ro logging: driver: "json-file" options: diff --git a/requirements.txt b/requirements.txt index 017fe0c..f90bbb0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,4 +15,13 @@ pydub psycopg2-binary>=2.9.9 asyncpg>=0.29.0 google-generativeai +imap-tools +weasyprint +fpdf +pymupdf +google-api-python-client +google-auth +google-auth-httplib2 +google-auth-oauthlib + diff --git a/web/app.py b/web/app.py index c7bc1b7..15a9864 100644 --- a/web/app.py +++ b/web/app.py @@ -690,18 +690,46 @@ async def emails_page(request: Request, username: str = Depends(get_current_admi last_sync_time = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc) except: last_sync_time = datetime.fromtimestamp(sync_time_file.stat().st_mtime, tz=timezone.utc) + + # Global stats calculation + total_inbound = sum(d.inbound_count for d in audit_data) + total_outbound = sum(d.outbound_count for d in audit_data) + total_emails = total_inbound + total_outbound + + total_attachments = 0 + export_log_path = Path("/home/matrixhasyou/mutt/export.log") + if export_log_path.exists(): + try: + content = export_log_path.read_text(errors='ignore') + matches = re.findall(r"Has (\d+) attachment", content) + total_attachments = sum(int(m) for m in matches) + except Exception as e: + pass + + pdf_size_mb = 0.0 + if master_pdf_path.exists(): + pdf_size_mb = round(master_pdf_path.stat().st_size / (1024 * 1024), 2) + + last_email_date = None + if audit_data: + dates = [d.last_email_date for d in audit_data if d.last_email_date] + if dates: + last_email_date = max(dates) return templates.TemplateResponse(request=request, name="emails.html", context={ "username": username, "audit_data": audit_data, "last_log": last_log, "latest_emails": latest_emails, "has_master_pdf": master_pdf_path.exists(), "last_update": last_update, - "last_sync_time": last_sync_time, "fmt_local": fmt_local + "last_sync_time": last_sync_time, "fmt_local": fmt_local, + "total_inbound": total_inbound, "total_outbound": total_outbound, + "total_emails": total_emails, "total_attachments": total_attachments, + "pdf_size_mb": pdf_size_mb, "last_email_date": last_email_date }) @app.post("/api/emails/run_full_sync") async def api_emails_run_full_sync(username: str = Depends(get_current_admin)): master_script = "/home/matrixhasyou/mutt/master_ai_sync.py" - python_path = "/usr/bin/python3" + python_path = sys.executable if not os.path.exists(master_script): return JSONResponse(status_code=404, content={"message": "Master sync script not found"}) @@ -716,7 +744,7 @@ async def api_emails_run_full_sync(username: str = Depends(get_current_admin)): @app.post("/api/emails/sync_ai") async def api_emails_sync_ai(username: str = Depends(get_current_admin)): sync_script = "/home/matrixhasyou/domovoy_drive_sync.py" - python_path = "/home/matrixhasyou/domovoy_bot/venv-google/bin/python" + python_path = sys.executable master_pdf = "/home/matrixhasyou/mutt/TOTAL_ARCHIVE_2025-2026.pdf" if not os.path.exists(sync_script): @@ -733,18 +761,68 @@ async def api_emails_sync_ai(username: str = Depends(get_current_admin)): async def api_emails_pipeline_status(username: str = Depends(get_current_admin)): import time status_file = Path("/home/matrixhasyou/mutt/pipeline_status.json") - if not status_file.exists(): - return JSONResponse(content={"status": "idle", "step": "Неактивен", "updated_at": "-", "new_emails": 0, "total_emails": 0, "pdf_size_mb": 0.0}) - try: - with open(status_file) as f: - data = json.load(f) - mtime = status_file.stat().st_mtime - if data.get("status") == "running" and (time.time() - mtime > 600): - data["status"] = "stuck" - data["step"] = "Процесс завис или был остановлен" - return JSONResponse(content=data) - except Exception as e: - return JSONResponse(content={"status": "error", "message": str(e)}) + master_pdf_path = Path("/home/matrixhasyou/mutt/ALL_EMAILS_CONSOLIDATED.pdf") + + # Calculate fallback values + pdf_size_mb = 0.0 + if master_pdf_path.exists(): + pdf_size_mb = round(master_pdf_path.stat().st_size / (1024 * 1024), 2) + + total_emails = 0 + total_inbound = 0 + total_outbound = 0 + async with AsyncSessionLocal() as session: + audit_data = (await session.execute(select(EmailAudit))).scalars().all() + total_inbound = sum(d.inbound_count for d in audit_data) + total_outbound = sum(d.outbound_count for d in audit_data) + total_emails = total_inbound + total_outbound + + total_attachments = 0 + export_log_path = Path("/home/matrixhasyou/mutt/export.log") + if export_log_path.exists(): + try: + content = export_log_path.read_text(errors='ignore') + matches = re.findall(r"Has (\d+) attachment", content) + total_attachments = sum(int(m) for m in matches) + except Exception as e: + pass + + sync_time_file = Path("/home/matrixhasyou/mutt/last_ai_sync.txt") + last_sync_time = "-" + if sync_time_file.exists(): + try: + last_sync_time = sync_time_file.read_text().strip() + except: + pass + + data = { + "status": "idle", + "step": "Неактивен", + "updated_at": "-", + "new_emails": 0, + "total_emails": total_emails, + "total_inbound": total_inbound, + "total_outbound": total_outbound, + "total_attachments": total_attachments, + "pdf_size_mb": pdf_size_mb, + "last_sync_time": last_sync_time + } + + if status_file.exists(): + try: + with open(status_file) as f: + file_data = json.load(f) + data.update(file_data) + + mtime = status_file.stat().st_mtime + if data.get("status") == "running" and (time.time() - mtime > 600): + data["status"] = "stuck" + data["step"] = "Процесс завис или был остановлен" + except Exception as e: + data["status"] = "error" + data["message"] = str(e) + + return JSONResponse(content=data) @app.get("/infra", response_class=HTMLResponse) async def get_infra_page(request: Request, username: str = Depends(get_current_admin)): diff --git a/web/templates/emails.html b/web/templates/emails.html index 8c3a3c8..c8e496c 100644 --- a/web/templates/emails.html +++ b/web/templates/emails.html @@ -4,6 +4,7 @@ {% block nav_emails %}active{% endblock %} {% block content %} +

@@ -15,20 +16,87 @@

+ +
+ +
+
+
+
+
>_ ВСЕГО_ПИСЕМ
+ +
+

{{ total_emails }}

+
+ Вх: {{ total_inbound }} / + Исх: {{ total_outbound }} +
+
+
+
+ +
+
+
+
+
>_ ОБЪЕМ_АРХИВА
+ +
+

{{ pdf_size_mb }} MB

+
+ Создан: {{ fmt_local(last_update) if last_update else '-' }} +
+
+
+
+ +
+
+
+
+
>_ ВШИТЫХ_ВЛОЖЕНИЙ
+ +
+

{{ total_attachments }}

+
Интегрировано в листы PDF
+
+
+
+ +
+
+
+
+
>_ DRIVE_SYNC
+ +
+

+ {% if last_sync_time %}{{ last_sync_time.strftime('%d.%m.%Y') }}{% else %}-{% endif %} +

+
+ Время: {{ fmt_local(last_sync_time) if last_sync_time else 'Неизвестно' }} +
+
+
+
+
+
- +
-
-
АРХИВ_PDF (NOTEBOOK_LM)
-
+ +
+
+ [CONTROL] УПРАВЛЕНИЕ_АРХИВОМ +
+
{% if has_master_pdf %} - -
ALL_EMAILS_CONSOLIDATED.pdf
-

Файл создан: {{ fmt_local(last_update) }}

-

G-Drive Sync: {{ fmt_local(last_sync_time) if last_sync_time else 'НЕИЗВЕСТНО' }}

+ +
ALL_EMAILS_CONSOLIDATED.pdf
+

Архив готов к использованию в NotebookLM

{% else %} - -

Архив еще не сформирован

- + +

Архив еще не сформирован

+ {% endif %}
+ + +
+
+ [PIPELINE] МОНИТОРИНГ_ПРОЦЕССА + +
+
+
+ [STEP] Шаг: Ожидание
+ [TIME] Обновлено: -
+
+
+
+ [DATA] Новых писем: 0
+ [DATA] Всего в архиве: 0
+ [DATA] Размер файла: 0.00 MB +
+
+
+ + +
+
+ [LOG] ЛОГ_ПОСЛЕДНЕГО_ЗАПУСКА +
+
+
+ {% if last_log %} + [TIME] Начат: {{ fmt_local(last_log.start_time) }}
+ [TIME] Завершен: {{ fmt_local(last_log.end_time) if last_log.end_time else 'В ПРОЦЕССЕ...' }}
+ [STATUS] Статус: {{ last_log.status.upper() }}
+ [DATA] Новых писем: {{ last_log.new_emails_count }} + {% else %} + [INFO] Запуски еще не производились. + {% endif %} +
+
+
- +
-
-
СТАТИСТИКА_ВЕДОМСТВ
+
+
+ [STATS] СТАТИСТИКА_ПО_ВЕДОМСТВАМ_И_ОРГАНАМ +
-
- - +
+
+ - - - + + + {% for domain in audit_data %} - - - + + + {% else %} @@ -88,27 +199,29 @@ +
-
-
-
СВЕЖИЕ_ПОСТУПЛЕНИЯ_В_АРХИВ (ПОСЛЕДНИЕ 10)
+
+
+ [FEED] СВЕЖИЕ_ПОСТУПЛЕНИЯ_В_АРХИВ (ПОСЛЕДНИЕ 10) +
Ведомство / ДоменВх.Исх.Последнее письмоВх.Исх.Последнее письмо
- {{ domain.domain }} -
{{ domain.description }} + {{ domain.domain }} +
{{ domain.description }}
{{ domain.inbound_count }}{{ domain.outbound_count }}{{ domain.last_email_date.strftime('%d.%m.%Y') if domain.last_email_date else '-' }}{{ domain.inbound_count }}{{ domain.outbound_count }}{{ domain.last_email_date.strftime('%d.%m.%Y %H:%M') if domain.last_email_date else '-' }}
- - + + {% for email in latest_emails %} - - - + + + {% else %} @@ -123,49 +236,7 @@ -
- -
- -
- - -
-
-
ЛОГ_ПОСЛЕДНЕГО_ЗАПУСКА (БАЗА ДАННЫХ)
-
-
- {% if last_log %} - [TIME] Начат: {{ fmt_local(last_log.start_time) }}
- [TIME] Завершен: {{ fmt_local(last_log.end_time) if last_log.end_time else 'В ПРОЦЕССЕ...' }}
- [STATUS] Статус: {{ last_log.status.upper() }}
- [DATA] Новых писем: {{ last_log.new_emails_count }} - {% else %} - [INFO] Запуски еще не производились. - {% endif %} -
-
-
-
-
- +
ДатаВедомствоДатаВедомство Тема письма
{{ email.date }}{{ email.domain }}{{ email.subject }}{{ email.date }}{{ email.domain }}{{ email.subject }}