Files
finance/backend/app/imports/readers.py
T
k1nq d582f15681 feat: add tests for import polling, login page, money utilities, and query keys
- Implemented tests for import polling functionality, ensuring correct behavior for active and terminal import statuses.
- Created tests for the login page, validating user interactions and error handling.
- Added unit tests for money utility functions, covering parsing, formatting, and summation of monetary values.
- Developed tests for query keys, ensuring proper scoping and invalidation roots.

chore: setup testing environment and configuration

- Added setup file for testing with Vitest and Testing Library.
- Configured TypeScript and Vite for the frontend project.
- Created Docker and Nginx configurations for local development and production environments.
- Established a PostgreSQL initialization script for setting up application roles.

docs: add README and environment configuration for infra

- Included README for the infra directory, detailing local development setup and service descriptions.
- Created an example environment configuration file for local development with placeholders for sensitive information.
2026-09-14 09:26:33 +05:00

58 lines
1.7 KiB
Python

import io
import zipfile
from collections.abc import Iterator
from contextlib import contextmanager
import defusedxml # noqa: F401 (hardened XML stack must be importable for openpyxl)
import pdfplumber
from openpyxl import load_workbook
from app.config import get_settings
from app.errors import AppError
def _quota_error() -> AppError:
return AppError(
"Document exceeds parser resource quotas",
code="IMPORT_QUOTA_EXCEEDED",
status_code=413,
)
@contextmanager
def open_xlsx(raw: bytes) -> Iterator:
settings = get_settings()
try:
with zipfile.ZipFile(io.BytesIO(raw)) as zf:
total_uncompressed = 0
for info in zf.infolist():
if info.flag_bits & 0x1:
raise AppError(
"Encrypted archives are not supported",
code="IMPORT_ENCRYPTED_ARCHIVE",
status_code=422,
)
total_uncompressed += info.file_size
if total_uncompressed > settings.max_archive_uncompressed_bytes:
raise _quota_error()
except zipfile.BadZipFile as exc:
raise AppError(
"File is not a valid XLSX container",
code="IMPORT_FILE_CORRUPT",
status_code=422,
) from exc
workbook = load_workbook(io.BytesIO(raw), read_only=True, data_only=True, keep_links=False)
try:
yield workbook
finally:
workbook.close()
@contextmanager
def open_pdf(raw: bytes) -> Iterator:
settings = get_settings()
with pdfplumber.open(io.BytesIO(raw)) as pdf:
if len(pdf.pages) > settings.max_pdf_pages:
raise _quota_error()
yield pdf