sharepoint-v1-api 0.2.2__tar.gz → 0.2.3__tar.gz

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.
Files changed (23) hide show
  1. sharepoint_v1_api-0.2.3/CHANGELOG.md +47 -0
  2. {sharepoint_v1_api-0.2.2/sharepoint_v1_api.egg-info → sharepoint_v1_api-0.2.3}/PKG-INFO +19 -1
  3. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/setup.py +1 -1
  4. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_api/SharePointAPI.py +74 -68
  5. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_api/SharePointListItem.py +20 -20
  6. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_api/SharePointTimeRegistration.py +5 -5
  7. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3/sharepoint_v1_api.egg-info}/PKG-INFO +19 -1
  8. sharepoint_v1_api-0.2.2/CHANGELOG.md +0 -29
  9. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/AGENTS.md +0 -0
  10. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/LICENSE +0 -0
  11. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/MANIFEST.in +0 -0
  12. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/README.md +0 -0
  13. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/pyproject.toml +0 -0
  14. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/setup.cfg +0 -0
  15. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_api/SharePointList.py +0 -0
  16. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_api/SharePointLists.py +0 -0
  17. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_api/SharePointUser.py +0 -0
  18. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_api/SharePointUserList.py +0 -0
  19. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_api/__init__.py +0 -0
  20. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_v1_api.egg-info/SOURCES.txt +0 -0
  21. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_v1_api.egg-info/dependency_links.txt +0 -0
  22. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_v1_api.egg-info/requires.txt +0 -0
  23. {sharepoint_v1_api-0.2.2 → sharepoint_v1_api-0.2.3}/sharepoint_v1_api.egg-info/top_level.txt +0 -0
@@ -0,0 +1,47 @@
1
+ ## 0.2.3 – 2025-12-02
2
+
3
+ ### Added
4
+
5
+ - GUID validation for list identifiers with fallback to list title lookup via `GetByTitle`.
6
+ - New helper method `_is_valid_guid` in `SharePointAPI`.
7
+ - Centralized list resolution helper `_resolve_sp_list` for GUID, title, or `SharePointList` instances.
8
+ - New method `get_list_metadata` to fetch only list metadata without items.
9
+ - Type hint enhancements: added `Optional`, `Tuple` imports; updated method signatures (e.g., `get_item_versions` now uses `Optional[List[str]]`).
10
+ - Updated `get_list` to return the resolved `SharePointList` object after appending items.
11
+ - Refactored multiple methods (`get_item`, `create_item`, `update_item`, `attach_file`, `get_item_versions`, `get_case`) to use `_resolve_sp_list`, removing redundant GUID validation logic.
12
+
13
+ ### Changed
14
+
15
+ - Cleaned up stray `else:` blocks and syntax errors.
16
+ - Improved consistency of return objects across list-related methods.
17
+ - Updated imports and docstrings accordingly.
18
+
19
+ ## 0.2.2 – 2025-11-28
20
+
21
+ ### Added
22
+
23
+ - `SharePointList.fields` property to retrieve list field definitions
24
+
25
+ ## 0.2.1 – 2025‑11‑06
26
+
27
+ ### Added
28
+
29
+ - Optional `select_fields` parameters to list retrieval methods for more efficient queries.
30
+ - New public API methods:
31
+ - `SharePointAPI.get_group_users` – fetch users of a SharePoint group.
32
+ - Improved error handling with explicit `TypeError` exceptions.
33
+ - Detailed docstrings for core classes and methods (enhances IDE support).
34
+
35
+ ### Changed
36
+
37
+ - HTTP header handling unified; corrected `X‑HTTP‑Method: PUT` for full updates.
38
+ - Error handling improved: generic `sys.exit(1)` replaced with explicit `TypeError`/`ConnectionError` exceptions.
39
+
40
+ ### Fixed
41
+
42
+ - Fixed incorrect PUT header that previously sent a MERGE header.
43
+ - Minor docstring formatting issues.
44
+
45
+ ### Security
46
+
47
+ - Enforced NTLM authentication across all request helpers.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sharepoint_v1_api
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Author: Aske Bluhme Klok
5
5
  License: MIT
6
6
  Keywords: Sharepoint
@@ -156,6 +156,24 @@ for v in versions:
156
156
  ```
157
157
 
158
158
 
159
+ ## 0.2.3 – 2025-12-02
160
+
161
+ ### Added
162
+
163
+ - GUID validation for list identifiers with fallback to list title lookup via `GetByTitle`.
164
+ - New helper method `_is_valid_guid` in `SharePointAPI`.
165
+ - Centralized list resolution helper `_resolve_sp_list` for GUID, title, or `SharePointList` instances.
166
+ - New method `get_list_metadata` to fetch only list metadata without items.
167
+ - Type hint enhancements: added `Optional`, `Tuple` imports; updated method signatures (e.g., `get_item_versions` now uses `Optional[List[str]]`).
168
+ - Updated `get_list` to return the resolved `SharePointList` object after appending items.
169
+ - Refactored multiple methods (`get_item`, `create_item`, `update_item`, `attach_file`, `get_item_versions`, `get_case`) to use `_resolve_sp_list`, removing redundant GUID validation logic.
170
+
171
+ ### Changed
172
+
173
+ - Cleaned up stray `else:` blocks and syntax errors.
174
+ - Improved consistency of return objects across list-related methods.
175
+ - Updated imports and docstrings accordingly.
176
+
159
177
  ## 0.2.2 – 2025-11-28
160
178
 
161
179
  ### Added
@@ -1,6 +1,6 @@
1
1
  from setuptools import setup
2
2
 
3
- __version__ = "0.2.2"
3
+ __version__ = "0.2.3"
4
4
 
5
5
  with open("README.md", "r") as readme_file, open("CHANGELOG.md", "r") as changelog_file:
6
6
  long_description = f"{readme_file.read()}\n\n{changelog_file.read()}"
@@ -1,10 +1,10 @@
1
- import sys
2
1
  import os
3
- import getpass
4
2
  import json
3
+ from datetime import timezone
5
4
  import requests
6
5
  from requests_ntlm import HttpNtlmAuth
7
- from typing import List
6
+ from typing import List, Optional, Tuple
7
+ import re
8
8
 
9
9
  from .SharePointUser import SharePointUser
10
10
  from .SharePointUserList import SharePointUserList
@@ -68,6 +68,43 @@ class SharePointAPI:
68
68
  self.password = password
69
69
  self.sharepoint_url = sharepoint_url
70
70
  self.proxies = proxies
71
+ self.timezone = timezone.utc
72
+
73
+ def _is_valid_guid(self, guid: str) -> bool:
74
+ """Validate that a string is a proper GUID."""
75
+ pattern = r'^[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}$'
76
+ return bool(re.fullmatch(pattern, guid))
77
+
78
+ def _resolve_sp_list(self, sharepoint_site: str, sp_list, SPListType: SharePointList = SharePointList) -> Tuple[str, Optional[SharePointList]]:
79
+ """
80
+ Resolve ``sp_list`` (GUID, title or ``SharePointList`` instance) to a GUID
81
+ and, when possible, a ``SharePointList`` object.
82
+
83
+ Returns
84
+ -------
85
+ tuple
86
+ (guid: str, list_obj: SharePointList | None)
87
+ """
88
+ # Case 1: already a SharePointList instance
89
+ if isinstance(sp_list, SharePointList):
90
+ return sp_list.guid, sp_list
91
+
92
+ # Case 2: string – could be GUID or title
93
+ if isinstance(sp_list, str):
94
+ if self._is_valid_guid(sp_list):
95
+ guid = sp_list
96
+ # Fetch minimal list metadata to build a SharePointList object
97
+ r = self._api_get_call(
98
+ f'{self.sharepoint_url}/cases/{sharepoint_site}/_api/Web/Lists(guid\'{guid}\')')
99
+ list_obj = SPListType(self, sharepoint_site, r.json()["d"])
100
+ return guid, list_obj
101
+ # Assume title – resolve via GetByTitle
102
+ list_obj = self.get_list_by_name(
103
+ sharepoint_site, sp_list, SPListType=SPListType)
104
+ return list_obj.guid, list_obj
105
+
106
+ raise TypeError(
107
+ 'Invalid sp_list argument; expected SharePointList or str')
71
108
 
72
109
  def _handle_response(self, response: requests.Response, success_codes: List[int]) -> requests.Response:
73
110
  """
@@ -530,21 +567,9 @@ class SharePointAPI:
530
567
  returned for each list item via the ``$select`` OData query option.
531
568
  '''
532
569
 
533
- # Uses either guid or SharePointList
534
- if isinstance(sp_list, SharePointList):
535
- guid = sp_list.guid
536
- r = self._api_get_call(
537
- f'{self.sharepoint_url}/cases/{sharepoint_site}/_api/Web/Lists(guid\'{guid}\')')
538
- sp_list = SPListType(self, sharepoint_site, r.json()["d"])
539
-
540
- elif isinstance(sp_list, str):
541
- guid = sp_list
542
- r = self._api_get_call(
543
- f'{self.sharepoint_url}/cases/{sharepoint_site}/_api/Web/Lists(guid\'{guid}\')')
544
- sp_list = SPListType(self, sharepoint_site, r.json()["d"])
545
- else:
546
- raise TypeError(
547
- 'Invalid sp_list argument; expected SharePointList or str')
570
+ # Resolve sp_list to GUID and (optionally) a SharePointList object
571
+ guid, sp_list_obj = self._resolve_sp_list(
572
+ sharepoint_site, sp_list, SPListType=SPListType)
548
573
 
549
574
  arguments = []
550
575
 
@@ -578,8 +603,8 @@ class SharePointAPI:
578
603
 
579
604
  items = [SPListType.SPItem(self, sharepoint_site, guid, item_settings)
580
605
  for item_settings in r.json()["d"]["results"]]
581
- sp_list.append_items(items)
582
- return sp_list
606
+ sp_list_obj.append_items(items)
607
+ return sp_list_obj
583
608
 
584
609
  def get_list_by_name(self, sharepoint_site, sp_list_name: str, filters=None, top=1000, view_path=None, select_fields=None, SPListType: SharePointList = SharePointList) -> SharePointList:
585
610
  '''
@@ -668,6 +693,21 @@ class SharePointAPI:
668
693
 
669
694
  # SP Item
670
695
 
696
+ def get_list_metadata(self, sharepoint_site, sp_list, SPListType: SharePointList = SharePointList) -> SharePointList:
697
+ """
698
+ Retrieve only the metadata of a SharePoint list without fetching its items.
699
+ Useful when only list properties (e.g., title, guid) are needed.
700
+ """
701
+ guid, list_obj = self._resolve_sp_list(
702
+ sharepoint_site, sp_list, SPListType=SPListType)
703
+ # If the list object was already resolved (e.g., via title lookup), reuse it.
704
+ if list_obj is not None:
705
+ return list_obj
706
+ # Otherwise, fetch minimal metadata using the GUID.
707
+ r = self._api_get_call(
708
+ f'{self.sharepoint_url}/cases/{sharepoint_site}/_api/Web/Lists(guid\'{guid}\')')
709
+ return SPListType(self, sharepoint_site, r.json()["d"])
710
+
671
711
  def get_item(self, sharepoint_site, sp_list, item_id, select_fields=None) -> SharePointListItem:
672
712
  '''
673
713
  Returns a single list item from a given sharepoint_site.
@@ -675,14 +715,8 @@ class SharePointAPI:
675
715
  returned via the ``$select`` OData query option.
676
716
  '''
677
717
 
678
- if isinstance(sp_list, SharePointList):
679
- guid = sp_list.guid
680
-
681
- elif isinstance(sp_list, str):
682
- guid = sp_list
683
- else:
684
- raise TypeError(
685
- 'Invalid sp_list argument; expected SharePointList or str')
718
+ # Resolve sp_list to GUID (and list object if needed)
719
+ guid, _ = self._resolve_sp_list(sharepoint_site, sp_list)
686
720
 
687
721
  # Build the request URL, adding $select if needed.
688
722
  if select_fields is not None:
@@ -701,14 +735,8 @@ class SharePointAPI:
701
735
 
702
736
  def create_item(self, sharepoint_site, sp_list, data) -> SharePointListItem:
703
737
  # Uses either guid or SharePointList
704
- if isinstance(sp_list, SharePointList):
705
- guid = sp_list.guid
706
- elif isinstance(sp_list, str):
707
- guid = sp_list
708
- r = self._api_get_call(
709
- f'{self.sharepoint_url}/cases/{sharepoint_site}/_api/Web/Lists(guid\'{guid}\')')
710
- sp_list = SharePointList(self,
711
- sharepoint_site, r.json()["d"]["results"][0])
738
+ # Resolve sp_list to GUID (and list object if needed)
739
+ guid, _ = self._resolve_sp_list(sharepoint_site, sp_list)
712
740
 
713
741
  r = self._api_post_call(
714
742
  f'{self.sharepoint_url}/cases/{sharepoint_site}/_api/contextinfo', {})
@@ -731,13 +759,8 @@ class SharePointAPI:
731
759
  item_id: The id of the item
732
760
  data: Data to push to the item
733
761
  '''
734
- if isinstance(sp_list, SharePointList):
735
- guid = sp_list.guid
736
- elif isinstance(sp_list, str):
737
- guid = sp_list
738
- else:
739
- raise TypeError(
740
- 'Only "SharePointList" and "str" types are allowed')
762
+ # Resolve sp_list to GUID (and list object if needed)
763
+ guid, _ = self._resolve_sp_list(sharepoint_site, sp_list)
741
764
 
742
765
  r = self._api_post_call(
743
766
  f'{self.sharepoint_url}/cases/{sharepoint_site}/_api/contextinfo', {})
@@ -752,13 +775,8 @@ class SharePointAPI:
752
775
 
753
776
  def attach_file(self, sharepoint_site, sp_list, item, file_name, file_content) -> dict:
754
777
  # Uses either guid or SharePointList
755
- if isinstance(sp_list, SharePointList):
756
- guid = sp_list.guid
757
- elif isinstance(sp_list, str):
758
- guid = sp_list
759
- else:
760
- raise TypeError(
761
- 'Only "SharePointList" and "str" types are allowed')
778
+ # Resolve sp_list to GUID (and list object if needed)
779
+ guid, _ = self._resolve_sp_list(sharepoint_site, sp_list)
762
780
 
763
781
  r = self._api_post_call(
764
782
  f'{self.sharepoint_url}/cases/{sharepoint_site}/_api/contextinfo', {})
@@ -766,23 +784,17 @@ class SharePointAPI:
766
784
  )["d"]["GetContextWebInformation"]["FormDigestValue"]
767
785
 
768
786
  r = self._api_attachment_call(
769
- f"{self.sharepoint_url}/cases/{sharepoint_site}/_api/Web/Lists(guid\'{guid}\')/items({item.Id})/AttachmentFiles/ add(FileName='{file_name}')", file_content, form_digest_value)
787
+ f"{self.sharepoint_url}/cases/{sharepoint_site}/_api/Web/Lists(guid\'{guid}\')/items({item.Id})/AttachmentFiles/add(FileName='{file_name}')", file_content, form_digest_value)
770
788
 
771
789
  return r.json()
772
790
 
773
- def get_item_versions(self, sharepoint_site, sp_list, item_id, select_fields=[]) -> list:
791
+ def get_item_versions(self, sharepoint_site, sp_list, item_id, select_fields: Optional[List[str]] = None) -> list:
774
792
  '''
775
793
  Returns a list of users from a given sharepoint_site
776
794
  '''
777
795
 
778
- if isinstance(sp_list, SharePointList):
779
- guid = sp_list.guid
780
-
781
- elif isinstance(sp_list, str):
782
- guid = sp_list
783
- else:
784
- raise TypeError(
785
- 'Invalid sp_list argument; expected SharePointList or str')
796
+ # Resolve sp_list to GUID (and list object if needed)
797
+ guid, _ = self._resolve_sp_list(sharepoint_site, sp_list)
786
798
 
787
799
  if select_fields:
788
800
  r = self._api_get_call(
@@ -836,14 +848,8 @@ class SharePointAPI:
836
848
  Returns a list of users from a given sharepoint_site
837
849
  '''
838
850
 
839
- if isinstance(sp_list, SharePointList):
840
- guid = sp_list.guid
841
-
842
- elif isinstance(sp_list, str):
843
- guid = sp_list
844
- else:
845
- raise TypeError(
846
- 'Invalid sp_list argument; expected SharePointList or str')
851
+ # Resolve sp_list to GUID (and list object if needed)
852
+ guid, _ = self._resolve_sp_list(sharepoint_site, sp_list)
847
853
 
848
854
  r = self._api_get_call(
849
855
  f'{self.sharepoint_url}/cases/{sharepoint_site}/_api/Web/Lists(guid\'{guid}\')/items({item_id})')
@@ -1,6 +1,7 @@
1
1
  from datetime import datetime
2
2
  # from .SharePointAPI import SharePointAPI as SP
3
3
 
4
+
4
5
  class SharePointListItem:
5
6
  """
6
7
  Represents a generic item in a SharePoint list.
@@ -34,12 +35,12 @@ class SharePointListItem:
34
35
  @property
35
36
  def versions(self) -> list:
36
37
  if not self._versions:
37
- self._versions = self.sp.get_item_versions(self.sharepoint_site, self.list_guid, self.Id)
38
+ self._versions = self.sp.get_item_versions(
39
+ self.sharepoint_site, self.list_guid, self.Id)
38
40
  if self._list is not None:
39
41
  self._list.CHANGE_DETECTED = True
40
- return self._versions
41
-
42
-
42
+ return self._versions
43
+
43
44
  @property
44
45
  def Id(self) -> str:
45
46
  """Unique identifier of the list item."""
@@ -62,7 +63,7 @@ class SharePointListItem:
62
63
  elif self.settings['Created'] is None:
63
64
  return None
64
65
  else:
65
- return datetime.strptime(self.settings['Created'], '%Y-%m-%dT%H:%M:%SZ')
66
+ return datetime.strptime(self.settings['Created'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=self.sp.timezone)
66
67
 
67
68
  @property
68
69
  def Modified(self) -> datetime:
@@ -76,39 +77,41 @@ class SharePointListItem:
76
77
  elif self.settings['Modified'] is None:
77
78
  return None
78
79
  else:
79
- return datetime.strptime(self.settings['Modified'], '%Y-%m-%dT%H:%M:%SZ')
80
+ return datetime.strptime(self.settings['Modified'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=self.sp.timezone)
80
81
 
81
82
  def attach_item(self, file_name, file_path):
82
83
  """
83
84
  Attach a file to this list item.
84
-
85
+
85
86
  Parameters
86
87
  ----------
87
88
  file_name : str
88
89
  The name of the file as it should appear in SharePoint.
89
90
  file_path : str
90
91
  Local path to the file to be uploaded.
91
-
92
+
92
93
  The method reads the file content and uses :meth:`SharePointAPI.attach_file`
93
94
  to upload it to the corresponding SharePoint list item.
94
95
  """
95
96
  with open(file_path, 'r') as f:
96
97
  file_content = f.read()
97
98
 
98
-
99
- self.sp.attach_file(self.sharepoint_site, self.list, self, file_name, file_content)
99
+ self.sp.attach_file(self.sharepoint_site, self.list,
100
+ self, file_name, file_content)
100
101
 
101
102
  def versions_select_fields(self, select_fields=[]) -> list:
102
103
  if not self._versions:
103
- self._versions = self.sp.get_item_versions(self.sharepoint_site, self.list_guid, self.Id, select_fields)
104
+ self._versions = self.sp.get_item_versions(
105
+ self.sharepoint_site, self.list_guid, self.Id, select_fields)
104
106
  return self._versions
105
107
 
106
108
  def update_fields(self, data):
107
109
  self.sp.update_item(self.sharepoint_site,
108
110
  self.list_guid, self.Id, data)
109
111
 
112
+
110
113
  class SharepointSiteCase(SharePointListItem):
111
-
114
+
112
115
  @property
113
116
  def AssignmentType(self) -> str:
114
117
  return self.settings['AssignmentType']
@@ -121,7 +124,7 @@ class SharepointSiteCase(SharePointListItem):
121
124
  elif self.settings['CaseClosed'] is None:
122
125
  return None
123
126
  else:
124
- return datetime.strptime(self.settings['CaseClosed'], '%Y-%m-%dT%H:%M:%SZ')
127
+ return datetime.strptime(self.settings['CaseClosed'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=self.sp.timezone)
125
128
 
126
129
  @property
127
130
  def Due(self) -> datetime:
@@ -131,7 +134,7 @@ class SharepointSiteCase(SharePointListItem):
131
134
  elif self.settings['DueDate'] is None:
132
135
  return None
133
136
  else:
134
- return datetime.strptime(self.settings['DueDate'], '%Y-%m-%dT%H:%M:%SZ')
137
+ return datetime.strptime(self.settings['DueDate'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=self.sp.timezone)
135
138
 
136
139
  @property
137
140
  def Priority(self) -> str:
@@ -156,7 +159,7 @@ class SharepointSiteCase(SharePointListItem):
156
159
  solved_in_time = True
157
160
  else:
158
161
  solved_in_time = True
159
-
162
+
160
163
  return solved_in_time
161
164
 
162
165
  @property
@@ -167,7 +170,7 @@ class SharepointSiteCase(SharePointListItem):
167
170
  elif self.settings['WorkBegun'] is None:
168
171
  return None
169
172
  else:
170
- return datetime.strptime(self.settings['WorkBegun'], '%Y-%m-%dT%H:%M:%SZ')
173
+ return datetime.strptime(self.settings['WorkBegun'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=self.sp.timezone)
171
174
 
172
175
  @property
173
176
  def DeadlineWorkTimestamp(self) -> datetime:
@@ -177,7 +180,7 @@ class SharepointSiteCase(SharePointListItem):
177
180
  elif self.settings['DeadlineWork'] is None:
178
181
  return None
179
182
  else:
180
- return datetime.strptime(self.settings['DeadlineWork'], '%Y-%m-%dT%H:%M:%SZ')
183
+ return datetime.strptime(self.settings['DeadlineWork'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=self.sp.timezone)
181
184
 
182
185
  @property
183
186
  def ReactedInTime(self) -> bool:
@@ -191,6 +194,3 @@ class SharepointSiteCase(SharePointListItem):
191
194
  else:
192
195
  reacted_in_time = True
193
196
  return reacted_in_time
194
-
195
-
196
-
@@ -3,6 +3,7 @@ from datetime import datetime
3
3
  from sharepoint_api.SharePointListItem import SharePointListItem
4
4
  # from .SharePointAPI import SharePointAPI as SP
5
5
 
6
+
6
7
  class TimeRegistration(SharePointListItem):
7
8
  """
8
9
  Represents a time‑registration entry associated with a SharePoint list item.
@@ -22,8 +23,8 @@ class TimeRegistration(SharePointListItem):
22
23
  elif self.settings['DoneDate'] is None:
23
24
  return None
24
25
  else:
25
- return datetime.strptime(self.settings['DoneDate'], '%Y-%m-%dT%H:%M:%SZ')
26
-
26
+ return datetime.strptime(self.settings['DoneDate'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=self.sp.timezone)
27
+
27
28
  @property
28
29
  def Hours(self) -> str:
29
30
  return self.settings['Hours']
@@ -38,7 +39,7 @@ class TimeRegistration(SharePointListItem):
38
39
  user_list = self.sp.get_users(self.sharepoint_site)
39
40
  self._DoneBy = user_list.get_user_by_id(self.DoneById)
40
41
  return self._DoneBy
41
-
42
+
42
43
  @property
43
44
  def DoneUsername(self) -> str:
44
45
  return self.DoneBy.UserName
@@ -46,8 +47,7 @@ class TimeRegistration(SharePointListItem):
46
47
  @property
47
48
  def CaseId(self) -> str:
48
49
  return self.settings['CaseId']
49
-
50
+
50
51
  @property
51
52
  def WorkPackageId(self) -> str:
52
53
  return self.settings['WorkPackageId']
53
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sharepoint_v1_api
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Author: Aske Bluhme Klok
5
5
  License: MIT
6
6
  Keywords: Sharepoint
@@ -156,6 +156,24 @@ for v in versions:
156
156
  ```
157
157
 
158
158
 
159
+ ## 0.2.3 – 2025-12-02
160
+
161
+ ### Added
162
+
163
+ - GUID validation for list identifiers with fallback to list title lookup via `GetByTitle`.
164
+ - New helper method `_is_valid_guid` in `SharePointAPI`.
165
+ - Centralized list resolution helper `_resolve_sp_list` for GUID, title, or `SharePointList` instances.
166
+ - New method `get_list_metadata` to fetch only list metadata without items.
167
+ - Type hint enhancements: added `Optional`, `Tuple` imports; updated method signatures (e.g., `get_item_versions` now uses `Optional[List[str]]`).
168
+ - Updated `get_list` to return the resolved `SharePointList` object after appending items.
169
+ - Refactored multiple methods (`get_item`, `create_item`, `update_item`, `attach_file`, `get_item_versions`, `get_case`) to use `_resolve_sp_list`, removing redundant GUID validation logic.
170
+
171
+ ### Changed
172
+
173
+ - Cleaned up stray `else:` blocks and syntax errors.
174
+ - Improved consistency of return objects across list-related methods.
175
+ - Updated imports and docstrings accordingly.
176
+
159
177
  ## 0.2.2 – 2025-11-28
160
178
 
161
179
  ### Added
@@ -1,29 +0,0 @@
1
- ## 0.2.2 – 2025-11-28
2
-
3
- ### Added
4
-
5
- - `SharePointList.fields` property to retrieve list field definitions
6
-
7
- ## 0.2.1 – 2025‑11‑06
8
-
9
- ### Added
10
-
11
- - Optional `select_fields` parameters to list retrieval methods for more efficient queries.
12
- - New public API methods:
13
- - `SharePointAPI.get_group_users` – fetch users of a SharePoint group.
14
- - Improved error handling with explicit `TypeError` exceptions.
15
- - Detailed docstrings for core classes and methods (enhances IDE support).
16
-
17
- ### Changed
18
-
19
- - HTTP header handling unified; corrected `X‑HTTP‑Method: PUT` for full updates.
20
- - Error handling improved: generic `sys.exit(1)` replaced with explicit `TypeError`/`ConnectionError` exceptions.
21
-
22
- ### Fixed
23
-
24
- - Fixed incorrect PUT header that previously sent a MERGE header.
25
- - Minor docstring formatting issues.
26
-
27
- ### Security
28
-
29
- - Enforced NTLM authentication across all request helpers.