"""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}