extract-msg 0.48.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- extract_msg/__init__.py +71 -0
- extract_msg/__main__.py +115 -0
- extract_msg/_rtf/__init__.py +21 -0
- extract_msg/_rtf/create_doc.py +29 -0
- extract_msg/_rtf/inject_rtf.py +174 -0
- extract_msg/_rtf/token.py +35 -0
- extract_msg/_rtf/tokenize_rtf.py +253 -0
- extract_msg/attachments/__init__.py +144 -0
- extract_msg/attachments/attachment.py +188 -0
- extract_msg/attachments/attachment_base.py +730 -0
- extract_msg/attachments/broken_att.py +42 -0
- extract_msg/attachments/custom_att.py +173 -0
- extract_msg/attachments/custom_att_handler/__init__.py +80 -0
- extract_msg/attachments/custom_att_handler/custom_handler.py +134 -0
- extract_msg/attachments/custom_att_handler/lnk_obj_att.py +109 -0
- extract_msg/attachments/custom_att_handler/outlook_image_dib.py +141 -0
- extract_msg/attachments/emb_msg_att.py +134 -0
- extract_msg/attachments/signed_att.py +234 -0
- extract_msg/attachments/unsupported_att.py +40 -0
- extract_msg/attachments/web_att.py +74 -0
- extract_msg/constants/__init__.py +294 -0
- extract_msg/constants/ps.py +46 -0
- extract_msg/constants/re.py +42 -0
- extract_msg/constants/st.py +110 -0
- extract_msg/data/logging-config/logging-nt.json +64 -0
- extract_msg/data/logging-config/logging-posix.json +64 -0
- extract_msg/encoding/__init__.py +277 -0
- extract_msg/encoding/_dt/__init__.py +4 -0
- extract_msg/encoding/_dt/_mac_ce.py +8 -0
- extract_msg/encoding/_dt/_mac_cyrillic.py +8 -0
- extract_msg/encoding/_dt/_mac_greek.py +8 -0
- extract_msg/encoding/_dt/_mac_iceland.py +8 -0
- extract_msg/encoding/_dt/_mac_turkish.py +8 -0
- extract_msg/encoding/_dt/_win874_dec.py +8 -0
- extract_msg/encoding/_dt/_win950_dec.py +6 -0
- extract_msg/encoding/utils.py +329 -0
- extract_msg/enums.py +2067 -0
- extract_msg/exceptions.py +170 -0
- extract_msg/msg_classes/__init__.py +49 -0
- extract_msg/msg_classes/appointment.py +195 -0
- extract_msg/msg_classes/calendar.py +92 -0
- extract_msg/msg_classes/calendar_base.py +496 -0
- extract_msg/msg_classes/contact.py +1335 -0
- extract_msg/msg_classes/journal.py +131 -0
- extract_msg/msg_classes/meeting_cancellation.py +108 -0
- extract_msg/msg_classes/meeting_exception.py +55 -0
- extract_msg/msg_classes/meeting_forward.py +115 -0
- extract_msg/msg_classes/meeting_related.py +65 -0
- extract_msg/msg_classes/meeting_request.py +189 -0
- extract_msg/msg_classes/meeting_response.py +74 -0
- extract_msg/msg_classes/message.py +12 -0
- extract_msg/msg_classes/message_base.py +1382 -0
- extract_msg/msg_classes/message_signed.py +17 -0
- extract_msg/msg_classes/message_signed_base.py +168 -0
- extract_msg/msg_classes/msg.py +1172 -0
- extract_msg/msg_classes/post.py +53 -0
- extract_msg/msg_classes/sticky_note.py +67 -0
- extract_msg/msg_classes/task.py +363 -0
- extract_msg/msg_classes/task_request.py +86 -0
- extract_msg/null_date.py +34 -0
- extract_msg/ole_writer.py +1086 -0
- extract_msg/open_msg.py +204 -0
- extract_msg/properties/__init__.py +23 -0
- extract_msg/properties/named.py +481 -0
- extract_msg/properties/prop.py +506 -0
- extract_msg/properties/properties_store.py +476 -0
- extract_msg/py.typed +0 -0
- extract_msg/recipient.py +362 -0
- extract_msg/structures/__init__.py +34 -0
- extract_msg/structures/_helpers.py +345 -0
- extract_msg/structures/business_card.py +422 -0
- extract_msg/structures/cfoas.py +108 -0
- extract_msg/structures/contact_link_entry.py +51 -0
- extract_msg/structures/dev_mode_a.py +621 -0
- extract_msg/structures/dv_target_device.py +168 -0
- extract_msg/structures/entry_id.py +697 -0
- extract_msg/structures/misc_id.py +222 -0
- extract_msg/structures/mon_stream.py +55 -0
- extract_msg/structures/odt.py +74 -0
- extract_msg/structures/ole_pres.py +302 -0
- extract_msg/structures/ole_stream_struct.py +117 -0
- extract_msg/structures/recurrence_pattern.py +199 -0
- extract_msg/structures/report_tag.py +127 -0
- extract_msg/structures/system_time.py +158 -0
- extract_msg/structures/time_zone_definition.py +111 -0
- extract_msg/structures/time_zone_struct.py +102 -0
- extract_msg/structures/toc_entry.py +137 -0
- extract_msg/structures/tz_rule.py +148 -0
- extract_msg/utils.py +1270 -0
- extract_msg-0.48.2.dist-info/LICENSE.txt +674 -0
- extract_msg-0.48.2.dist-info/METADATA +321 -0
- extract_msg-0.48.2.dist-info/RECORD +95 -0
- extract_msg-0.48.2.dist-info/WHEEL +5 -0
- extract_msg-0.48.2.dist-info/entry_points.txt +2 -0
- extract_msg-0.48.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
extract_msg.exceptions
|
|
5
|
+
~~~~~~~~~~~~~~~~~~~~~~
|
|
6
|
+
This module contains the set of extract_msg exceptions.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
'ExMsgBaseException',
|
|
11
|
+
|
|
12
|
+
'ConversionError',
|
|
13
|
+
'DataNotFoundError',
|
|
14
|
+
'DeencapMalformedData',
|
|
15
|
+
'DeencapNotEncapsulated',
|
|
16
|
+
'ExecutableNotFound',
|
|
17
|
+
'IncompatibleOptionsError',
|
|
18
|
+
'InvalidFileFormatError',
|
|
19
|
+
'InvalidPropertyIdError',
|
|
20
|
+
'StandardViolationError',
|
|
21
|
+
'TZError',
|
|
22
|
+
'UnknownCodepageError',
|
|
23
|
+
'UnsupportedEncodingError',
|
|
24
|
+
'UnknownTypeError',
|
|
25
|
+
'UnsupportedMSGTypeError',
|
|
26
|
+
'UnrecognizedMSGTypeError',
|
|
27
|
+
'WKError',
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
# Base exception types.
|
|
31
|
+
|
|
32
|
+
class ExMsgBaseException(Exception):
|
|
33
|
+
"""
|
|
34
|
+
The base class for all custom exceptions the module uses.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
# I would want this to also be a subclass of NotImplementedError, but Python
|
|
38
|
+
# docs say that CPython can make that a bit problematic due to things from the C
|
|
39
|
+
# side of the code.
|
|
40
|
+
class FeatureNotImplemented(ExMsgBaseException):
|
|
41
|
+
"""
|
|
42
|
+
The base class for a feature not yet being implemented in the module.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# More specific exceptions.
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ConversionError(ExMsgBaseException):
|
|
50
|
+
"""
|
|
51
|
+
An error occured during type conversion.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
class DataNotFoundError(ExMsgBaseException):
|
|
55
|
+
"""
|
|
56
|
+
Requested stream type was unavailable.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
class DeencapMalformedData(ExMsgBaseException):
|
|
60
|
+
"""
|
|
61
|
+
Data to deencapsulate was malformed in some way.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
class DeencapNotEncapsulated(ExMsgBaseException):
|
|
65
|
+
"""
|
|
66
|
+
Data to deencapsulate did not contain any encapsulated data.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
class DependencyError(ExMsgBaseException):
|
|
70
|
+
"""
|
|
71
|
+
An optional dependency could not be found or was unable to be used as
|
|
72
|
+
expected.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
class ExecutableNotFound(DependencyError):
|
|
76
|
+
"""
|
|
77
|
+
Could not find the specified executable.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
class IncompatibleOptionsError(ExMsgBaseException):
|
|
81
|
+
"""
|
|
82
|
+
Provided options are incompatible with each other.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
class InvalidFileFormatError(ExMsgBaseException):
|
|
86
|
+
"""
|
|
87
|
+
An Invalid File Format Error occurred.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
class InvalidPropertyIdError(ExMsgBaseException):
|
|
91
|
+
"""
|
|
92
|
+
The provided property ID was invalid.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
class NotWritableError(ExMsgBaseException):
|
|
96
|
+
"""
|
|
97
|
+
Modification was attempted on an instance that is not writable.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
class PrefixError(ExMsgBaseException):
|
|
101
|
+
"""
|
|
102
|
+
An issue was detected with the provided prefix.
|
|
103
|
+
|
|
104
|
+
This should never occur if you have no manually provided a prefix.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
class SecurityError(ExMsgBaseException):
|
|
108
|
+
"""
|
|
109
|
+
A code path was triggered that would use an insecure feature, but that
|
|
110
|
+
insecure feature was not enabled.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
class StandardViolationError(InvalidFileFormatError):
|
|
114
|
+
"""
|
|
115
|
+
A critical violation of the MSG standards was detected and could not be
|
|
116
|
+
recovered from.
|
|
117
|
+
|
|
118
|
+
Recoverable violations will result in log messages instead.
|
|
119
|
+
|
|
120
|
+
Any that could reasonably be skipped, although are likely to still cause
|
|
121
|
+
errors down the line, can be suppressed.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
class TooManySectorsError(ExMsgBaseException):
|
|
125
|
+
"""
|
|
126
|
+
Ole writer has too much data to write to the file.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
class TZError(ExMsgBaseException):
|
|
130
|
+
"""
|
|
131
|
+
Specifically not an OSError to avoid being caught by parts of the module.
|
|
132
|
+
|
|
133
|
+
This error represents a fatal error in the datetime parsing as it usually
|
|
134
|
+
means your installation of tzlocal or tzdata are broken. If you have
|
|
135
|
+
received this error after using PyInstaller, you must include the resource
|
|
136
|
+
files for tzdata for it to work properly. See TeamMsgExtractor#272 and
|
|
137
|
+
TeamMsgExtractor#169 for information on why you are getting this error.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
class UnknownCodepageError(ExMsgBaseException):
|
|
141
|
+
"""
|
|
142
|
+
The codepage provided was not one we know of.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
class UnsupportedEncodingError(FeatureNotImplemented):
|
|
146
|
+
"""
|
|
147
|
+
The codepage provided is known but is not supported.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
class UnknownTypeError(ExMsgBaseException):
|
|
151
|
+
"""
|
|
152
|
+
The type specified is not one that is recognized.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
class UnsupportedMSGTypeError(FeatureNotImplemented):
|
|
156
|
+
"""
|
|
157
|
+
An exception that is raised when an MSG class is recognized by not
|
|
158
|
+
supported.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
class UnrecognizedMSGTypeError(ExMsgBaseException):
|
|
162
|
+
"""
|
|
163
|
+
An exception that is raised when the module cannot determine how to properly
|
|
164
|
+
open a specific class of MSG file.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
class WKError(DependencyError):
|
|
168
|
+
"""
|
|
169
|
+
An error occured while running wkhtmltopdf.
|
|
170
|
+
"""
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Classes for opening an MSG file with.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
# Classes:
|
|
7
|
+
'AppointmentMeeting',
|
|
8
|
+
'Calendar',
|
|
9
|
+
'CalendarBase',
|
|
10
|
+
'Contact',
|
|
11
|
+
'Journal',
|
|
12
|
+
'MeetingCancellation',
|
|
13
|
+
'MeetingException',
|
|
14
|
+
'MeetingForwardNotification',
|
|
15
|
+
'MeetingRelated',
|
|
16
|
+
'MeetingRequest',
|
|
17
|
+
'MeetingResponse',
|
|
18
|
+
'Message',
|
|
19
|
+
'MessageBase',
|
|
20
|
+
'MessageSigned',
|
|
21
|
+
'MessageSignedBase',
|
|
22
|
+
'MSGFile',
|
|
23
|
+
'Post',
|
|
24
|
+
'StickyNote',
|
|
25
|
+
'Task',
|
|
26
|
+
'TaskRequest',
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
from .appointment import AppointmentMeeting
|
|
31
|
+
from .calendar_base import CalendarBase
|
|
32
|
+
from .calendar import Calendar
|
|
33
|
+
from .contact import Contact
|
|
34
|
+
from .journal import Journal
|
|
35
|
+
from .meeting_cancellation import MeetingCancellation
|
|
36
|
+
from .meeting_exception import MeetingException
|
|
37
|
+
from .meeting_forward import MeetingForwardNotification
|
|
38
|
+
from .meeting_related import MeetingRelated
|
|
39
|
+
from .meeting_request import MeetingRequest
|
|
40
|
+
from .meeting_response import MeetingResponse
|
|
41
|
+
from .message import Message
|
|
42
|
+
from .message_base import MessageBase
|
|
43
|
+
from .message_signed import MessageSigned
|
|
44
|
+
from .message_signed_base import MessageSignedBase
|
|
45
|
+
from .msg import MSGFile
|
|
46
|
+
from .post import Post
|
|
47
|
+
from .sticky_note import StickyNote
|
|
48
|
+
from .task import Task
|
|
49
|
+
from .task_request import TaskRequest
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'AppointmentMeeting',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
import functools
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from ..constants import HEADER_FORMAT_TYPE, ps
|
|
13
|
+
from .calendar import Calendar
|
|
14
|
+
from ..enums import AppointmentStateFlag, RecurPatternType, ResponseStatus
|
|
15
|
+
from ..structures.entry_id import EntryID
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AppointmentMeeting(Calendar):
|
|
19
|
+
"""
|
|
20
|
+
Parser for Microsoft Outlook Appointment or Meeting files.
|
|
21
|
+
|
|
22
|
+
Both Appointment and Meeting have the same class type but Meeting has
|
|
23
|
+
additional properties. These properties are meaningless on an Appointment
|
|
24
|
+
object.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def getJson(self) -> str:
|
|
28
|
+
meetingStatusString = {
|
|
29
|
+
ResponseStatus.NONE: None,
|
|
30
|
+
ResponseStatus.ORGANIZED: 'Meeting organizer',
|
|
31
|
+
ResponseStatus.TENTATIVE: 'Tentatively accepted',
|
|
32
|
+
ResponseStatus.ACCEPTED: 'Accepted',
|
|
33
|
+
ResponseStatus.DECLINED: 'Declined',
|
|
34
|
+
ResponseStatus.NOT_RESPONDED: 'Not yet responded',
|
|
35
|
+
}[self.responseStatus]
|
|
36
|
+
|
|
37
|
+
# Get the recurrence string.
|
|
38
|
+
recur = '(none)'
|
|
39
|
+
if self.appointmentRecur:
|
|
40
|
+
recur = {
|
|
41
|
+
RecurPatternType.DAY: 'Daily',
|
|
42
|
+
RecurPatternType.WEEK: 'Weekly',
|
|
43
|
+
RecurPatternType.MONTH: 'Monthly',
|
|
44
|
+
RecurPatternType.MONTH_NTH: 'Monthly',
|
|
45
|
+
RecurPatternType.MONTH_END: 'Monthly',
|
|
46
|
+
RecurPatternType.HJ_MONTH: 'Monthly',
|
|
47
|
+
RecurPatternType.HJ_MONTH_NTH: 'Monthly',
|
|
48
|
+
RecurPatternType.HJ_MONTH_END: 'Monthly',
|
|
49
|
+
}[self.appointmentRecur.patternType]
|
|
50
|
+
|
|
51
|
+
return json.dumps({
|
|
52
|
+
'recurrence': recur,
|
|
53
|
+
'recurrencePattern': self.recurrencePattern,
|
|
54
|
+
'body': self.body,
|
|
55
|
+
'meetingStatus': meetingStatusString,
|
|
56
|
+
'organizer': self.organizer,
|
|
57
|
+
'requiredAttendees': self.to,
|
|
58
|
+
'optionalAttendees': self.cc,
|
|
59
|
+
'resources': self.bcc,
|
|
60
|
+
'start': self.startDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
61
|
+
'end': self.endDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
@functools.cached_property
|
|
65
|
+
def appointmentCounterProposal(self) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Indicates to the organizer that there are counter proposals that have
|
|
68
|
+
not been accepted or rejected by the organizer.
|
|
69
|
+
"""
|
|
70
|
+
return bool(self.getNamedProp('8257', ps.PSETID_APPOINTMENT))
|
|
71
|
+
|
|
72
|
+
@functools.cached_property
|
|
73
|
+
def appointmentLastSequence(self) -> Optional[int]:
|
|
74
|
+
"""
|
|
75
|
+
The last sequence number that was sent to any attendee.
|
|
76
|
+
"""
|
|
77
|
+
return self.getNamedProp('8203', ps.PSETID_APPOINTMENT)
|
|
78
|
+
|
|
79
|
+
@functools.cached_property
|
|
80
|
+
def appointmentProposalNumber(self) -> Optional[int]:
|
|
81
|
+
"""
|
|
82
|
+
The number of attendees who have sent counter propostals that have not
|
|
83
|
+
been accepted or rejected by the organizer.
|
|
84
|
+
"""
|
|
85
|
+
return self.getNamedProp('8259', ps.PSETID_APPOINTMENT)
|
|
86
|
+
|
|
87
|
+
@functools.cached_property
|
|
88
|
+
def appointmentReplyName(self) -> Optional[datetime.datetime]:
|
|
89
|
+
"""
|
|
90
|
+
The user who last replied to the meeting request or meeting update.
|
|
91
|
+
"""
|
|
92
|
+
return self.getNamedProp('8230', ps.PSETID_APPOINTMENT)
|
|
93
|
+
|
|
94
|
+
@functools.cached_property
|
|
95
|
+
def appointmentReplyTime(self) -> Optional[datetime.datetime]:
|
|
96
|
+
"""
|
|
97
|
+
The date and time at which the attendee responded to a received Meeting
|
|
98
|
+
Request object of Meeting Update object in UTC.
|
|
99
|
+
"""
|
|
100
|
+
return self.getNamedProp('8220', ps.PSETID_APPOINTMENT)
|
|
101
|
+
|
|
102
|
+
@functools.cached_property
|
|
103
|
+
def appointmentSequenceTime(self) -> Optional[datetime.datetime]:
|
|
104
|
+
"""
|
|
105
|
+
The date and time at which the appointmentSequence property was last
|
|
106
|
+
modified.
|
|
107
|
+
"""
|
|
108
|
+
return self.getNamedProp('8202', ps.PSETID_APPOINTMENT)
|
|
109
|
+
|
|
110
|
+
@functools.cached_property
|
|
111
|
+
def autoFillLocation(self) -> bool:
|
|
112
|
+
"""
|
|
113
|
+
A value of True indicates that the value of the location property is set
|
|
114
|
+
to the value of the displayName property from the recipientRow structure
|
|
115
|
+
that represents a Resource object.
|
|
116
|
+
|
|
117
|
+
A value of ``False`` indicates that the value of the location property
|
|
118
|
+
is not automatically set.
|
|
119
|
+
"""
|
|
120
|
+
return bool(self.getNamedProp('823A', ps.PSETID_APPOINTMENT))
|
|
121
|
+
|
|
122
|
+
@functools.cached_property
|
|
123
|
+
def fInvited(self) -> bool:
|
|
124
|
+
"""
|
|
125
|
+
Whether a Meeting Request object has been sent out.
|
|
126
|
+
"""
|
|
127
|
+
return bool(self.getNamedProp('8229', ps.PSETID_APPOINTMENT))
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def headerFormatProperties(self) -> HEADER_FORMAT_TYPE:
|
|
131
|
+
meetingStatusString = {
|
|
132
|
+
ResponseStatus.NONE: None,
|
|
133
|
+
ResponseStatus.ORGANIZED: 'Meeting organizer',
|
|
134
|
+
ResponseStatus.TENTATIVE: 'Tentatively accepted',
|
|
135
|
+
ResponseStatus.ACCEPTED: 'Accepted',
|
|
136
|
+
ResponseStatus.DECLINED: 'Declined',
|
|
137
|
+
ResponseStatus.NOT_RESPONDED: 'Not yet responded',
|
|
138
|
+
}[self.responseStatus]
|
|
139
|
+
|
|
140
|
+
# Get the recurrence string.
|
|
141
|
+
recur = '(none)'
|
|
142
|
+
if self.appointmentRecur:
|
|
143
|
+
recur = {
|
|
144
|
+
RecurPatternType.DAY: 'Daily',
|
|
145
|
+
RecurPatternType.WEEK: 'Weekly',
|
|
146
|
+
RecurPatternType.MONTH: 'Monthly',
|
|
147
|
+
RecurPatternType.MONTH_NTH: 'Monthly',
|
|
148
|
+
RecurPatternType.MONTH_END: 'Monthly',
|
|
149
|
+
RecurPatternType.HJ_MONTH: 'Monthly',
|
|
150
|
+
RecurPatternType.HJ_MONTH_NTH: 'Monthly',
|
|
151
|
+
RecurPatternType.HJ_MONTH_END: 'Monthly',
|
|
152
|
+
}[self.appointmentRecur.patternType]
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
'-main info-': {
|
|
156
|
+
'Subject': self.subject,
|
|
157
|
+
'Location': self.location,
|
|
158
|
+
},
|
|
159
|
+
'-date-': {
|
|
160
|
+
'Start': self.startDate.__format__(self.datetimeFormat) if self.startDate else None,
|
|
161
|
+
'End': self.endDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
162
|
+
},
|
|
163
|
+
'-recurrence-': {
|
|
164
|
+
'Recurrance': recur,
|
|
165
|
+
'Recurrence Pattern': self.recurrencePattern,
|
|
166
|
+
},
|
|
167
|
+
'-status-': {
|
|
168
|
+
'Meeting Status': meetingStatusString,
|
|
169
|
+
},
|
|
170
|
+
'-attendees-': {
|
|
171
|
+
'Organizer': self.organizer,
|
|
172
|
+
'Required Attendees': self.to,
|
|
173
|
+
'Optional Attendees': self.cc,
|
|
174
|
+
'Resources': self.bcc,
|
|
175
|
+
},
|
|
176
|
+
'-importance-': {
|
|
177
|
+
'Importance': self.importanceString,
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@functools.cached_property
|
|
182
|
+
def isMeeting(self) -> bool:
|
|
183
|
+
"""
|
|
184
|
+
Attempts to determine if the object is a Meeting.
|
|
185
|
+
|
|
186
|
+
``True`` if meeting, ``False`` if appointment.
|
|
187
|
+
"""
|
|
188
|
+
return bool(self.appointmentStateFlags) and (AppointmentStateFlag.MEETING in self.appointmentStateFlags)
|
|
189
|
+
|
|
190
|
+
@functools.cached_property
|
|
191
|
+
def originalStoreEntryID(self) -> Optional[EntryID]:
|
|
192
|
+
"""
|
|
193
|
+
The EntryID of the delegator's message store.
|
|
194
|
+
"""
|
|
195
|
+
return self.getNamedAs('8237', ps.PSETID_APPOINTMENT, EntryID.autoCreate)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'Calendar',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
import functools
|
|
8
|
+
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from ..constants import ps
|
|
12
|
+
from .calendar_base import CalendarBase
|
|
13
|
+
from ..enums import ClientIntentFlag
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Calendar(CalendarBase):
|
|
17
|
+
"""
|
|
18
|
+
A calendar object.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@functools.cached_property
|
|
22
|
+
def clientIntent(self) -> Optional[ClientIntentFlag]:
|
|
23
|
+
"""
|
|
24
|
+
A set of the actions a user has taken on a Meeting object.
|
|
25
|
+
"""
|
|
26
|
+
return self.getNamedAs('0015', ps.PSETID_CALENDAR_ASSISTANT, ClientIntentFlag)
|
|
27
|
+
|
|
28
|
+
@functools.cached_property
|
|
29
|
+
def fExceptionalAttendees(self) -> Optional[bool]:
|
|
30
|
+
"""
|
|
31
|
+
Indicates that it is a Recurring Calendar object with one or more
|
|
32
|
+
excpetions and that at least one of the Exception Embedded Message
|
|
33
|
+
objects has at least one RecipientRow structure.
|
|
34
|
+
|
|
35
|
+
SHOULD NOT be set for any Calendar object other than that of the
|
|
36
|
+
organizer's.
|
|
37
|
+
"""
|
|
38
|
+
return self.getNamedProp('822B', ps.PSETID_APPOINTMENT)
|
|
39
|
+
|
|
40
|
+
@functools.cached_property
|
|
41
|
+
def reminderDelta(self) -> Optional[int]:
|
|
42
|
+
"""
|
|
43
|
+
The interval, in minutes, between the time at which the reminder first
|
|
44
|
+
becomes overdue and the start time of the Calendar object.
|
|
45
|
+
"""
|
|
46
|
+
return self.getNamedProp('8501', ps.PSETID_COMMON)
|
|
47
|
+
|
|
48
|
+
@functools.cached_property
|
|
49
|
+
def reminderFileParameter(self) -> Optional[str]:
|
|
50
|
+
"""
|
|
51
|
+
The full path (MAY only specify the file name) of the sound that a
|
|
52
|
+
client SHOULD play when the reminder for the Message Object becomes
|
|
53
|
+
overdue.
|
|
54
|
+
"""
|
|
55
|
+
return self.getNamedProp('851F', ps.PSETID_COMMON)
|
|
56
|
+
|
|
57
|
+
@functools.cached_property
|
|
58
|
+
def reminderOverride(self) -> bool:
|
|
59
|
+
"""
|
|
60
|
+
Specifies if clients SHOULD respect the value of the reminderPlaySound
|
|
61
|
+
property and the reminderFileParameter property.
|
|
62
|
+
"""
|
|
63
|
+
return bool(self.getNamedProp('851C', ps.PSETID_COMMON))
|
|
64
|
+
|
|
65
|
+
@functools.cached_property
|
|
66
|
+
def reminderPlaySound(self) -> bool:
|
|
67
|
+
"""
|
|
68
|
+
Specified that the cliebnt should play a sound when the reminder becomes
|
|
69
|
+
overdue.
|
|
70
|
+
"""
|
|
71
|
+
return bool(self.getNamedProp('851E', ps.PSETID_COMMON))
|
|
72
|
+
|
|
73
|
+
@functools.cached_property
|
|
74
|
+
def reminderSet(self) -> bool:
|
|
75
|
+
"""
|
|
76
|
+
Specifies whether a reminder is set on the object.
|
|
77
|
+
"""
|
|
78
|
+
return bool(self.getNamedProp('8503', ps.PSETID_COMMON))
|
|
79
|
+
|
|
80
|
+
@functools.cached_property
|
|
81
|
+
def reminderSignalTime(self) -> Optional[datetime.datetime]:
|
|
82
|
+
"""
|
|
83
|
+
The point in time when a reminder transitions from pending to overdue.
|
|
84
|
+
"""
|
|
85
|
+
return self.getNamedProp('8560', ps.PSETID_COMMON)
|
|
86
|
+
|
|
87
|
+
@functools.cached_property
|
|
88
|
+
def reminderTime(self) -> Optional[datetime.datetime]:
|
|
89
|
+
"""
|
|
90
|
+
The time after which the user would be late.
|
|
91
|
+
"""
|
|
92
|
+
return self.getNamedProp('8502', ps.PSETID_COMMON)
|