diff --git a/site/app.py b/site/app.py index e493ed4..450e651 100644 --- a/site/app.py +++ b/site/app.py @@ -1,9 +1,13 @@ """contracts-flask v2.0 — полный перенос с ВМ на Flask. Больше никаких прокси на contracts.kube5s.ru — всё локально. """ +import sys, os +# site/ в sys.path — импортируем модули напрямую, без префиксов +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + from flask import Flask -from .config import VERSION, MAX_CONTENT_LENGTH -from .routes import register_routes +from config import VERSION, MAX_CONTENT_LENGTH +from routes import register_routes def create_app(): @@ -11,13 +15,9 @@ def create_app(): app.config["VERSION"] = VERSION app.config["MAX_CONTENT_LENGTH"] = MAX_CONTENT_LENGTH - # DB auto-seed — schema migration + seed prompts + crash recovery _init_db() - - # Регистрация всех blueprint'ов register_routes(app) - # no_cache на все ответы @app.after_request def no_cache(response): response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" @@ -30,10 +30,10 @@ def create_app(): def _init_db(): """Создать БД + схему + seed prompts. SQLite — всё в одном файле /tmp.""" - from .db.connection import init_db + from db.connection import init_db init_db() try: - from .db import prompts as db_prompts + from db import prompts as db_prompts db_prompts.seed_defaults() except Exception: pass diff --git a/site/db/contracts.py b/site/db/contracts.py index 4d13216..5654364 100644 --- a/site/db/contracts.py +++ b/site/db/contracts.py @@ -1,5 +1,5 @@ """Contracts CRUD.""" -from .connection import query, execute, execute_returning +from connection import query, execute, execute_returning def insert(number, client=""): diff --git a/site/db/documents.py b/site/db/documents.py index 03a3e85..3b5364f 100644 --- a/site/db/documents.py +++ b/site/db/documents.py @@ -1,5 +1,5 @@ """Documents CRUD.""" -from .connection import query, execute, execute_returning +from connection import query, execute, execute_returning def insert(filename, mime_type, original_bytes, status="uploaded", batch_id=None, zip_source=None, content_hash=None): diff --git a/site/db/prompts.py b/site/db/prompts.py index cfbb3ef..ffd2413 100644 --- a/site/db/prompts.py +++ b/site/db/prompts.py @@ -1,5 +1,5 @@ """Prompts CRUD.""" -from .connection import query, execute, execute_returning +from connection import query, execute, execute_returning def _serialize(row): diff --git a/site/db/spec_current.py b/site/db/spec_current.py index 63e9de3..f3724dc 100644 --- a/site/db/spec_current.py +++ b/site/db/spec_current.py @@ -1,5 +1,5 @@ """spec_current — текущее состояние спецификации.""" -from .connection import query +from connection import query def list_by_contract(contract_id): diff --git a/site/db/spec_events.py b/site/db/spec_events.py index 2eb10eb..585833b 100644 --- a/site/db/spec_events.py +++ b/site/db/spec_events.py @@ -1,6 +1,6 @@ """spec_events — event sourcing: apply ops, reset contract.""" import json, uuid -from .connection import query, execute, get_pool +from connection import query, execute, get_pool def reset(contract_id): diff --git a/site/db/supplements.py b/site/db/supplements.py index 41fa40e..901c836 100644 --- a/site/db/supplements.py +++ b/site/db/supplements.py @@ -1,5 +1,5 @@ """Supplements CRUD.""" -from .connection import query, execute, execute_returning +from connection import query, execute, execute_returning def insert(contract_id, document_id, supp_type="additional"): diff --git a/site/repository.py b/site/repository.py index 524238e..9f3f6e5 100644 --- a/site/repository.py +++ b/site/repository.py @@ -95,77 +95,77 @@ class PgRepository: """Реальный доступ к PostgreSQL через существующие db/*.py.""" def insert_document(self, filename, mime_type, original_bytes, batch_id=None, zip_source=None): - from .db import documents + from db import documents return documents.insert(filename, mime_type, original_bytes, batch_id=batch_id, zip_source=zip_source) def set_document_parsed(self, doc_id, elements): - from .db import documents + from db import documents documents.set_parsed(doc_id, elements) # documents.set_parsed уже делает json.dumps def set_document_error(self, doc_id, error): - from .db import documents + from db import documents documents.set_error(doc_id, error) def set_classification(self, doc_id, doc_type, own_number=None, parent_number=None, doc_date=None, counterparty=None, classify_raw=None, classify_input=None): - from .db import documents + from db import documents documents.set_classification(doc_id, doc_type, own_number, parent_number, doc_date, counterparty, classify_raw=classify_raw, classify_input=classify_input) def set_classify_garbage(self, doc_id, reason=""): - from .db import documents + from db import documents documents.set_classify_garbage(doc_id, reason) def set_classify_failed(self, doc_id, error): - from .db import documents + from db import documents documents.set_classify_failed(doc_id, error) def list_pending(self, batch_id): - from .db import documents + from db import documents return documents.list_pending(batch_id) def insert_contract(self, number, client=""): - from .db import contracts + from db import contracts c = contracts.insert(number, client) return c["id"] if c else "" def insert_supplement(self, contract_id, doc_id, supp_type): - from .db import supplements + from db import supplements supplements.insert(contract_id, doc_id, supp_type) def list_supplements(self, contract_id): - from .db import supplements + from db import supplements return supplements.list_by_contract(contract_id) def get_spec_current(self, contract_id): - from .db import spec_current + from db import spec_current return spec_current.list_by_contract(contract_id) def get_document(self, doc_id): - from .db import documents + from db import documents return documents.get(doc_id) def list_by_batch(self, batch_id): - from .db import documents + from db import documents return documents.list_by_batch(batch_id) def count_by_status(self, batch_id): - from .db import documents + from db import documents return documents.count_by_status(batch_id) def reset_classify_status(self, batch_id): - from .db import documents + from db import documents documents.reset_classify_status(batch_id) def set_classify_processing(self, doc_id): - from .db import documents + from db import documents documents.set_classify_processing(doc_id) def delete_document(self, doc_id): - from .db import documents + from db import documents documents.delete(doc_id) diff --git a/site/routes/__init__.py b/site/routes/__init__.py index ae8983b..33ab456 100644 --- a/site/routes/__init__.py +++ b/site/routes/__init__.py @@ -2,12 +2,12 @@ def register_routes(app): - from .routes.upload_bp import upload_bp - from .routes.pipeline_bp import pipeline_bp - from .routes.api_bp import api_bp - from .routes.prompts_bp import prompts_bp - from .routes.health_bp import health_bp - from .routes.pages_bp import pages_bp + from routes.upload_bp import upload_bp + from routes.pipeline_bp import pipeline_bp + from routes.api_bp import api_bp + from routes.prompts_bp import prompts_bp + from routes.health_bp import health_bp + from routes.pages_bp import pages_bp app.register_blueprint(upload_bp) app.register_blueprint(pipeline_bp) diff --git a/site/routes/api_bp.py b/site/routes/api_bp.py index 15f5a75..6d7fae2 100644 --- a/site/routes/api_bp.py +++ b/site/routes/api_bp.py @@ -1,9 +1,9 @@ """API blueprint — groups, documents, supplements, sync, cleanup, spec-current, chat.""" from flask import Blueprint, request, jsonify -from .db import documents, supplements, spec_current -from .db.connection import execute, query -from .services.grouping import group_documents, apply_groups -from .config import LLM_URL, LLM_KEY, LLM_MODEL +from db import documents, supplements, spec_current +from db.connection import execute, query +from services.grouping import group_documents, apply_groups +from config import LLM_URL, LLM_KEY, LLM_MODEL import httpx api_bp = Blueprint("api", __name__) @@ -177,6 +177,6 @@ def chat(): @api_bp.route("/api/cleanup", methods=["POST"]) def api_cleanup(): """Полная очистка: os.remove(DB) + init новой. Данные гарантированно стёрты.""" - from .db.connection import cleanup_db + from db.connection import cleanup_db cleanup_db() return jsonify(ok=True, message="all data cleaned") diff --git a/site/routes/health_bp.py b/site/routes/health_bp.py index f03ad05..4e74071 100644 --- a/site/routes/health_bp.py +++ b/site/routes/health_bp.py @@ -1,6 +1,6 @@ """Health probe — обязательно для Штурвала.""" from flask import Blueprint, jsonify -from .config import VERSION +from config import VERSION health_bp = Blueprint("health", __name__) diff --git a/site/routes/pipeline_bp.py b/site/routes/pipeline_bp.py index ed7770a..409c1e3 100644 --- a/site/routes/pipeline_bp.py +++ b/site/routes/pipeline_bp.py @@ -1,10 +1,10 @@ """Pipeline blueprint — SSE-сравнение + classify.""" import json, re, os, threading from flask import Blueprint, request, jsonify, Response, stream_with_context -from .services.process import run_pipeline -from .services.classify import classify_batch -from .llm_prompt import build_prompt -from .db import documents +from services.process import run_pipeline +from services.classify import classify_batch +from llm_prompt import build_prompt +from db import documents pipeline_bp = Blueprint("pipeline", __name__) diff --git a/site/routes/prompts_bp.py b/site/routes/prompts_bp.py index c51b1a0..40cf565 100644 --- a/site/routes/prompts_bp.py +++ b/site/routes/prompts_bp.py @@ -1,6 +1,6 @@ """Prompts blueprint — CRUD + activate для версионирования промптов.""" from flask import Blueprint, request, jsonify -from .db import prompts as db_prompts +from db import prompts as db_prompts prompts_bp = Blueprint("prompts", __name__) diff --git a/site/routes/upload_bp.py b/site/routes/upload_bp.py index 062181e..6e954d5 100644 --- a/site/routes/upload_bp.py +++ b/site/routes/upload_bp.py @@ -1,9 +1,9 @@ """Upload blueprint — загрузка, конвертация, распаковка.""" import io, os, base64, hashlib, zipfile, tempfile, subprocess from flask import Blueprint, request, jsonify, send_file -from .services.parse import parse_file -from .db import documents -from .config import MAX_CONTENT_LENGTH +from services.parse import parse_file +from db import documents +from config import MAX_CONTENT_LENGTH upload_bp = Blueprint("upload", __name__) diff --git a/site/services/classify.py b/site/services/classify.py index e641c88..f5cca6b 100644 --- a/site/services/classify.py +++ b/site/services/classify.py @@ -14,8 +14,8 @@ import json, re, os from concurrent.futures import ThreadPoolExecutor, as_completed import httpx -from .db import documents as db_docs -from .llm_prompt import build_classify_prompt +from db import documents as db_docs +from llm_prompt import build_classify_prompt log = __import__("logging").getLogger(__name__) @@ -34,7 +34,7 @@ _classify_llm = None def _get_classify_client(): global _classify_llm if _classify_llm is None: - from .services.llm_client import HttpxLLMClient + from services.llm_client import HttpxLLMClient _classify_llm = HttpxLLMClient(url=LLM_URL, key=LLM_KEY, model=LLM_MODEL, max_tokens=1000, timeout=60) return _classify_llm diff --git a/site/services/grouping.py b/site/services/grouping.py index 857b4eb..58a15aa 100644 --- a/site/services/grouping.py +++ b/site/services/grouping.py @@ -9,9 +9,9 @@ Grouping service — match classified documents into contract groups. - apply_groups(): создаёт contracts + supplements с авто-порядком по дате. """ import re -from .db import documents as db_docs -from .db import contracts as db_contracts -from .db import supplements as db_supplements +from db import documents as db_docs +from db import contracts as db_contracts +from db import supplements as db_supplements def normalize_number(num): diff --git a/site/services/llm.py b/site/services/llm.py index 9ecc97e..993d0d1 100644 --- a/site/services/llm.py +++ b/site/services/llm.py @@ -12,7 +12,7 @@ _llm_client = None def _get_default_client(): global _llm_client if _llm_client is None: - from .services.llm_client import HttpxLLMClient + from services.llm_client import HttpxLLMClient _llm_client = HttpxLLMClient(url=LLM_URL, key=LLM_KEY, model=LLM_MODEL) return _llm_client diff --git a/site/services/process.py b/site/services/process.py index c345302..39b6fe8 100644 --- a/site/services/process.py +++ b/site/services/process.py @@ -2,8 +2,8 @@ Адаптирован из deploy/compare/process.py: callback → generator. """ import json, time -from .db import supplements, spec_current, spec_events -from .services.metrics import check_arithmetic +from db import supplements, spec_current, spec_events +from services.metrics import check_arithmetic def run_pipeline(contract_id, order_ids, build_prompt_fn): @@ -31,7 +31,7 @@ def run_pipeline(contract_id, order_ids, build_prompt_fn): yield {"type": "error", "message": "Нет распарсенных файлов"} return - from .services.llm import call_llm + from services.llm import call_llm for s in supps: sid = s["id"]