pyGuardPoint 2.1.1__tar.gz → 2.1.2__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 (68) hide show
  1. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/PKG-INFO +1 -1
  2. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/CustomWebsocketTransport.py +15 -3
  3. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_cardholders.py +10 -22
  4. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_cards.py +5 -2
  5. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_departments.py +4 -1
  6. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_readers.py +4 -1
  7. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_odata_filter.py +13 -6
  8. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_str_match_algo.py +11 -2
  9. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/guardpoint_connection_asyncio.py +4 -2
  10. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_connection.py +2 -1
  11. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_dataclasses.py +16 -11
  12. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_utils.py +17 -7
  13. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/PKG-INFO +1 -1
  14. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/setup.py +1 -1
  15. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/LICENSE.txt +0 -0
  16. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/README.rst +0 -0
  17. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/CustomWebsocketTransportOld.py +0 -0
  18. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/__init__.py +0 -0
  19. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_accessgroups.py +0 -0
  20. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_alarmstates.py +0 -0
  21. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_alarmzones.py +0 -0
  22. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_areas.py +0 -0
  23. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_cardholdertypes.py +0 -0
  24. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_controllers.py +0 -0
  25. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_customizedfields.py +0 -0
  26. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_diagnostic.py +0 -0
  27. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_events.py +0 -0
  28. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_genericinformation.py +0 -0
  29. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_inputs.py +0 -0
  30. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_manualevents.py +0 -0
  31. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_ouputs.py +0 -0
  32. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_personaldetails.py +0 -0
  33. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_scheduledmags.py +0 -0
  34. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_securitygroups.py +0 -0
  35. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_sites.py +0 -0
  36. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_weeklyprograms.py +0 -0
  37. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/__init__.py +0 -0
  38. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_accessgroups.py +0 -0
  39. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_alarmstates.py +0 -0
  40. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_alarmzones.py +0 -0
  41. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_areas.py +0 -0
  42. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_cardholders.py +0 -0
  43. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_cardholdertypes.py +0 -0
  44. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_cards.py +0 -0
  45. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_controllers.py +0 -0
  46. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_customizedfields.py +0 -0
  47. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_departments.py +0 -0
  48. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_diagnostic.py +0 -0
  49. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_events.py +0 -0
  50. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_genericinformation.py +0 -0
  51. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_manualevents.py +0 -0
  52. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_ouputs.py +0 -0
  53. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_personaldetails.py +0 -0
  54. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_readers.py +0 -0
  55. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_scheduledmags.py +0 -0
  56. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_securitygroups.py +0 -0
  57. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_sites.py +0 -0
  58. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_weeklyprograms.py +0 -0
  59. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint.py +0 -0
  60. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_asyncio.py +0 -0
  61. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_error.py +0 -0
  62. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_threaded.py +0 -0
  63. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/SOURCES.txt +0 -0
  64. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/dependency_links.txt +0 -0
  65. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/not-zip-safe +0 -0
  66. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/requires.txt +0 -0
  67. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/top_level.txt +0 -0
  68. {pyguardpoint-2.1.1 → pyguardpoint-2.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyGuardPoint
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Python wrapper for GuardPoint 10 Access Control System
5
5
  Author: John Owen
6
6
  Maintainer-email: sales@sensoraccess.co.uk
@@ -8,10 +8,22 @@ from typing import TYPE_CHECKING
8
8
  from aiohttp import ClientSession, TCPConnector
9
9
  from aiohttp import ClientTimeout
10
10
  from aiohttp import ServerConnectionError
11
- from websockets.asyncio.client import ClientConnection
12
- from websockets.asyncio.client import connect
11
+
12
+ # Handle websockets version compatibility
13
+ try:
14
+ from websockets.asyncio.client import ClientConnection, connect
15
+ except ImportError:
16
+ # websockets<13.0 compatibility
17
+ from websockets.client import WebSocketClientProtocol as ClientConnection
18
+ from websockets.client import connect
19
+
13
20
  from websockets.exceptions import ConnectionClosed
14
- from websockets.protocol import State
21
+
22
+ try:
23
+ from websockets.protocol import State
24
+ except ImportError:
25
+ # websockets<13.0 compatibility
26
+ State = None
15
27
 
16
28
  import pysignalr.exceptions as exceptions
17
29
  from pysignalr.messages import CompletionMessage
@@ -298,9 +298,6 @@ class CardholdersAPI:
298
298
  return None
299
299
  else:
300
300
  return None
301
- # Part of the Cards_API
302
- # (Broken API Call)
303
- #return self.get_cardholder_by_card_code(card_code)
304
301
  else:
305
302
  return self._get_card_holder(uid)
306
303
 
@@ -355,12 +352,6 @@ class CardholdersAPI:
355
352
  "securityGroup," \
356
353
  "insideArea"
357
354
 
358
- # Do not apply site filter, when looking for individuals
359
- #if self.site_uid is not None:
360
- # match_args = {'ownerSiteUID': self.site_uid}
361
- # filter_str = _compose_filter(exact_match=match_args)
362
- # url_query_params += ("&" + filter_str)
363
-
364
355
  code, json_body = self.gp_json_query("GET", url=(url + url_query_params))
365
356
 
366
357
  if code == 404: # Not Found
@@ -428,14 +419,14 @@ class CardholdersAPI:
428
419
  return []
429
420
  if limit > 50 and count is False:
430
421
  i_offset = offset
431
- offset = 0
422
+ current_offset = 0
432
423
  batch_limit = 50
433
424
  card_holders = []
434
- while len(card_holders) == offset:
435
- if offset + batch_limit > limit:
436
- batch_limit = limit - offset
425
+ while len(card_holders) < limit:
426
+ if current_offset + batch_limit > limit:
427
+ batch_limit = limit - current_offset
437
428
  if batch_limit > 0:
438
- batch = self._split_get_card_holders_query(offset=offset + i_offset, limit=batch_limit,
429
+ batch = self._split_get_card_holders_query(offset=current_offset + i_offset, limit=batch_limit,
439
430
  search_terms=search_terms,
440
431
  areas=areas,
441
432
  filter_expired=filter_expired,
@@ -447,11 +438,11 @@ class CardholdersAPI:
447
438
  **cardholder_kwargs)
448
439
  if isinstance(batch, list):
449
440
  card_holders.extend(batch)
450
-
451
- if (offset + batch_limit) >= limit:
452
- break
453
- elif len(card_holders) > offset:
454
- offset = len(card_holders)
441
+ if len(batch) < batch_limit:
442
+ break
443
+ else:
444
+ break
445
+ current_offset = len(card_holders)
455
446
  else:
456
447
  break
457
448
 
@@ -571,9 +562,6 @@ class CardholdersAPI:
571
562
  url_query_params += "$top=" + str(limit) + "&$skip=" + str(offset)
572
563
 
573
564
  code, json_body = self.gp_json_query("GET", url=(url + url_query_params))
574
- # Check response body is formatted correctly
575
- # if json_body:
576
- # GuardPointResponse.check_odata_body_structure(json_body)
577
565
 
578
566
  if code != 200:
579
567
  error_msg = GuardPointResponse.extract_error_msg(json_body)
@@ -72,7 +72,7 @@ class CardsAPI:
72
72
  if code == 401:
73
73
  raise GuardPointUnauthorized(f"Unauthorized - ({error_msg})")
74
74
  elif code == 404: # Not Found
75
- raise GuardPointError(f"Cardholder Not Found")
75
+ raise GuardPointError(f"Cards Not Found")
76
76
  else:
77
77
  raise GuardPointError(f"{error_msg}")
78
78
 
@@ -244,7 +244,10 @@ class CardsAPI:
244
244
  if 'value' not in json_body:
245
245
  raise GuardPointError("Badly formatted response.")
246
246
 
247
- return Card(json_body['value'])
247
+ value = json_body['value']
248
+ if isinstance(value, list):
249
+ return Card(value[0]) if value else None
250
+ return Card(value)
248
251
 
249
252
  def get_cardholder_by_card_code(self, card_code):
250
253
  """
@@ -33,7 +33,10 @@ class DepartmentsAPI:
33
33
  if 'value' not in json_body:
34
34
  raise GuardPointError("Badly formatted response.")
35
35
 
36
- return Department(json_body['value'])
36
+ value = json_body['value']
37
+ if isinstance(value, list):
38
+ return Department(value[0]) if value else None
39
+ return Department(value)
37
40
 
38
41
  def get_departments(self):
39
42
  url = "/odata/API_Departments"
@@ -141,5 +141,8 @@ class ReadersAPI:
141
141
  if 'value' not in json_body:
142
142
  raise GuardPointError("Badly formatted response.")
143
143
 
144
- return Reader(json_body['value'])
144
+ value = json_body['value']
145
+ if isinstance(value, list):
146
+ return Reader(value[0]) if value else None
147
+ return Reader(value)
145
148
 
@@ -93,29 +93,36 @@ def _compose_filter(search_words=None,
93
93
  if isinstance(v, str):
94
94
  v = v.replace("'", "''")
95
95
  if validators.uuid(v):
96
- filter_phrases.append(f"({k}%20eq%20{quote(v)})")
96
+ filter_phrases.append(f"({k}%20eq%20{v})")
97
97
  else:
98
98
  filter_phrases.append(f"({k}%20eq%20'{quote(v)}')")
99
99
  if isinstance(v, (bool, int)):
100
- filter_phrases.append(f"({k}%20eq%20{quote(v)})")
100
+ filter_phrases.append(f"({k}%20eq%20{v})")
101
101
  if k == "cardholderPersonalDetail" and v.__class__.__name__ == "CardholderPersonalDetail":
102
102
  details = v.dict(changed_only=True)
103
103
  for dk, dv in details.items():
104
104
  if isinstance(dv, str):
105
105
  dv = dv.replace("'", "''")
106
- filter_phrases.append(f"(CardholderPersonalDetail/{dk}%20eq%20'{quote(dv)}')")
106
+ filter_phrases.append(f"(CardholderPersonalDetail/{dk}%20eq%20'{quote(dv)}')")
107
+ elif isinstance(dv, (bool, int)):
108
+ filter_phrases.append(f"(CardholderPersonalDetail/{dk}%20eq%20{dv})")
109
+ elif isinstance(dv, type(None)):
110
+ filter_phrases.append(f"(CardholderPersonalDetail/{dk}%20eq%20null)")
111
+ else:
112
+ dv_str = str(dv).replace("'", "''")
113
+ filter_phrases.append(f"(CardholderPersonalDetail/{dk}%20eq%20'{quote(dv_str)}')")
107
114
  if k == "cardholderCustomizedField" and v.__class__.__name__ == "CardholderCustomizedField":
108
115
  details = v.dict(changed_only=True)
109
116
  for dk, dv in details.items():
110
117
  if isinstance(dv, type(None)):
111
- filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20{quote('null')})")
118
+ filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20null)")
112
119
  elif isinstance(dv, (bool, int)):
113
120
  filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20{str(dv).lower()})")
114
121
  elif isinstance(dv, str):
115
122
  dv = dv.replace("'", "''")
116
123
  filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20'{quote(dv)}')")
117
124
  else:
118
- filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20'{quote(dv)}')")
125
+ filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20'{str(dv)}')")
119
126
 
120
127
  if earliest_last_pass:
121
128
  if isinstance(earliest_last_pass, datetime):
@@ -136,7 +143,7 @@ def _compose_filter(search_words=None,
136
143
  if areas:
137
144
  if isinstance(areas, Area):
138
145
  filter_phrases.append(f"(insideAreaUID%20eq%20{areas.uid})")
139
- if isinstance(areas, list):
146
+ elif isinstance(areas, list):
140
147
  if len(areas) > 0:
141
148
  area_phrases = []
142
149
  for area in areas:
@@ -12,11 +12,20 @@ def fuzzy_match(search_words: str, cardholders: list, threshold: int = 75):
12
12
 
13
13
  match_ratios = process.extract(search_words, cardholder_patterns, scorer=fuzz.WRatio, limit=20)
14
14
 
15
+ pattern_to_indices = {}
16
+ for idx, pattern in enumerate(cardholder_patterns):
17
+ if pattern not in pattern_to_indices:
18
+ pattern_to_indices[pattern] = []
19
+ pattern_to_indices[pattern].append(idx)
20
+
15
21
  sorted_cardholders = []
22
+ seen = set()
16
23
  for match in match_ratios:
17
24
  if match[1] >= threshold:
18
- pos = cardholder_patterns.index(match[0])
19
- sorted_cardholders.append(cardholders[pos])
25
+ for pos in pattern_to_indices.get(match[0], []):
26
+ if pos not in seen:
27
+ sorted_cardholders.append(cardholders[pos])
28
+ seen.add(pos)
20
29
 
21
30
  return sorted_cardholders
22
31
 
@@ -59,9 +59,10 @@ class GuardPointConnection:
59
59
  if self.session:
60
60
  await self.session.close()
61
61
 
62
- async def reopen(self):
62
+ def reopen(self):
63
63
  conn = aiohttp.TCPConnector(ssl_context=self.ssl_context)
64
64
  self.session = aiohttp.ClientSession(connector=conn)
65
+
65
66
  def open(self, url_components, auth, user, pwd, key, token=None,
66
67
  cert_file=None, key_file=None, key_pwd="", ca_file=None, p12_file=None, p12_pwd="", timeout=5):
67
68
  self.ssl_context = None
@@ -116,7 +117,8 @@ class GuardPointConnection:
116
117
 
117
118
  if cert_file and key_file:
118
119
  # Loading of client certificate
119
- self.ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file, password=key_pwd)
120
+ pwd_bytes = key_pwd.encode() if isinstance(key_pwd, str) else key_pwd
121
+ self.ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file, password=pwd_bytes if pwd_bytes else None)
120
122
 
121
123
  if ca_file:
122
124
  # Loading of CA certificate.
@@ -108,7 +108,8 @@ class GuardPointConnection:
108
108
 
109
109
  if cert_file and key_file:
110
110
  # Loading of client certificate
111
- self.ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file, password=p12_pwd)
111
+ pwd_bytes = p12_pwd.encode() if isinstance(p12_pwd, str) else p12_pwd
112
+ self.ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file, password=pwd_bytes if pwd_bytes else None)
112
113
 
113
114
  if ca_file:
114
115
  # Loading of CA certificate.
@@ -33,7 +33,7 @@ class Observable:
33
33
 
34
34
  def add_observer(self, name):
35
35
  with self._observer_lock:
36
- self.observed[name].append(lambda name: self.changed_attributes.add(name))
36
+ self.observed[name].append(lambda attr_name: self.changed_attributes.add(attr_name))
37
37
 
38
38
 
39
39
  def sanitise_args(obj: Observable, args, kwargs):
@@ -56,12 +56,12 @@ def sanitise_args(obj: Observable, args, kwargs):
56
56
 
57
57
 
58
58
  class SortAlgorithm(Enum):
59
- SERVER_DEFAULT = 0,
59
+ SERVER_DEFAULT = 0
60
60
  FUZZY_MATCH = 1
61
61
 
62
62
 
63
63
  class CardholderOrderBy(Enum):
64
- fromDateValid_DESC = 0,
64
+ fromDateValid_DESC = 0
65
65
  lastPassDate_DESC = 1
66
66
 
67
67
 
@@ -99,6 +99,7 @@ class CustomizedField:
99
99
  cf_dict[k] = None
100
100
  else:
101
101
  cf_dict[k] = str(v)
102
+ return cf_dict
102
103
 
103
104
  @dataclass
104
105
  class WeeklyProgram:
@@ -124,6 +125,7 @@ class WeeklyProgram:
124
125
  me_dict[k] = None
125
126
  else:
126
127
  me_dict[k] = str(v)
128
+ return me_dict
127
129
 
128
130
 
129
131
  @dataclass
@@ -150,6 +152,7 @@ class ManualEvent:
150
152
  me_dict[k] = None
151
153
  else:
152
154
  me_dict[k] = str(v)
155
+ return me_dict
153
156
 
154
157
 
155
158
  @dataclass
@@ -158,12 +161,12 @@ class Input:
158
161
  logicalStatus: str = ""
159
162
  isUnderAlarm: bool = False
160
163
  uid: str = ""
161
- number: int = 0,
164
+ number: int = 0
162
165
  name: str = ""
163
166
  descriprion: any = None
164
167
  weeklyProgramUID: any = None
165
168
  delayType: str = ""
166
- delayTime: int = 0,
169
+ delayTime: int = 0
167
170
  inputType: str = ""
168
171
  statusType: str = ""
169
172
  controllerUID: str = ""
@@ -172,12 +175,12 @@ class Input:
172
175
  lastEventType: any = None
173
176
  latestAction: any = None
174
177
  inputGroupUID: any = None
175
- alarmPriority: int = 0,
178
+ alarmPriority: int = 0
176
179
  isArm: bool = False
177
180
  isBypassed: bool = False
178
181
  instructions: any = None
179
182
  isGalaxy: bool = False
180
- omitted: int = 0,
183
+ omitted: int = 0
181
184
  apiKey: any = None
182
185
 
183
186
  def __init__(self, *args, **kwargs):
@@ -197,6 +200,7 @@ class Input:
197
200
  input_dict[k] = None
198
201
  else:
199
202
  input_dict[k] = str(v)
203
+ return input_dict
200
204
 
201
205
 
202
206
  @dataclass
@@ -233,6 +237,7 @@ class AlarmState:
233
237
  alarm_state_dict[k] = None
234
238
  else:
235
239
  alarm_state_dict[k] = str(v)
240
+ return alarm_state_dict
236
241
 
237
242
 
238
243
  @dataclass
@@ -544,7 +549,7 @@ class AccessEvent:
544
549
  accessDeniedCode: str = ""
545
550
  cardCode: str = ""
546
551
  cardholderFirstName: any = None
547
- cardholderIdNumber: str = None
552
+ cardholderIdNumber: any = None
548
553
  cardholderLastName: any = None
549
554
  cardholderTypeName: any = None
550
555
  cardholderTypeUID: any = None
@@ -868,7 +873,7 @@ class ScheduledMag(Observable):
868
873
  @dataclass
869
874
  class CardholderCustomizedField(Observable):
870
875
  uid: str = ""
871
- cF_BoolField_1: bool = False,
876
+ cF_BoolField_1: bool = False
872
877
  cF_BoolField_2: bool = False
873
878
  cF_BoolField_3: bool = False
874
879
  cF_BoolField_4: bool = False
@@ -1183,8 +1188,8 @@ class Cardholder(Observable):
1183
1188
 
1184
1189
  for property_name in cardholder_dict:
1185
1190
  if isinstance(cardholder_dict[property_name], list):
1186
- if property_name == "accessGroupUIDs":
1187
- setattr(self, property_name, ";".join(cardholder_dict[property_name]))
1191
+ if property_name == "accessGroupUIDs" or property_name == "liftAccessGroupUIDs":
1192
+ setattr(self, property_name, cardholder_dict[property_name])
1188
1193
  elif property_name == "cards":
1189
1194
  setattr(self, property_name, [])
1190
1195
  for card_entry in cardholder_dict[property_name]:
@@ -6,19 +6,29 @@ from .guardpoint_error import GuardPointError
6
6
 
7
7
 
8
8
  def url_parser(url):
9
+ if not url or not isinstance(url, str):
10
+ raise GuardPointError("Invalid URL: must be a non-empty string")
11
+
9
12
  parts = urlparse(url)
10
- directories = parts.path.strip('/').split('/')
11
- queries = parts.query.strip('&').split('&')
13
+
14
+ if not parts.netloc:
15
+ raise GuardPointError("Invalid URL: missing host")
16
+
17
+ directories = parts.path.strip('/').split('/') if parts.path else ['']
18
+ queries = parts.query.strip('&').split('&') if parts.query else []
12
19
  host = parts.netloc.strip(':').split(':')[0]
13
20
 
21
+ if not host:
22
+ raise GuardPointError("Invalid URL: empty host")
23
+
14
24
  elements = {
15
- 'scheme': parts.scheme,
25
+ 'scheme': parts.scheme or '',
16
26
  'host': host,
17
- 'path': parts.path,
18
- 'params': parts.params,
19
- 'query': parts.query,
27
+ 'path': parts.path or '',
28
+ 'params': parts.params or '',
29
+ 'query': parts.query or '',
20
30
  'port': parts.port,
21
- 'fragment': parts.fragment,
31
+ 'fragment': parts.fragment or '',
22
32
  'directories': directories,
23
33
  'queries': queries,
24
34
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyGuardPoint
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Python wrapper for GuardPoint 10 Access Control System
5
5
  Author: John Owen
6
6
  Maintainer-email: sales@sensoraccess.co.uk
@@ -5,7 +5,7 @@ long_description = open('README.rst').read()
5
5
  setup(name="pyGuardPoint",
6
6
  python_requires='>3.9.0',
7
7
  packages=find_packages(),
8
- version="2.1.1",
8
+ version="2.1.2",
9
9
  author="John Owen",
10
10
  description="Python wrapper for GuardPoint 10 Access Control System",
11
11
  long_description_content_type='text/markdown',
File without changes
File without changes
File without changes