1# Copyright (c) 2010-2024 openpyxl
2
3import datetime
4
5from openpyxl.descriptors import (
6 DateTime,
7 Alias,
8)
9from openpyxl.descriptors.serialisable import Serialisable
10from openpyxl.descriptors.nested import NestedText
11from openpyxl.xml.functions import (
12 Element,
13 QName,
14)
15from openpyxl.xml.constants import (
16 COREPROPS_NS,
17 DCORE_NS,
18 XSI_NS,
19 DCTERMS_NS,
20)
21
22
23class NestedDateTime(DateTime, NestedText):
24
25 expected_type = datetime.datetime
26
27 def to_tree(self, tagname=None, value=None, namespace=None):
28 namespace = getattr(self, "namespace", namespace)
29 if namespace is not None:
30 tagname = "{%s}%s" % (namespace, tagname)
31 el = Element(tagname)
32 if value is not None:
33 value = value.replace(tzinfo=None)
34 el.text = value.isoformat(timespec="seconds") + 'Z'
35 return el
36
37
38class QualifiedDateTime(NestedDateTime):
39
40 """In certain situations Excel will complain if the additional type
41 attribute isn't set"""
42
43 def to_tree(self, tagname=None, value=None, namespace=None):
44 el = super().to_tree(tagname, value, namespace)
45 el.set("{%s}type" % XSI_NS, QName(DCTERMS_NS, "W3CDTF"))
46 return el
47
48
49class DocumentProperties(Serialisable):
50 """High-level properties of the document.
51 Defined in ECMA-376 Par2 Annex D
52 """
53
54 tagname = "coreProperties"
55 namespace = COREPROPS_NS
56
57 category = NestedText(expected_type=str, allow_none=True)
58 contentStatus = NestedText(expected_type=str, allow_none=True)
59 keywords = NestedText(expected_type=str, allow_none=True)
60 lastModifiedBy = NestedText(expected_type=str, allow_none=True)
61 lastPrinted = NestedDateTime(allow_none=True)
62 revision = NestedText(expected_type=str, allow_none=True)
63 version = NestedText(expected_type=str, allow_none=True)
64 last_modified_by = Alias("lastModifiedBy")
65
66 # Dublin Core Properties
67 subject = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
68 title = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
69 creator = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
70 description = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
71 identifier = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
72 language = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
73 # Dublin Core Terms
74 created = QualifiedDateTime(allow_none=True, namespace=DCTERMS_NS) # assumed to be UTC
75 modified = QualifiedDateTime(allow_none=True, namespace=DCTERMS_NS) # assumed to be UTC
76
77 __elements__ = ("creator", "title", "description", "subject","identifier",
78 "language", "created", "modified", "lastModifiedBy", "category",
79 "contentStatus", "version", "revision", "keywords", "lastPrinted",
80 )
81
82
83 def __init__(self,
84 category=None,
85 contentStatus=None,
86 keywords=None,
87 lastModifiedBy=None,
88 lastPrinted=None,
89 revision=None,
90 version=None,
91 created=None,
92 creator="openpyxl",
93 description=None,
94 identifier=None,
95 language=None,
96 modified=None,
97 subject=None,
98 title=None,
99 ):
100 now = datetime.datetime.now(tz=datetime.timezone.utc).replace(tzinfo=None)
101 self.contentStatus = contentStatus
102 self.lastPrinted = lastPrinted
103 self.revision = revision
104 self.version = version
105 self.creator = creator
106 self.lastModifiedBy = lastModifiedBy
107 self.modified = modified or now
108 self.created = created or now
109 self.title = title
110 self.subject = subject
111 self.description = description
112 self.identifier = identifier
113 self.language = language
114 self.keywords = keywords
115 self.category = category