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 %} +
Файл создан: {{ fmt_local(last_update) }}
-G-Drive Sync: {{ fmt_local(last_sync_time) if last_sync_time else 'НЕИЗВЕСТНО' }}
+ +Архив готов к использованию в NotebookLM
Архив еще не сформирован
- + +Архив еще не сформирован
+ {% endif %}| Ведомство / Домен | -Вх. | -Исх. | -Последнее письмо | +Вх. | +Исх. | +Последнее письмо | ||||
|---|---|---|---|---|---|---|---|---|---|---|
|
- {{ 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 '-' }} | ||||
| Дата | -Ведомство | +Дата | +Ведомство | Тема письма | |
|---|---|---|---|---|---|
{{ email.date }} |
- {{ email.domain }} | -{{ email.subject }} | +{{ email.date }} |
+ {{ email.domain }} | +{{ email.subject }} |