Files
contractor/deploy/services/grouping.py
T

96 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Grouping service — match classified documents into contract groups."""
import re
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):
"""Normalize contract number for matching: uppercase, only letters/digits."""
if not num:
return ""
return re.sub(r"[^A-Z0-9А-Я]", "", num.upper())
def group_documents(batch_id):
"""Group classified documents by contract. Returns list of groups."""
docs = db_docs.list_by_batch(batch_id)
classified = [d for d in docs if d.get("classify_status") == "classified"]
# Separate contracts and supplements
contracts_list = []
supplements_list = []
for d in classified:
if d.get("doc_type") == "contract":
contracts_list.append(d)
else:
supplements_list.append(d)
groups = []
# Each contract becomes a group
for c in contracts_list:
group = {
"contract_number": c.get("own_number") or c.get("filename", ""),
"counterparty": c.get("counterparty") or "",
"documents": [c],
}
# Find supplements matching this contract
c_norm = normalize_number(c.get("own_number"))
for s in supplements_list:
parent = normalize_number(s.get("parent_number") or "")
own = normalize_number(s.get("own_number") or "")
if parent == c_norm or own == c_norm:
if s not in group["documents"]:
group["documents"].append(s)
# Sort by date
group["documents"].sort(key=lambda x: x.get("doc_date") or "")
# Remove matched supplements from the pool
for s in group["documents"]:
if s in supplements_list:
supplements_list.remove(s)
groups.append(group)
# Unmatched documents → "unresolved" group
unmatched = [s for s in supplements_list if s.get("doc_type") != "contract"]
unmatched += [d for d in classified if d.get("classify_status") != "classified"]
if unmatched or len(contracts_list) == 0:
# Put unmatched into a separate group
if unmatched:
groups.append({
"contract_number": "__unresolved__",
"counterparty": "",
"documents": unmatched,
})
return {"ok": True, "groups": groups, "total_docs": len(classified)}
def apply_groups(batch_id, groups_data):
"""Apply confirmed groups: create contracts + supplements."""
created = 0
for g in groups_data:
contract_number = g.get("contract_number", "")
if contract_number == "__unresolved__":
continue
counterparty = g.get("counterparty", "")
docs = g.get("documents", [])
# Create contract
c = db_contracts.insert(contract_number, counterparty)
contract_id = c["id"]
# Create supplements in order
for i, d in enumerate(docs):
doc_id = d.get("id")
supp_type = "initial" if i == 0 else "additional"
db_supplements.insert(contract_id, doc_id, supp_type)
created += 1
return {"ok": True, "created": created}