Template mapping and detect issue fixed
This commit is contained in:
16
check_matches.py
Normal file
16
check_matches.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.append(os.path.join(os.getcwd(), 'docengine'))
|
||||||
|
|
||||||
|
from app.core.database import SessionLocal
|
||||||
|
from app.models.document import TemplateMatch
|
||||||
|
from app.models.template import DocumentFormat
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
matches = db.query(TemplateMatch).order_by(TemplateMatch.created_at.desc()).limit(10).all()
|
||||||
|
print(f"Found {len(matches)} matches.")
|
||||||
|
for m in matches:
|
||||||
|
fmt = db.query(DocumentFormat).filter(DocumentFormat.id == m.format_id).first()
|
||||||
|
fmt_name = fmt.name if fmt else 'Unknown'
|
||||||
|
print(f"Match: doc_id={m.document_id}, format_id={m.format_id}, name={fmt_name}, score={m.confidence_score}")
|
||||||
|
print(f"Details: {m.match_details}")
|
||||||
26
debug_match.py
Normal file
26
debug_match.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.append(os.path.join(os.getcwd(), 'docengine'))
|
||||||
|
|
||||||
|
from app.core.database import SessionLocal
|
||||||
|
from app.models.document import Document
|
||||||
|
from app.services.matching_service import MatchingService
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
doc = db.query(Document).order_by(Document.created_at.desc()).first()
|
||||||
|
if not doc:
|
||||||
|
print("No documents found.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
print(f"Latest document: {doc.id} (status: {doc.status})")
|
||||||
|
|
||||||
|
svc = MatchingService(db)
|
||||||
|
try:
|
||||||
|
# Match with min_confidence=0.0 so it returns EVERYTHING
|
||||||
|
matches = svc.match_document(doc.id, min_confidence=0.0)
|
||||||
|
print(f"Returned {len(matches)} matches.")
|
||||||
|
for m in matches:
|
||||||
|
print(f"Match: format_id={m.format_id}, score={m.confidence_score}")
|
||||||
|
print(f"Details: {m.match_details}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error matching: {e}")
|
||||||
@@ -83,6 +83,63 @@ def create_template(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.put(
|
||||||
|
"/{template_id}",
|
||||||
|
response_model=dict,
|
||||||
|
summary="Update Template",
|
||||||
|
description="Update an existing template name and its fields.",
|
||||||
|
)
|
||||||
|
def update_template(
|
||||||
|
template_id: uuid.UUID,
|
||||||
|
payload: TemplateCreateRequest,
|
||||||
|
current_user: CurrentUser,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
) -> dict:
|
||||||
|
"""Update a template."""
|
||||||
|
template_repo = TemplateRepository(db)
|
||||||
|
template = template_repo.get_by_id(template_id)
|
||||||
|
if not template:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"Template '{template_id}' not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
if template.name != payload.template_name:
|
||||||
|
template.name = payload.template_name
|
||||||
|
|
||||||
|
from app.models.template import DocumentCell
|
||||||
|
db.query(DocumentCell).filter(DocumentCell.format_id == template_id).delete()
|
||||||
|
|
||||||
|
saved_fields = []
|
||||||
|
cell_repo = DocumentCellRepository(db)
|
||||||
|
for index, field in enumerate(payload.fields):
|
||||||
|
cell = cell_repo.create_cell(
|
||||||
|
format_id=template.id,
|
||||||
|
page_number=1,
|
||||||
|
row_no=0,
|
||||||
|
column_no=0,
|
||||||
|
field_name=field.field_label,
|
||||||
|
data_type=field.field_type,
|
||||||
|
x=0.0,
|
||||||
|
y=0.0,
|
||||||
|
width=0.0,
|
||||||
|
height=0.0,
|
||||||
|
is_dynamic=True
|
||||||
|
)
|
||||||
|
saved_fields.append({
|
||||||
|
"field_label": cell.field_name,
|
||||||
|
"field_type": cell.data_type
|
||||||
|
})
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"pk_template_id": str(template.id),
|
||||||
|
"template_name": template.name,
|
||||||
|
"fields": saved_fields
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/{template_id}/mappings/save",
|
"/{template_id}/mappings/save",
|
||||||
response_model=SuccessResponse,
|
response_model=SuccessResponse,
|
||||||
@@ -109,6 +166,15 @@ def save_mappings(
|
|||||||
table_repo = TableFormatRepository(db)
|
table_repo = TableFormatRepository(db)
|
||||||
table_col_repo = TableColumnRepository(db)
|
table_col_repo = TableColumnRepository(db)
|
||||||
|
|
||||||
|
# Delete existing field mappings and table formats for this template
|
||||||
|
from app.models.template import DocumentRegion, TableFormat
|
||||||
|
db.query(DocumentRegion).filter(
|
||||||
|
DocumentRegion.format_id == template_id,
|
||||||
|
DocumentRegion.region_type == "field_mapping"
|
||||||
|
).delete()
|
||||||
|
db.query(TableFormat).filter(TableFormat.format_id == template_id).delete()
|
||||||
|
db.flush()
|
||||||
|
|
||||||
cells = cell_repo.get_template_cells(template_id)
|
cells = cell_repo.get_template_cells(template_id)
|
||||||
cell_map = {c.field_name: c for c in cells}
|
cell_map = {c.field_name: c for c in cells}
|
||||||
table_format = None
|
table_format = None
|
||||||
|
|||||||
@@ -145,6 +145,11 @@ class FingerprintService:
|
|||||||
if not template.table_formats:
|
if not template.table_formats:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Filter out dummy tables created by manual mappings (width=1000, height=1000)
|
||||||
|
valid_tables = [tf for tf in template.table_formats if tf.width != 1000.0 and tf.height != 1000.0]
|
||||||
|
if not valid_tables:
|
||||||
|
return None
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
@@ -156,15 +161,36 @@ class FingerprintService:
|
|||||||
"rows": tf.rows,
|
"rows": tf.rows,
|
||||||
"columns": tf.columns,
|
"columns": tf.columns,
|
||||||
}
|
}
|
||||||
for tf in template.table_formats
|
for tf in valid_tables
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
def _extract_cell_coordinates(self, template: DocumentFormat) -> dict[str, Any] | None:
|
def _extract_cell_coordinates(self, template: DocumentFormat) -> dict[str, Any] | None:
|
||||||
"""Extract cell coordinates from template."""
|
"""Extract cell coordinates from template."""
|
||||||
|
# For manually mapped templates, prefer the actual mapped regions
|
||||||
|
field_regions = [r for r in template.regions if r.region_type == "field_mapping"]
|
||||||
|
if field_regions:
|
||||||
|
return {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"page": r.page_number,
|
||||||
|
"x": r.x,
|
||||||
|
"y": r.y,
|
||||||
|
"width": r.width,
|
||||||
|
"height": r.height,
|
||||||
|
}
|
||||||
|
for r in field_regions
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Otherwise fallback to template cells, but skip if they are all 0,0 (unmapped placeholders)
|
||||||
if not template.cells:
|
if not template.cells:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
has_real_coords = any(c.width > 0 or c.height > 0 for c in template.cells)
|
||||||
|
if not has_real_coords:
|
||||||
|
return None
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
@@ -215,44 +241,49 @@ class FingerprintService:
|
|||||||
weights.append(3.0)
|
weights.append(3.0)
|
||||||
|
|
||||||
# Logo coordinates similarity
|
# Logo coordinates similarity
|
||||||
logo_score = self._compare_coordinates(
|
if fingerprint1.logo_coordinates and fingerprint1.logo_coordinates.get("items"):
|
||||||
fingerprint1.logo_coordinates,
|
logo_score = self._compare_coordinates(
|
||||||
fingerprint2_data.get("logo_coordinates"),
|
fingerprint1.logo_coordinates,
|
||||||
)
|
fingerprint2_data.get("logo_coordinates"),
|
||||||
scores.append(logo_score)
|
)
|
||||||
weights.append(2.0)
|
scores.append(logo_score)
|
||||||
|
weights.append(2.0)
|
||||||
|
|
||||||
# Header coordinates similarity
|
# Header coordinates similarity
|
||||||
header_score = self._compare_coordinates(
|
if fingerprint1.header_coordinates and fingerprint1.header_coordinates.get("items"):
|
||||||
fingerprint1.header_coordinates,
|
header_score = self._compare_coordinates(
|
||||||
fingerprint2_data.get("header_coordinates"),
|
fingerprint1.header_coordinates,
|
||||||
)
|
fingerprint2_data.get("header_coordinates"),
|
||||||
scores.append(header_score)
|
)
|
||||||
weights.append(2.0)
|
scores.append(header_score)
|
||||||
|
weights.append(2.0)
|
||||||
|
|
||||||
# Footer coordinates similarity
|
# Footer coordinates similarity
|
||||||
footer_score = self._compare_coordinates(
|
if fingerprint1.footer_coordinates and fingerprint1.footer_coordinates.get("items"):
|
||||||
fingerprint1.footer_coordinates,
|
footer_score = self._compare_coordinates(
|
||||||
fingerprint2_data.get("footer_coordinates"),
|
fingerprint1.footer_coordinates,
|
||||||
)
|
fingerprint2_data.get("footer_coordinates"),
|
||||||
scores.append(footer_score)
|
)
|
||||||
weights.append(1.5)
|
scores.append(footer_score)
|
||||||
|
weights.append(1.5)
|
||||||
|
|
||||||
# Table coordinates similarity
|
# Table coordinates similarity
|
||||||
table_score = self._compare_coordinates(
|
if fingerprint1.table_coordinates and fingerprint1.table_coordinates.get("items"):
|
||||||
fingerprint1.table_coordinates,
|
table_score = self._compare_coordinates(
|
||||||
fingerprint2_data.get("table_coordinates"),
|
fingerprint1.table_coordinates,
|
||||||
)
|
fingerprint2_data.get("table_coordinates"),
|
||||||
scores.append(table_score)
|
)
|
||||||
weights.append(2.5)
|
scores.append(table_score)
|
||||||
|
weights.append(2.0)
|
||||||
|
|
||||||
# Cell coordinates similarity
|
# Cell coordinates similarity
|
||||||
cell_score = self._compare_coordinates(
|
if fingerprint1.cell_coordinates and fingerprint1.cell_coordinates.get("items"):
|
||||||
fingerprint1.cell_coordinates,
|
cell_score = self._compare_coordinates(
|
||||||
fingerprint2_data.get("cell_coordinates"),
|
fingerprint1.cell_coordinates,
|
||||||
)
|
fingerprint2_data.get("cell_coordinates"),
|
||||||
scores.append(cell_score)
|
)
|
||||||
weights.append(1.5)
|
scores.append(cell_score)
|
||||||
|
weights.append(1.5)
|
||||||
|
|
||||||
# Weighted average
|
# Weighted average
|
||||||
total_weight = sum(weights)
|
total_weight = sum(weights)
|
||||||
|
|||||||
@@ -81,8 +81,12 @@ export class TemplateService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
createTemplate(data: { template_name: string, source_document_id?: string, fields: TemplateField[] }): Observable<Template> {
|
createTemplate(payload: any): Observable<any> {
|
||||||
return this.http.post<Template>(`${this.apiUrl}/templates`, data);
|
return this.http.post<any>(`${this.apiUrl}/templates`, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTemplate(templateId: string, payload: any): Observable<any> {
|
||||||
|
return this.http.put<any>(`${this.apiUrl}/templates/${templateId}`, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveMappings(templateId: string, mappings: any[]): Observable<any> {
|
saveMappings(templateId: string, mappings: any[]): Observable<any> {
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ export class TemplatesComponent implements OnInit {
|
|||||||
|
|
||||||
// UI State
|
// UI State
|
||||||
displayAddField: boolean = false;
|
displayAddField: boolean = false;
|
||||||
|
isScanning: boolean = false;
|
||||||
|
scanProgress: number = 0;
|
||||||
|
currentTemplateId: string | null = null;
|
||||||
newField: TemplateField = { field_label: '', field_type: 'TEXT', display_order: 0, required_flag: false };
|
newField: TemplateField = { field_label: '', field_type: 'TEXT', display_order: 0, required_flag: false };
|
||||||
fieldTypes = [
|
fieldTypes = [
|
||||||
{ label: 'TEXT', value: 'TEXT' },
|
{ label: 'TEXT', value: 'TEXT' },
|
||||||
@@ -67,6 +70,11 @@ export class TemplatesComponent implements OnInit {
|
|||||||
onFileUpload(event: any) {
|
onFileUpload(event: any) {
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
|
this.currentTemplateId = null;
|
||||||
|
this.templateName = '';
|
||||||
|
this.templateFields = [];
|
||||||
|
this.mappings = {};
|
||||||
|
|
||||||
this.templateService.uploadDocument(file).subscribe({
|
this.templateService.uploadDocument(file).subscribe({
|
||||||
next: (res) => {
|
next: (res) => {
|
||||||
this.documentId = res.id || res.pk_document_id;
|
this.documentId = res.id || res.pk_document_id;
|
||||||
@@ -126,11 +134,12 @@ export class TemplatesComponent implements OnInit {
|
|||||||
const match = matches[0];
|
const match = matches[0];
|
||||||
this.messageService.add({ severity: 'info', summary: 'Template Recognized', detail: `Confidence: ${(match.confidence_score * 100).toFixed(1)}%` });
|
this.messageService.add({ severity: 'info', summary: 'Template Recognized', detail: `Confidence: ${(match.confidence_score * 100).toFixed(1)}%` });
|
||||||
|
|
||||||
this.templateService.getTemplate(match.format_id).subscribe({
|
this.templateService.getTemplate(match.format_id).subscribe({
|
||||||
next: (templateData) => {
|
next: (templateData) => {
|
||||||
this.templateName = templateData.name;
|
this.templateName = templateData.name;
|
||||||
this.templateFields = [];
|
this.currentTemplateId = match.format_id;
|
||||||
this.mappings = {};
|
this.templateFields = [];
|
||||||
|
this.mappings = {};
|
||||||
|
|
||||||
if (templateData.cells) {
|
if (templateData.cells) {
|
||||||
templateData.cells.forEach((cell: any) => {
|
templateData.cells.forEach((cell: any) => {
|
||||||
@@ -221,11 +230,17 @@ export class TemplatesComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.templateService.createTemplate({
|
const payload = {
|
||||||
template_name: this.templateName,
|
template_name: this.templateName,
|
||||||
source_document_id: this.documentId || undefined,
|
source_document_id: this.documentId || undefined,
|
||||||
fields: this.templateFields
|
fields: this.templateFields
|
||||||
}).subscribe({
|
};
|
||||||
|
|
||||||
|
const saveRequest = this.currentTemplateId
|
||||||
|
? this.templateService.updateTemplate(this.currentTemplateId, payload)
|
||||||
|
: this.templateService.createTemplate(payload);
|
||||||
|
|
||||||
|
saveRequest.subscribe({
|
||||||
next: (res: any) => {
|
next: (res: any) => {
|
||||||
const templateId = res.pk_template_id;
|
const templateId = res.pk_template_id;
|
||||||
|
|
||||||
|
|||||||
14
print_cells.py
Normal file
14
print_cells.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.append(os.path.join(os.getcwd(), 'docengine'))
|
||||||
|
|
||||||
|
from app.core.database import SessionLocal
|
||||||
|
from app.models.template import DocumentFormat
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
t = db.query(DocumentFormat).filter(DocumentFormat.is_active == True).first()
|
||||||
|
if t:
|
||||||
|
print(f"Template Name: {t.name}")
|
||||||
|
print(f"Number of cells: {len(t.cells)}")
|
||||||
|
for c in t.cells:
|
||||||
|
print(f"Cell: x={c.x}, y={c.y}, w={c.width}, h={c.height}")
|
||||||
13
print_fp.py
Normal file
13
print_fp.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.append(os.path.join(os.getcwd(), 'docengine'))
|
||||||
|
|
||||||
|
from app.core.database import SessionLocal
|
||||||
|
from app.models.template import DocumentFormat
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
t = db.query(DocumentFormat).filter(DocumentFormat.is_active == True).first()
|
||||||
|
if t:
|
||||||
|
print(f"Template Name: {t.name}, Fingerprint is None: {t.fingerprint is None}")
|
||||||
|
else:
|
||||||
|
print("No active templates.")
|
||||||
18
print_fp2.py
Normal file
18
print_fp2.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
sys.path.append(os.path.join(os.getcwd(), 'docengine'))
|
||||||
|
|
||||||
|
from app.core.database import SessionLocal
|
||||||
|
from app.models.template import TemplateFingerprint
|
||||||
|
from app.models.template import DocumentFormat
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
t = db.query(DocumentFormat).filter(DocumentFormat.is_active == True).first()
|
||||||
|
if t:
|
||||||
|
fp = db.query(TemplateFingerprint).filter(TemplateFingerprint.format_id == t.id).first()
|
||||||
|
if fp:
|
||||||
|
print(f"Cell Coords items: {len(fp.cell_coordinates.get('items', [])) if fp.cell_coordinates else 'None'}")
|
||||||
|
print(json.dumps(fp.cell_coordinates, indent=2))
|
||||||
|
else:
|
||||||
|
print("No TemplateFingerprint row found!")
|
||||||
21
regenerate_fingerprints.py
Normal file
21
regenerate_fingerprints.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.append(os.path.join(os.getcwd(), 'docengine'))
|
||||||
|
|
||||||
|
from app.core.database import SessionLocal
|
||||||
|
from app.models.template import DocumentFormat
|
||||||
|
from app.services.fingerprint_service import FingerprintService
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
templates = db.query(DocumentFormat).filter(DocumentFormat.is_active == True).all()
|
||||||
|
|
||||||
|
fp_service = FingerprintService(db)
|
||||||
|
count = 0
|
||||||
|
for t in templates:
|
||||||
|
# Refresh the relations
|
||||||
|
db.refresh(t)
|
||||||
|
fp_service.generate_fingerprint(t)
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
print(f"Regenerated fingerprints for {count} active templates.")
|
||||||
Reference in New Issue
Block a user