fix: site/ with __init__.py — proper Python package, no stdlib conflict
Deploy contracts-flask / validate (push) Successful in 0s

This commit is contained in:
2026-07-15 11:23:13 +04:00
parent 81aba97304
commit cd77e7d709
43 changed files with 53 additions and 53 deletions
+2 -2
View File
@@ -6,8 +6,8 @@ COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt
COPY src /app/src COPY site /app/site
EXPOSE 5000 EXPOSE 5000
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--timeout", "300", "--workers", "1", "src.app:app"] CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--timeout", "300", "--workers", "1", "site.app:app"]
+4 -4
View File
@@ -2,8 +2,8 @@
Больше никаких прокси на contracts.kube5s.ru всё локально. Больше никаких прокси на contracts.kube5s.ru всё локально.
""" """
from flask import Flask from flask import Flask
from src.config import VERSION, MAX_CONTENT_LENGTH from site.config import VERSION, MAX_CONTENT_LENGTH
from src.routes import register_routes from site.routes import register_routes
def create_app(): def create_app():
@@ -30,10 +30,10 @@ def create_app():
def _init_db(): def _init_db():
"""Создать БД + схему + seed prompts. SQLite — всё в одном файле /tmp.""" """Создать БД + схему + seed prompts. SQLite — всё в одном файле /tmp."""
from src.db.connection import init_db from site.db.connection import init_db
init_db() init_db()
try: try:
from src.db import prompts as db_prompts from site.db import prompts as db_prompts
db_prompts.seed_defaults() db_prompts.seed_defaults()
except Exception: except Exception:
pass pass
View File
View File
+17 -17
View File
@@ -95,77 +95,77 @@ class PgRepository:
"""Реальный доступ к PostgreSQL через существующие db/*.py.""" """Реальный доступ к PostgreSQL через существующие db/*.py."""
def insert_document(self, filename, mime_type, original_bytes, batch_id=None, zip_source=None): def insert_document(self, filename, mime_type, original_bytes, batch_id=None, zip_source=None):
from src.db import documents from site.db import documents
return documents.insert(filename, mime_type, original_bytes, return documents.insert(filename, mime_type, original_bytes,
batch_id=batch_id, zip_source=zip_source) batch_id=batch_id, zip_source=zip_source)
def set_document_parsed(self, doc_id, elements): def set_document_parsed(self, doc_id, elements):
from src.db import documents from site.db import documents
documents.set_parsed(doc_id, elements) # documents.set_parsed уже делает json.dumps documents.set_parsed(doc_id, elements) # documents.set_parsed уже делает json.dumps
def set_document_error(self, doc_id, error): def set_document_error(self, doc_id, error):
from src.db import documents from site.db import documents
documents.set_error(doc_id, error) documents.set_error(doc_id, error)
def set_classification(self, doc_id, doc_type, own_number=None, parent_number=None, def set_classification(self, doc_id, doc_type, own_number=None, parent_number=None,
doc_date=None, counterparty=None, doc_date=None, counterparty=None,
classify_raw=None, classify_input=None): classify_raw=None, classify_input=None):
from src.db import documents from site.db import documents
documents.set_classification(doc_id, doc_type, own_number, parent_number, documents.set_classification(doc_id, doc_type, own_number, parent_number,
doc_date, counterparty, doc_date, counterparty,
classify_raw=classify_raw, classify_input=classify_input) classify_raw=classify_raw, classify_input=classify_input)
def set_classify_garbage(self, doc_id, reason=""): def set_classify_garbage(self, doc_id, reason=""):
from src.db import documents from site.db import documents
documents.set_classify_garbage(doc_id, reason) documents.set_classify_garbage(doc_id, reason)
def set_classify_failed(self, doc_id, error): def set_classify_failed(self, doc_id, error):
from src.db import documents from site.db import documents
documents.set_classify_failed(doc_id, error) documents.set_classify_failed(doc_id, error)
def list_pending(self, batch_id): def list_pending(self, batch_id):
from src.db import documents from site.db import documents
return documents.list_pending(batch_id) return documents.list_pending(batch_id)
def insert_contract(self, number, client=""): def insert_contract(self, number, client=""):
from src.db import contracts from site.db import contracts
c = contracts.insert(number, client) c = contracts.insert(number, client)
return c["id"] if c else "" return c["id"] if c else ""
def insert_supplement(self, contract_id, doc_id, supp_type): def insert_supplement(self, contract_id, doc_id, supp_type):
from src.db import supplements from site.db import supplements
supplements.insert(contract_id, doc_id, supp_type) supplements.insert(contract_id, doc_id, supp_type)
def list_supplements(self, contract_id): def list_supplements(self, contract_id):
from src.db import supplements from site.db import supplements
return supplements.list_by_contract(contract_id) return supplements.list_by_contract(contract_id)
def get_spec_current(self, contract_id): def get_spec_current(self, contract_id):
from src.db import spec_current from site.db import spec_current
return spec_current.list_by_contract(contract_id) return spec_current.list_by_contract(contract_id)
def get_document(self, doc_id): def get_document(self, doc_id):
from src.db import documents from site.db import documents
return documents.get(doc_id) return documents.get(doc_id)
def list_by_batch(self, batch_id): def list_by_batch(self, batch_id):
from src.db import documents from site.db import documents
return documents.list_by_batch(batch_id) return documents.list_by_batch(batch_id)
def count_by_status(self, batch_id): def count_by_status(self, batch_id):
from src.db import documents from site.db import documents
return documents.count_by_status(batch_id) return documents.count_by_status(batch_id)
def reset_classify_status(self, batch_id): def reset_classify_status(self, batch_id):
from src.db import documents from site.db import documents
documents.reset_classify_status(batch_id) documents.reset_classify_status(batch_id)
def set_classify_processing(self, doc_id): def set_classify_processing(self, doc_id):
from src.db import documents from site.db import documents
documents.set_classify_processing(doc_id) documents.set_classify_processing(doc_id)
def delete_document(self, doc_id): def delete_document(self, doc_id):
from src.db import documents from site.db import documents
documents.delete(doc_id) documents.delete(doc_id)
@@ -2,12 +2,12 @@
def register_routes(app): def register_routes(app):
from src.routes.upload_bp import upload_bp from site.routes.upload_bp import upload_bp
from src.routes.pipeline_bp import pipeline_bp from site.routes.pipeline_bp import pipeline_bp
from src.routes.api_bp import api_bp from site.routes.api_bp import api_bp
from src.routes.prompts_bp import prompts_bp from site.routes.prompts_bp import prompts_bp
from src.routes.health_bp import health_bp from site.routes.health_bp import health_bp
from src.routes.pages_bp import pages_bp from site.routes.pages_bp import pages_bp
app.register_blueprint(upload_bp) app.register_blueprint(upload_bp)
app.register_blueprint(pipeline_bp) app.register_blueprint(pipeline_bp)
@@ -1,9 +1,9 @@
"""API blueprint — groups, documents, supplements, sync, cleanup, spec-current, chat.""" """API blueprint — groups, documents, supplements, sync, cleanup, spec-current, chat."""
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from src.db import documents, supplements, spec_current from site.db import documents, supplements, spec_current
from src.db.connection import execute, query from site.db.connection import execute, query
from src.services.grouping import group_documents, apply_groups from site.services.grouping import group_documents, apply_groups
from src.config import LLM_URL, LLM_KEY, LLM_MODEL from site.config import LLM_URL, LLM_KEY, LLM_MODEL
import httpx import httpx
api_bp = Blueprint("api", __name__) api_bp = Blueprint("api", __name__)
@@ -177,6 +177,6 @@ def chat():
@api_bp.route("/api/cleanup", methods=["POST"]) @api_bp.route("/api/cleanup", methods=["POST"])
def api_cleanup(): def api_cleanup():
"""Полная очистка: os.remove(DB) + init новой. Данные гарантированно стёрты.""" """Полная очистка: os.remove(DB) + init новой. Данные гарантированно стёрты."""
from src.db.connection import cleanup_db from site.db.connection import cleanup_db
cleanup_db() cleanup_db()
return jsonify(ok=True, message="all data cleaned") return jsonify(ok=True, message="all data cleaned")
@@ -1,6 +1,6 @@
"""Health probe — обязательно для Штурвала.""" """Health probe — обязательно для Штурвала."""
from flask import Blueprint, jsonify from flask import Blueprint, jsonify
from src.config import VERSION from site.config import VERSION
health_bp = Blueprint("health", __name__) health_bp = Blueprint("health", __name__)
@@ -1,10 +1,10 @@
"""Pipeline blueprint — SSE-сравнение + classify.""" """Pipeline blueprint — SSE-сравнение + classify."""
import json, re, os, threading import json, re, os, threading
from flask import Blueprint, request, jsonify, Response, stream_with_context from flask import Blueprint, request, jsonify, Response, stream_with_context
from src.services.process import run_pipeline from site.services.process import run_pipeline
from src.services.classify import classify_batch from site.services.classify import classify_batch
from src.llm_prompt import build_prompt from site.llm_prompt import build_prompt
from src.db import documents from site.db import documents
pipeline_bp = Blueprint("pipeline", __name__) pipeline_bp = Blueprint("pipeline", __name__)
@@ -1,6 +1,6 @@
"""Prompts blueprint — CRUD + activate для версионирования промптов.""" """Prompts blueprint — CRUD + activate для версионирования промптов."""
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from src.db import prompts as db_prompts from site.db import prompts as db_prompts
prompts_bp = Blueprint("prompts", __name__) prompts_bp = Blueprint("prompts", __name__)
@@ -1,9 +1,9 @@
"""Upload blueprint — загрузка, конвертация, распаковка.""" """Upload blueprint — загрузка, конвертация, распаковка."""
import io, os, base64, hashlib, zipfile, tempfile, subprocess import io, os, base64, hashlib, zipfile, tempfile, subprocess
from flask import Blueprint, request, jsonify, send_file from flask import Blueprint, request, jsonify, send_file
from src.services.parse import parse_file from site.services.parse import parse_file
from src.db import documents from site.db import documents
from src.config import MAX_CONTENT_LENGTH from site.config import MAX_CONTENT_LENGTH
upload_bp = Blueprint("upload", __name__) upload_bp = Blueprint("upload", __name__)
@@ -14,8 +14,8 @@ import json, re, os
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
import httpx import httpx
from src.db import documents as db_docs from site.db import documents as db_docs
from src.llm_prompt import build_classify_prompt from site.llm_prompt import build_classify_prompt
log = __import__("logging").getLogger(__name__) log = __import__("logging").getLogger(__name__)
@@ -34,7 +34,7 @@ _classify_llm = None
def _get_classify_client(): def _get_classify_client():
global _classify_llm global _classify_llm
if _classify_llm is None: if _classify_llm is None:
from src.services.llm_client import HttpxLLMClient from site.services.llm_client import HttpxLLMClient
_classify_llm = HttpxLLMClient(url=LLM_URL, key=LLM_KEY, model=LLM_MODEL, max_tokens=1000, timeout=60) _classify_llm = HttpxLLMClient(url=LLM_URL, key=LLM_KEY, model=LLM_MODEL, max_tokens=1000, timeout=60)
return _classify_llm return _classify_llm
@@ -9,9 +9,9 @@ Grouping service — match classified documents into contract groups.
- apply_groups(): создаёт contracts + supplements с авто-порядком по дате. - apply_groups(): создаёт contracts + supplements с авто-порядком по дате.
""" """
import re import re
from src.db import documents as db_docs from site.db import documents as db_docs
from src.db import contracts as db_contracts from site.db import contracts as db_contracts
from src.db import supplements as db_supplements from site.db import supplements as db_supplements
def normalize_number(num): def normalize_number(num):
+1 -1
View File
@@ -12,7 +12,7 @@ _llm_client = None
def _get_default_client(): def _get_default_client():
global _llm_client global _llm_client
if _llm_client is None: if _llm_client is None:
from src.services.llm_client import HttpxLLMClient from site.services.llm_client import HttpxLLMClient
_llm_client = HttpxLLMClient(url=LLM_URL, key=LLM_KEY, model=LLM_MODEL) _llm_client = HttpxLLMClient(url=LLM_URL, key=LLM_KEY, model=LLM_MODEL)
return _llm_client return _llm_client
@@ -2,8 +2,8 @@
Адаптирован из deploy/compare/process.py: callback generator. Адаптирован из deploy/compare/process.py: callback generator.
""" """
import json, time import json, time
from src.db import supplements, spec_current, spec_events from site.db import supplements, spec_current, spec_events
from src.services.metrics import check_arithmetic from site.services.metrics import check_arithmetic
def run_pipeline(contract_id, order_ids, build_prompt_fn): 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": "Нет распарсенных файлов"} yield {"type": "error", "message": "Нет распарсенных файлов"}
return return
from src.services.llm import call_llm from site.services.llm import call_llm
for s in supps: for s in supps:
sid = s["id"] sid = s["id"]

Before

Width:  |  Height:  |  Size: 246 B

After

Width:  |  Height:  |  Size: 246 B

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB