sharepoint-v1-api 0.2.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.
@@ -0,0 +1,202 @@
1
+ # from .SharePointAPI import SharePointAPI as SP
2
+ from sharepoint_api.SharePointTimeRegistration import TimeRegistration
3
+ from .SharePointListItem import SharePointListItem, SharepointSiteCase
4
+ from typing import List
5
+ import json
6
+
7
+
8
+ class SharePointList:
9
+ """
10
+ Base class representing a SharePoint list.
11
+
12
+ Provides common functionality for handling list items, including
13
+ retrieval, addition, and serialization to JSON. Subclasses such as
14
+ :class:`CasesList` and :class:`TimeRegistrationList` extend this
15
+ class with domain‑specific behavior.
16
+ """
17
+
18
+ settings = None
19
+ _items = None
20
+ _fields = None
21
+ sharepoint_site = None
22
+ SPItem = SharePointListItem
23
+
24
+ CHANGE_DETECTED = False
25
+ SAVE_ON_CHANGE = False
26
+ JSON_FILENAME = None
27
+
28
+ def __init__(self, sp, sharepoint_site, settings: dict = None, items: List[SPItem] = None):
29
+ self.sp = sp
30
+ self.sharepoint_site = sharepoint_site
31
+ self.settings = settings
32
+ self.append_items(items)
33
+
34
+ def __str__(self):
35
+ items = ''
36
+ for _, _item in self.all_items.items():
37
+ items = items+str(_item.Title)+'\n'
38
+ return items
39
+
40
+ def __del__(self):
41
+ if self.CHANGE_DETECTED and self.SAVE_ON_CHANGE and self.JSON_FILENAME is not None:
42
+ print('Change was definiteley detected')
43
+ print('Saving items')
44
+ self.save_as_json(self.JSON_FILENAME)
45
+
46
+ @property
47
+ def all_items(self) -> dict[int, SPItem]:
48
+ '''
49
+ Get list of all SharePointListItem objects
50
+ '''
51
+ if self._items is None:
52
+ self._items = {}
53
+ return self._items
54
+
55
+ @property
56
+ def list_all_items(self) -> List[SPItem]:
57
+ '''
58
+ Get list of all SharePointListItem objects
59
+ '''
60
+ if self._items is None:
61
+ self._items = {}
62
+ return list(self._items.values())
63
+
64
+ @property
65
+ def Title(self) -> str:
66
+ return self.settings['Title']
67
+
68
+ @property
69
+ def guid(self):
70
+ return self.settings['Id']
71
+
72
+ def append_items(self, items):
73
+ """
74
+ Append a collection of items to the list.
75
+
76
+ Parameters
77
+ ----------
78
+ items : list or SharePointListItem
79
+ A list of :class:`SharePointListItem` instances or a single
80
+ instance to be added to the internal ``_items`` dictionary.
81
+ """
82
+ if self._items is None:
83
+ self._items = {}
84
+
85
+ if isinstance(items, list):
86
+ for item in items:
87
+ item._list = self
88
+ self._items[item.Id] = item
89
+
90
+ elif isinstance(items, self.SPItem):
91
+ items._list = self
92
+ self.all_items[items.Id] = items
93
+
94
+ def create_item(self, data):
95
+ self.sp.create_item(self.sharepoint_site, self, data)
96
+
97
+ def get_item_by_name(self, name):
98
+ """
99
+ Retrieve items from the list that match a given title.
100
+
101
+ Parameters
102
+ ----------
103
+ name : str
104
+ The title of the item to search for.
105
+
106
+ Returns
107
+ -------
108
+ dict[int, SharePointListItem]
109
+ A dictionary of matching items keyed by their IDs.
110
+ """
111
+ '''
112
+ '''
113
+ items = {}
114
+ for item_id, item in self._items.items():
115
+ if name == item.Title:
116
+ items[item_id] = item
117
+ return items
118
+
119
+ def get_item_by_id(self, id):
120
+ '''
121
+ '''
122
+ if id in self._items:
123
+ return self._items[id]
124
+ else:
125
+ return None
126
+
127
+ @property
128
+ def fields(self) -> dict:
129
+ """
130
+ Retrieve all fields from the SharePoint list.
131
+
132
+ Returns:
133
+ dict: Field definitions keyed by Title, cached for subsequent calls
134
+
135
+ Raises:
136
+ ConnectionError: If API request fails
137
+ """
138
+ if not self._fields:
139
+ try:
140
+ response = self.sp._api_get_call(
141
+ f"{self.sp.sharepoint_url}/cases/{self.sharepoint_site}/_api/Web/Lists(guid'{self.guid}')/Fields"
142
+ )
143
+ response.raise_for_status()
144
+ self._fields = {field["Title"]: field for field in response.json().get(
145
+ 'd', {}).get('results', [])}
146
+ except Exception as e:
147
+ print(f"Failed to fetch fields: {str(e)}")
148
+ raise ConnectionError("Field retrieval failed") from e
149
+ return self._fields
150
+
151
+ def get_items_by_assigned_id(self, id) -> List[SPItem]:
152
+ '''
153
+ '''
154
+ items = {}
155
+ for item_id, item in self._items.items():
156
+ if id == item.ResponsibleId:
157
+ items[item_id] = item
158
+ return items
159
+
160
+ def save_as_json(self, file_name):
161
+ """
162
+ Serialize the list to a JSON file.
163
+
164
+ Parameters
165
+ ----------
166
+ file_name : str
167
+ Path to the output JSON file.
168
+
169
+ The resulting JSON contains the SharePoint site identifier, the list GUID,
170
+ settings, and all case items with their settings and version information.
171
+ """
172
+
173
+ out_dict = {
174
+ 'sharepoint_site': self.sharepoint_site,
175
+ 'GUID': self.guid,
176
+ 'Settings': self.settings,
177
+ "cases": [{'settings': case.settings, 'versions': case._versions} for _, case in self.all_items.items()]
178
+ }
179
+
180
+ with open(file_name, 'w') as fp:
181
+ json.dump(out_dict, fp)
182
+
183
+
184
+ class CasesList(SharePointList):
185
+ """
186
+ Specialized list representing a collection of case items.
187
+
188
+ Inherits all functionality from :class:`SharePointList` and sets the
189
+ ``SPItem`` attribute to :class:`SharepointSiteCase` for proper item
190
+ instantiation.
191
+ """
192
+ SPItem = SharepointSiteCase
193
+
194
+
195
+ class TimeRegistrationList(SharePointList):
196
+ """
197
+ Specialized list for time‑registration entries.
198
+
199
+ Inherits from :class:`SharePointList` and sets ``SPItem`` to the
200
+ :class:`TimeRegistration` class.
201
+ """
202
+ SPItem = TimeRegistration
@@ -0,0 +1,196 @@
1
+ from datetime import datetime
2
+ # from .SharePointAPI import SharePointAPI as SP
3
+
4
+ class SharePointListItem:
5
+ """
6
+ Represents a generic item in a SharePoint list.
7
+
8
+ Provides access to common fields such as ``Id``, ``Title``, ``Created`` and
9
+ ``Modified`` and handles lazy loading of related list data and version
10
+ history. Sub‑classes (e.g. :class:`SharepointSiteCase` or
11
+ :class:`TimeRegistration`) extend this base with domain‑specific properties.
12
+ """
13
+ '''
14
+ '''
15
+ settings = {}
16
+
17
+ def __init__(self, sp, sharepoint_site, list_guid, settings: dict = None, versions: list = None):
18
+ self.sp = sp
19
+ self.sharepoint_site = sharepoint_site
20
+ self.list_guid = list_guid
21
+ self._list = None
22
+ self._versions = versions
23
+ self.settings = settings
24
+
25
+ def __str__(self):
26
+ return self.Title
27
+
28
+ @property
29
+ def list(self):
30
+ if not self._list:
31
+ self._list = self.sp.get_list(self.sharepoint_site, self.list_guid)
32
+ return self._list
33
+
34
+ @property
35
+ def versions(self) -> list:
36
+ if not self._versions:
37
+ self._versions = self.sp.get_item_versions(self.sharepoint_site, self.list_guid, self.Id)
38
+ if self._list is not None:
39
+ self._list.CHANGE_DETECTED = True
40
+ return self._versions
41
+
42
+
43
+ @property
44
+ def Id(self) -> str:
45
+ """Unique identifier of the list item."""
46
+ return self.settings['Id']
47
+
48
+ @property
49
+ def Title(self) -> str:
50
+ """Title of the list item."""
51
+ return self.settings['Title']
52
+
53
+ @property
54
+ def Created(self) -> datetime:
55
+ """
56
+ Timestamp of when the item was created.
57
+
58
+ Returns ``None`` if the ``Created`` field is missing or null.
59
+ """
60
+ if 'Created' not in self.settings:
61
+ return None
62
+ elif self.settings['Created'] is None:
63
+ return None
64
+ else:
65
+ return datetime.strptime(self.settings['Created'], '%Y-%m-%dT%H:%M:%SZ')
66
+
67
+ @property
68
+ def Modified(self) -> datetime:
69
+ """
70
+ Timestamp of when the item was modified.
71
+
72
+ Returns ``None`` if the ``Modified`` field is missing or null.
73
+ """
74
+ if 'Modified' not in self.settings:
75
+ return None
76
+ elif self.settings['Modified'] is None:
77
+ return None
78
+ else:
79
+ return datetime.strptime(self.settings['Modified'], '%Y-%m-%dT%H:%M:%SZ')
80
+
81
+ def attach_item(self, file_name, file_path):
82
+ """
83
+ Attach a file to this list item.
84
+
85
+ Parameters
86
+ ----------
87
+ file_name : str
88
+ The name of the file as it should appear in SharePoint.
89
+ file_path : str
90
+ Local path to the file to be uploaded.
91
+
92
+ The method reads the file content and uses :meth:`SharePointAPI.attach_file`
93
+ to upload it to the corresponding SharePoint list item.
94
+ """
95
+ with open(file_path, 'r') as f:
96
+ file_content = f.read()
97
+
98
+
99
+ self.sp.attach_file(self.sharepoint_site, self.list, self, file_name, file_content)
100
+
101
+ def versions_select_fields(self, select_fields=[]) -> list:
102
+ if not self._versions:
103
+ self._versions = self.sp.get_item_versions(self.sharepoint_site, self.list_guid, self.Id, select_fields)
104
+ return self._versions
105
+
106
+ def update_fields(self, data):
107
+ self.sp.update_item(self.sharepoint_site,
108
+ self.list_guid, self.Id, data)
109
+
110
+ class SharepointSiteCase(SharePointListItem):
111
+
112
+ @property
113
+ def AssignmentType(self) -> str:
114
+ return self.settings['AssignmentType']
115
+
116
+ @property
117
+ def CaseClosedTimestamp(self) -> datetime:
118
+ 'Timestamp of when the case was closed'
119
+ if 'CaseClosed' not in self.settings:
120
+ return None
121
+ elif self.settings['CaseClosed'] is None:
122
+ return None
123
+ else:
124
+ return datetime.strptime(self.settings['CaseClosed'], '%Y-%m-%dT%H:%M:%SZ')
125
+
126
+ @property
127
+ def Due(self) -> datetime:
128
+ 'Timestamp of when the case is due'
129
+ if 'DueDate' not in self.settings:
130
+ return None
131
+ elif self.settings['DueDate'] is None:
132
+ return None
133
+ else:
134
+ return datetime.strptime(self.settings['DueDate'], '%Y-%m-%dT%H:%M:%SZ')
135
+
136
+ @property
137
+ def Priority(self) -> str:
138
+ return self.settings['Priority']
139
+
140
+ @property
141
+ def ResponsibleId(self) -> str:
142
+ return self.settings['AssignedToId']
143
+
144
+ @property
145
+ def Status(self) -> str:
146
+ return self.settings['Status']
147
+
148
+ @property
149
+ def SolvedInTime(self) -> bool:
150
+ 'Bool indicating whether work was solved in time'
151
+ solved_in_time = False
152
+ if self.Due:
153
+ if not self.CaseClosedTimestamp:
154
+ solved_in_time = True
155
+ elif self.CaseClosedTimestamp <= self.Due:
156
+ solved_in_time = True
157
+ else:
158
+ solved_in_time = True
159
+
160
+ return solved_in_time
161
+
162
+ @property
163
+ def WorkBegunTimestamp(self) -> datetime:
164
+ 'Timestamp of when work was started on the case'
165
+ if 'WorkBegun' not in self.settings:
166
+ return None
167
+ elif self.settings['WorkBegun'] is None:
168
+ return None
169
+ else:
170
+ return datetime.strptime(self.settings['WorkBegun'], '%Y-%m-%dT%H:%M:%SZ')
171
+
172
+ @property
173
+ def DeadlineWorkTimestamp(self) -> datetime:
174
+ 'Timestamp of when work should start on the case'
175
+ if 'DeadlineWork' not in self.settings:
176
+ return None
177
+ elif self.settings['DeadlineWork'] is None:
178
+ return None
179
+ else:
180
+ return datetime.strptime(self.settings['DeadlineWork'], '%Y-%m-%dT%H:%M:%SZ')
181
+
182
+ @property
183
+ def ReactedInTime(self) -> bool:
184
+ 'Bool indicating whether work was started in time'
185
+ reacted_in_time = False
186
+ if self.DeadlineWorkTimestamp:
187
+ if not self.WorkBegunTimestamp:
188
+ reacted_in_time = True
189
+ elif self.WorkBegunTimestamp <= self.DeadlineWorkTimestamp:
190
+ reacted_in_time = True
191
+ else:
192
+ reacted_in_time = True
193
+ return reacted_in_time
194
+
195
+
196
+
@@ -0,0 +1,59 @@
1
+ from .SharePointList import SharePointList, CasesList
2
+ from typing import List
3
+
4
+ class SharePointLists:
5
+
6
+ _lists = None
7
+
8
+ def __init__(self, lists):
9
+ self._lists = lists
10
+
11
+ def __str__(self):
12
+ lists = ''
13
+ for _list in self.all_lists:
14
+ lists = lists+str(_list.Title)+'\n'
15
+ return lists
16
+
17
+ @property
18
+ def all_lists(self) -> List[SharePointList]:
19
+ '''
20
+ Get list of all SharePointList objects
21
+ '''
22
+ if self._lists is None:
23
+ self._lists = []
24
+ return self._lists
25
+
26
+ @property
27
+ def all_list_titles(self) -> str:
28
+ '''
29
+ Get list of all SharePointList objects
30
+ '''
31
+
32
+ return [_list.Title for _list in self.all_lists]
33
+
34
+
35
+ def get_list(self, key, SPListType: SharePointList=SharePointList) -> SharePointList:
36
+ '''
37
+ Get SharePointList by:
38
+ - Index: int
39
+ - List title: str
40
+ '''
41
+ if isinstance(key, int):
42
+ return self.all_lists[key]
43
+ elif isinstance(key, str):
44
+ for list_object in self.all_lists:
45
+ if list_object.Title == key:
46
+ return list_object
47
+ raise KeyError("No list with title '{0}'".format(key))
48
+ raise KeyError
49
+
50
+ def get_cases_list(self, key) -> CasesList:
51
+ '''
52
+ Get SharePointList by:
53
+ - Index: int
54
+ - List title: str
55
+ '''
56
+
57
+ list_object = self.get_list(key, SPListType=CasesList)
58
+
59
+ return CasesList(list_object.sharepoint_site, list_object.settings)
@@ -0,0 +1,53 @@
1
+ from datetime import datetime
2
+
3
+ from sharepoint_api.SharePointListItem import SharePointListItem
4
+ # from .SharePointAPI import SharePointAPI as SP
5
+
6
+ class TimeRegistration(SharePointListItem):
7
+ """
8
+ Represents a time‑registration entry associated with a SharePoint list item.
9
+
10
+ Extends :class:`SharePointListItem` with fields specific to time tracking,
11
+ such as ``Hours``, ``DoneBy`` and related timestamps. Provides lazy loading of the
12
+ ``DoneBy`` user via the :meth:`SharePointAPI.get_users` call.
13
+ """
14
+
15
+ _DoneBy = None
16
+
17
+ @property
18
+ def Created(self) -> datetime:
19
+ 'Timestamp of when the item was created'
20
+ if 'DoneDate' not in self.settings:
21
+ return None
22
+ elif self.settings['DoneDate'] is None:
23
+ return None
24
+ else:
25
+ return datetime.strptime(self.settings['DoneDate'], '%Y-%m-%dT%H:%M:%SZ')
26
+
27
+ @property
28
+ def Hours(self) -> str:
29
+ return self.settings['Hours']
30
+
31
+ @property
32
+ def DoneById(self) -> str:
33
+ return self.settings['DoneById']
34
+
35
+ @property
36
+ def DoneBy(self) -> str:
37
+ if not self._DoneBy:
38
+ user_list = self.sp.get_users(self.sharepoint_site)
39
+ self._DoneBy = user_list.get_user_by_id(self.DoneById)
40
+ return self._DoneBy
41
+
42
+ @property
43
+ def DoneUsername(self) -> str:
44
+ return self.DoneBy.UserName
45
+
46
+ @property
47
+ def CaseId(self) -> str:
48
+ return self.settings['CaseId']
49
+
50
+ @property
51
+ def WorkPackageId(self) -> str:
52
+ return self.settings['WorkPackageId']
53
+
@@ -0,0 +1,33 @@
1
+ class SharePointUser:
2
+
3
+ settings = None
4
+ _users = None
5
+ sharepoint_site = None
6
+
7
+ def __init__(self, settings: dict = None):
8
+ self.settings = settings
9
+
10
+ @property
11
+ def Id(self):
12
+ if self.settings is None:
13
+ return None
14
+ return self.settings['Id']
15
+
16
+ @property
17
+ def Title(self):
18
+ if self.settings is None:
19
+ return None
20
+ return self.settings['Title']
21
+
22
+ @property
23
+ def UserName(self):
24
+ if self.settings is None:
25
+ return None
26
+ return self.settings['Email'].split('@')[0].lower()
27
+
28
+ @property
29
+ def Email(self):
30
+ if self.settings is None:
31
+ return None
32
+ return self.settings['Email']
33
+
@@ -0,0 +1,36 @@
1
+ class SharePointUserList:
2
+ """
3
+ Container for SharePoint user objects.
4
+
5
+ Holds a list of :class:`SharePointUser` instances retrieved from a
6
+ SharePoint site and provides helper methods to look up users by
7
+ username or ID. The ``sharepoint_site`` attribute records the site
8
+ from which the users were fetched.
9
+ """
10
+
11
+ _users = []
12
+ sharepoint_site = ''
13
+
14
+ def __init__(self, sharepoint_site, users):
15
+ self._users = users
16
+ self.sharepoint_site = sharepoint_site
17
+
18
+ def __str__(self):
19
+ n_users = len(self._users)
20
+ if n_users == 1:
21
+ return 'A list of 1 user'
22
+
23
+ return f'A list of {n_users} users'
24
+
25
+ def get_user_by_username(self, name):
26
+ for user in self._users:
27
+ if name == user.UserName:
28
+ return user
29
+ return
30
+
31
+ def get_user_by_id(self, user_id):
32
+ for user in self._users:
33
+ if user_id == user.Id:
34
+ return user
35
+ return
36
+
@@ -0,0 +1,21 @@
1
+ """sharepoint_api package
2
+
3
+ Provides a high‑level Python client for interacting with SharePoint sites via the
4
+ `SharePointAPI` class. The package includes helpers for working with lists,
5
+ list items, users, and time registrations.
6
+
7
+ Typical usage:
8
+
9
+ ```python
10
+ from sharepoint_api import SharePointAPI
11
+
12
+ creds = {
13
+ "username": "my_user",
14
+ "password": "my_pass",
15
+ "sharepoint_url": "https://my.sharepoint.com"
16
+ }
17
+ api = SharePointAPI._compact_init(creds)
18
+ ```
19
+ """
20
+
21
+ from .SharePointAPI import SharePointAPI