Fixed ui and template mapping issue
This commit is contained in:
@@ -10,7 +10,13 @@ from app.core.database import get_db
|
||||
from app.core.dependencies import CurrentUser
|
||||
from app.core.logging_config import get_logger
|
||||
from app.repositories.document_repository import DocumentRepository, TemplateMatchRepository
|
||||
from app.repositories.template_repository import TemplateRepository
|
||||
from app.repositories.template_repository import (
|
||||
TemplateRepository,
|
||||
DocumentRegionRepository,
|
||||
DocumentCellRepository,
|
||||
TableFormatRepository,
|
||||
TableColumnRepository
|
||||
)
|
||||
from app.schemas.common import PaginatedResponse, SuccessResponse
|
||||
from app.schemas.document import TemplateMatchRequest, TemplateMatchResponse
|
||||
from app.schemas.template import (
|
||||
@@ -18,6 +24,8 @@ from app.schemas.template import (
|
||||
TemplateRenderRequest,
|
||||
TemplateRenderResponse,
|
||||
TemplateResponse,
|
||||
TemplateCreateRequest,
|
||||
TemplateMappingSaveRequest,
|
||||
)
|
||||
|
||||
logger = get_logger(__name__)
|
||||
@@ -25,6 +33,132 @@ logger = get_logger(__name__)
|
||||
router = APIRouter(prefix="/templates", tags=["Templates"])
|
||||
|
||||
|
||||
@router.post(
|
||||
"",
|
||||
response_model=dict,
|
||||
summary="Create Template",
|
||||
description="Create a new template.",
|
||||
)
|
||||
def create_template(
|
||||
payload: TemplateCreateRequest,
|
||||
current_user: CurrentUser,
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict:
|
||||
"""Create a template."""
|
||||
template_repo = TemplateRepository(db)
|
||||
|
||||
template = template_repo.create_template(
|
||||
name=payload.template_name,
|
||||
page_width=1000.0,
|
||||
page_height=1000.0,
|
||||
source_document_id=uuid.UUID(payload.source_document_id) if payload.source_document_id else None
|
||||
)
|
||||
cell_repo = DocumentCellRepository(db)
|
||||
saved_fields = []
|
||||
|
||||
for index, field in enumerate(payload.fields):
|
||||
cell = cell_repo.create_cell(
|
||||
format_id=template.id,
|
||||
page_number=1,
|
||||
x=0.0,
|
||||
y=0.0,
|
||||
width=0.0,
|
||||
height=0.0,
|
||||
data_type=field.field_type,
|
||||
field_name=field.field_label,
|
||||
sequence=field.display_order or index,
|
||||
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(
|
||||
"/{template_id}/mappings/save",
|
||||
response_model=SuccessResponse,
|
||||
summary="Save Template Mappings",
|
||||
description="Save the field mappings for a template.",
|
||||
)
|
||||
def save_mappings(
|
||||
template_id: uuid.UUID,
|
||||
payload: list[TemplateMappingSaveRequest],
|
||||
current_user: CurrentUser,
|
||||
db: Session = Depends(get_db),
|
||||
) -> SuccessResponse:
|
||||
"""Save template mappings."""
|
||||
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",
|
||||
)
|
||||
|
||||
region_repo = DocumentRegionRepository(db)
|
||||
cell_repo = DocumentCellRepository(db)
|
||||
table_repo = TableFormatRepository(db)
|
||||
table_col_repo = TableColumnRepository(db)
|
||||
|
||||
cells = cell_repo.get_template_cells(template_id)
|
||||
cell_map = {c.field_name: c for c in cells}
|
||||
table_format = None
|
||||
|
||||
# Save each mapped node
|
||||
for mapping in payload:
|
||||
cell = cell_map.get(mapping.field_name)
|
||||
is_table_column = cell and cell.data_type == 'TABLE_COLUMN'
|
||||
|
||||
for node_idx, node in enumerate(mapping.mapped_nodes):
|
||||
if is_table_column:
|
||||
if not table_format:
|
||||
table_format = table_repo.create_table_format(
|
||||
format_id=template_id,
|
||||
page_number=node.page_no or 1,
|
||||
x=0.0, y=0.0, width=1000.0, height=1000.0,
|
||||
rows=1, columns=10
|
||||
)
|
||||
table_col_repo.create_column(
|
||||
table_format_id=table_format.id,
|
||||
column_index=node_idx,
|
||||
width=node.width,
|
||||
header_text=mapping.field_name,
|
||||
data_type="text"
|
||||
)
|
||||
|
||||
region_repo.create_region(
|
||||
format_id=template_id,
|
||||
page_number=node.page_no or 1,
|
||||
region_type="field_mapping",
|
||||
x=node.x_coordinate,
|
||||
y=node.y_coordinate,
|
||||
width=node.width,
|
||||
height=node.height,
|
||||
content={
|
||||
"field_name": mapping.field_name,
|
||||
"text_value": node.text_value
|
||||
}
|
||||
)
|
||||
|
||||
db.commit()
|
||||
|
||||
# Generate fingerprint now that mappings are populated
|
||||
from app.services.fingerprint_service import FingerprintService
|
||||
FingerprintService(db).generate_fingerprint(template)
|
||||
db.commit()
|
||||
|
||||
return SuccessResponse(message="Mappings saved successfully")
|
||||
|
||||
|
||||
@router.get(
|
||||
"",
|
||||
response_model=PaginatedResponse[TemplateListResponse],
|
||||
@@ -32,9 +166,9 @@ router = APIRouter(prefix="/templates", tags=["Templates"])
|
||||
description="List all active templates with pagination.",
|
||||
)
|
||||
def list_templates(
|
||||
current_user: CurrentUser,
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=100),
|
||||
current_user: CurrentUser = None,
|
||||
db: Session = Depends(get_db),
|
||||
) -> PaginatedResponse[TemplateListResponse]:
|
||||
"""List all active templates."""
|
||||
@@ -61,7 +195,7 @@ def list_templates(
|
||||
)
|
||||
def get_template(
|
||||
template_id: uuid.UUID,
|
||||
current_user: CurrentUser = None,
|
||||
current_user: CurrentUser,
|
||||
db: Session = Depends(get_db),
|
||||
) -> TemplateResponse:
|
||||
"""Get a template by ID."""
|
||||
@@ -83,7 +217,7 @@ def get_template(
|
||||
)
|
||||
def delete_template(
|
||||
template_id: uuid.UUID,
|
||||
current_user: CurrentUser = None,
|
||||
current_user: CurrentUser,
|
||||
db: Session = Depends(get_db),
|
||||
) -> SuccessResponse:
|
||||
"""Soft-delete a template."""
|
||||
@@ -109,7 +243,7 @@ def delete_template(
|
||||
)
|
||||
def match_template(
|
||||
payload: TemplateMatchRequest,
|
||||
current_user: CurrentUser = None,
|
||||
current_user: CurrentUser,
|
||||
db: Session = Depends(get_db),
|
||||
) -> list[TemplateMatchResponse]:
|
||||
"""Match a document against existing templates."""
|
||||
@@ -162,7 +296,7 @@ def match_template(
|
||||
)
|
||||
def render_template(
|
||||
payload: TemplateRenderRequest,
|
||||
current_user: CurrentUser = None,
|
||||
current_user: CurrentUser,
|
||||
db: Session = Depends(get_db),
|
||||
) -> TemplateRenderResponse:
|
||||
"""Render a template to PDF."""
|
||||
@@ -199,8 +333,8 @@ def render_template(
|
||||
)
|
||||
def download_rendered_pdf(
|
||||
template_id: uuid.UUID,
|
||||
current_user: CurrentUser,
|
||||
filename: str = Query(..., description="Filename of the rendered PDF"),
|
||||
current_user: CurrentUser = None,
|
||||
db: Session = Depends(get_db),
|
||||
) -> FileResponse:
|
||||
"""Download a rendered PDF."""
|
||||
|
||||
@@ -70,6 +70,7 @@ class TemplateRepository(BaseRepository[DocumentFormat]):
|
||||
fingerprint: dict | None = None,
|
||||
source_document_id: uuid.UUID | None = None,
|
||||
created_by: uuid.UUID | None = None,
|
||||
is_active: bool = True,
|
||||
) -> DocumentFormat:
|
||||
"""Create a new template."""
|
||||
template = DocumentFormat(
|
||||
@@ -85,6 +86,7 @@ class TemplateRepository(BaseRepository[DocumentFormat]):
|
||||
fingerprint=fingerprint,
|
||||
source_document_id=source_document_id,
|
||||
created_by=created_by,
|
||||
is_active=is_active,
|
||||
)
|
||||
return self.create(template)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from typing import Any, List, Optional, Union
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
@@ -248,3 +248,30 @@ class TemplateRenderResponse(BaseSchema):
|
||||
file_size: int
|
||||
page_count: int
|
||||
rendered_at: datetime
|
||||
|
||||
|
||||
class TemplateFieldCreate(BaseSchema):
|
||||
field_label: str
|
||||
field_type: str = "text"
|
||||
display_order: int = 0
|
||||
required_flag: bool = False
|
||||
|
||||
class TemplateCreateRequest(BaseSchema):
|
||||
template_name: str
|
||||
source_document_id: Optional[str] = None
|
||||
fields: List[TemplateFieldCreate] = Field(default_factory=list)
|
||||
|
||||
class MappingNodeRequest(BaseSchema):
|
||||
pk_document_data_id: Union[int, str, None] = None
|
||||
x_coordinate: float
|
||||
y_coordinate: float
|
||||
width: float
|
||||
height: float
|
||||
text_value: Optional[str] = None
|
||||
page_width: Optional[float] = None
|
||||
page_height: Optional[float] = None
|
||||
page_no: Optional[int] = None
|
||||
|
||||
class TemplateMappingSaveRequest(BaseSchema):
|
||||
field_name: str
|
||||
mapped_nodes: List[MappingNodeRequest] = Field(default_factory=list)
|
||||
|
||||
@@ -58,11 +58,7 @@ class DocumentProcessingService:
|
||||
metadata["layout"] = layout_results
|
||||
document.document_metadata = metadata
|
||||
|
||||
# Step 3: Generate template
|
||||
self.db.refresh(document)
|
||||
template = self.template_service.generate_template(document)
|
||||
|
||||
# Step 4: Update document status
|
||||
# Step 3: Update document status
|
||||
self.doc_repo.update_status(document_id, "completed")
|
||||
self.db.commit()
|
||||
|
||||
@@ -70,7 +66,6 @@ class DocumentProcessingService:
|
||||
"processing_completed",
|
||||
document_id=str(document_id),
|
||||
pages=document.page_count,
|
||||
template_id=str(template.id),
|
||||
)
|
||||
|
||||
return document
|
||||
|
||||
@@ -187,7 +187,7 @@ class FingerprintService:
|
||||
serialized = json.dumps(normalized, sort_keys=True, default=str)
|
||||
return hashlib.sha256(serialized.encode()).hexdigest()
|
||||
|
||||
def _normalize_coordinates(self, data: dict[str, Any]) -> dict[str, Any]:
|
||||
def _normalize_coordinates(self, data: Any) -> Any:
|
||||
"""Normalize coordinates by rounding to reduce sensitivity to small variations."""
|
||||
if isinstance(data, dict):
|
||||
return {k: self._normalize_coordinates(v) for k, v in data.items()}
|
||||
|
||||
@@ -80,6 +80,7 @@ class TemplateService:
|
||||
description=f"Auto-generated template from {document.original_filename}",
|
||||
source_document_id=document.id,
|
||||
created_by=user_id,
|
||||
is_active=False,
|
||||
)
|
||||
|
||||
# Process each page
|
||||
|
||||
Reference in New Issue
Block a user