rm-date-parser 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Vaibhav Singh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,81 @@
1
+ Metadata-Version: 2.4
2
+ Name: rm-date-parser
3
+ Version: 0.1.0
4
+ Summary: Finds every date in text and returns each one with the detected format
5
+ Author-email: Vaibhav Singh <admin@rationalminds.net>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Vaibhav Singh
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/vbhavsingh/DateParserPython
29
+ Project-URL: Issues, https://github.com/vbhavsingh/DateParserPython/issues
30
+ Keywords: date,parser,nlp,datetime
31
+ Classifier: Development Status :: 3 - Alpha
32
+ Classifier: Intended Audience :: Developers
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Programming Language :: Python
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Programming Language :: Python :: 3.9
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Topic :: Text Processing :: Linguistic
41
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
42
+ Requires-Python: >=3.9
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE
45
+ Dynamic: license-file
46
+
47
+ # DateParserPython
48
+
49
+ A lightweight Python port of the DateParser logic used to identify and normalize dates and times in natural-language text. The package exposes a single `Parser` class that scans free-form strings and returns structured `LocalDateModel` results containing normalized values, the detected format, and the source offsets.
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ pip install dateparserpython
55
+ ```
56
+
57
+ To work with a local clone, install the project in editable mode (this also makes the `dateparserpython` package importable straight from the repo):
58
+
59
+ ```bash
60
+ python -m pip install -e .
61
+ ```
62
+
63
+ ## Quickstart
64
+
65
+ ```python
66
+ from dateparserpython import Parser
67
+
68
+ parser = Parser()
69
+ for local_date in parser.parse("12/12/2025"): # Pass any free‑form text
70
+ print(local_date.date_time_string, local_date.identified_date_format)
71
+ ```
72
+
73
+ Running `python test.py` prints a set of parsed examples for convenience.
74
+
75
+ ## Development
76
+
77
+ 1. Create a virtual environment and activate it.
78
+ 2. Install editable dependencies: `python -m pip install -e .[dev]` (dev extras coming soon; currently empty).
79
+ 3. Run unit tests / scripts from the project root. For ad-hoc checks you can run `python test.py` or create your own fixtures.
80
+
81
+ Please open an issue or PR if you hit a parsing case that is not currently supported.
@@ -0,0 +1,35 @@
1
+ # DateParserPython
2
+
3
+ A lightweight Python port of the DateParser logic used to identify and normalize dates and times in natural-language text. The package exposes a single `Parser` class that scans free-form strings and returns structured `LocalDateModel` results containing normalized values, the detected format, and the source offsets.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install dateparserpython
9
+ ```
10
+
11
+ To work with a local clone, install the project in editable mode (this also makes the `dateparserpython` package importable straight from the repo):
12
+
13
+ ```bash
14
+ python -m pip install -e .
15
+ ```
16
+
17
+ ## Quickstart
18
+
19
+ ```python
20
+ from dateparserpython import Parser
21
+
22
+ parser = Parser()
23
+ for local_date in parser.parse("12/12/2025"): # Pass any free‑form text
24
+ print(local_date.date_time_string, local_date.identified_date_format)
25
+ ```
26
+
27
+ Running `python test.py` prints a set of parsed examples for convenience.
28
+
29
+ ## Development
30
+
31
+ 1. Create a virtual environment and activate it.
32
+ 2. Install editable dependencies: `python -m pip install -e .[dev]` (dev extras coming soon; currently empty).
33
+ 3. Run unit tests / scripts from the project root. For ad-hoc checks you can run `python test.py` or create your own fixtures.
34
+
35
+ Please open an issue or PR if you hit a parsing case that is not currently supported.
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rm-date-parser"
7
+ version = "0.1.0"
8
+ description = "Finds every date in text and returns each one with the detected format"
9
+ readme = "README.md"
10
+ authors = [
11
+ { name = "Vaibhav Singh", email="admin@rationalminds.net" }
12
+ ]
13
+ license = { file = "LICENSE" }
14
+ requires-python = ">=3.9"
15
+ dependencies = []
16
+ keywords = ["date", "parser", "nlp", "datetime"]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.9",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Topic :: Text Processing :: Linguistic",
28
+ "Topic :: Software Development :: Libraries :: Python Modules",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/vbhavsingh/DateParserPython"
33
+ Issues = "https://github.com/vbhavsingh/DateParserPython/issues"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+
38
+ [tool.setuptools.package-data]
39
+ dateparserpython = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,13 @@
1
+ """Public package interface for dateparserpython."""
2
+
3
+ from .parser import Parser
4
+ from .models import DateElement, LocalDateModel
5
+
6
+ __version__ = "0.1.0"
7
+
8
+ __all__ = [
9
+ "Parser",
10
+ "LocalDateModel",
11
+ "DateElement",
12
+ "__version__",
13
+ ]
@@ -0,0 +1,449 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import List
4
+
5
+ try:
6
+ from .display import DisplayObject, TreeChar
7
+ from .prediction import PredictionModelNode
8
+ except ImportError: # pragma: no cover - fallback for direct module execution
9
+ from dateparserpython.display import DisplayObject, TreeChar
10
+ from dateparserpython.prediction import PredictionModelNode
11
+
12
+ monthPredictionTree = PredictionModelNode()
13
+ weekPredictionTree = PredictionModelNode()
14
+ patternPredictionTree = PredictionModelNode()
15
+ timePredictionTree = PredictionModelNode()
16
+
17
+ WEEKDAY_FULL: List[str] = [
18
+ "sunday",
19
+ "monday",
20
+ "tuesday",
21
+ "wednesday",
22
+ "thursday",
23
+ "friday",
24
+ "saturday",
25
+ ]
26
+
27
+ WEEKDAY_SHORT: List[str] = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"]
28
+
29
+ MONTH_FULL: List[str] = [
30
+ "january",
31
+ "february",
32
+ "march",
33
+ "april",
34
+ "may",
35
+ "june",
36
+ "july",
37
+ "august",
38
+ "september",
39
+ "october",
40
+ "november",
41
+ "december",
42
+ ]
43
+
44
+ MONTH_SHORT: List[str] = [
45
+ "jan",
46
+ "feb",
47
+ "mar",
48
+ "apr",
49
+ "may",
50
+ "jun",
51
+ "jul",
52
+ "aug",
53
+ "sep",
54
+ "oct",
55
+ "nov",
56
+ "dec",
57
+ ]
58
+
59
+ PATTERN: List[str] = [
60
+ "DDDD*DD*DD",
61
+ "DDDD*D*DD",
62
+ "DDDD*DD*D",
63
+ "DDDD*D*D",
64
+ "DD*DD*DD",
65
+ "DD*D*DD",
66
+ "DD*DD*D",
67
+ "DD*D*D",
68
+ "DD*DD*DDDD",
69
+ "D*DD*DDDD",
70
+ "DD*D*DDDD",
71
+ "D*D*DDDD",
72
+ "DD*DD*DD",
73
+ "D*DD*DD",
74
+ "DD*D*DD",
75
+ "D*D*DD",
76
+ "DDDD*M*DD",
77
+ "DDDD*M*D",
78
+ "DD*M*DD",
79
+ "DD*M*D",
80
+ "DD*M*DDDD",
81
+ "D*M*DDDD",
82
+ "DD*M*DD",
83
+ "D*M*DD",
84
+ "M*DD*DDDD",
85
+ "M*D*DDDD",
86
+ "M*DD*DD",
87
+ "M*D*DD",
88
+ "M*DD**DDDD",
89
+ "M*D**DDDD",
90
+ "M*DD**DD",
91
+ "M*D**DD",
92
+ ]
93
+
94
+ TIME_PATTERN: List[str] = [
95
+ "DD:DD:DD",
96
+ "DD:DD:DD ",
97
+ "DD:DD:DD.DDD",
98
+ "DD:DD:DD.DDD ",
99
+ "DD:DD:DD,DDD",
100
+ "DD:DD:DD,DDD ",
101
+ "D:DD:DD",
102
+ "D:DD:DD ",
103
+ "D:DD:DD.DDD",
104
+ "D:DD:DD.DDD ",
105
+ "D:DD:DD,DDD",
106
+ "D:DD:DD,DDD ",
107
+ ]
108
+
109
+
110
+ MONTH_LITERAL = 1
111
+ WEEKDAY_LITERAL = 2
112
+ DIGIT_LITERAL = 3
113
+ THREE_DIGIT_LITERAL = 4
114
+ FOUR_DIGIT_LITERAL = 5
115
+ DATE_PHRASE = 6
116
+ NOT_DATE_LITERAL = -1
117
+
118
+ HOROZONTAL_PRINT_GAP = 3
119
+ VERTICAL_PRINT_GAP = 2
120
+ TERMINATION_SYMBOL = "^"
121
+
122
+
123
+ def buildTree(item_list: List[str], parent_node: PredictionModelNode) -> None:
124
+ for item in item_list:
125
+ parent = parent_node
126
+ parent_node.charcter = "\x00"
127
+ for idx, char in enumerate(item):
128
+ node = parent.get_child(char)
129
+ if node is None:
130
+ child = PredictionModelNode()
131
+ child.charcter = char
132
+ if idx + 1 == len(item):
133
+ child.explict_date_fragment = True
134
+ parent.add_child(child)
135
+ parent = child
136
+ else:
137
+ if idx + 1 == len(item):
138
+ node.explict_date_fragment = True
139
+ parent = node
140
+
141
+
142
+ def getProbables(tree: PredictionModelNode) -> str:
143
+ if tree is None:
144
+ return ""
145
+ kids = tree.childern
146
+ pattern = f"{tree.charcter} -->"
147
+ for kid in kids:
148
+ pattern = f"{pattern} {kid.charcter}"
149
+ return pattern
150
+
151
+
152
+ def printTree(tree: PredictionModelNode) -> None:
153
+ tree = sortTree(tree)
154
+ display_object = displayBuilder(DisplayObject(), tree)
155
+ print_buffer: List[str] = []
156
+ line = getBlankLine(display_object.width)
157
+ prev_level = 0
158
+ for tc in display_object.displayBuffer:
159
+ if prev_level == tc.level:
160
+ line = line[: tc.leftSpace] + tc.symbol + line[tc.leftSpace + len(tc.symbol) :]
161
+ else:
162
+ line = line.rstrip() + "\n"
163
+ print_buffer.append(line)
164
+ line = getBlankLine(display_object.width)
165
+ line = line[: tc.leftSpace] + tc.symbol + line[tc.leftSpace + len(tc.symbol) :]
166
+ prev_level = tc.level
167
+ print_buffer.append(line)
168
+ print("".join(print_buffer))
169
+
170
+
171
+ def displayBuilder(display: DisplayObject, tree: PredictionModelNode) -> DisplayObject:
172
+ if isFamilyUnit(tree):
173
+ return familyDisplayBuilder(tree)
174
+
175
+ horizontal_display = DisplayObject()
176
+ for kid in tree.childern:
177
+ recursive_display = displayBuilder(horizontal_display, kid)
178
+ horizontal_display = adjustHorizontalDisplay(horizontal_display, recursive_display)
179
+
180
+ horizontal_display.displayBuffer.sort()
181
+
182
+ vertical_display = DisplayObject()
183
+ dad_joints = [0] * tree.children_count()
184
+
185
+ j = 0
186
+ for tc in horizontal_display.displayBuffer:
187
+ if tc.level > 0 or j >= len(dad_joints):
188
+ break
189
+ if tc.symbol not in {"/", "\\", "_", "|"} and tc.symbol.strip():
190
+ dad_joints[j] = tc.leftSpace
191
+ j += 1
192
+
193
+ if tree.children_count() == 0:
194
+ return horizontal_display
195
+ if tree.children_count() == 1:
196
+ symbol = f"{tree.charcter}{TERMINATION_SYMBOL}" if tree.explict_date_fragment else f"{tree.charcter}"
197
+ tc = TreeChar(symbol, dad_joints[0], 0)
198
+ vertical_display.displayBuffer.append(tc)
199
+ else:
200
+ dad_joints.sort()
201
+ symbol = "_"
202
+ if len(dad_joints) % 2 == 0:
203
+ papa_position = 1 + (dad_joints[-1] + dad_joints[0]) // 2
204
+ else:
205
+ papa_position = dad_joints[len(dad_joints) // 2] + 1
206
+ papa_position -= 1
207
+ for k in range(dad_joints[0] + 1, dad_joints[-1] + 1 - 2 + 1):
208
+ if k == papa_position:
209
+ s = f"{tree.charcter or '$'}"
210
+ if tree.explict_date_fragment:
211
+ s = f"{s}{TERMINATION_SYMBOL}"
212
+ tc = TreeChar(s, k, 0)
213
+ else:
214
+ tc = TreeChar(symbol, k, 0)
215
+ vertical_display.displayBuffer.append(tc)
216
+
217
+ for joint in dad_joints:
218
+ for m in range(VERTICAL_PRINT_GAP):
219
+ vertical_display.displayBuffer.append(TreeChar("|", joint, m + 1))
220
+
221
+ display = adjustVerticalDisplay(horizontal_display, vertical_display)
222
+ return display
223
+
224
+
225
+ def adjustVerticalDisplay(horizontal_display: DisplayObject, vertical_display: DisplayObject) -> DisplayObject:
226
+ if not horizontal_display.displayBuffer:
227
+ return vertical_display
228
+ vertical_display.displayBuffer.sort()
229
+ for tc in horizontal_display.displayBuffer:
230
+ tc.level += VERTICAL_PRINT_GAP + 1
231
+ vertical_display.displayBuffer.append(tc)
232
+ vertical_display.width = horizontal_display.width
233
+ return vertical_display
234
+
235
+
236
+ def adjustHorizontalDisplay(display: DisplayObject, kid_display: DisplayObject) -> DisplayObject:
237
+ if not display.displayBuffer:
238
+ return kid_display
239
+
240
+ left_pos = display.width
241
+ for tc in kid_display.displayBuffer:
242
+ tc.leftSpace += left_pos + HOROZONTAL_PRINT_GAP
243
+ inserted = False
244
+ for index, display_tc in enumerate(display.displayBuffer):
245
+ if display_tc.level <= tc.level:
246
+ continue
247
+ display.displayBuffer.insert(max(0, index - 1), tc)
248
+ inserted = True
249
+ break
250
+ if not inserted:
251
+ display.displayBuffer.append(tc)
252
+ display.height = max(display.height, kid_display.height)
253
+ display.width = display.width + kid_display.width + HOROZONTAL_PRINT_GAP
254
+ return display
255
+
256
+
257
+ def familyDisplayBuilder(tree: PredictionModelNode) -> DisplayObject:
258
+ display_object = DisplayObject()
259
+ kids = tree.children_count()
260
+ char_val = f"{tree.charcter}"
261
+ if tree.explict_date_fragment:
262
+ char_val = f"{char_val}{TERMINATION_SYMBOL}"
263
+ if kids == 1:
264
+ display_object.displayBuffer.append(TreeChar(char_val, 0, 0))
265
+ display_object.displayBuffer.append(TreeChar("|", 0, 1))
266
+ kid_char = f"{tree.childern[0].charcter}"
267
+ display_object.displayBuffer.append(TreeChar(kid_char, 0, 2))
268
+ display_object.width = 1
269
+ display_object.height = 3
270
+ elif kids % 2 == 0:
271
+ display_object = evenKidCountTree(display_object, tree)
272
+ else:
273
+ display_object = oddKidCountTree(display_object, tree)
274
+ return display_object
275
+
276
+
277
+ def evenKidCountTree(display_object: DisplayObject, tree: PredictionModelNode) -> DisplayObject:
278
+ kids = tree.children_count()
279
+ papa_position = kids // 2
280
+ char_val = f"{tree.charcter}"
281
+ if tree.explict_date_fragment:
282
+ char_val = f"{char_val}{TERMINATION_SYMBOL}"
283
+ display_object.displayBuffer.append(TreeChar(char_val, papa_position, 0))
284
+ display_object.height = papa_position + 2
285
+ display_object.width = kids + 1
286
+ for i in range(kids // 2):
287
+ markers = (i + 1) * 2
288
+ coeff = 1
289
+ for j in range(markers):
290
+ if coeff == markers // 2 + 1:
291
+ coeff = 1
292
+ if j < markers // 2:
293
+ symbol = "|"
294
+ left_pos = papa_position - coeff
295
+ coeff += 1
296
+ if j == markers // 2 - 1:
297
+ symbol = "/"
298
+ display_object.displayBuffer.append(TreeChar(symbol, left_pos, i + 1))
299
+ else:
300
+ right_pos = papa_position + coeff
301
+ coeff += 1
302
+ symbol = "|"
303
+ if (j == markers - 1 and i != 0) or markers < 3:
304
+ symbol = "\\"
305
+ display_object.displayBuffer.append(TreeChar(symbol, right_pos, i + 1))
306
+ for i in range(kids):
307
+ kid_val = f"{tree.childern[i].charcter}"
308
+ if i < kids // 2:
309
+ display_object.displayBuffer.append(TreeChar(kid_val, i, kids // 2 + 1))
310
+ else:
311
+ display_object.displayBuffer.append(TreeChar(kid_val, i + 1, kids // 2 + 1))
312
+ return display_object
313
+
314
+
315
+ def oddKidCountTree(display_object: DisplayObject, tree: PredictionModelNode) -> DisplayObject:
316
+ kids = tree.children_count()
317
+ papa_position = (kids - 1) // 2
318
+ char_val = f"{tree.charcter}"
319
+ if tree.explict_date_fragment:
320
+ char_val = f"{char_val}{TERMINATION_SYMBOL}"
321
+ display_object.displayBuffer.append(TreeChar(char_val, papa_position, 0))
322
+ display_object.height = papa_position + 2
323
+ display_object.width = kids
324
+ for i in range((kids - 1) // 2):
325
+ markers = 2 * i + 3
326
+ coeff = 1
327
+ for j in range(markers):
328
+ if coeff == markers // 2 + 1:
329
+ coeff = 1
330
+ if j < markers // 2:
331
+ symbol = "|"
332
+ left_pos = papa_position - coeff
333
+ coeff += 1
334
+ if j == markers // 2 - 1 and markers > 2:
335
+ symbol = "/"
336
+ display_object.displayBuffer.append(TreeChar(symbol, left_pos, i + 1))
337
+ elif j == markers // 2:
338
+ display_object.displayBuffer.append(TreeChar("|", papa_position, i + 1))
339
+ else:
340
+ right_pos = papa_position + coeff
341
+ coeff += 1
342
+ symbol = "|"
343
+ if j == markers - 1 and markers > 2:
344
+ symbol = "\\"
345
+ display_object.displayBuffer.append(TreeChar(symbol, right_pos, i + 1))
346
+ for i in range(kids):
347
+ kid_val = f"{tree.childern[i].charcter}"
348
+ display_object.displayBuffer.append(TreeChar(kid_val, i, kids // 2 + 1))
349
+ return display_object
350
+
351
+
352
+ def getMaxTreeHeight(tree: PredictionModelNode) -> int:
353
+ if tree.has_childern():
354
+ heights = [getMaxTreeHeight(child) for child in tree.childern]
355
+ return max(heights) + 1
356
+ return 0
357
+
358
+
359
+ def sortTree(tree: PredictionModelNode) -> PredictionModelNode:
360
+ if tree.has_childern():
361
+ for idx, child in enumerate(tree.childern):
362
+ tree.childern[idx] = sortTree(child)
363
+ swapped = True
364
+ if tree.children_count() <= 1:
365
+ return tree
366
+ while swapped:
367
+ swapped = False
368
+ for i in range(tree.children_count() - 1):
369
+ this_height = getMaxTreeHeight(tree.childern[i])
370
+ next_height = getMaxTreeHeight(tree.childern[i + 1])
371
+ if this_height < next_height:
372
+ swapped = True
373
+ tree.childern[i], tree.childern[i + 1] = tree.childern[i + 1], tree.childern[i]
374
+ return tree
375
+
376
+
377
+ def isFamilyUnit(tree: PredictionModelNode) -> bool:
378
+ if tree.has_childern():
379
+ return all(not node.has_childern() for node in tree.childern)
380
+ return True
381
+
382
+
383
+ def getBlankLine(length: int) -> str:
384
+ result = " "
385
+ for _ in range(length):
386
+ result = f" {result}"
387
+ return result
388
+
389
+
390
+ def getRegexPattern(tree_elements: List[str]) -> str:
391
+ tree = PredictionModelNode()
392
+ buildTree(tree_elements, tree)
393
+ tree = sortTree(tree)
394
+ return _getRegexPattern(tree)
395
+
396
+
397
+ def _getRegexPattern(tree: PredictionModelNode) -> str:
398
+ if isFamilyUnit(tree):
399
+ return getFamilyUnitPattern(tree)
400
+ if tree.has_childern():
401
+ buffer = []
402
+ buffer.append(tree.charcter)
403
+ if tree.children_count() == 1:
404
+ buffer.append(_getRegexPattern(tree.childern[0]))
405
+ return "".join(buffer)
406
+ buffer.append("(")
407
+ for idx, kid in enumerate(tree.childern):
408
+ buffer.append(_getRegexPattern(kid))
409
+ if idx + 1 < tree.children_count():
410
+ buffer.append("|")
411
+ buffer.append(")")
412
+ return "".join(buffer)
413
+ return str(tree.charcter)
414
+
415
+
416
+ def getFamilyUnitPattern(tree: PredictionModelNode) -> str:
417
+ buffer: List[str] = []
418
+ if not tree.childern:
419
+ return ""
420
+ buffer.append(str(tree.charcter))
421
+ if len(tree.childern) == 1:
422
+ buffer.append(str(tree.childern[0].charcter))
423
+ return "".join(buffer)
424
+ buffer.append("([")
425
+ for kid in tree.childern:
426
+ buffer.append(str(kid.charcter))
427
+ buffer.append("])")
428
+ return "".join(buffer)
429
+
430
+
431
+ def _initialize_trees() -> None:
432
+ monthPredictionTree.level = 0
433
+ monthPredictionTree.charcter = "0"
434
+ patternPredictionTree.level = 0
435
+ patternPredictionTree.charcter = "0"
436
+ timePredictionTree.level = 0
437
+ timePredictionTree.charcter = "0"
438
+ weekPredictionTree.level = 0
439
+ weekPredictionTree.charcter = "0"
440
+
441
+ buildTree(MONTH_FULL, monthPredictionTree)
442
+ buildTree(MONTH_SHORT, monthPredictionTree)
443
+ buildTree(WEEKDAY_FULL, weekPredictionTree)
444
+ buildTree(WEEKDAY_SHORT, weekPredictionTree)
445
+ buildTree(PATTERN, patternPredictionTree)
446
+ buildTree(TIME_PATTERN, timePredictionTree)
447
+
448
+
449
+ _initialize_trees()
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import List
5
+
6
+
7
+ @dataclass
8
+ class TreeChar:
9
+ symbol: str = ""
10
+ leftSpace: int = 0
11
+ level: int = 0
12
+
13
+ def __lt__(self, other: "TreeChar") -> bool:
14
+ return self.level < other.level
15
+
16
+
17
+ @dataclass
18
+ class DisplayObject:
19
+ displayBuffer: List[TreeChar] = field(default_factory=list)
20
+ height: int = 0
21
+ width: int = 0
22
+