Files
Igor20264 510f7e7adf
Python application / build (push) Waiting to run
v0.5.2
Что то сделал
2026-09-08 19:37:54 +03:00

149 lines
4.7 KiB
Python

"""DOCX core properties (File → Info) for generated reports."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from getpass import getuser
from typing import Any
CORE_PROP_MAX = 255
AUTHOR_SOURCES = ("os", "student", "custom")
DEFAULT_AUTHOR_SOURCE = "os"
DEFAULT_DOC_COMMENTS = "Создано при помощи md2gost (ТЗ МИРЭА)"
def os_user() -> str:
try:
return (getuser() or "").strip()
except Exception:
return ""
def clip_core(value: str | None) -> str:
text = "" if value is None else str(value)
if len(text) > CORE_PROP_MAX:
return text[:CORE_PROP_MAX]
return text
def _normalize_source(value: str | None) -> str:
src = (value or DEFAULT_AUTHOR_SOURCE).strip().lower()
return src if src in AUTHOR_SOURCES else DEFAULT_AUTHOR_SOURCE
@dataclass
class DocumentMetadata:
author_source: str = DEFAULT_AUTHOR_SOURCE
author: str = ""
last_modified_by: str = ""
title: str = ""
subject: str = ""
keywords: str = ""
comments: str = DEFAULT_DOC_COMMENTS
category: str = ""
def to_dict(self) -> dict[str, str]:
return {
"author_source": _normalize_source(self.author_source),
"author": (self.author or "").strip(),
"last_modified_by": (self.last_modified_by or "").strip(),
"title": (self.title or "").strip(),
"subject": (self.subject or "").strip(),
"keywords": (self.keywords or "").strip(),
"comments": "" if self.comments is None else str(self.comments).strip(),
"category": (self.category or "").strip(),
}
@classmethod
def from_dict(cls, raw: object) -> DocumentMetadata:
if not isinstance(raw, dict):
return cls()
comments = raw.get("comments")
if comments is None:
comments_s = DEFAULT_DOC_COMMENTS
else:
comments_s = str(comments).strip()
return cls(
author_source=_normalize_source(str(raw.get("author_source") or "")),
author=str(raw.get("author") or "").strip(),
last_modified_by=str(raw.get("last_modified_by") or "").strip(),
title=str(raw.get("title") or "").strip(),
subject=str(raw.get("subject") or "").strip(),
keywords=str(raw.get("keywords") or "").strip(),
comments=comments_s,
category=str(raw.get("category") or "").strip(),
)
def _pick(override: str | None, fallback: str) -> str:
if override is not None:
return str(override)
return fallback
def resolve_document_metadata(
*,
stored: DocumentMetadata | None = None,
student: str | None = None,
author_source: str | None = None,
author: str | None = None,
last_modified_by: str | None = None,
title: str | None = None,
subject: str | None = None,
keywords: str | None = None,
comments: str | None = None,
category: str | None = None,
) -> DocumentMetadata:
"""Merge CLI/request overrides over the saved profile into concrete core properties."""
base = stored or DocumentMetadata()
src = _normalize_source(author_source or base.author_source)
if author is not None:
resolved_author = str(author).strip()
elif src == "custom":
resolved_author = (base.author or "").strip()
elif src == "student":
resolved_author = (student or "").strip()
else:
resolved_author = ""
if not resolved_author:
resolved_author = os_user()
last_mod = _pick(last_modified_by, base.last_modified_by).strip()
if not last_mod:
last_mod = resolved_author
return DocumentMetadata(
author_source=src,
author=resolved_author,
last_modified_by=last_mod,
title=_pick(title, base.title).strip(),
subject=_pick(subject, base.subject).strip(),
keywords=_pick(keywords, base.keywords).strip(),
comments=_pick(comments, base.comments).strip(),
category=_pick(category, base.category).strip(),
)
def apply_document_metadata(
document: Any,
meta: DocumentMetadata,
*,
now: datetime | None = None,
) -> None:
stamp = now or datetime.utcnow()
if stamp.tzinfo is not None:
stamp = stamp.replace(tzinfo=None)
cp = document.core_properties
cp.author = clip_core(meta.author)
cp.last_modified_by = clip_core(meta.last_modified_by or meta.author)
cp.title = clip_core(meta.title)
cp.subject = clip_core(meta.subject)
cp.keywords = clip_core(meta.keywords)
cp.comments = clip_core(meta.comments)
cp.category = clip_core(meta.category)
cp.created = stamp
cp.modified = stamp
cp.revision = 1