@@ -0,0 +1,148 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user