invesytoolbox 0.0.22__tar.gz → 0.0.24__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 (29) hide show
  1. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/HISTORY.md +12 -0
  2. {invesytoolbox-0.0.22/src/invesytoolbox.egg-info → invesytoolbox-0.0.24}/PKG-INFO +15 -3
  3. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/setup.cfg +2 -2
  4. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox/itb_data.py +102 -22
  5. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox/itb_date_time.py +11 -1
  6. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox/itb_email_phone.py +1 -0
  7. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox/itb_html.py +1 -1
  8. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox/itb_text_name.py +50 -2
  9. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24/src/invesytoolbox.egg-info}/PKG-INFO +15 -3
  10. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/tests/test_data.py +50 -9
  11. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/tests/test_text_name.py +33 -0
  12. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/LICENSE.txt +0 -0
  13. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/README.md +0 -0
  14. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/setup.py +0 -0
  15. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox/__init__.py +0 -0
  16. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox/itb_locales.py +0 -0
  17. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox/itb_restricted_python.py +0 -0
  18. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox/itb_security.py +0 -0
  19. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox/itb_www.py +0 -0
  20. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox.egg-info/SOURCES.txt +0 -0
  21. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox.egg-info/dependency_links.txt +0 -0
  22. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox.egg-info/requires.txt +0 -0
  23. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/invesytoolbox.egg-info/top_level.txt +0 -0
  24. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/tests/__init__.py +0 -0
  25. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/tests/test_date_time.py +0 -0
  26. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/tests/test_email_phone.py +0 -0
  27. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/tests/test_html.py +0 -0
  28. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/tests/test_locales.py +0 -0
  29. {invesytoolbox-0.0.22 → invesytoolbox-0.0.24}/src/tests/test_www.py +0 -0
@@ -1,4 +1,16 @@
1
1
  # History
2
+ ## 0.0.24 (2024-09-29)
3
+ **timezone** can be passed as an argument to **dictlist_2datatypes** and **dict_2datatypes**
4
+
5
+ ## 0.0.23 (2024-07-22)
6
+ * **adjust_spaces_on_punctuation**: inserts narrow (or normal spaces) for certain languages
7
+ * **value_2datatype**:
8
+ - default fmt changed to None (hopefully it doesn't break somewhere, but this is necessary)
9
+ - 'pendulum' is now an option for the parameter
10
+ - default for parameter **fmt** changed to None (this is necessary for the new pendulum conversion in **value_2datatype** to work) for
11
+ - **dict_2datatypes**
12
+ - **dictlist_2datatypes**
13
+
2
14
  ## 0.0.22 (2023-10-14)
3
15
  * **normalize_name** and **could_be_a_name** now have the boolean parameter *lastname* that indicates that a single-word name is to be treated as a last name, not a first name.
4
16
  * **get_locale**: locale can be only the language, without the country.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: invesytoolbox
3
- Version: 0.0.22
3
+ Version: 0.0.24
4
4
  Summary: Tools for Python scripts or terminal
5
5
  Home-page: https://gitlab.com/Rastaf/invesytoolbox
6
6
  Author: Georg Pfolz
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: License :: OSI Approved :: MIT License
18
18
  Classifier: Operating System :: OS Independent
19
- Requires-Python: >=3.7
19
+ Requires-Python: <3.12,>=3.7
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE.txt
22
22
  Requires-Dist: bs4
@@ -44,6 +44,18 @@ That's also why all date and time functions also take into account the old DateT
44
44
  The documentation can be found [here](https://rastaf.gitlab.io/invesytoolbox/).
45
45
 
46
46
  # History
47
+ ## 0.0.24 (2024-09-29)
48
+ **timezone** can be passed as an argument to **dictlist_2datatypes** and **dict_2datatypes**
49
+
50
+ ## 0.0.23 (2024-07-22)
51
+ * **adjust_spaces_on_punctuation**: inserts narrow (or normal spaces) for certain languages
52
+ * **value_2datatype**:
53
+ - default fmt changed to None (hopefully it doesn't break somewhere, but this is necessary)
54
+ - 'pendulum' is now an option for the parameter
55
+ - default for parameter **fmt** changed to None (this is necessary for the new pendulum conversion in **value_2datatype** to work) for
56
+ - **dict_2datatypes**
57
+ - **dictlist_2datatypes**
58
+
47
59
  ## 0.0.22 (2023-10-14)
48
60
  * **normalize_name** and **could_be_a_name** now have the boolean parameter *lastname* that indicates that a single-word name is to be treated as a last name, not a first name.
49
61
  * **get_locale**: locale can be only the language, without the country.
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = invesytoolbox
3
- version = 0.0.22
3
+ version = 0.0.24
4
4
  author = Georg Pfolz
5
5
  author_email = georg.pfolz@invesy.at
6
6
  description = Tools for Python scripts or terminal
@@ -26,7 +26,7 @@ classifiers =
26
26
  package_dir =
27
27
  = src
28
28
  packages = find:
29
- python_requires = >=3.7
29
+ python_requires = >=3.7,<3.12
30
30
  install_requires =
31
31
  bs4
32
32
  babel
@@ -8,6 +8,7 @@ from typing import Union, List, Dict, Any, Optional
8
8
  import DateTime
9
9
  import datetime
10
10
  import vobject
11
+ import pendulum
11
12
  from dateutil.parser import parse
12
13
  from itb_date_time import convert_datetime, str_to_date, str_to_DT, get_dateformat
13
14
  from itb_email_phone import process_phonenumber
@@ -35,12 +36,10 @@ def any_2boolean(
35
36
  def dict_from_dict_list(
36
37
  dict_list: List[dict],
37
38
  key: str,
38
- single_value: Optional[bool] = None,
39
+ single_value: Optional[str] = None,
39
40
  include_key: Optional[bool] = False
40
41
  ) -> dict:
41
- """Create a dictionary from a list of dictionaries
42
-
43
- .. note:: The annotated return type is not true if a single_value argument is given.
42
+ """ Create a dictionary from a list of dictionaries
44
43
 
45
44
  :param dictList: the list of dictionaries to be converted
46
45
  :param key: the key of the dictionaries to use as key for the new dictionary.
@@ -70,7 +69,7 @@ def create_vcard(
70
69
  data: Dict[str, str],
71
70
  returning: str = 'vcard'
72
71
  ) -> str:
73
- """create a vCard from a dictionary
72
+ """ create a vCard from a dictionary
74
73
 
75
74
  .. note:: The serialized vcard has Windows line-breaks, which is fine, I guess
76
75
  .. note:: colons in notes are escaped in vobject
@@ -134,7 +133,7 @@ def dict_2unicode(
134
133
  d: Dict[Union[str, bytes], Any],
135
134
  encoding: str = 'utf-8'
136
135
  ) -> dict:
137
- """ Converts all keys and values in a dictionary from bytes to unicode
136
+ """ Convert all keys and values in a dictionary from bytes to unicode
138
137
 
139
138
  All keys and values are changed from bytes to unicode, if applicable.
140
139
  Other data types are left unchaged, including other compound data types.
@@ -156,9 +155,29 @@ def dict_2datatypes(
156
155
  convert_keys: bool = False,
157
156
  convert_to_unicode: bool = False,
158
157
  dt: str = 'datetime',
159
- fmt: str = '%d.%m.%Y'
158
+ fmt: str = None,
159
+ timezone: str = 'local'
160
160
  ) -> Dict[Any, Any]:
161
- """ convert all data of a dictionary to specific (json metadata) or guessed types """
161
+ """ Convert all data of a dictionary to specific (json metadata) or guessed types
162
+
163
+ :param d: dictionary
164
+ :param metadata: dictionary containing types for the keys:
165
+ - boolean
166
+ - int
167
+ - float
168
+ - date
169
+ - datetime: date and time
170
+ - time: conversion or not based on dt
171
+ :param convert_keys: convert keys to unicode
172
+ :param convert_to_unicode: convert all bytes to unicode in the process
173
+ :param dt: - datetime or dt
174
+ - DateTime or DT
175
+ - ignore or string
176
+ - pendulum
177
+ - default: datetime, because it's Python's default
178
+ :param fmt: format for datetime parsing
179
+ :param timezone: default is 'local', other options are 'UTC' or 'Europe/Vienna'
180
+ """
162
181
 
163
182
  if convert_keys:
164
183
  return {
@@ -173,7 +192,8 @@ def dict_2datatypes(
173
192
  key=key,
174
193
  convert_to_unicode=convert_to_unicode,
175
194
  dt=dt,
176
- fmt=fmt
195
+ fmt=fmt,
196
+ timezone=timezone
177
197
  )
178
198
  for key, val in d.items()
179
199
  } # dict comprehension
@@ -185,7 +205,8 @@ def dict_2datatypes(
185
205
  key=key,
186
206
  convert_to_unicode=convert_to_unicode,
187
207
  dt=dt,
188
- fmt=fmt
208
+ fmt=fmt,
209
+ timezone=timezone
189
210
  )
190
211
  for key, val in d.items()
191
212
  } # dict comprehension
@@ -197,15 +218,38 @@ def dictlist_2datatypes(
197
218
  convert_keys: bool = False,
198
219
  convert_to_unicode: bool = False,
199
220
  dt: str = 'datetime',
200
- fmt: str = '%d.%m.%Y'
221
+ fmt: str = None,
222
+ timezone: str = 'local'
201
223
  ) -> list:
224
+ """ Convert all data of a list of dictionaries to specific (json metadata) or guessed types
225
+
226
+ :param dictList: list of dictionaries
227
+ :param metadata: dictionary containing types for the keys:
228
+ - boolean
229
+ - int
230
+ - float
231
+ - date
232
+ - datetime: date and time
233
+ - time: conversion or not based on dt
234
+ :param convert_keys: convert keys to unicode
235
+ :param convert_to_unicode: convert all bytes to unicode in the process
236
+ :param dt: - datetime or dt
237
+ - DateTime or DT
238
+ - ignore or string
239
+ - pendulum
240
+ - default: datetime, because it's Python's default
241
+ :param fmt: format for datetime parsing
242
+ :param timezone: default is 'local', other options are 'UTC' or 'Europe/Vienna'
243
+ """
202
244
  return [
203
245
  dict_2datatypes(
204
246
  d=dic,
205
247
  metadata=metadata,
206
248
  convert_keys=convert_keys,
207
249
  convert_to_unicode=convert_to_unicode,
208
- dt=dt
250
+ dt=dt,
251
+ fmt=fmt,
252
+ timezone=timezone
209
253
  )
210
254
  for dic
211
255
  in dictlist
@@ -258,17 +302,18 @@ def value_2datatype(
258
302
  convert_to_unicode: bool = False,
259
303
  encoding: str = 'utf-8',
260
304
  dt: str = 'datetime',
261
- fmt: str = '%d.%m.%Y', # only for datetime
262
- UTC: bool = False
305
+ fmt: str = None,
306
+ timezone: str = 'local'
263
307
  ) -> Union[str, int, float]:
264
308
  """ Convert a value to a datatype
265
309
 
266
310
  this function has two modes:
267
311
 
268
- 1. it is provided a type for the conversion
312
+ 1. a type is provided for the conversion
269
313
  2. it makes educated guesses in converting a string
270
314
 
271
315
  :param value:
316
+ :param typ: type for the conversion
272
317
  :param metadata: dictionary containing types for the keys:
273
318
  - boolean
274
319
  - int
@@ -281,7 +326,10 @@ def value_2datatype(
281
326
  :param encoding: if bytes are present, use this encoding to convert them to unicode
282
327
  :param dt: - datetime or dt
283
328
  - DateTime or DT
329
+ - ignore or string
330
+ - pendulum
284
331
  - default: datetime, because it's Python's default
332
+ :param timezone: default is 'local', other options are 'UTC' or 'Europe/Vienna'
285
333
  """
286
334
  if typ:
287
335
  if typ == 'boolean':
@@ -311,6 +359,17 @@ def value_2datatype(
311
359
  value,
312
360
  datefmt='international'
313
361
  ) + 0.5 # to avoid day-shifting due to timezones, use 12am
362
+ elif dt == 'pendulum':
363
+ if fmt:
364
+ p_dt = pendulum.from_format(value, fmt)
365
+ else:
366
+ p_dt = pendulum.parse(value)
367
+
368
+ if p_dt.tzinfo == 'UTC' and timezone == 'local':
369
+ return p_dt.in_tz(pendulum.local_timezone())
370
+ elif timezone:
371
+ return p_dt.in_tz(timezone)
372
+ return p_dt.date()
314
373
  elif dt in ('ignore', 'string'):
315
374
  return value
316
375
  else:
@@ -327,6 +386,17 @@ def value_2datatype(
327
386
  )
328
387
  except DateTime.interfaces.SyntaxError:
329
388
  return value
389
+ elif dt == 'pendulum':
390
+ if fmt:
391
+ p_dt = pendulum.from_format(value, fmt)
392
+ else:
393
+ p_dt = pendulum.parse(value)
394
+
395
+ if p_dt.tzinfo == 'UTC' and timezone == 'local':
396
+ return p_dt.in_tz(pendulum.local_timezone())
397
+ elif timezone:
398
+ return p_dt.in_tz(timezone)
399
+ return p_dt
330
400
  elif dt in ('ignore', 'string'):
331
401
  return value
332
402
  else:
@@ -339,7 +409,6 @@ def value_2datatype(
339
409
  return value # DT: return the string
340
410
 
341
411
  else:
342
-
343
412
  if isinstance(value, (bool, int, float)):
344
413
  return value
345
414
 
@@ -373,12 +442,7 @@ def value_2datatype(
373
442
  datestring=value,
374
443
  checkonly=True
375
444
  ):
376
- if ':' in value:
377
- return convert_datetime(
378
- date=value,
379
- convert_to=dt
380
- )
381
- elif dt in ('datetime', 'dt'):
445
+ if dt in ('datetime', 'dt'):
382
446
  return str_to_date(
383
447
  datestring=value
384
448
  )
@@ -386,6 +450,22 @@ def value_2datatype(
386
450
  return str_to_DT(
387
451
  datestring=value
388
452
  )
453
+ elif dt == 'pendulum':
454
+ if fmt:
455
+ p_dt = pendulum.from_format(value, fmt)
456
+ else:
457
+ p_dt = pendulum.parse(value)
458
+
459
+ if p_dt.tzinfo == 'UTC' and timezone == 'local':
460
+ return p_dt.in_tz(pendulum.local_timezone())
461
+ elif timezone:
462
+ return p_dt.in_tz(timezone)
463
+ return p_dt
464
+ else:
465
+ return convert_datetime(
466
+ date=value,
467
+ convert_to=dt
468
+ )
389
469
 
390
470
  except Exception:
391
471
  # in case it's a subset of datetime_chars but not a date or datetime or time
@@ -37,6 +37,8 @@ def is_valid_datetime_string(
37
37
  a conflict with 4-digit year values, it is considered invalid
38
38
 
39
39
  :param returning: can return a tuple
40
+
41
+ .. note:: This does not work with ISO8601 strings! (returns False)
40
42
  """
41
43
  return str_to_dt(
42
44
  datestring=datestring,
@@ -57,6 +59,14 @@ def get_dateformat(
57
59
  :todo: times!
58
60
  :raises ValueError: if an invalid datetime or DateTime string is provided
59
61
  """
62
+ # first let's check for ISO8601 strings
63
+ if checkonly:
64
+ try:
65
+ pendulum.parse(datestring)
66
+ return True
67
+ except pendulum.parsing.exceptions.ParserError:
68
+ pass # don't return False, because it may be a valid date string
69
+
60
70
  timefmt = ''
61
71
  if ' ' in datestring:
62
72
  try:
@@ -225,7 +235,7 @@ def str_to_pendulum(
225
235
  if not fmt:
226
236
  return pendulum.parse(datestring)
227
237
  else:
228
- return pendulum.datetime.strptime(datestring, fmt)
238
+ return pendulum.from_format(datestring, fmt)
229
239
 
230
240
 
231
241
  def str_to_date(
@@ -10,6 +10,7 @@ import phonenumbers
10
10
  from email.mime.multipart import MIMEMultipart
11
11
  from email.mime.text import MIMEText
12
12
  from email import encoders
13
+ from email.header import Header
13
14
  from email.mime.base import MIMEBase
14
15
 
15
16
  from itb_locales import get_locale
@@ -15,7 +15,7 @@ def prettify_html(
15
15
 
16
16
  .. note:: This function is needed for Zope Python Scripts
17
17
  because even if bs4 can be imported, prettify throws
18
- an Unauthorized error in restricted Python
18
+ an Unauthorized error in Restricted Python
19
19
  """
20
20
 
21
21
  return BeautifulSoup(html, "html.parser").prettify()
@@ -14,7 +14,7 @@ from nameparser import HumanName
14
14
  d = gender.Detector(case_sensitive=False)
15
15
 
16
16
 
17
- char_nb_map = {
17
+ CHAR_NB_MAP = {
18
18
  'a': ('a', '4', '@'),
19
19
  'b': ('b', '6', '8', 'I3', '13', '!3'),
20
20
  'c': ('c', '('),
@@ -43,6 +43,54 @@ char_nb_map = {
43
43
  'z': ('z', '2', '7_')
44
44
  }
45
45
 
46
+ # regex pattern
47
+ LANG_PUNCT_SPACES = {
48
+ 'fr': {
49
+ 'regex': r'([!?:;%])',
50
+ 'narrow': '\u202f\\1',
51
+ 'normal': r' \1'
52
+ },
53
+ 'es': {
54
+ 'regex': r'([¿¡])',
55
+ 'narrow': '\\1\u202f',
56
+ 'normal': r'\1 '
57
+ },
58
+ 'ro': {
59
+ 'regex': r'([!?:;])',
60
+ 'narrow': '\u202f\\1',
61
+ 'normal': r' \1'
62
+ }
63
+ }
64
+
65
+ def adjust_spaces_on_punctuation(
66
+ text: str,
67
+ language: str,
68
+ space: str = 'narrow'
69
+ ):
70
+ """
71
+ Adjust spaces before or after punctuation marks according to the language.
72
+ By default the narrow space (\u202f) is used for adjustments.
73
+ """
74
+ # Step 1: Remove existing spaces before the punctuation
75
+ text = re.sub(r'\s+([!?:;%\),.])', r'\1', text)
76
+
77
+ lang_data = LANG_PUNCT_SPACES.get(language)
78
+
79
+ if lang_data:
80
+ # Step 2: Add a narrow space (\u202f) before the punctuation
81
+ try:
82
+ text = re.sub(
83
+ lang_data['regex'],
84
+ lang_data[space],
85
+ text
86
+ )
87
+ except re.error as e:
88
+ raise Exception(f"Error in regex for language {language}: {e}")
89
+ else:
90
+ # Original behavior for other languages
91
+ text = re.sub(r'\s+([,.!?;:\)])', r'\1', text)
92
+ return text
93
+
46
94
 
47
95
  def and_list(
48
96
  elements: list,
@@ -202,7 +250,7 @@ def leet(
202
250
  if not c.isalnum():
203
251
  continue # without counting
204
252
 
205
- c = random.choice(char_nb_map[c])
253
+ c = random.choice(CHAR_NB_MAP[c])
206
254
 
207
255
  if not random.randrange(change_uppercase):
208
256
  c = c.upper()
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: invesytoolbox
3
- Version: 0.0.22
3
+ Version: 0.0.24
4
4
  Summary: Tools for Python scripts or terminal
5
5
  Home-page: https://gitlab.com/Rastaf/invesytoolbox
6
6
  Author: Georg Pfolz
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: License :: OSI Approved :: MIT License
18
18
  Classifier: Operating System :: OS Independent
19
- Requires-Python: >=3.7
19
+ Requires-Python: <3.12,>=3.7
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE.txt
22
22
  Requires-Dist: bs4
@@ -44,6 +44,18 @@ That's also why all date and time functions also take into account the old DateT
44
44
  The documentation can be found [here](https://rastaf.gitlab.io/invesytoolbox/).
45
45
 
46
46
  # History
47
+ ## 0.0.24 (2024-09-29)
48
+ **timezone** can be passed as an argument to **dictlist_2datatypes** and **dict_2datatypes**
49
+
50
+ ## 0.0.23 (2024-07-22)
51
+ * **adjust_spaces_on_punctuation**: inserts narrow (or normal spaces) for certain languages
52
+ * **value_2datatype**:
53
+ - default fmt changed to None (hopefully it doesn't break somewhere, but this is necessary)
54
+ - 'pendulum' is now an option for the parameter
55
+ - default for parameter **fmt** changed to None (this is necessary for the new pendulum conversion in **value_2datatype** to work) for
56
+ - **dict_2datatypes**
57
+ - **dictlist_2datatypes**
58
+
47
59
  ## 0.0.22 (2023-10-14)
48
60
  * **normalize_name** and **could_be_a_name** now have the boolean parameter *lastname* that indicates that a single-word name is to be treated as a last name, not a first name.
49
61
  * **get_locale**: locale can be only the language, without the country.
@@ -8,18 +8,21 @@ import sys
8
8
  import unittest
9
9
  import datetime
10
10
  import DateTime
11
+ import pendulum
11
12
  from dateutil.parser import parse
12
13
 
13
14
  sys.path.append(".")
14
15
 
15
- from itb_data import \
16
- any_2boolean, \
17
- create_vcard, \
18
- dict_2unicode, \
19
- dict_2datatypes, \
20
- dict_from_dict_list, \
21
- dictlist_2datatypes, \
22
- sort_dictlist
16
+ from itb_data import (
17
+ any_2boolean,
18
+ create_vcard,
19
+ dict_2unicode,
20
+ dict_2datatypes,
21
+ dict_from_dict_list,
22
+ dictlist_2datatypes,
23
+ sort_dictlist,
24
+ value_2datatype
25
+ )
23
26
 
24
27
  dict_list = [
25
28
  {'a': 1, 'b': 2, 'c': 5},
@@ -154,6 +157,9 @@ END:VCARD
154
157
 
155
158
 
156
159
  class TestData(unittest.TestCase):
160
+ # verbose, show complete diff
161
+ maxDiff = None
162
+
157
163
  def test_any_2boolean(self):
158
164
  for a, b in test_boolean.items():
159
165
  self.assertEqual(
@@ -260,7 +266,28 @@ class TestData(unittest.TestCase):
260
266
  )
261
267
 
262
268
  def test_value_2datatype(self):
263
- pass # this is already tested extensively in test_dict_2datatypes
269
+ # this is already tested extensively in test_dict_2datatypes
270
+ # so we just test pendulum here
271
+ print('test_value_2datatype: this should result in pendulum datetimes')
272
+ value = '01.04.2022 10:32'
273
+ fmt = 'DD.MM.YYYY HH:mm'
274
+
275
+ p = value_2datatype(
276
+ value=value,
277
+ typ='datetime',
278
+ dt='pendulum',
279
+ fmt=fmt
280
+ )
281
+ print(value, '→', p, p.tzinfo)
282
+
283
+ value = '2024-04-01T10:32:00'
284
+
285
+ p = value_2datatype(
286
+ value=value,
287
+ dt='pendulum'
288
+ )
289
+ print(value, '→', p, p.tzinfo)
290
+
264
291
 
265
292
  def test_sort_dictlist(self):
266
293
  sorted_dict_list = sort_dictlist(
@@ -297,6 +324,10 @@ class TestData(unittest.TestCase):
297
324
  )
298
325
 
299
326
  def test_dictlist_2datatypes(self):
327
+ # print('dict_for_testing', dict_for_testing)
328
+ # print('dict_for_testing_datatyped_unicoded', dict_for_testing_datatyped_unicoded)
329
+
330
+
300
331
  dictlist_for_testing = [
301
332
  dict_for_testing,
302
333
  dict_for_testing,
@@ -323,6 +354,16 @@ class TestData(unittest.TestCase):
323
354
  dictlist_tested_datatyped
324
355
  )
325
356
 
357
+ dictlist_pendulum = dictlist_2datatypes(
358
+ dictlist=dictlist_for_testing,
359
+ convert_keys=True,
360
+ convert_to_unicode=True,
361
+ dt='pendulum',
362
+ fmt='DD.MM.YYYY'
363
+ )
364
+
365
+ print('dictlist_pendulum', dictlist_pendulum)
366
+
326
367
 
327
368
  if __name__ == '__main__':
328
369
  unittest.main()
@@ -11,6 +11,7 @@ import random
11
11
  sys.path.append(".")
12
12
 
13
13
  from itb_text_name import (
14
+ adjust_spaces_on_punctuation,
14
15
  and_list,
15
16
  capitalize_name,
16
17
  get_gender,
@@ -295,8 +296,40 @@ reference_names_sorted = [
295
296
 
296
297
  lower_text = 'das ist ein Beispiel-Text, der kapitalisiert werden kann.'
297
298
 
299
+ # Language Texts (for adjusting spaces on the indentation)
300
+ punct_texts = {
301
+ 'de': "Hallo Welt ! Wie geht es Ihnen ? Ich hoffe, Sie haben einen schönen Tag : Das Wetter ist herrlich . Wussten Sie, dass 75 % aller Statistiken erfunden sind?",
302
+ 'en': "Hello world ! How are you today ? I hope you're having a great day : The weather is lovely . Did you know that 80 % of all statistics are made up?",
303
+ 'fr': "Bonjour le monde! Comment allez-vous aujourd'hui? J'espère que vous passez une bonne journée: Le temps est magnifique . Saviez-vous que 85% des statistiques sont inventées?",
304
+ 'es': "¡Hola mundo! ¿Cómo estás hoy ? Espero que tengas un buen día : El clima es hermoso. ¿Sabías que el 90 % de las estadísticas son inventadas ?",
305
+ 'ro': "Salut lume! Cum ești azi? Sper că ai o zi bună: Vremea este minunată.Știai că 95 % din statistici sunt inventate?"
306
+ }
307
+
308
+ punct_texts_corrected = {
309
+ 'de': "Hallo Welt! Wie geht es Ihnen? Ich hoffe, Sie haben einen schönen Tag: Das Wetter ist herrlich. Wussten Sie, dass 75% aller Statistiken erfunden sind?",
310
+ 'en': "Hello world! How are you today? I hope you're having a great day: The weather is lovely. Did you know that 80% of all statistics are made up?",
311
+ 'fr': "Bonjour le monde\u202f! Comment allez-vous aujourd'hui\u202f? J'espère que vous passez une bonne journée\u202f: Le temps est magnifique. Saviez-vous que 85\u202f% des statistiques sont inventées\u202f?",
312
+ 'es': "¡\u202fHola mundo! ¿\u202fCómo estás hoy? Espero que tengas un buen día: El clima es hermoso. ¿\u202fSabías que el 90% de las estadísticas son inventadas?",
313
+ 'ro': "Salut lume\u202f! Cum ești azi\u202f? Sper că ai o zi bună\u202f: Vremea este minunată.Știai că 95% din statistici sunt inventate\u202f?"
314
+ }
298
315
 
299
316
  class TestTextName(unittest.TestCase):
317
+ def test_adjust_spaces_on_punctuation(self):
318
+ for lang, text in punct_texts.items():
319
+ print(f'Language: {lang}')
320
+ try:
321
+ corrected_text = adjust_spaces_on_punctuation(
322
+ text = text,
323
+ language = lang
324
+ )
325
+ except Exception as e:
326
+ print(f'Error: {e}')
327
+ raise AssertionError(f'Error: {e}')
328
+ self.assertEqual(
329
+ corrected_text,
330
+ punct_texts_corrected[lang]
331
+ )
332
+
300
333
  def test_and_list(self):
301
334
  a_list = [1, 'Georg', 'Haus', True]
302
335
  correct_str = '1, Georg, Haus and True'
File without changes
File without changes