from enum import Enum, IntFlag, auto
from marshmallow import RAISE, fields, validate
from parseur.schemas import BaseSchema
from parseur.schemas.document import DocumentStatus
from parseur.schemas.paserfield import (
ParserFieldReadSchema,
ParserFieldWriteSchema,
TableFieldReadSchema,
)
from parseur.schemas.webhook import WebhookSchema
[docs]SUPPORTED_FILE_EXTENSIONS = frozenset(
{
"bmp",
"csv",
"doc",
"docx",
"eml",
"gif",
"html",
"ics",
"jpg",
"mbox",
"msg",
"odp",
"ods",
"odt",
"pdf",
"png",
"ppt",
"pptx",
"rtf",
"tif",
"txt",
"xhtml",
"xls",
"xlsm",
"xlsx",
"xml",
"zip",
}
)
# Read/write field mixins for the metadata columns, generated from Metadata so
# the column list is never repeated. Read keeps them lenient (server output);
# write exposes them as optional toggles.
[docs]class AIEngine(str, Enum):
"""
Enumeration of AI engines that can be set on a mailbox when creating or
updating it.
The values mirror the ``parser.ai_engine`` choices returned by the
``/bootstrap`` endpoint.
Members:
- `DISABLED`: No AI engine (template-based parsing only).
- `GCP_AI_2_5`: AI Text engine v2.5 (analyzes extracted text).
- `GCP_AI_3_TXT`: AI Text engine v3 (analyzes extracted text).
- `GCP_AI_2`: AI Vision engine v3 (understands layout and images).
"""
[docs] GCP_AI_2_5 = "GCP_AI_2_5"
[docs] GCP_AI_3_TXT = "GCP_AI_3_TXT"
[docs]class IdentificationStatus(str, Enum):
"""Identification status accepted when creating/updating a mailbox."""
[docs] REQUESTED = "REQUESTED"
[docs] COMPLETED = "COMPLETED"
[docs]class DecimalSeparator(str, Enum):
"""Decimal separator for numbers in documents (the ``decimal_separator``)."""
[docs]class SplitKeyWordsSchema(BaseSchema):
[docs] is_before = fields.Boolean(required=True)
[docs] keyword = fields.String(required=True)
[docs]class MailboxBaseSchema(BaseSchema):
"""
Mailbox settings shared by the read and write representations.
These fields have the same definition whether a mailbox is read from the
API or sent to it, so they are declared once here and inherited by both
:class:`MailboxReadSchema` and :class:`MailboxWriteSchema`.
"""
[docs] ai_instructions = fields.String(allow_none=True)
[docs] decimal_separator = fields.String(
allow_none=True,
# "" kept for read leniency (a mailbox may report no override).
validate=validate.OneOf(
[e.value for e in DecimalSeparator] + [""],
error="Must be '.', ',' or null.",
),
)
[docs] default_timezone = fields.String(allow_none=True)
[docs] default_language = fields.String(allow_none=True)
# Input date format for parsing dates. Accepts "MONTH_FIRST", "DAY_FIRST", or None.
# MONTH_FIRST: mm/dd/yyyy, mm-dd-yyyy
# DAY_FIRST: dd/mm/yyyy, dd-mm-yyyy
# Parseur will automatically delete documents once they get older than the selected threshold.
[docs] retention_policy = fields.Int(allow_none=True)
# List of allowed file extensions for document processing.
# Example: ["pdf", "docx", "png"]
[docs] allowed_extensions = fields.List(fields.String(), allow_none=True)
# Email sender block/allow list.
# True = allowlist mode (only allow listed senders).
# False = blocklist mode (block listed senders).
[docs] use_whitelist_instead_of_blacklist = fields.Boolean(allow_none=True)
[docs] emails_or_domains = fields.List(fields.String(), allow_none=True)
# Page processing: only this page ranges. (same as split_page_range_set)
[docs] page_range_set = fields.Nested(PageRangeSchema, allow_none=True, many=True)
# Split documents every N pages.
[docs] split_page = fields.Int(allow_none=True)
# Split documents by page ranges.
# Example input: 1-5, 8, 11-13
# Enter ranges separated by commas. Use brackets to count from the end.
# E.g., (1) is last page. Example: 1, 2-(1) splits into two docs:
# - first page only
# - from page 2 to the end.
[docs] split_page_range_set = fields.Nested(PageRangeSchema, allow_none=True, many=True)
# Split documents by keywords.
# Enter the list of keywords to split on.
# Supports splitting before or after keywords.
# Keywords are case-sensitive.
[docs] split_keywords = fields.Nested(SplitKeyWordsSchema, allow_none=True, many=True)
[docs]class MailboxReadSchema(MailboxBaseSchema, MetadataReadFields):
"""Schema for a mailbox as returned by the API."""
[docs] id = fields.Int(required=True)
[docs] name = fields.String(required=True)
[docs] email_prefix = fields.String(required=True)
[docs] account_uuid = fields.String(required=True)
[docs] ai_engine = fields.String(required=True)
# AI document splitting
[docs] is_ai_split_enabled = fields.Boolean(allow_none=True)
[docs] ai_split_instructions = fields.String(allow_none=True)
# Email processing: process emails and attachments.
[docs] process_attachments = fields.Boolean(required=True)
# Email processing: process attachments only. Skip emails.
[docs] attachments_only = fields.Boolean(required=True)
# Page processing: only even pages (2, 4, 6, ...) / odd pages (1, 3, 5, ...)
[docs] even_pages = fields.Boolean(required=True)
[docs] odd_pages = fields.Boolean(required=True)
# Counters
[docs] document_count = fields.Int(allow_none=True)
[docs] webhook_count = fields.Int(allow_none=True)
[docs] template_count = fields.Int(allow_none=True)
[docs] parser_object_count = fields.Int(allow_none=True)
# Document per status count
[docs] document_per_status_count = fields.Dict(
keys=fields.String(validate=validate.OneOf([e.value for e in DocumentStatus])),
values=fields.Int(),
required=True,
)
# Last activity and modification timestamps
[docs] last_activity = fields.DateTime(allow_none=True)
[docs] template_set_last_modified = fields.DateTime(allow_none=True)
[docs] parser_object_set_last_modified = fields.DateTime(allow_none=True)
# URLs
[docs] csv_download = fields.String(allow_none=True)
[docs] json_download = fields.String(allow_none=True)
[docs] xls_download = fields.String(allow_none=True)
# Webhooks
[docs] available_webhook_set = fields.List(fields.Nested(WebhookSchema), required=True)
[docs] webhook_set = fields.List(fields.Nested(WebhookSchema), required=True)
# Parser and tables fields
[docs] table_set = fields.List(fields.Nested(TableFieldReadSchema))
[docs] parser_object_set = fields.List(fields.Nested(ParserFieldReadSchema))
[docs]class MailboxWriteSchema(MailboxBaseSchema, MetadataWriteFields):
"""
Schema describing the writable fields of a mailbox.
Used to validate and serialize the request body sent when creating or
updating a mailbox. Every field is optional, but unknown fields are
rejected so that typos and read-only fields never reach the API silently.
"""
[docs] ai_engine = fields.String(
validate=validate.OneOf(
[e.value for e in AIEngine],
error="Must be one of: " + ", ".join(e.value for e in AIEngine) + ".",
)
)
[docs] identification_status = fields.String(
validate=validate.OneOf([e.value for e in IdentificationStatus])
)
[docs] process_attachments = fields.Boolean()
[docs] attachments_only = fields.Boolean()
[docs] even_pages = fields.Boolean()
[docs] odd_pages = fields.Boolean()
# AI document splitting (the page-range / N-page / keyword split fields are
# inherited as writable from MailboxBaseSchema).
[docs] is_ai_split_enabled = fields.Boolean(allow_none=True)
[docs] ai_split_instructions = fields.String(allow_none=True)
# "Files to process": validated against the supported extensions on write
[docs] allowed_extensions = fields.List(
fields.String(validate=validate.OneOf(sorted(SUPPORTED_FILE_EXTENSIONS))),
allow_none=True,
)
# Field definitions, validated against the writable field schema.
[docs] parser_object_set = fields.Nested(
ParserFieldWriteSchema, many=True, allow_none=True
)
[docs]class MailboxCreateSchema(MailboxWriteSchema):
"""Validate/serialize the body of a ``POST /parser`` (create) request."""
[docs]class MailboxUpdateSchema(MailboxWriteSchema):
"""Validate/serialize the body of a ``PUT /parser/{id}`` (update) request."""
[docs] id = fields.Int(required=True)