papi-projects 0.1.3__tar.gz → 0.1.6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: papi-projects
3
- Version: 0.1.3
3
+ Version: 0.1.6
4
4
  Summary: PAPI is an API for managing projects
5
5
  License: MIT
6
6
  Author: sandyjmacdonald
@@ -129,3 +129,153 @@ To collate your hours worked in August 2024:
129
129
  ```
130
130
  collate-toggl-hours -s 2024-08-01 -e 2024-08-31 -o august-2024-hours.tsv
131
131
  ```
132
+
133
+ ## API reference
134
+
135
+ ## project module
136
+
137
+ ## Project class
138
+
139
+ The `Project` class is central to the whole library. A `Project` instance can be created in a few different ways.
140
+
141
+ At the most basic level, a valid `user_id` (either three letter initials or two letter initials and an integer number from 1 to 9) can be provided when instantiating the class, and the prefix and suffix will be generated.
142
+
143
+ ```
144
+ from papi.project import Project
145
+
146
+ proj = Project(user_id="CRD")
147
+ ```
148
+
149
+ This will generate the project ID, `id` attribute using the current year, and a random four-letter suffix.
150
+
151
+ ```
152
+ print(proj.id)
153
+ print(proj.year)
154
+ print(proj.suffix)
155
+ ```
156
+
157
+ ```
158
+ P2024-CRD-FZLL
159
+ 2024
160
+ FZLL
161
+ ```
162
+
163
+ If a valid project ID has already been created, then a `Project` instance can be instantiated with the `id` attribute, and the `year`, `user_id`, and `suffix` attributes will be pulled out and set on the instance.
164
+
165
+ ```
166
+ proj = Project(id="P2024-CRD-FZLL")
167
+
168
+ print(proj.year)
169
+ print(proj.user_id)
170
+ print(proj.suffix)
171
+ ```
172
+
173
+ ```
174
+ 2024
175
+ CRD
176
+ FZLL
177
+ ```
178
+
179
+ If a grant code and/or project name are available, then these can be passed in when instantiating the class.
180
+
181
+ ```
182
+ proj = Project(user_id="CRD", grant_code="R12345", name="RNA-seq analysis")
183
+ ```
184
+
185
+ A version 4 UUID is also generated for the project when instantiated.
186
+
187
+ ```
188
+ proj = Project(user_id="CRD")
189
+
190
+ print(proj.p_uuid)
191
+ ```
192
+
193
+ ```
194
+ 6697e457-9785-4668-b78b-72616b27aede
195
+ ```
196
+
197
+ Or if a version 4 UUID has been generated separately then it can be provided when instantiating.
198
+
199
+ ```
200
+ proj = Project(user_id="CRD", p_uuid="6697e457-9785-4668-b78b-72616b27aede")
201
+ ```
202
+
203
+ ## project functions
204
+
205
+ A couple of functions are provided to check the validity of a project ID, to check the validity of a suffix, and to check for a valid version 4 UUID.
206
+
207
+ You can check the validity of a project ID as follows:
208
+
209
+ ```
210
+ from papi.project import check_project_id
211
+
212
+ print(check_project_id("P2024-CRD-FZLL"))
213
+ print(check_project_id("P2024-CRD-1234"))
214
+ ```
215
+
216
+ ```
217
+ True
218
+ False
219
+ ```
220
+
221
+ You can check the validity of a project suffix as follows:
222
+
223
+ ```
224
+ from papi.project import check_suffix
225
+
226
+ print(check_suffix("FZLL"))
227
+ print(check_suffix("1234"))
228
+ ```
229
+
230
+ ```
231
+ True
232
+ False
233
+ ```
234
+
235
+ ## user module
236
+
237
+ ## User class
238
+
239
+ The `User` class stores attributes of a user: their name, a three-letter initial (or two-letter initial and integer number from 1 to 9), and an optional email address.
240
+
241
+ The most basic way of instantiating a `User` instance is as follows:
242
+
243
+ ```
244
+ from papi.user import User
245
+
246
+ usr = User("Charles Robert Darwin")
247
+
248
+ print(usr.user_id)
249
+ print(usr.user_name)
250
+ ```
251
+
252
+ ```
253
+ CRD
254
+ Charles Robert Darwin
255
+ ```
256
+
257
+ The first initials are converted into the `user_id` attribute.
258
+
259
+ If an email address is available, then this can be provided when instantiating:
260
+
261
+ ```
262
+ from papi.user import User
263
+
264
+ usr = User("Charles Robert Darwin", email="cdarwin@beaglemail.com")
265
+
266
+ print(usr.email)
267
+ ```
268
+
269
+ ```
270
+ cdarwin@beaglemail.com
271
+ ```
272
+
273
+ Because our user ID naming scheme enforces that a user ID must be unique, the `user_id` attribute should not really be set directly, although it can in theory:
274
+
275
+ ```
276
+ usr = User("Charles Darwin")
277
+ usr.user_id = "CD1"
278
+ ```
279
+
280
+ Setting the `user_id` attribute creates the possibility of a clash in user IDs, therefore the `user` module provides a means to create a basic user database with the TinyDB library. This avoids the possibility of a clash and appends and increments integer numbers to the end of the user ID if a matching one is already in the database.
281
+
@@ -110,4 +110,153 @@ To collate your hours worked in August 2024:
110
110
 
111
111
  ```
112
112
  collate-toggl-hours -s 2024-08-01 -e 2024-08-31 -o august-2024-hours.tsv
113
- ```
113
+ ```
114
+
115
+ ## API reference
116
+
117
+ ## project module
118
+
119
+ ## Project class
120
+
121
+ The `Project` class is central to the whole library. A `Project` instance can be created in a few different ways.
122
+
123
+ At the most basic level, a valid `user_id` (either three letter initials or two letter initials and an integer number from 1 to 9) can be provided when instantiating the class, and the prefix and suffix will be generated.
124
+
125
+ ```
126
+ from papi.project import Project
127
+
128
+ proj = Project(user_id="CRD")
129
+ ```
130
+
131
+ This will generate the project ID, `id` attribute using the current year, and a random four-letter suffix.
132
+
133
+ ```
134
+ print(proj.id)
135
+ print(proj.year)
136
+ print(proj.suffix)
137
+ ```
138
+
139
+ ```
140
+ P2024-CRD-FZLL
141
+ 2024
142
+ FZLL
143
+ ```
144
+
145
+ If a valid project ID has already been created, then a `Project` instance can be instantiated with the `id` attribute, and the `year`, `user_id`, and `suffix` attributes will be pulled out and set on the instance.
146
+
147
+ ```
148
+ proj = Project(id="P2024-CRD-FZLL")
149
+
150
+ print(proj.year)
151
+ print(proj.user_id)
152
+ print(proj.suffix)
153
+ ```
154
+
155
+ ```
156
+ 2024
157
+ CRD
158
+ FZLL
159
+ ```
160
+
161
+ If a grant code and/or project name are available, then these can be passed in when instantiating the class.
162
+
163
+ ```
164
+ proj = Project(user_id="CRD", grant_code="R12345", name="RNA-seq analysis")
165
+ ```
166
+
167
+ A version 4 UUID is also generated for the project when instantiated.
168
+
169
+ ```
170
+ proj = Project(user_id="CRD")
171
+
172
+ print(proj.p_uuid)
173
+ ```
174
+
175
+ ```
176
+ 6697e457-9785-4668-b78b-72616b27aede
177
+ ```
178
+
179
+ Or if a version 4 UUID has been generated separately then it can be provided when instantiating.
180
+
181
+ ```
182
+ proj = Project(user_id="CRD", p_uuid="6697e457-9785-4668-b78b-72616b27aede")
183
+ ```
184
+
185
+ ## project functions
186
+
187
+ A couple of functions are provided to check the validity of a project ID, to check the validity of a suffix, and to check for a valid version 4 UUID.
188
+
189
+ You can check the validity of a project ID as follows:
190
+
191
+ ```
192
+ from papi.project import check_project_id
193
+
194
+ print(check_project_id("P2024-CRD-FZLL"))
195
+ print(check_project_id("P2024-CRD-1234"))
196
+ ```
197
+
198
+ ```
199
+ True
200
+ False
201
+ ```
202
+
203
+ You can check the validity of a project suffix as follows:
204
+
205
+ ```
206
+ from papi.project import check_suffix
207
+
208
+ print(check_suffix("FZLL"))
209
+ print(check_suffix("1234"))
210
+ ```
211
+
212
+ ```
213
+ True
214
+ False
215
+ ```
216
+
217
+ ## user module
218
+
219
+ ## User class
220
+
221
+ The `User` class stores attributes of a user: their name, a three-letter initial (or two-letter initial and integer number from 1 to 9), and an optional email address.
222
+
223
+ The most basic way of instantiating a `User` instance is as follows:
224
+
225
+ ```
226
+ from papi.user import User
227
+
228
+ usr = User("Charles Robert Darwin")
229
+
230
+ print(usr.user_id)
231
+ print(usr.user_name)
232
+ ```
233
+
234
+ ```
235
+ CRD
236
+ Charles Robert Darwin
237
+ ```
238
+
239
+ The first initials are converted into the `user_id` attribute.
240
+
241
+ If an email address is available, then this can be provided when instantiating:
242
+
243
+ ```
244
+ from papi.user import User
245
+
246
+ usr = User("Charles Robert Darwin", email="cdarwin@beaglemail.com")
247
+
248
+ print(usr.email)
249
+ ```
250
+
251
+ ```
252
+ cdarwin@beaglemail.com
253
+ ```
254
+
255
+ Because our user ID naming scheme enforces that a user ID must be unique, the `user_id` attribute should not really be set directly, although it can in theory:
256
+
257
+ ```
258
+ usr = User("Charles Darwin")
259
+ usr.user_id = "CD1"
260
+ ```
261
+
262
+ Setting the `user_id` attribute creates the possibility of a clash in user IDs, therefore the `user` module provides a means to create a basic user database with the TinyDB library. This avoids the possibility of a clash and appends and increments integer numbers to the end of the user ID if a matching one is already in the database.
@@ -0,0 +1,48 @@
1
+ import os
2
+ import logging
3
+ from dotenv import dotenv_values
4
+
5
+ dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
6
+ config = dotenv_values(dotenv_path)
7
+
8
+ ASANA_API_KEY = config["ASANA_API_KEY"]
9
+ ASANA_PASSWORD = config["ASANA_PASSWORD"]
10
+
11
+ TOGGL_TRACK_API_KEY = config["TOGGL_TRACK_API_KEY"]
12
+ TOGGL_TRACK_PASSWORD = config["TOGGL_TRACK_PASSWORD"]
13
+
14
+ NOTION_API_SECRET = config["NOTION_API_SECRET"]
15
+ NOTION_CLIENTS_DB = config["NOTION_CLIENTS_DB"]
16
+ NOTION_PROJECTS_DB = config["NOTION_PROJECTS_DB"]
17
+
18
+ def setup_logger(enable_logging: bool, log_level: str = 'INFO', log_file: str = None):
19
+ logger = logging.getLogger('papi')
20
+
21
+ if enable_logging:
22
+ # Convert log_level string to logging level
23
+ numeric_level = getattr(logging, log_level.upper(), logging.INFO)
24
+ logger.setLevel(numeric_level)
25
+
26
+ # Create handler
27
+ if log_file:
28
+ handler = logging.FileHandler(log_file)
29
+ else:
30
+ handler = logging.StreamHandler()
31
+
32
+ handler.setLevel(numeric_level)
33
+
34
+ # Create formatter
35
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
36
+ handler.setFormatter(formatter)
37
+
38
+ # Add handler to logger if not already added
39
+ if not logger.handlers:
40
+ logger.addHandler(handler)
41
+
42
+ # Prevent propagation to root logger
43
+ logger.propagate = False
44
+ else:
45
+ # Set a higher log level to suppress lower-level logs
46
+ logger.setLevel(logging.WARNING)
47
+
48
+ return logger
@@ -1,5 +1,7 @@
1
1
  import random
2
+ import logging
2
3
 
4
+ logger = logging.getLogger(__name__)
3
5
 
4
6
  def random_number(length):
5
7
  return "".join([str(random.randint(1, 9)) for i in range(length)])
@@ -4,11 +4,13 @@ import random
4
4
  import re
5
5
  import uuid
6
6
  import warnings
7
+ import logging
7
8
  from typing import Protocol, runtime_checkable
8
9
  from papi.user import check_user_id
9
10
 
10
- THIS_YEAR = pendulum.now().year
11
+ logger = logging.getLogger(__name__)
11
12
 
13
+ THIS_YEAR = pendulum.now().year
12
14
 
13
15
  def check_project_id(id: str) -> bool:
14
16
  """Checks whether a project ID is correctly formed.
@@ -18,10 +20,14 @@ def check_project_id(id: str) -> bool:
18
20
  :return: True/False for whether project ID is correctly formed.
19
21
  :rtype: bool
20
22
  """
23
+ logger.debug("Calling check_project_id function")
21
24
  valid = False
22
- pattern = re.compile(r"^P[0-9]{4}-[A-Z]{2}[A-Z0-9]{1}-[A-Z]{4}$")
25
+ pattern = re.compile(r"^P[0-9]{4}-[A-Z]{2}[A-Z1-9]{1}-[A-Z]{4}$")
23
26
  if pattern.match(id):
24
27
  valid = True
28
+ logger.info(f"Project ID '{id}' is valid")
29
+ else:
30
+ logger.info(f"Project ID '{id}' is not valid")
25
31
  return valid
26
32
 
27
33
 
@@ -33,10 +39,14 @@ def check_suffix(suffix: str) -> bool:
33
39
  :return: True/False for whether project suffix is correctly formed.
34
40
  :rtype: bool
35
41
  """
42
+ logger.debug("Calling check_suffix function")
36
43
  valid = False
37
44
  pattern = re.compile(r"^[A-Z]{4}$")
38
45
  if pattern.match(suffix):
39
46
  valid = True
47
+ logger.info(f"Project suffix '{suffix}' is valid")
48
+ else:
49
+ logger.info(f"Project suffix '{suffix}' is not valid")
40
50
  return valid
41
51
 
42
52
 
@@ -48,9 +58,12 @@ def check_uuid(p_uuid: str) -> bool:
48
58
  :return: True/False for whether the UUID is valid.
49
59
  :rtype: bool
50
60
  """
61
+ logger.debug("Calling check_uuid function")
51
62
  try:
52
63
  uuid_obj = uuid.UUID(p_uuid, version=4)
64
+ logger.info(f"Project UUID '{p_uuid}' is valid")
53
65
  except ValueError:
66
+ logger.error(f"Project UUID '{p_uuid}' is not valid")
54
67
  return False
55
68
  return str(uuid_obj) == p_uuid
56
69
 
@@ -94,6 +107,7 @@ class Project(Protocol):
94
107
  grant_code: str = None,
95
108
  ) -> None:
96
109
  """Constructor method"""
110
+ logger.debug("Creating Project instance")
97
111
  self.year = year
98
112
  self.user_id = user_id
99
113
  self.grant_code = grant_code
@@ -104,6 +118,9 @@ class Project(Protocol):
104
118
  self.generate_suffix()
105
119
  if id is not None and check_project_id(id):
106
120
  self.id = id
121
+ self.year = int(id[1:5])
122
+ self.user_id = id.split("-")[1]
123
+ self.suffix = id.split("-")[2]
107
124
  elif (
108
125
  isinstance(year, int)
109
126
  and check_user_id(user_id)
@@ -123,22 +140,16 @@ class Project(Protocol):
123
140
  )
124
141
  else:
125
142
  self.p_uuid = str(uuid.uuid4())
126
-
127
- def __str__(self) -> str:
128
- """Human-readable representation of class. Currently just the project ID.
129
-
130
- :return: Project ID.
131
- :rtype: str
132
- """
133
- return self.id
143
+ logger.info(f"Project '{self.id}' instance created")
134
144
 
135
145
  def __repr__(self) -> str:
136
- """Machine-readable representation of class. Currently just the project ID.
146
+ """Machine-readable representation of class..
137
147
 
138
- :return: Project ID.
148
+ :return: basic Project() attrs.
139
149
  :rtype: str
140
150
  """
141
- return self.id
151
+ logger.debug("Calling Project.__repr__ method")
152
+ return f'Project("{self.id}", "{self.name}")'
142
153
 
143
154
  def generate_suffix(self) -> str:
144
155
  """Generates a 4-character, uppercase, alphabetical suffix for a project, and
@@ -147,6 +158,7 @@ class Project(Protocol):
147
158
  :return: Project suffix.
148
159
  :rtype: str
149
160
  """
161
+ logger.debug("Calling Project.generate_suffix method")
150
162
  letters = string.ascii_uppercase
151
163
  suffix = "".join(random.choice(letters) for i in range(4))
152
164
  self.suffix = suffix
@@ -158,4 +170,5 @@ class Project(Protocol):
158
170
  :return: True/False for whether the project ID is valid.
159
171
  :rtype: bool
160
172
  """
173
+ logger.debug("Calling Project.id_is_valid method")
161
174
  return check_project_id(self.id)
@@ -1,9 +1,11 @@
1
1
  import re
2
2
  import pendulum
3
+ import logging
3
4
  from typing import Protocol, runtime_checkable
4
5
  from tinydb import TinyDB, Query
5
6
  from tinydb.operations import *
6
7
 
8
+ logger = logging.getLogger(__name__)
7
9
 
8
10
  def user_name_to_user_id(user_name: str) -> str:
9
11
  """Generates a 3-character, uppercase, alphabetical user ID from a user name,
@@ -14,8 +16,10 @@ def user_name_to_user_id(user_name: str) -> str:
14
16
  :return: Three-character user ID.
15
17
  :rtype: str
16
18
  """
19
+ logger.debug("Calling user_name_to_user_id function")
17
20
  user_name_parts = user_name.split()
18
21
  user_id = "".join([word[0] for word in user_name_parts]).upper()
22
+ logger.info(f"User name '{user_name}' converted to user ID '{user_id}'")
19
23
  return user_id
20
24
 
21
25
 
@@ -28,8 +32,13 @@ def check_valid_email(email: str) -> bool:
28
32
  :return: True/False for whether the email is valid.
29
33
  :rtype: bool
30
34
  """
35
+ logger.debug("Calling check_valid_email function")
31
36
  valid_email = re.compile(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")
32
37
  valid = valid_email.match(email)
38
+ if valid:
39
+ logger.info(f"Email '{email}' is valid")
40
+ else:
41
+ logger.warning(f"Email '{email}' is not valid")
33
42
  return valid
34
43
 
35
44
 
@@ -43,13 +52,16 @@ def check_user_id(user_id: str) -> bool:
43
52
  :return: True/False for whether the user ID is valid.
44
53
  :rtype: bool
45
54
  """
55
+ logger.debug("Calling check_user_id function")
46
56
  if not isinstance(user_id, str):
57
+ logger.warning(f"User ID '{user_id}' is not valid")
47
58
  return False
48
59
  else:
49
60
  valid = False
50
61
  pattern = re.compile(r"^[A-Z]{2}[A-Z0-9]{1}$")
51
62
  if pattern.match(user_id):
52
63
  valid = True
64
+ logger.info(f"User ID '{user_id}' is valid")
53
65
  return valid
54
66
 
55
67
 
@@ -60,6 +72,8 @@ class User(Protocol):
60
72
 
61
73
  :param user_name: User name, e.g. John Smith.
62
74
  :type user_name: str
75
+ :param user_id: User ID, e.g. JS1.
76
+ :type user_id: str
63
77
  :param email: Email address, defaults to None.
64
78
  :type email: str, optional
65
79
  :raises ValueError: If the name does not consist of either 2 or 3 parts, then
@@ -68,10 +82,12 @@ class User(Protocol):
68
82
  ValueError is raised.
69
83
  """
70
84
 
71
- def __init__(self, user_name: str, email: str = None):
85
+ def __init__(self, user_name: str = None, user_id: str = None, email: str = None):
72
86
  """Constructor method"""
73
- if len(user_name.split()) == 1 or len(user_name.split()) > 3:
74
- raise ValueError("Name must consist of two or three parts only")
87
+ logger.debug("Creating User instance")
88
+ if user_name is not None:
89
+ if len(user_name.split()) == 0 or len(user_name.split()) > 3:
90
+ raise ValueError("Name must consist of one to three parts only")
75
91
  self.user_name = user_name
76
92
  if email is not None:
77
93
  valid_email = check_valid_email(email)
@@ -80,8 +96,12 @@ class User(Protocol):
80
96
  self.email = email
81
97
  else:
82
98
  self.email = ""
83
- self.user_id = user_name_to_user_id(user_name)
99
+ if user_id is None:
100
+ self.user_id = user_name_to_user_id(user_name)
101
+ else:
102
+ self.user_id = user_id
84
103
  self.created_at = str(pendulum.now())
104
+ logger.info(f"User '{self.user_id}' instance created")
85
105
 
86
106
  def to_json(self):
87
107
  """Returns a user in JSON (dictionary) form.
@@ -89,12 +109,22 @@ class User(Protocol):
89
109
  :return: JSON-formatted (i.e. dictionary) user.
90
110
  :rtype: dict
91
111
  """
112
+ logger.debug("Calling User.to_json method")
92
113
  return {
93
114
  "user_name": self.user_name,
94
115
  "user_id": self.user_id,
95
116
  "email": self.email,
96
117
  "created_at": self.created_at,
97
118
  }
119
+
120
+ def __repr__(self):
121
+ """Machine-readable representation of class.
122
+
123
+ :return: basic User() attrs.
124
+ :rtype: str
125
+ """
126
+ logger.debug("Calling User.__repr__ method")
127
+ return f'User("{self.user_name}", "{self.user_id}", {self.email})'
98
128
 
99
129
 
100
130
  @runtime_checkable
@@ -110,6 +140,7 @@ class UserDB(Protocol):
110
140
 
111
141
  def __init__(self, db_file: str = None) -> None:
112
142
  """Constructor method"""
143
+ logger.debug("Creating UserDB instance")
113
144
  if db_file is not None:
114
145
  self.db = TinyDB(db_file, sort_keys=True, indent=4, separators=(",", ": "))
115
146
  self.db_file = db_file
@@ -118,6 +149,8 @@ class UserDB(Protocol):
118
149
  self.db = TinyDB(
119
150
  self.db_file, sort_keys=True, indent=4, separators=(",", ": ")
120
151
  )
152
+ logger.info(f"UserDB '{self.db_file}' instance created")
153
+
121
154
 
122
155
  def insert_user(self, user) -> User:
123
156
  """Inserts a user into the database, using a User instance.
@@ -128,6 +161,7 @@ class UserDB(Protocol):
128
161
  :return: ID of the inserted user.
129
162
  :rtype: int
130
163
  """
164
+ logger.debug("Calling UserDB.insert_user method")
131
165
  if len(user.user_name.split()) == 2:
132
166
  matches = self.check_matching_user_ids(user.user_id)
133
167
  if len(matches):
@@ -150,6 +184,7 @@ class UserDB(Protocol):
150
184
  else:
151
185
  user.user_id = f"{first_last_initial}1"
152
186
  self.db.insert(user.to_json())
187
+ logger.info(f"User ID '{useruser_id}' inserted into user database")
153
188
  return user.user_id
154
189
 
155
190
  def search_by_user_name(self, user_name: str) -> list:
@@ -161,8 +196,13 @@ class UserDB(Protocol):
161
196
  :return: A list of matching documents.
162
197
  :rtype: list
163
198
  """
199
+ logger.debug("Calling UserDB.search_by_user_name method")
164
200
  Users = Query()
165
201
  result = self.db.search(Users.user_name == user_name)
202
+ if len(result):
203
+ logger.info(f"{len(result)} matches for {user_name} found in user database")
204
+ else:
205
+ logger.info(f"No matches for {user_name} found in user database")
166
206
  return result
167
207
 
168
208
  def search_by_user_id(self, user_id: str) -> list:
@@ -174,8 +214,13 @@ class UserDB(Protocol):
174
214
  :return: A list of matching documents.
175
215
  :rtype: list
176
216
  """
217
+ logger.debug("Calling UserDB.search_by_user_id method")
177
218
  Users = Query()
178
219
  result = self.db.search(Users.user_id == user_id)
220
+ if len(result):
221
+ logger.info(f"{len(result)} matches for {user_id} found in user database")
222
+ else:
223
+ logger.info(f"No matches for {user_id} found in user database")
179
224
  return result
180
225
 
181
226
  def check_matching_user_ids(self, user_id: str) -> list:
@@ -189,6 +234,7 @@ class UserDB(Protocol):
189
234
  :return: A list of matching documents.
190
235
  :rtype: list
191
236
  """
237
+ logger.debug("Calling UserDB.check_matching_user_ids method")
192
238
  Users = Query()
193
239
  if user_id[-1].isnumeric() or len(user_id) == 2:
194
240
  result = self.db.search(Users.user_id.search(rf"^{user_id[0:2]}\d{{1}}"))