gui-utilities 1.0.0__py3-none-any.whl

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.

Potentially problematic release.


This version of gui-utilities might be problematic. Click here for more details.

@@ -0,0 +1,642 @@
1
+ from PyQt6.QtCore import Qt, QObject, QEvent, QSize
2
+ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QLineEdit, QComboBox, QDialog, QToolButton, QWidgetAction
3
+ from PyQt6.QtGui import QIcon
4
+ import re
5
+ import requests
6
+ import importlib.resources
7
+
8
+ main_dir = importlib.resources.files("gui_utilities")
9
+ icons_dir = main_dir / "icons"
10
+ tlds_path = main_dir / "tlds"
11
+
12
+ def create_window(title, background_color = "#1e1e1e"):
13
+ window = QWidget()
14
+ window.setObjectName("window")
15
+ window.setWindowTitle(title)
16
+ window.setStyleSheet(f"#window {{background-color: {background_color};}}")
17
+ main_layout = QVBoxLayout(window)
18
+ window.setLayout(main_layout)
19
+ return window
20
+
21
+ def create_label(
22
+ message,
23
+ font_family = "Segoe UI",
24
+ font_size = 14,
25
+ font_color = "#ffffff",
26
+ font_weight = "normal",
27
+ background_color = "#1e1e1e",
28
+ padding = 15,
29
+ border_width = 0,
30
+ border_color = "#ffffff",
31
+ border_radius = 0
32
+ ):
33
+ label = QLabel(message)
34
+ style = f"""
35
+ font-family: {font_family};
36
+ font-size: {font_size}px;
37
+ color: {font_color};
38
+ font-weight: {font_weight};
39
+ background-color: {background_color};
40
+ padding: {padding}px;
41
+ border: {border_width}px solid {border_color};
42
+ border-radius: {border_radius}px;
43
+ """
44
+ label.setStyleSheet(style)
45
+ return label
46
+
47
+ def create_button(
48
+ message,
49
+ font_family = "Segoe UI",
50
+ font_size = 14,
51
+ font_color = "#ffffff",
52
+ font_weight = "bold",
53
+ background_color = "#1e1e1e",
54
+ padding = 15,
55
+ border_width = 2,
56
+ border_color = "#5c5c5c",
57
+ border_radius = 0,
58
+ hover_background_color = "#333333",
59
+ hover_border_width = 3,
60
+ hover_border_color = "#777777",
61
+ pressed_background_color = "#4a4a4a",
62
+ pressed_border_width = 3,
63
+ pressed_border_color = "#0078d7",
64
+ disabled_font_color = "#888888",
65
+ disabled_background_color = "#2d2d2d",
66
+ disabled_border_width = 2,
67
+ disabled_border_color = "#4a4a4a"
68
+ ):
69
+ button = QPushButton(message)
70
+ style_sheet = f"""
71
+ QPushButton {{
72
+ font-family: {font_family};
73
+ font-size: {font_size}px;
74
+ color: {font_color};
75
+ font-weight: {font_weight};
76
+ background-color: {background_color};
77
+ padding: {padding}px;
78
+ border: {border_width}px solid {border_color};
79
+ border-radius: {border_radius}px;
80
+ }}
81
+ QPushButton:hover {{
82
+ background-color: {hover_background_color};
83
+ border: {hover_border_width}px solid {hover_border_color};
84
+ }}
85
+ QPushButton:pressed {{
86
+ background-color: {pressed_background_color};
87
+ border: {pressed_border_width}px solid {pressed_border_color};
88
+ }}
89
+ QPushButton:disabled {{
90
+ color: {disabled_font_color};
91
+ background-color: {disabled_background_color};
92
+ border: {disabled_border_width}px solid {disabled_border_color};
93
+ }}
94
+ """
95
+ button.setStyleSheet(style_sheet)
96
+ return button
97
+
98
+ def create_text_box(
99
+ placeholder_text,
100
+ font_family = "Segoe UI",
101
+ font_size = 14,
102
+ font_color = "#ffffff",
103
+ font_weight = "normal",
104
+ background_color = "#1e1e1e",
105
+ padding = 15,
106
+ border_width = 2,
107
+ border_color = "#5c5c5c",
108
+ border_radius = 0,
109
+ hover_background_color = "#333333",
110
+ hover_border_width = 3,
111
+ hover_border_color = "#777777",
112
+ focus_font_color = "#000000",
113
+ focus_background_color = "#ffffff",
114
+ focus_border_width = 3,
115
+ focus_border_color = "#0078d7",
116
+ disabled_background_color = "#2d2d2d",
117
+ disabled_border_width = 2,
118
+ disabled_border_color = "#4a4a4a",
119
+ hide_text = False
120
+ ):
121
+ text_box = QLineEdit()
122
+ text_box.setPlaceholderText(placeholder_text)
123
+ style_sheet = f"""
124
+ QLineEdit {{
125
+ font-family: {font_family};
126
+ font-size: {font_size}px;
127
+ color: {font_color};
128
+ font-weight: {font_weight};
129
+ background-color: {background_color};
130
+ padding: {padding}px;
131
+ border: {border_width}px solid {border_color};
132
+ border-radius: {border_radius}px;
133
+ }}
134
+ QLineEdit:hover {{
135
+ background-color: {hover_background_color};
136
+ border: {hover_border_width}px solid {hover_border_color};
137
+ }}
138
+ QLineEdit:focus {{
139
+ color: {focus_font_color};
140
+ background-color: {focus_background_color};
141
+ border: {focus_border_width}px solid {focus_border_color};
142
+ }}
143
+ QLineEdit:disabled {{
144
+ background-color: {disabled_background_color};
145
+ border: {disabled_border_width}px solid {disabled_border_color};
146
+ }}
147
+ """
148
+ text_box.setStyleSheet(style_sheet)
149
+ if hide_text:
150
+ show_text_icon = QIcon(str(icons_dir / "show_text_icon.png"))
151
+ hide_text_icon = QIcon(str(icons_dir / "hide_text_icon.png"))
152
+ focused_show_text_icon = QIcon(str(icons_dir / "focused_show_text_icon.png"))
153
+ focused_hide_text_icon = QIcon(str(icons_dir / "focused_hide_text_icon.png"))
154
+ text_box.setEchoMode(QLineEdit.EchoMode.Password)
155
+ toggle_text_visibility_button = QToolButton(text_box)
156
+ toggle_text_visibility_button.setCursor(Qt.CursorShape.PointingHandCursor)
157
+ toggle_text_visibility_button.setAutoRaise(True)
158
+ toggle_text_visibility_button.setIcon(hide_text_icon)
159
+ toggle_text_visibility_button.setIconSize(QSize(25, 25))
160
+ toggle_text_visibility_action = QWidgetAction(text_box)
161
+ toggle_text_visibility_action.setDefaultWidget(toggle_text_visibility_button)
162
+ style_sheet = f"""
163
+ QToolButton {{
164
+ border: none;
165
+ margin-right: 10px;
166
+ }}
167
+ QToolButton:hover, QToolButton:pressed {{
168
+ background-color: transparent;
169
+ border: none;
170
+ }}
171
+ """
172
+ toggle_text_visibility_button.setStyleSheet(style_sheet)
173
+ text_box.addAction(toggle_text_visibility_action, QLineEdit.ActionPosition.TrailingPosition)
174
+
175
+ def update_icon():
176
+ is_password = text_box.echoMode() == QLineEdit.EchoMode.Password
177
+ if text_box.hasFocus(): icon = focused_hide_text_icon if is_password else focused_show_text_icon
178
+ else: icon = hide_text_icon if is_password else show_text_icon
179
+ toggle_text_visibility_button.setIcon(icon)
180
+
181
+ def toggle_visibility():
182
+ if text_box.echoMode() == QLineEdit.EchoMode.Password: text_box.setEchoMode(QLineEdit.EchoMode.Normal)
183
+ else: text_box.setEchoMode(QLineEdit.EchoMode.Password)
184
+ update_icon()
185
+
186
+ toggle_text_visibility_button.clicked.connect(toggle_visibility)
187
+
188
+ class FocusWatcher(QObject):
189
+ def eventFilter(self, watched, event):
190
+ if event.type() in (QEvent.Type.FocusIn, QEvent.Type.FocusOut): update_icon()
191
+ return super().eventFilter(watched, event)
192
+
193
+ focus_watcher = FocusWatcher(text_box)
194
+ text_box.installEventFilter(focus_watcher)
195
+ setattr(text_box, "focus_watcher", focus_watcher)
196
+ update_icon()
197
+ return text_box
198
+
199
+ def create_combo_box(
200
+ placeholder_text,
201
+ items,
202
+ font_family = "Segoe UI",
203
+ font_size = 14,
204
+ placeholder_font_color = "#888888",
205
+ font_color = "#ffffff",
206
+ background_color = "#1e1e1e",
207
+ padding = 15,
208
+ border_width = 2,
209
+ border_color = "#5c5c5c",
210
+ border_radius = 0,
211
+ hover_background_color = "#333333",
212
+ hover_border_width = 3,
213
+ hover_border_color = "#777777",
214
+ on_font_color = "#000000",
215
+ on_background_color = "#ffffff",
216
+ on_border_width = 3,
217
+ on_border_color = "#0078d7",
218
+ dropdown_font_color = "#ffffff",
219
+ dropdown_background_color = "#1e1e1e",
220
+ dropdown_selection_background_color = "#0078d7",
221
+ dropdown_border_width = 1,
222
+ dropdown_border_color = "#5c5c5c"
223
+ ):
224
+ combo_box = QComboBox()
225
+ combo_box.setPlaceholderText(f"{placeholder_text}")
226
+ combo_box.addItems(items)
227
+ combo_box.setCurrentIndex(-1)
228
+
229
+ def get_stylesheet(font_color):
230
+ return f"""
231
+ QComboBox {{
232
+ font-family: {font_family};
233
+ font-size: {font_size}px;
234
+ color: {font_color};
235
+ background-color: {background_color};
236
+ padding: {padding}px;
237
+ border: {border_width}px solid {border_color};
238
+ border-radius: {border_radius}px;
239
+ }}
240
+ QComboBox:hover {{
241
+ background-color: {hover_background_color};
242
+ border: {hover_border_width}px solid {hover_border_color};
243
+ }}
244
+ QComboBox:on {{
245
+ color: {on_font_color};
246
+ background-color: {on_background_color};
247
+ border: {on_border_width}px solid {on_border_color};
248
+ }}
249
+ QComboBox QAbstractItemView {{
250
+ color: {dropdown_font_color};
251
+ background-color: {dropdown_background_color};
252
+ selection-background-color: {dropdown_selection_background_color};
253
+ border: {dropdown_border_width}px solid {dropdown_border_color};
254
+ }}
255
+ QComboBox::drop-down {{
256
+ border: none;
257
+ }}
258
+ """
259
+
260
+ def change_color(index):
261
+ if index == -1: combo_box.setStyleSheet(get_stylesheet(placeholder_font_color))
262
+ else: combo_box.setStyleSheet(get_stylesheet(font_color))
263
+
264
+ combo_box.currentIndexChanged.connect(change_color)
265
+ change_color(-1)
266
+ return combo_box
267
+
268
+ def create_information_message_box(
269
+ message,
270
+ top_margin = 25,
271
+ bottom_margin = 25,
272
+ left_margin = 25,
273
+ right_margin = 25,
274
+ spacing = 10,
275
+ background_color = "#1e1e1e",
276
+ border_width = 2,
277
+ border_color = "#5c5c5c",
278
+ border_radius = 0,
279
+ label_font_family = "Segoe UI",
280
+ label_font_size = 14,
281
+ label_font_color = "#ffffff",
282
+ label_padding = 15,
283
+ label_border_width = 0,
284
+ label_border_color = "#ffffff",
285
+ label_border_radius = 0,
286
+ button_message = "Aceptar",
287
+ button_font_family = "Segoe UI",
288
+ button_font_size = 14,
289
+ button_font_color = "#ffffff",
290
+ button_background_color = "#1e1e1e",
291
+ button_padding = 15,
292
+ button_border_width = 2,
293
+ button_border_color = "#5c5c5c",
294
+ button_border_radius = 0,
295
+ button_hover_background_color = "#333333",
296
+ button_hover_border_width = 3,
297
+ button_hover_border_color = "#777777",
298
+ button_pressed_background_color = "#4a4a4a",
299
+ button_pressed_border_width = 3,
300
+ button_pressed_border_color = "#0078d7",
301
+ button_disabled_font_color = "#888888",
302
+ button_disabled_background_color = "#2d2d2d",
303
+ button_disabled_border_width = 2,
304
+ button_disabled_border_color = "#4a4a4a"
305
+ ):
306
+ message_box = QDialog()
307
+ message_box.setObjectName("message_box")
308
+ message_box.setWindowFlags(Qt.WindowType.FramelessWindowHint)
309
+ message_box.setMaximumWidth(480)
310
+ style_sheet = f"""
311
+ QDialog#message_box {{
312
+ background-color: {background_color};
313
+ border: {border_width}px solid {border_color};
314
+ border-radius: {border_radius}px;
315
+ }}
316
+ """
317
+ message_box.setStyleSheet(style_sheet)
318
+ main_layout = QVBoxLayout(message_box)
319
+ message_box.setLayout(main_layout)
320
+ main_layout.setContentsMargins(left_margin, top_margin, right_margin, bottom_margin)
321
+ main_layout.setSpacing(spacing)
322
+ message_label = create_label(
323
+ message = message,
324
+ font_family = label_font_family,
325
+ font_size = label_font_size,
326
+ font_color= label_font_color,
327
+ background_color = "transparent",
328
+ padding = label_padding,
329
+ border_width = label_border_width,
330
+ border_color = label_border_color,
331
+ border_radius = label_border_radius
332
+ )
333
+ main_layout.addWidget(message_label)
334
+
335
+ accept_button = create_button(
336
+ message = button_message,
337
+ font_family = button_font_family,
338
+ font_size = button_font_size,
339
+ font_color = button_font_color,
340
+ background_color = button_background_color,
341
+ padding = button_padding,
342
+ border_width = button_border_width,
343
+ border_color = button_border_color,
344
+ border_radius = button_border_radius,
345
+ hover_background_color = button_hover_background_color,
346
+ hover_border_width = button_hover_border_width,
347
+ hover_border_color = button_hover_border_color,
348
+ pressed_background_color = button_pressed_background_color,
349
+ pressed_border_width = button_pressed_border_width,
350
+ pressed_border_color = button_pressed_border_color,
351
+ disabled_font_color = button_disabled_font_color,
352
+ disabled_background_color = button_disabled_background_color,
353
+ disabled_border_width = button_disabled_border_width,
354
+ disabled_border_color = button_disabled_border_color
355
+ )
356
+ main_layout.addWidget(accept_button)
357
+ accept_button.clicked.connect(message_box.accept)
358
+ return message_box
359
+
360
+ def create_confirmation_message_box(
361
+ message,
362
+ top_margin = 25,
363
+ bottom_margin = 25,
364
+ left_margin = 25,
365
+ right_margin = 25,
366
+ main_spacing = 10,
367
+ button_spacing = 10,
368
+ background_color = "#1e1e1e",
369
+ border_width = 2,
370
+ border_color = "#5c5c5c",
371
+ border_radius = 0,
372
+ label_font_family = "Segoe UI",
373
+ label_font_size = 14,
374
+ label_font_color = "#ffffff",
375
+ label_padding = 15,
376
+ label_border_width = 0,
377
+ label_border_color = "#ffffff",
378
+ label_border_radius = 0,
379
+ button_font_family = "Segoe UI",
380
+ button_font_size = 14,
381
+ button_font_color = "#ffffff",
382
+ button_background_color = "#1e1e1e",
383
+ button_padding = 15,
384
+ button_border_width = 2,
385
+ button_border_color = "#5c5c5c",
386
+ button_border_radius = 0,
387
+ button_hover_background_color = "#333333",
388
+ button_hover_border_width = 3,
389
+ button_hover_border_color = "#777777",
390
+ button_pressed_background_color = "#4a4a4a",
391
+ button_pressed_border_width = 3,
392
+ button_pressed_border_color = "#0078d7",
393
+ button_disabled_font_color = "#888888",
394
+ button_disabled_background_color = "#2d2d2d",
395
+ button_disabled_border_width = 2,
396
+ button_disabled_border_color = "#4a4a4a"
397
+ ):
398
+ confirm_message_box = QDialog()
399
+ confirm_message_box.setObjectName("confirm_message_box")
400
+ confirm_message_box.setWindowFlags(Qt.WindowType.FramelessWindowHint)
401
+ confirm_message_box.setMaximumWidth(480)
402
+ style_sheet = f"""
403
+ QDialog#confirm_message_box {{
404
+ background-color: {background_color};
405
+ border: {border_width}px solid {border_color};
406
+ border-radius: {border_radius}px;
407
+ }}
408
+ """
409
+ confirm_message_box.setStyleSheet(style_sheet)
410
+ main_layout = QVBoxLayout(confirm_message_box)
411
+ confirm_message_box.setLayout(main_layout)
412
+ main_layout.setContentsMargins(left_margin, top_margin, right_margin, bottom_margin)
413
+ main_layout.setSpacing(main_spacing)
414
+ message_layout = QHBoxLayout()
415
+ main_layout.addLayout(message_layout)
416
+ message_label = create_label(
417
+ message = message,
418
+ font_family = label_font_family,
419
+ font_size = label_font_size,
420
+ font_color = label_font_color,
421
+ background_color = "transparent",
422
+ padding = label_padding,
423
+ border_width = label_border_width,
424
+ border_color = label_border_color,
425
+ border_radius = label_border_radius
426
+ )
427
+ message_layout.addWidget(message_label)
428
+ buttons_layout = QHBoxLayout()
429
+ main_layout.addLayout(buttons_layout)
430
+ buttons_layout.setSpacing(button_spacing)
431
+ confirm_button = create_button(
432
+ message = "Sí",
433
+ font_family = button_font_family,
434
+ font_size = button_font_size,
435
+ font_color = button_font_color,
436
+ background_color = button_background_color,
437
+ padding = button_padding,
438
+ border_width = button_border_width,
439
+ border_color = button_border_color,
440
+ border_radius = button_border_radius,
441
+ hover_background_color = button_hover_background_color,
442
+ hover_border_width = button_hover_border_width,
443
+ hover_border_color = button_hover_border_color,
444
+ pressed_background_color = button_pressed_background_color,
445
+ pressed_border_width = button_pressed_border_width,
446
+ pressed_border_color = button_pressed_border_color,
447
+ disabled_font_color = button_disabled_font_color,
448
+ disabled_background_color = button_disabled_background_color,
449
+ disabled_border_width = button_disabled_border_width,
450
+ disabled_border_color = button_disabled_border_color
451
+ )
452
+ buttons_layout.addWidget(confirm_button)
453
+ confirm_button.clicked.connect(confirm_message_box.accept)
454
+ decline_button = create_button(
455
+ message = "No",
456
+ font_family = button_font_family,
457
+ font_size = button_font_size,
458
+ font_color = button_font_color,
459
+ background_color = button_background_color,
460
+ padding = button_padding,
461
+ border_width = button_border_width,
462
+ border_color = button_border_color,
463
+ border_radius = button_border_radius,
464
+ hover_background_color = button_hover_background_color,
465
+ hover_border_width = button_hover_border_width,
466
+ hover_border_color = button_hover_border_color,
467
+ pressed_background_color = button_pressed_background_color,
468
+ pressed_border_width = button_pressed_border_width,
469
+ pressed_border_color = button_pressed_border_color,
470
+ disabled_font_color = button_disabled_font_color,
471
+ disabled_background_color = button_disabled_background_color,
472
+ disabled_border_width = button_disabled_border_width,
473
+ disabled_border_color = button_disabled_border_color
474
+ )
475
+ buttons_layout.addWidget(decline_button)
476
+ decline_button.clicked.connect(confirm_message_box.reject)
477
+ return confirm_message_box
478
+
479
+ def confirm_exit(
480
+ window,
481
+ background_color = "#1e1e1e",
482
+ border_width = 2,
483
+ border_color = "#5c5c5c",
484
+ border_radius = 0,
485
+ label_font_family = "Segoe UI",
486
+ label_font_size = 14,
487
+ label_font_color = "#ffffff",
488
+ label_padding = 15,
489
+ label_border_width = 0,
490
+ label_border_color = "#ffffff",
491
+ label_border_radius = 0,
492
+ button_font_family = "Segoe UI",
493
+ button_font_size = 14,
494
+ button_font_color = "#ffffff",
495
+ button_background_color = "#1e1e1e",
496
+ button_padding = 15,
497
+ button_border_width = 2,
498
+ button_border_color = "#5c5c5c",
499
+ button_border_radius = 0,
500
+ button_hover_background_color = "#333333",
501
+ button_hover_border_width = 3,
502
+ button_hover_border_color = "#777777",
503
+ button_pressed_background_color = "#4a4a4a",
504
+ button_pressed_border_width = 3,
505
+ button_pressed_border_color = "#0078d7",
506
+ button_disabled_font_color = "#888888",
507
+ button_disabled_background_color = "#2d2d2d",
508
+ button_disabled_border_width = 2,
509
+ button_disabled_border_color = "#4a4a4a"
510
+ ):
511
+ confirmation_message_box = create_confirmation_message_box(
512
+ message = "¿Está seguro de querer salir del programa?",
513
+ background_color = background_color,
514
+ border_width = border_width,
515
+ border_color = border_color,
516
+ border_radius = border_radius,
517
+ label_font_family = label_font_family,
518
+ label_font_size = label_font_size,
519
+ label_font_color = label_font_color,
520
+ label_padding = label_padding,
521
+ label_border_width = label_border_width,
522
+ label_border_color = label_border_color,
523
+ label_border_radius = label_border_radius,
524
+ button_font_family = button_font_family,
525
+ button_font_size = button_font_size,
526
+ button_font_color = button_font_color,
527
+ button_background_color = button_background_color,
528
+ button_padding = button_padding,
529
+ button_border_width = button_border_width,
530
+ button_border_color = button_border_color,
531
+ button_border_radius = button_border_radius,
532
+ button_hover_background_color = button_hover_background_color,
533
+ button_hover_border_width = button_hover_border_width,
534
+ button_hover_border_color = button_hover_border_color,
535
+ button_pressed_background_color = button_pressed_background_color,
536
+ button_pressed_border_width = button_pressed_border_width,
537
+ button_pressed_border_color = button_pressed_border_color,
538
+ button_disabled_font_color = button_disabled_font_color,
539
+ button_disabled_background_color = button_disabled_background_color,
540
+ button_disabled_border_width = button_disabled_border_width,
541
+ button_disabled_border_color = button_disabled_border_color
542
+ )
543
+ result = confirmation_message_box.exec()
544
+ if result == QDialog.DialogCode.Accepted: window.close()
545
+
546
+ def switch_instance(gui_instance, menu_function):
547
+ new_widget = QWidget()
548
+ new_layout = menu_function()
549
+ new_widget.setLayout(new_layout)
550
+ if gui_instance.central_widget is not None:
551
+ gui_instance.window.layout().replaceWidget(gui_instance.central_widget, new_widget)
552
+ gui_instance.central_widget.deleteLater()
553
+ else: gui_instance.window.layout().addWidget(new_widget)
554
+ gui_instance.central_widget = new_widget
555
+
556
+ def get_responsive_width(window, fraction = 3.0):
557
+ screen_width = window.screen().size().width()
558
+ return round(screen_width / fraction)
559
+
560
+ def validate_string(string, suffix = "El", field = "campo"):
561
+ if string and string.strip(): return None
562
+ return f"{suffix} {field} no puede dejarse {"vacío" if suffix == "El" else "vacía"}."
563
+
564
+ def validate_integer(integer, suffix = "El", field = "campo"):
565
+ if not integer or not integer.strip(): return f"{suffix} {field} no puede dejarse {"vacío" if suffix == "El" else "vacía"}."
566
+ pattern = re.compile(r"^\d+$")
567
+ unformatted_integer = integer.replace(".", "")
568
+ if pattern.match(unformatted_integer): return None
569
+ return f"No ha ingresado {"un" if suffix == "El" else "una"} {field} {"válido" if suffix == "El" else "válida"}."
570
+
571
+ def validate_id(id_str):
572
+ if not id_str or not id_str.strip(): return "El D.N.I. no puede dejarse vacio."
573
+ pattern = re.compile(r"^(?:\d{8}|(?:\d{1,2}\.\d{3}\.\d{3}))$")
574
+ if pattern.match(id_str): return None
575
+ return "No ha ingresado un D.N.I. válido."
576
+
577
+ def validate_cellphone_number(cellphone_number):
578
+ if not cellphone_number or not cellphone_number.strip(): return "El número telefónico no puede dejarse vacío."
579
+ clean_number = "".join(filter(str.isdigit, cellphone_number))
580
+ if len(clean_number) == 10: return None
581
+ return "No ha ingresado un número telefónico válido."
582
+
583
+ tlds_list_path = main_dir / tlds_path / "tlds_list.txt"
584
+
585
+ def export_tlds(tlds_list):
586
+ try:
587
+ with open(tlds_list_path, "w", encoding = "utf-8") as saved_tlds: saved_tlds.write("\n".join(tlds_list))
588
+ except IOError: pass
589
+
590
+ def import_tlds():
591
+ try:
592
+ with open(tlds_list_path, "r", encoding = "utf-8") as saved_tlds: return [tld.strip() for tld in saved_tlds]
593
+ except FileNotFoundError: return []
594
+
595
+ def get_tlds():
596
+ url = "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"
597
+ try:
598
+ response = requests.get(url, timeout = 10)
599
+ response.raise_for_status()
600
+ tlds_list = [tld.lower() for tld in response.text.splitlines()[1:] if tld]
601
+ if tlds_list: export_tlds(tlds_list)
602
+ return tlds_list
603
+ except requests.exceptions.RequestException: return import_tlds()
604
+
605
+ def build_email_pattern(tlds_list):
606
+ if not tlds_list:
607
+ return re.compile(
608
+ r"^(?P<local>[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+"
609
+ r"(?:\[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*)@"
610
+ r"(?P<dominio>(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+"
611
+ r"[a-zA-Z]{2,63})$",
612
+ re.IGNORECASE
613
+ )
614
+ tld_pattern = "|".join(re.escape(tld) for tld in sorted(tlds_list, key = len, reverse = True))
615
+ return re.compile(
616
+ r"^(?P<local>[a-zA-Z0-9!#$%&'+/=?^_{|}~-]+"
617
+ r"(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*)@"
618
+ r"(?P<dominio>(?:[a-zA-Z0-9-]+\.)+"
619
+ r"(?:" + tld_pattern + r"))$", re.IGNORECASE
620
+ )
621
+
622
+ email_pattern = build_email_pattern(get_tlds())
623
+
624
+ def validate_email(email):
625
+ if not email or not email.strip(): return "El correo electrónico no puede dejarse vacío."
626
+ if email_pattern.match(email): return None
627
+ return "No ha ingresado un correo electrónico válido."
628
+
629
+ def decimal_format(number):
630
+ if isinstance(number, float) and number.is_integer(): number = int(number)
631
+ return f"{number:,}".replace(",", "X").replace(".", ",").replace("X", ".")
632
+
633
+ def format_id(id_string):
634
+ clean_id = id_string.replace(".", "")
635
+ if len(clean_id) == 8: return f"{clean_id[0:2]}.{clean_id[2:5]}.{clean_id[5:8]}"
636
+ elif len(clean_id) == 7: return f"{clean_id[0:1]}.{clean_id[1:4]}.{clean_id[4:7]}"
637
+ return id_string
638
+
639
+ def cellphone_number_format(cellphone_number):
640
+ clean_number = "".join(filter(str.isdigit, cellphone_number))
641
+ if len(clean_number) == 10: return f"{clean_number[0:4]} - {clean_number[4:10]}"
642
+ return cellphone_number
Binary file
Binary file