hspylib-clitt 0.9.119__py3-none-any.whl → 0.9.120__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 hspylib-clitt might be problematic. Click here for more details.

Files changed (172) hide show
  1. build/lib/build/lib/build/lib/clitt/__init__.py +3 -3
  2. build/lib/build/lib/build/lib/clitt/addons/__init__.py +3 -3
  3. build/lib/build/lib/build/lib/clitt/addons/appman/__init__.py +3 -3
  4. build/lib/build/lib/build/lib/clitt/addons/appman/templates/__init__.py +3 -3
  5. build/lib/build/lib/build/lib/clitt/addons/widman/__init__.py +3 -3
  6. build/lib/build/lib/build/lib/clitt/addons/widman/widgets/__init__.py +3 -3
  7. build/lib/build/lib/build/lib/clitt/core/__init__.py +3 -3
  8. build/lib/build/lib/build/lib/clitt/core/exception/__init__.py +3 -3
  9. build/lib/build/lib/build/lib/clitt/core/icons/__init__.py +3 -3
  10. build/lib/build/lib/build/lib/clitt/core/icons/emojis/__init__.py +3 -3
  11. build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/__init__.py +3 -3
  12. build/lib/build/lib/build/lib/clitt/core/term/__init__.py +3 -3
  13. build/lib/build/lib/build/lib/clitt/core/tui/__init__.py +3 -3
  14. build/lib/build/lib/build/lib/clitt/core/tui/line_input/__init__.py +3 -3
  15. build/lib/build/lib/build/lib/clitt/core/tui/line_input/keyboard_input.py +22 -22
  16. build/lib/build/lib/build/lib/clitt/core/tui/mchoose/__init__.py +3 -3
  17. build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/__init__.py +3 -3
  18. build/lib/build/lib/build/lib/clitt/core/tui/menu/__init__.py +3 -3
  19. build/lib/build/lib/build/lib/clitt/core/tui/minput/__init__.py +3 -3
  20. build/lib/build/lib/build/lib/clitt/core/tui/mselect/__init__.py +3 -3
  21. build/lib/build/lib/build/lib/clitt/core/tui/table/__init__.py +3 -3
  22. build/lib/build/lib/build/lib/clitt/utils/__init__.py +3 -3
  23. build/lib/build/lib/clitt/__init__.py +2 -2
  24. build/lib/build/lib/clitt/addons/__init__.py +2 -2
  25. build/lib/build/lib/clitt/addons/appman/__init__.py +2 -2
  26. build/lib/build/lib/clitt/addons/appman/templates/__init__.py +2 -2
  27. build/lib/build/lib/clitt/addons/widman/__init__.py +2 -2
  28. build/lib/build/lib/clitt/addons/widman/widgets/__init__.py +2 -2
  29. build/lib/build/lib/clitt/core/__init__.py +2 -2
  30. build/lib/build/lib/clitt/core/exception/__init__.py +2 -2
  31. build/lib/build/lib/clitt/core/icons/__init__.py +2 -2
  32. build/lib/build/lib/clitt/core/icons/emojis/__init__.py +2 -2
  33. build/lib/build/lib/clitt/core/icons/font_awesome/__init__.py +2 -2
  34. build/lib/build/lib/clitt/core/term/__init__.py +2 -2
  35. build/lib/build/lib/clitt/core/tui/__init__.py +2 -2
  36. build/lib/build/lib/clitt/core/tui/line_input/__init__.py +2 -2
  37. build/lib/build/lib/clitt/core/tui/line_input/keyboard_input.py +22 -22
  38. build/lib/build/lib/clitt/core/tui/mchoose/__init__.py +2 -2
  39. build/lib/build/lib/clitt/core/tui/mdashboard/__init__.py +2 -2
  40. build/lib/build/lib/clitt/core/tui/menu/__init__.py +2 -2
  41. build/lib/build/lib/clitt/core/tui/minput/__init__.py +2 -2
  42. build/lib/build/lib/clitt/core/tui/mselect/__init__.py +2 -2
  43. build/lib/build/lib/clitt/core/tui/table/__init__.py +2 -2
  44. build/lib/build/lib/clitt/utils/__init__.py +2 -2
  45. build/lib/clitt/__init__.py +2 -2
  46. build/lib/clitt/addons/__init__.py +2 -2
  47. build/lib/clitt/addons/appman/__init__.py +2 -2
  48. build/lib/clitt/addons/appman/templates/__init__.py +2 -2
  49. build/lib/clitt/addons/widman/__init__.py +2 -2
  50. build/lib/clitt/addons/widman/widgets/__init__.py +2 -2
  51. build/lib/clitt/core/__init__.py +2 -2
  52. build/lib/clitt/core/exception/__init__.py +2 -2
  53. build/lib/clitt/core/icons/__init__.py +2 -2
  54. build/lib/clitt/core/icons/emojis/__init__.py +2 -2
  55. build/lib/clitt/core/icons/font_awesome/__init__.py +2 -2
  56. build/lib/clitt/core/term/__init__.py +2 -2
  57. build/lib/clitt/core/tui/__init__.py +2 -2
  58. build/lib/clitt/core/tui/line_input/__init__.py +2 -2
  59. build/lib/clitt/core/tui/line_input/keyboard_input.py +22 -22
  60. build/lib/clitt/core/tui/mchoose/__init__.py +2 -2
  61. build/lib/clitt/core/tui/mdashboard/__init__.py +2 -2
  62. build/lib/clitt/core/tui/menu/__init__.py +2 -2
  63. build/lib/clitt/core/tui/minput/__init__.py +2 -2
  64. build/lib/clitt/core/tui/mselect/__init__.py +2 -2
  65. build/lib/clitt/core/tui/table/__init__.py +2 -2
  66. build/lib/clitt/utils/__init__.py +2 -2
  67. clitt/.version +1 -1
  68. clitt/__init__.py +2 -2
  69. clitt/addons/__init__.py +2 -2
  70. clitt/addons/appman/__init__.py +2 -2
  71. clitt/addons/appman/templates/__init__.py +2 -2
  72. clitt/addons/widman/__init__.py +2 -2
  73. clitt/addons/widman/widgets/__init__.py +2 -2
  74. clitt/core/__init__.py +2 -2
  75. clitt/core/exception/__init__.py +2 -2
  76. clitt/core/icons/__init__.py +2 -2
  77. clitt/core/icons/emojis/__init__.py +2 -2
  78. clitt/core/icons/font_awesome/__init__.py +2 -2
  79. clitt/core/term/__init__.py +2 -2
  80. clitt/core/tui/__init__.py +2 -2
  81. clitt/core/tui/line_input/__init__.py +2 -2
  82. clitt/core/tui/line_input/keyboard_input.py +22 -22
  83. clitt/core/tui/mchoose/__init__.py +2 -2
  84. clitt/core/tui/mdashboard/__init__.py +2 -2
  85. clitt/core/tui/menu/__init__.py +2 -2
  86. clitt/core/tui/minput/__init__.py +2 -2
  87. clitt/core/tui/mselect/__init__.py +2 -2
  88. clitt/core/tui/table/__init__.py +2 -2
  89. clitt/utils/__init__.py +2 -2
  90. {hspylib_clitt-0.9.119.dist-info → hspylib_clitt-0.9.120.dist-info}/METADATA +2 -2
  91. {hspylib_clitt-0.9.119.dist-info → hspylib_clitt-0.9.120.dist-info}/RECORD +93 -172
  92. build/lib/build/lib/build/lib/build/lib/clitt/__classpath__.py +0 -28
  93. build/lib/build/lib/build/lib/build/lib/clitt/__init__.py +0 -13
  94. build/lib/build/lib/build/lib/build/lib/clitt/__main__.py +0 -139
  95. build/lib/build/lib/build/lib/build/lib/clitt/addons/__init__.py +0 -12
  96. build/lib/build/lib/build/lib/build/lib/clitt/addons/appman/__init__.py +0 -13
  97. build/lib/build/lib/build/lib/build/lib/clitt/addons/appman/appman.py +0 -305
  98. build/lib/build/lib/build/lib/build/lib/clitt/addons/appman/appman_enums.py +0 -39
  99. build/lib/build/lib/build/lib/build/lib/clitt/addons/appman/templates/__init__.py +0 -11
  100. build/lib/build/lib/build/lib/build/lib/clitt/addons/widman/__init__.py +0 -14
  101. build/lib/build/lib/build/lib/build/lib/clitt/addons/widman/widget.py +0 -70
  102. build/lib/build/lib/build/lib/build/lib/clitt/addons/widman/widget_entry.py +0 -54
  103. build/lib/build/lib/build/lib/build/lib/clitt/addons/widman/widgets/__init__.py +0 -14
  104. build/lib/build/lib/build/lib/build/lib/clitt/addons/widman/widgets/widget_free.py +0 -110
  105. build/lib/build/lib/build/lib/build/lib/clitt/addons/widman/widgets/widget_punch.py +0 -246
  106. build/lib/build/lib/build/lib/build/lib/clitt/addons/widman/widgets/widget_send_msg.py +0 -272
  107. build/lib/build/lib/build/lib/build/lib/clitt/addons/widman/widgets/widget_time_calc.py +0 -146
  108. build/lib/build/lib/build/lib/build/lib/clitt/addons/widman/widman.py +0 -123
  109. build/lib/build/lib/build/lib/build/lib/clitt/core/__init__.py +0 -15
  110. build/lib/build/lib/build/lib/build/lib/clitt/core/exception/__init__.py +0 -11
  111. build/lib/build/lib/build/lib/build/lib/clitt/core/exception/exceptions.py +0 -19
  112. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/__init__.py +0 -12
  113. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/emojis/__init__.py +0 -12
  114. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/emojis/emojis.py +0 -41
  115. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/emojis/face_smiling.py +0 -40
  116. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/__init__.py +0 -18
  117. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/app_icons.py +0 -55
  118. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/awesome.py +0 -76
  119. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/dashboard_icons.py +0 -93
  120. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/form_icons.py +0 -69
  121. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/game_icons.py +0 -59
  122. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/nav_icons.py +0 -42
  123. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/trickplay_icons.py +0 -39
  124. build/lib/build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/widget_icons.py +0 -37
  125. build/lib/build/lib/build/lib/build/lib/clitt/core/preferences.py +0 -87
  126. build/lib/build/lib/build/lib/build/lib/clitt/core/term/__init__.py +0 -14
  127. build/lib/build/lib/build/lib/build/lib/clitt/core/term/commons.py +0 -106
  128. build/lib/build/lib/build/lib/build/lib/clitt/core/term/cursor.py +0 -174
  129. build/lib/build/lib/build/lib/build/lib/clitt/core/term/screen.py +0 -106
  130. build/lib/build/lib/build/lib/build/lib/clitt/core/term/terminal.py +0 -202
  131. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/__init__.py +0 -20
  132. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/line_input/__init__.py +0 -12
  133. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/line_input/keyboard_input.py +0 -229
  134. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/line_input/line_input.py +0 -31
  135. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mchoose/__init__.py +0 -12
  136. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mchoose/mchoose.py +0 -43
  137. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mchoose/menu_choose.py +0 -192
  138. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/__init__.py +0 -14
  139. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/dashboard_builder.py +0 -54
  140. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/dashboard_item.py +0 -31
  141. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/mdashboard.py +0 -26
  142. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/menu_dashboard.py +0 -148
  143. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/menu/__init__.py +0 -16
  144. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu.py +0 -111
  145. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu_action.py +0 -47
  146. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu_factory.py +0 -117
  147. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu_item.py +0 -196
  148. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu_ui.py +0 -98
  149. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu_view.py +0 -57
  150. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/minput/__init__.py +0 -19
  151. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/minput/access_type.py +0 -26
  152. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/minput/field_builder.py +0 -117
  153. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/minput/form_builder.py +0 -72
  154. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/minput/form_field.py +0 -180
  155. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/minput/input_type.py +0 -30
  156. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/minput/input_validator.py +0 -98
  157. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/minput/menu_input.py +0 -292
  158. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/minput/minput.py +0 -44
  159. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/minput/minput_utils.py +0 -156
  160. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mselect/__init__.py +0 -12
  161. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mselect/menu_select.py +0 -170
  162. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/mselect/mselect.py +0 -36
  163. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/table/__init__.py +0 -12
  164. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/table/table_enums.py +0 -47
  165. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/table/table_renderer.py +0 -339
  166. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/tui_application.py +0 -52
  167. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/tui_component.py +0 -154
  168. build/lib/build/lib/build/lib/build/lib/clitt/core/tui/tui_preferences.py +0 -103
  169. build/lib/build/lib/build/lib/build/lib/clitt/utils/__init__.py +0 -11
  170. build/lib/build/lib/build/lib/build/lib/clitt/utils/git_utils.py +0 -66
  171. {hspylib_clitt-0.9.119.dist-info → hspylib_clitt-0.9.120.dist-info}/WHEEL +0 -0
  172. {hspylib_clitt-0.9.119.dist-info → hspylib_clitt-0.9.120.dist-info}/top_level.txt +0 -0
@@ -1,110 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- @project: HsPyLib-Clitt
6
- @package: clitt.addons.widman.widgets
7
- @file: widget_free.py
8
- @created: Thu, 20 May 2021
9
- @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
10
- @site: https://github.com/yorevs/hspylib
11
- @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
12
-
13
- Copyright·(c)·2024,·HSPyLib
14
- """
15
- from clitt.addons.widman.widget import Widget
16
- from clitt.core.icons.font_awesome.widget_icons import WidgetIcons
17
- from clitt.core.term.terminal import Terminal
18
- from concurrent import futures
19
- from hspylib.core.tools.commons import human_readable_bytes, sysout
20
- from hspylib.modules.application.exit_status import ExitStatus
21
- from hspylib.modules.application.version import Version
22
- from hspylib.modules.cli.keyboard import Keyboard
23
- from textwrap import dedent
24
- from time import sleep
25
-
26
- import re
27
-
28
-
29
- class WidgetFree(Widget):
30
- """HsPyLib Widget to Report current system memory usage"""
31
-
32
- # fmt: off
33
- WIDGET_ICON = WidgetIcons.CHIP
34
- WIDGET_NAME = "Free"
35
- VERSION = Version(0, 2, 0)
36
- TOOLTIP = "Report system memory usage."
37
- USAGE = "Usage: Free"
38
- # fmt: on
39
-
40
- def __init__(self) -> None:
41
- super().__init__(self.WIDGET_ICON, self.WIDGET_NAME, self.TOOLTIP, self.USAGE, self.VERSION)
42
- self._is_alive = True
43
- self._report_interval = 1.5
44
- self._exit_code = ExitStatus.SUCCESS
45
-
46
- def execute(self, *args) -> ExitStatus:
47
- with futures.ThreadPoolExecutor() as executor:
48
- done = False
49
- while not done and not Keyboard.kbhit():
50
- future = executor.submit(self._report_usage)
51
- done = not future.result()
52
- if not done:
53
- sleep(self._report_interval)
54
- return self._exit_code
55
-
56
- # pylint: disable=too-many-locals
57
- @staticmethod
58
- def _report_usage() -> bool:
59
- """Display the memory usage for the cycle."""
60
- ps, ec1 = Terminal.shell_exec("ps -caxm -orss,comm") # Get process info
61
- vm, ec2 = Terminal.shell_exec("vm_stat") # Grabbing memory characteristics
62
-
63
- if ec1 == ExitStatus.SUCCESS and ec2 == ExitStatus.SUCCESS and ps:
64
- process_lines = ps.split("\n") # Iterate processes
65
- sep = re.compile(" +")
66
- rss_total = 0 # kB
67
-
68
- for row in range(1, len(process_lines)):
69
- row_text = process_lines[row].strip()
70
- row_elements = sep.split(row_text)
71
- if re.match("^[0-9]+$", row_elements[0]):
72
- rss = float(row_elements[0]) * 1024
73
- else:
74
- rss = 0
75
- rss_total += rss
76
-
77
- vm_lines = vm.split("\n") # Process vm_stat
78
- sep = re.compile(": +")
79
- vm_stats = {}
80
-
81
- for row in range(1, len(vm_lines) - 2):
82
- row_text = vm_lines[row].strip()
83
- row_elements = sep.split(row_text)
84
- vm_stats[(row_elements[0])] = int(row_elements[1].strip("\\.")) * 4096
85
-
86
- wired, wu = human_readable_bytes(vm_stats["Pages wired down"])
87
- active, au = human_readable_bytes(vm_stats["Pages active"])
88
- inactive, iu = human_readable_bytes(vm_stats["Pages inactive"])
89
- free, fu = human_readable_bytes(vm_stats["Pages free"])
90
- real, ru = human_readable_bytes(rss_total) # Total memory
91
-
92
- sysout(
93
- dedent(
94
- f"""
95
- %HOM%%ED2%%MOD(0)%
96
- %ORANGE%Reporting system memory usage:%NC%
97
- {'-' * 30}
98
- %GREEN% Wired Memory: {wired:6s} {wu:2s}
99
- %GREEN% Active Memory: {active:6s} {au:2s}
100
- %GREEN%Inactive Memory: {inactive:6s} {iu:2s}
101
- %GREEN% Free Memory: {free:6s} {fu:2s}
102
- %GREEN% Real Memory: {real:6s} {ru:2s}
103
-
104
- %YELLOW%Press [Enter] to exit ..."""
105
- )
106
- )
107
-
108
- return True
109
-
110
- return False
@@ -1,246 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- @project: HsPyLib-Clitt
6
- @package: clitt.addons.widman.widgets
7
- @file: widget_punch.py
8
- @created: Thu, 20 Sep 2022
9
- @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
10
- @site: https://github.com/yorevs/hspylib
11
- @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
12
-
13
- Copyright·(c)·2024,·HSPyLib
14
- """
15
- from clitt.addons.widman.widget import Widget
16
- from clitt.addons.widman.widgets.widget_time_calc import WidgetTimeCalc
17
- from clitt.core.icons.font_awesome.widget_icons import WidgetIcons
18
- from clitt.core.term.terminal import Terminal
19
- from hspylib.core.enums.charset import Charset
20
- from hspylib.core.exception.exceptions import WidgetExecutionError
21
- from hspylib.core.tools.commons import syserr, sysout
22
- from hspylib.core.zoned_datetime import now
23
- from hspylib.modules.application.argparse.argument_parser import HSArgumentParser
24
- from hspylib.modules.application.exit_status import ExitStatus
25
- from hspylib.modules.application.version import Version
26
- from textwrap import dedent
27
- from typing import List
28
-
29
- import argparse
30
- import os
31
- import re
32
- import sys
33
-
34
-
35
- class WidgetPunch(Widget):
36
- """HsPyLib Widget to Report current system memory usage"""
37
-
38
- # fmt: off
39
- WIDGET_ICON = WidgetIcons.PUNCH
40
- WIDGET_NAME = "Punch"
41
- VERSION = Version(0, 1, 0)
42
- TOOLTIP = "!!! PUNCH THE CLOCK !!!"
43
- USAGE = dedent("""
44
- "Usage: ${FUNCNAME[0]} [options] <args>"
45
-
46
- Options: '
47
- -l : List all registered punches.'
48
- -e : Edit current punch file.'
49
- -r : Reset punches for the current week and save the previous one.'
50
- -w <week> : Report (list) all punches of specified week using the pattern: week-N.punch.'
51
-
52
- Notes: '
53
- When no arguments are provided it will !!PUNCH THE CLOCK!!.'
54
- """)
55
-
56
- HHS_DIR = os.getenv("HHS_PUNCH_FILE", os.getenv("HOME", "/"))
57
-
58
- HHS_PUNCH_FILE = os.getenv("HHS_PUNCH_FILE", f"-{HHS_DIR}/.punches")
59
-
60
- DATE_STAMP = now("%a %d-%m-%Y")
61
-
62
- TIME_STAMP = now("%H:%M")
63
-
64
- WEEK_STAMP = int(now("%V"))
65
-
66
- RE_TODAY_PUNCH_LINE = rf"({DATE_STAMP}).*"
67
-
68
- RE_PUNCH_LINE = r"^((Mon|Tue|Wed|Thu|Fri|Sat|Sun) )(([0-9]+-?)+) =>.*"
69
-
70
- MAX_PUNCHES = 7 # 7 week days. Do you work on Saturdays and Sundays ?? :~
71
-
72
- # fmt: on
73
-
74
- @staticmethod
75
- def _daily_total(daily_punches: List[str], decimal: bool = False) -> str:
76
- """Calculate the total time from the daily punches.
77
- :param daily_punches the list of daily punches.
78
- :param decimal return the total as a decimal number.
79
- """
80
- # Up to 3 pairs of timestamps: morning, afternoon and evening
81
- if (n := len(daily_punches)) > 0 and n % 2 == 0:
82
- stamps = []
83
- stamps += [daily_punches[1], "-", daily_punches[0], "+"] if n // 2 >= 1 else [] # Morning
84
- stamps += [daily_punches[3], "-", daily_punches[2], "+"] if n // 2 >= 2 else [] # Afternoon
85
- stamps += [daily_punches[5], "-", daily_punches[4]] if n // 2 >= 3 else [] # Evening
86
- h, m, _ = WidgetTimeCalc.calc_time(*stamps)
87
- m = WidgetTimeCalc.to_decimal(m) if decimal else m
88
- return f"{'%GREEN%' if h >= 8 else '%RED%'}{h:02d}{'.' if decimal else ':'}{m:02d}%NC%"
89
-
90
- return f"%RED%--{'.' if decimal else ':'}--%NC%"
91
-
92
- def __init__(self) -> None:
93
- super().__init__(self.WIDGET_ICON, self.WIDGET_NAME, self.TOOLTIP, self.USAGE, self.VERSION)
94
-
95
- self._exit_code = ExitStatus.SUCCESS
96
- self._fn = self._do_the_punch
97
- self._args = None
98
- self._today = None
99
- self._punches = []
100
- self._total_hour = self._total_min = 0
101
- self._week_num = self.WEEK_STAMP
102
-
103
- def execute(self, *args) -> ExitStatus:
104
- # Create the current week punch file if it does not yet exist.
105
- if not os.path.exists(self.HHS_PUNCH_FILE):
106
- with open(self.HHS_PUNCH_FILE, "w", encoding=Charset.UTF_8.val) as f_punch:
107
- f_punch.write(f"{now('%d-%m-%Y')} => ")
108
-
109
- ret_val = self._parse_args(*args)
110
-
111
- if not ret_val:
112
- return ExitStatus.ABORTED
113
-
114
- if self._args:
115
- if "list" == self._args.action:
116
- self._punches = self._read_punches(self.HHS_PUNCH_FILE)
117
- self._fn = self._list_punches
118
- elif "week" == self._args.action:
119
- punch_dir = os.path.dirname(self.HHS_PUNCH_FILE)
120
- self._week_num = self._args.week_num
121
- self._punches = self._read_punches(f"{punch_dir}/week-{self._week_num:02d}.punch")
122
- self._fn = self._list_punches
123
- elif "edit" == self._args.action:
124
- self._fn = self._edit_punches
125
- elif "reset" == self._args.action:
126
- self._fn = self._reset_punches
127
- else:
128
- self._punches = self._read_punches(self.HHS_PUNCH_FILE)
129
-
130
- self._fn()
131
-
132
- return ExitStatus.SUCCESS
133
-
134
- def _parse_args(self, *args) -> bool:
135
- """When arguments are passed from the command line, parse them.
136
- :param args the widget arguments
137
- """
138
-
139
- if not args:
140
- return True
141
-
142
- parser = HSArgumentParser(
143
- prog="punch",
144
- prefix_chars="+",
145
- formatter_class=argparse.RawDescriptionHelpFormatter,
146
- description="PUNCH-THE-CLOCK. This is a helper tool to aid with the timesheet.",
147
- )
148
-
149
- subparsers = parser.add_subparsers(title="action", dest="action")
150
- subparsers.add_parser("list", help="list all registered punches.")
151
- subparsers.add_parser("edit", help="edit current punch file.")
152
- subparsers.add_parser("reset", help="reset punches for the current week and save the previous one.")
153
-
154
- w_parser = subparsers.add_parser("week", help="list all punches of the specified week-num (week-N.punch).")
155
- w_parser.add_argument("week_num", type=int, default=1, help="the week number")
156
-
157
- self._args = parser.parse_args(args)
158
-
159
- return bool(self._args)
160
-
161
- def _read_punches(self, punch_file: str) -> List[str]:
162
- """Read all punches from the punch file.
163
- :param punch_file the punch file path
164
- """
165
- if not os.path.exists(punch_file):
166
- syserr(f"Punch file '{punch_file}' not found !")
167
- raise FileNotFoundError(f"Punch file '{punch_file}' not found !")
168
-
169
- with open(punch_file, "r", encoding=Charset.UTF_8.val) as f_punch:
170
- all_punches = list(
171
- map(self._set_today, filter(lambda l: re.match(self.RE_PUNCH_LINE, l), f_punch.readlines()))
172
- )
173
- if len(all_punches) > self.MAX_PUNCHES:
174
- sysout(f"%RED%Punch file contains more than {self.MAX_PUNCHES} punch lines !%NC%")
175
- sys.exit(int(str(ExitStatus.FAILED.value)))
176
- return all_punches
177
-
178
- def _is_today(self, punch_line: str) -> bool:
179
- """Whether the punch line refer to today's date.
180
- :param punch_line a line read from the punch file (following the punch syntax).
181
- """
182
- return bool(re.match(self.RE_TODAY_PUNCH_LINE, punch_line))
183
-
184
- def _set_today(self, punch_line: str) -> str:
185
- """Set the today's date if the line represents today.
186
- :param punch_line a line read from the punch file (following the punch syntax).
187
- """
188
- if not self._today and self._is_today(punch_line):
189
- self._today = punch_line
190
- return punch_line
191
-
192
- def _do_the_punch(self) -> None:
193
- """Punch the clock."""
194
- with open(self.HHS_PUNCH_FILE, "w", encoding=Charset.UTF_8.val) as f_punch:
195
- if not self._today: # Write the first punch of the day
196
- self._today = f"{self.DATE_STAMP} => {self.TIME_STAMP}"
197
- self._punches.append(self._today)
198
- elif self._today.count(":") < 6: # Only allowed 3 groups of 3 pairs of punches
199
- pat = rf"({self.DATE_STAMP}) => (.*)"
200
- if mat := re.match(pat, self._today):
201
- self._today = re.sub(pat, f"{mat.group(1)} => {mat.group(2)} {self.TIME_STAMP} ", self._today)
202
- else:
203
- raise WidgetExecutionError("Invalid punch file!")
204
- self._punches[-1] = self._today
205
- list(map(f_punch.write, [f"{punch.strip()}\n" for punch in self._punches]))
206
- sysout(f"{re.sub(self.DATE_STAMP, '%GREEN%Today%NC%', self._today)} ")
207
-
208
- def _list_punches(self) -> None:
209
- """List all punches from the punch file."""
210
- total = 0, 0
211
- total_dec = 0, 0
212
- sysout(f"\n%WHITE%Week-{self._week_num:02d} Punches%NC%")
213
- sysout("-" * 82)
214
- for punch_line in self._punches:
215
- daily_punches = punch_line[17:].strip().split()
216
- n = len(daily_punches)
217
- padding = "." * (35 if n == 0 else (36 - n * 6))
218
- line_color = "%BLUE%" if self._is_today(punch_line) else ""
219
- sysout(f"{line_color}{punch_line[:17]} {' '.join(daily_punches) + (' ' if n % 2 != 0 else '')}", end="")
220
- daily_total = self._daily_total(daily_punches)
221
- daily_total_dec = self._daily_total(daily_punches, decimal=True)
222
- if n > 0 and n % 2 == 0:
223
- sysout(f" {padding} : Subtotal = {daily_total} -> {daily_total_dec}%NC%")
224
- total = WidgetTimeCalc.calc_time(f"{total[0]}:{total[1]}", "+", daily_total[-9:-4])
225
- total_dec = total[0], WidgetTimeCalc.to_decimal(total[1])
226
- else:
227
- sysout(f"{daily_total}%NC%")
228
- sysout("-" * 82)
229
- bh, bm, _ = WidgetTimeCalc.calc_time(f"{total[0]}:{total[1]}", "-", "40:00")
230
- balance = f"{'%BLUE%' if bh >= 0 else '%RED%'}{bh:02d}:{bm:02d}%NC%"
231
- totals = f"{total[0]:02d}:{total[1]:02d} -> {total_dec[0]:02d}.{total_dec[1]:02d}"
232
- sysout(f"%WHITE%Total: ({totals}) Balance: {balance}\n")
233
-
234
- def _edit_punches(self) -> None:
235
- """Open the default system editor to edit punches."""
236
- Terminal.open(self.HHS_PUNCH_FILE)
237
-
238
- def _reset_punches(self) -> None:
239
- """Rename the punch file as a weekly punch file and reset current punch file."""
240
- punch_dir = os.path.dirname(self.HHS_PUNCH_FILE)
241
- new_name = f"{punch_dir}/week-{self._week_num:02d}.punch"
242
- if os.path.exists(new_name):
243
- sysout(f"%RED%Punch file '{new_name}' already exists!")
244
- else:
245
- os.rename(self.HHS_PUNCH_FILE, new_name)
246
- sysout(f"%YELLOW%Punch file {self.HHS_PUNCH_FILE} renamed to {new_name}")
@@ -1,272 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- @project: HsPyLib-Clitt
6
- @package: clitt.addons.widman.widgets
7
- @file: widget_send_msg.py
8
- @created: Thu, 26 Aug 2017
9
- @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
10
- @site: https://github.com/yorevs/hspylib
11
- @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
12
-
13
- Copyright·(c)·2024,·HSPyLib
14
- """
15
- from clitt.addons.widman.widget import Widget
16
- from clitt.core.icons.font_awesome.widget_icons import WidgetIcons
17
- from clitt.core.tui.minput.input_validator import InputValidator
18
- from clitt.core.tui.minput.minput import MenuInput, minput
19
- from hspylib.core.exception.exceptions import WidgetExecutionError
20
- from hspylib.core.tools.commons import hook_exit_signals, sysout
21
- from hspylib.modules.application.argparse.argument_parser import HSArgumentParser
22
- from hspylib.modules.application.exit_status import ExitStatus
23
- from hspylib.modules.application.version import Version
24
- from hspylib.modules.cli.keyboard import Keyboard
25
- from textwrap import dedent
26
- from time import sleep
27
-
28
- import os
29
- import socket
30
- import threading
31
-
32
-
33
- class WidgetSendMsg(Widget):
34
- """HsPyLib Widget to send TCP/UDP messages (multi-threaded)"""
35
-
36
- # fmt: off
37
- MAX_THREADS = 1000
38
- NET_TYPE_UDP = "UDP"
39
- NET_TYPE_TCP = "TCP"
40
-
41
- WIDGET_ICON = WidgetIcons.NETWORK
42
- WIDGET_NAME = "SendMsg"
43
- VERSION = Version(0, 3, 0)
44
- TOOLTIP = "Multi-Threaded IP Message Sender. Sends TCP/UDP messages"
45
- USAGE = dedent(f"""Usage: SendMsg [options]
46
-
47
- Options:
48
- +n, ++net_type <network_type> : The network type to be used. Either udp or tcp ( default is tcp ).
49
- +p, ++port <port_num> : The port number [1-65535] ( default is 12345).
50
- +a, ++address <host_address> : The address of the datagram receiver ( default is 127.0.0.1 ).
51
- +k, ++packets <num_packets> : The number of max datagrams to be send. If zero is specified, then the app
52
- is going to send indefinitely ( default is 100 ).
53
- +i, ++interval <interval_MS> : The interval in seconds between each datagram ( default is 1 Second ).
54
- +t, ++threads <threads_num> : Number of threads [1-{MAX_THREADS}] to be opened to send simultaneously
55
- ( default is 1 ).
56
- +m, ++message <message/filename> : The message to be sent. If the message matches a filename, then the file
57
- contents sent instead.
58
-
59
- E.g:. send-msg.py +n tcp +m "Hello" +p 12345 +a 0.0.0.0 +k 100 +i 500 +t 2
60
- """)
61
- # fmt: on
62
-
63
- def __init__(self) -> None:
64
- super().__init__(self.WIDGET_ICON, self.WIDGET_NAME, self.TOOLTIP, self.USAGE, self.VERSION)
65
- self.is_alive = True
66
- self.net_type = None
67
- self.host = None
68
- self.packets = None
69
- self.interval = None
70
- self.threads = None
71
- self.message = None
72
- self._args = None
73
- self.socket = None
74
- self.counter = 1
75
-
76
- def execute(self, *args) -> ExitStatus:
77
- hook_exit_signals(self.cleanup)
78
- if args and args[0] in ["-h", "--help"]:
79
- sysout(self.usage())
80
- return ExitStatus.SUCCESS
81
- if args and args[0] in ["-v", "--version"]:
82
- sysout(self.version())
83
- return ExitStatus.SUCCESS
84
- if args and not self._parse_args(*args):
85
- return ExitStatus.ERROR
86
- if not args and not self._prompt():
87
- return ExitStatus.ERROR
88
- if not args and not self._args:
89
- return ExitStatus.ERROR
90
-
91
- self.net_type = self._args.net_type or self.NET_TYPE_TCP
92
- self.host = (self._args.address or "127.0.0.1", self._args.port or 12345)
93
- self.packets = self._args.packets or 100
94
- self.interval = self._args.interval or 1
95
- self.threads = self._args.threads or 1
96
-
97
- if self._args.message and os.path.isfile(self._args.message):
98
- file_size = os.stat(self._args.message).st_size
99
- sysout(f"Reading contents from file: {self._args.message} ({file_size}) [Bs] instead")
100
- with open(self._args.message, "r", encoding="utf-8") as f_msg:
101
- self.message = f_msg.read()
102
- else:
103
- self.message = self._args.message or f"This is a {self._args.net_type} test %(count)"
104
-
105
- self._start_send()
106
-
107
- Keyboard.wait_keystroke()
108
-
109
- return ExitStatus.SUCCESS
110
-
111
- def cleanup(self) -> None:
112
- """Stops workers and close socket connection."""
113
- sysout("Terminating threads%NC%")
114
- self.is_alive = False
115
- if self.net_type == self.NET_TYPE_TCP:
116
- sysout("Closing TCP connection")
117
- self.socket.close()
118
-
119
- def _prompt(self) -> bool:
120
- """When no input is provided (e.g:. when executed from dashboard). Prompt the user for the info."""
121
- # fmt: off
122
- form_fields = MenuInput.builder() \
123
- .field() \
124
- .label('Net Type') \
125
- .itype('select') \
126
- .value(f"{self.NET_TYPE_TCP}|{self.NET_TYPE_UDP}") \
127
- .build() \
128
- .field() \
129
- .label('Address') \
130
- .validator(InputValidator.custom(r"^([0-9]{0,3}){0,1}(\.[0-9]{0,3}){0,3}$")) \
131
- .value('127.0.0.1') \
132
- .build() \
133
- .field() \
134
- .label('Port') \
135
- .validator(InputValidator.numbers()) \
136
- .min_max_length(2, 5) \
137
- .value(12345) \
138
- .build() \
139
- .field() \
140
- .label('Packets') \
141
- .validator(InputValidator.numbers()) \
142
- .min_max_length(1, 4) \
143
- .value(100) \
144
- .build() \
145
- .field() \
146
- .label('Interval') \
147
- .validator(InputValidator.numbers()) \
148
- .min_max_length(1, 4) \
149
- .value(1) \
150
- .build() \
151
- .field() \
152
- .label('Threads') \
153
- .validator(InputValidator.numbers()) \
154
- .min_max_length(1, 4) \
155
- .value(1) \
156
- .build() \
157
- .field() \
158
- .label('Message') \
159
- .validator(InputValidator.anything()) \
160
- .min_max_length(1, 40) \
161
- .value('This is a test') \
162
- .build() \
163
- .build()
164
- # fmt: on
165
- self._args = minput(form_fields)
166
- sysout("%HOM%%ED2%%MOD(0)%", end="")
167
-
168
- return len(self._args) > 1 if self._args else False
169
-
170
- def _parse_args(self, *args):
171
- """When arguments are passed from the command line, parse them.
172
- :param args the widget arguments
173
- """
174
- parser = HSArgumentParser(
175
- prog="sendmsg", prefix_chars="+", description="Sends TCP/UDP messages (multi-threaded)"
176
- )
177
- # fmt: off
178
- parser.add_argument(
179
- "+n", "++net-type", action="store", choices=["udp", "tcp"],
180
- type=str, default="tcp", required=False,
181
- help="The network type to be used. Either udp or tcp ( default is tcp )",
182
- )
183
- parser.add_argument(
184
- "+a", "++address", action="store",
185
- type=str, default="127.0.0.1", required=False,
186
- help="The address of the datagram receiver ( default is 127.0.0.1 )",
187
- )
188
- parser.add_argument(
189
- "+p", "++port", action="store",
190
- type=int, default=12345, required=False,
191
- help="The port number [1-65535] ( default is 12345)",
192
- )
193
- parser.add_argument(
194
- "+k", "++packets", action="store",
195
- type=int, default=100, required=False,
196
- help="The number of max datagrams to be send. If zero is specified, then the app "
197
- "is going to send indefinitely ( default is 100 ).",
198
- )
199
- parser.add_argument(
200
- "+i", "++interval", action="store",
201
- type=float, default=1, required=False,
202
- help="The interval in seconds between each datagram ( default is 1 Second )",
203
- )
204
- parser.add_argument(
205
- "+t", "++threads", action="store",
206
- type=int, default=1, required=False,
207
- help=f"Number of threads [1-{self.MAX_THREADS}] to be opened to send simultaneously ( default is 1 )",
208
- )
209
- parser.add_argument(
210
- "+m", "++message", action="store",
211
- type=str, required=False,
212
- help="The message to be sent. If the message matches a filename, then the file contents sent instead",
213
- )
214
- # fmt: on
215
- self._args = parser.parse_args(*args)
216
-
217
- return bool(self._args)
218
-
219
- def _init_sockets(self) -> None:
220
- """Initialize sockets."""
221
- if self.net_type == self.NET_TYPE_UDP:
222
- self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
223
- else:
224
- self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
225
- try:
226
- self.socket.connect(self.host)
227
- sysout(f"Successfully connected to {self.host}")
228
- except socket.error as err:
229
- raise WidgetExecutionError("Unable to initialize sockets") from err
230
-
231
- def _start_send(self) -> None:
232
- """Start sending packets."""
233
- thread_relief = 0.05
234
- self._init_sockets()
235
- sysout(
236
- f"\n%ORANGE%Start sending {self.packets} "
237
- f"{self.net_type.upper()} packet(s) "
238
- f"every {self.interval} second(s) to {self.host} using {self.threads} thread(s)"
239
- )
240
- threads_num = threading.active_count()
241
-
242
- for thread_num in range(1, int(self.threads) + 1):
243
- tr = threading.Thread(target=self._send_packet, args=(thread_num,))
244
- tr.daemon = True
245
- tr.start()
246
- sleep(thread_relief)
247
-
248
- while self.is_alive and threading.active_count() > threads_num:
249
- sleep(2 * thread_relief)
250
-
251
- def _send_packet(self, thread_num: int) -> None:
252
- """Send a packet."""
253
- lock = threading.Lock()
254
-
255
- while self.is_alive and self.packets <= 0 or self.counter <= self.packets:
256
- message = self.message.replace("%(count)", str(self.counter))
257
- length = len(message)
258
- sysout(
259
- f"%BLUE%[Thread-{thread_num:d}] "
260
- f'%GREEN%Sending "{message:s}" ({length:d}) bytes, '
261
- f"Pkt = {self.counter:>d}/{self.packets:>d} %NC%..."
262
- )
263
- if self.net_type == self.NET_TYPE_UDP:
264
- self.socket.sendto(message.encode(), self.host)
265
- else:
266
- try:
267
- self.socket.sendall((message + "\n").encode())
268
- with lock:
269
- self.counter += 1
270
- except socket.error as err:
271
- raise WidgetExecutionError("Unable to send packet") from err
272
- sleep(self.interval)