pomera-ai-commander 0.1.0 → 1.2.1

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 (191) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +105 -680
  3. package/bin/pomera-ai-commander.js +62 -62
  4. package/core/__init__.py +65 -65
  5. package/core/app_context.py +482 -482
  6. package/core/async_text_processor.py +421 -421
  7. package/core/backup_manager.py +655 -655
  8. package/core/backup_recovery_manager.py +1033 -1033
  9. package/core/content_hash_cache.py +508 -508
  10. package/core/context_menu.py +313 -313
  11. package/core/data_validator.py +1066 -1066
  12. package/core/database_connection_manager.py +744 -744
  13. package/core/database_curl_settings_manager.py +608 -608
  14. package/core/database_promera_ai_settings_manager.py +446 -446
  15. package/core/database_schema.py +411 -411
  16. package/core/database_schema_manager.py +395 -395
  17. package/core/database_settings_manager.py +1507 -1507
  18. package/core/database_settings_manager_interface.py +456 -456
  19. package/core/dialog_manager.py +734 -734
  20. package/core/efficient_line_numbers.py +510 -510
  21. package/core/error_handler.py +746 -746
  22. package/core/error_service.py +431 -431
  23. package/core/event_consolidator.py +511 -511
  24. package/core/mcp/__init__.py +43 -43
  25. package/core/mcp/protocol.py +288 -288
  26. package/core/mcp/schema.py +251 -251
  27. package/core/mcp/server_stdio.py +299 -299
  28. package/core/mcp/tool_registry.py +2372 -2345
  29. package/core/memory_efficient_text_widget.py +711 -711
  30. package/core/migration_manager.py +914 -914
  31. package/core/migration_test_suite.py +1085 -1085
  32. package/core/migration_validator.py +1143 -1143
  33. package/core/optimized_find_replace.py +714 -714
  34. package/core/optimized_pattern_engine.py +424 -424
  35. package/core/optimized_search_highlighter.py +552 -552
  36. package/core/performance_monitor.py +674 -674
  37. package/core/persistence_manager.py +712 -712
  38. package/core/progressive_stats_calculator.py +632 -632
  39. package/core/regex_pattern_cache.py +529 -529
  40. package/core/regex_pattern_library.py +350 -350
  41. package/core/search_operation_manager.py +434 -434
  42. package/core/settings_defaults_registry.py +1087 -1087
  43. package/core/settings_integrity_validator.py +1111 -1111
  44. package/core/settings_serializer.py +557 -557
  45. package/core/settings_validator.py +1823 -1823
  46. package/core/smart_stats_calculator.py +709 -709
  47. package/core/statistics_update_manager.py +619 -619
  48. package/core/stats_config_manager.py +858 -858
  49. package/core/streaming_text_handler.py +723 -723
  50. package/core/task_scheduler.py +596 -596
  51. package/core/update_pattern_library.py +168 -168
  52. package/core/visibility_monitor.py +596 -596
  53. package/core/widget_cache.py +498 -498
  54. package/mcp.json +51 -61
  55. package/package.json +61 -57
  56. package/pomera.py +7482 -7482
  57. package/pomera_mcp_server.py +183 -144
  58. package/requirements.txt +32 -0
  59. package/tools/__init__.py +4 -4
  60. package/tools/ai_tools.py +2891 -2891
  61. package/tools/ascii_art_generator.py +352 -352
  62. package/tools/base64_tools.py +183 -183
  63. package/tools/base_tool.py +511 -511
  64. package/tools/case_tool.py +308 -308
  65. package/tools/column_tools.py +395 -395
  66. package/tools/cron_tool.py +884 -884
  67. package/tools/curl_history.py +600 -600
  68. package/tools/curl_processor.py +1207 -1207
  69. package/tools/curl_settings.py +502 -502
  70. package/tools/curl_tool.py +5467 -5467
  71. package/tools/diff_viewer.py +1071 -1071
  72. package/tools/email_extraction_tool.py +248 -248
  73. package/tools/email_header_analyzer.py +425 -425
  74. package/tools/extraction_tools.py +250 -250
  75. package/tools/find_replace.py +1750 -1750
  76. package/tools/folder_file_reporter.py +1463 -1463
  77. package/tools/folder_file_reporter_adapter.py +480 -480
  78. package/tools/generator_tools.py +1216 -1216
  79. package/tools/hash_generator.py +255 -255
  80. package/tools/html_tool.py +656 -656
  81. package/tools/jsonxml_tool.py +729 -729
  82. package/tools/line_tools.py +419 -419
  83. package/tools/markdown_tools.py +561 -561
  84. package/tools/mcp_widget.py +1417 -1417
  85. package/tools/notes_widget.py +973 -973
  86. package/tools/number_base_converter.py +372 -372
  87. package/tools/regex_extractor.py +571 -571
  88. package/tools/slug_generator.py +310 -310
  89. package/tools/sorter_tools.py +458 -458
  90. package/tools/string_escape_tool.py +392 -392
  91. package/tools/text_statistics_tool.py +365 -365
  92. package/tools/text_wrapper.py +430 -430
  93. package/tools/timestamp_converter.py +421 -421
  94. package/tools/tool_loader.py +710 -710
  95. package/tools/translator_tools.py +522 -522
  96. package/tools/url_link_extractor.py +261 -261
  97. package/tools/url_parser.py +204 -204
  98. package/tools/whitespace_tools.py +355 -355
  99. package/tools/word_frequency_counter.py +146 -146
  100. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  101. package/core/__pycache__/app_context.cpython-313.pyc +0 -0
  102. package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
  103. package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
  104. package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
  105. package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
  106. package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
  107. package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
  108. package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
  109. package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
  110. package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
  111. package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
  112. package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
  113. package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
  114. package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
  115. package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
  116. package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
  117. package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
  118. package/core/__pycache__/error_service.cpython-313.pyc +0 -0
  119. package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
  120. package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
  121. package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
  122. package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
  123. package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
  124. package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
  125. package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
  126. package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
  127. package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
  128. package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
  129. package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
  130. package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
  131. package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
  132. package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
  133. package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
  134. package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
  135. package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
  136. package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
  137. package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
  138. package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
  139. package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
  140. package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
  141. package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
  142. package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
  143. package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
  144. package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  145. package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
  146. package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
  147. package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
  148. package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
  149. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  150. package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
  151. package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
  152. package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
  153. package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
  154. package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
  155. package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
  156. package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
  157. package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
  158. package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
  159. package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
  160. package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
  161. package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
  162. package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
  163. package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
  164. package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
  165. package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
  166. package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
  167. package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
  168. package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
  169. package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
  170. package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
  171. package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
  172. package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
  173. package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
  174. package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
  175. package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
  176. package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
  177. package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
  178. package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
  179. package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
  180. package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
  181. package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
  182. package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
  183. package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
  184. package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
  185. package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
  186. package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
  187. package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
  188. package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
  189. package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
  190. package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
  191. package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
@@ -1,422 +1,422 @@
1
- """
2
- Timestamp Converter Module - Date/time conversion utilities
3
-
4
- This module provides timestamp conversion functionality
5
- for the Pomera AI Commander application.
6
-
7
- Features:
8
- - Unix timestamp to human-readable date
9
- - Human-readable date to Unix timestamp
10
- - Multiple date format options
11
- - Relative time display
12
- """
13
-
14
- import tkinter as tk
15
- from tkinter import ttk
16
- from datetime import datetime, timezone
17
- import time
18
- import re
19
-
20
-
21
- class TimestampConverterProcessor:
22
- """Timestamp converter processor."""
23
-
24
- DATE_FORMATS = {
25
- "iso": "%Y-%m-%dT%H:%M:%S",
26
- "iso_date": "%Y-%m-%d",
27
- "us": "%m/%d/%Y %I:%M:%S %p",
28
- "us_date": "%m/%d/%Y",
29
- "eu": "%d/%m/%Y %H:%M:%S",
30
- "eu_date": "%d/%m/%Y",
31
- "long": "%B %d, %Y %H:%M:%S",
32
- "short": "%b %d, %Y %H:%M",
33
- "rfc2822": "%a, %d %b %Y %H:%M:%S",
34
- "custom": None
35
- }
36
-
37
- @staticmethod
38
- def unix_to_datetime(timestamp, use_utc=False):
39
- """Convert Unix timestamp to datetime object."""
40
- try:
41
- ts = float(timestamp)
42
- # Handle milliseconds
43
- if ts > 1e12:
44
- ts = ts / 1000
45
-
46
- if use_utc:
47
- return datetime.fromtimestamp(ts, tz=timezone.utc)
48
- else:
49
- return datetime.fromtimestamp(ts)
50
- except (ValueError, OSError) as e:
51
- return None
52
-
53
- @staticmethod
54
- def datetime_to_unix(dt):
55
- """Convert datetime object to Unix timestamp."""
56
- return int(dt.timestamp())
57
-
58
- @staticmethod
59
- def format_datetime(dt, format_name="iso", custom_format=None):
60
- """Format datetime using specified format."""
61
- if format_name == "custom" and custom_format:
62
- fmt = custom_format
63
- else:
64
- fmt = TimestampConverterProcessor.DATE_FORMATS.get(format_name, "%Y-%m-%d %H:%M:%S")
65
-
66
- try:
67
- return dt.strftime(fmt)
68
- except Exception:
69
- return str(dt)
70
-
71
- @staticmethod
72
- def parse_datetime(text, format_name="iso", custom_format=None):
73
- """Parse datetime from string."""
74
- text = text.strip()
75
-
76
- # Try to detect Unix timestamp
77
- if re.match(r'^\d{10,13}$', text):
78
- ts = float(text)
79
- if ts > 1e12:
80
- ts = ts / 1000
81
- return datetime.fromtimestamp(ts)
82
-
83
- # Try specified format
84
- if format_name == "custom" and custom_format:
85
- fmt = custom_format
86
- else:
87
- fmt = TimestampConverterProcessor.DATE_FORMATS.get(format_name)
88
-
89
- if fmt:
90
- try:
91
- return datetime.strptime(text, fmt)
92
- except ValueError:
93
- pass
94
-
95
- # Try common formats
96
- common_formats = [
97
- "%Y-%m-%dT%H:%M:%S",
98
- "%Y-%m-%d %H:%M:%S",
99
- "%Y-%m-%d",
100
- "%m/%d/%Y %H:%M:%S",
101
- "%m/%d/%Y",
102
- "%d/%m/%Y %H:%M:%S",
103
- "%d/%m/%Y",
104
- "%B %d, %Y",
105
- "%b %d, %Y",
106
- ]
107
-
108
- for fmt in common_formats:
109
- try:
110
- return datetime.strptime(text, fmt)
111
- except ValueError:
112
- continue
113
-
114
- return None
115
-
116
- @staticmethod
117
- def relative_time(dt):
118
- """Get relative time string (e.g., '2 hours ago')."""
119
- now = datetime.now()
120
- diff = now - dt
121
-
122
- seconds = diff.total_seconds()
123
-
124
- if seconds < 0:
125
- # Future
126
- seconds = abs(seconds)
127
- suffix = "from now"
128
- else:
129
- suffix = "ago"
130
-
131
- if seconds < 60:
132
- return f"{int(seconds)} seconds {suffix}"
133
- elif seconds < 3600:
134
- minutes = int(seconds / 60)
135
- return f"{minutes} minute{'s' if minutes != 1 else ''} {suffix}"
136
- elif seconds < 86400:
137
- hours = int(seconds / 3600)
138
- return f"{hours} hour{'s' if hours != 1 else ''} {suffix}"
139
- elif seconds < 2592000: # 30 days
140
- days = int(seconds / 86400)
141
- return f"{days} day{'s' if days != 1 else ''} {suffix}"
142
- elif seconds < 31536000: # 365 days
143
- months = int(seconds / 2592000)
144
- return f"{months} month{'s' if months != 1 else ''} {suffix}"
145
- else:
146
- years = int(seconds / 31536000)
147
- return f"{years} year{'s' if years != 1 else ''} {suffix}"
148
-
149
- @staticmethod
150
- def convert_timestamp(text, input_format="unix", output_format="iso",
151
- use_utc=False, custom_format=None, show_relative=False):
152
- """Convert timestamp between formats."""
153
- text = text.strip()
154
-
155
- if not text:
156
- return ""
157
-
158
- # Parse input
159
- if input_format == "unix":
160
- dt = TimestampConverterProcessor.unix_to_datetime(text, use_utc)
161
- else:
162
- dt = TimestampConverterProcessor.parse_datetime(text, input_format, custom_format)
163
-
164
- if dt is None:
165
- return f"Error: Could not parse '{text}'"
166
-
167
- # Format output
168
- if output_format == "unix":
169
- result = str(TimestampConverterProcessor.datetime_to_unix(dt))
170
- else:
171
- result = TimestampConverterProcessor.format_datetime(dt, output_format, custom_format)
172
-
173
- if show_relative:
174
- relative = TimestampConverterProcessor.relative_time(dt)
175
- result += f" ({relative})"
176
-
177
- return result
178
-
179
- @staticmethod
180
- def convert_batch(text, input_format="unix", output_format="iso",
181
- use_utc=False, custom_format=None, show_relative=False):
182
- """Convert multiple timestamps."""
183
- lines = text.strip().split('\n')
184
- results = []
185
-
186
- for line in lines:
187
- line = line.strip()
188
- if line:
189
- result = TimestampConverterProcessor.convert_timestamp(
190
- line, input_format, output_format, use_utc, custom_format, show_relative
191
- )
192
- results.append(result)
193
- else:
194
- results.append('')
195
-
196
- return '\n'.join(results)
197
-
198
- @staticmethod
199
- def get_current_timestamp():
200
- """Get current Unix timestamp."""
201
- return str(int(time.time()))
202
-
203
- @staticmethod
204
- def get_current_datetime(format_name="iso", custom_format=None):
205
- """Get current datetime in specified format."""
206
- return TimestampConverterProcessor.format_datetime(
207
- datetime.now(), format_name, custom_format
208
- )
209
-
210
-
211
- class TimestampConverterWidget(ttk.Frame):
212
- """Widget for timestamp converter tool."""
213
-
214
- def __init__(self, parent, app):
215
- super().__init__(parent)
216
- self.app = app
217
- self.processor = TimestampConverterProcessor()
218
-
219
- self.input_format = tk.StringVar(value="unix")
220
- self.output_format = tk.StringVar(value="iso")
221
- self.use_utc = tk.BooleanVar(value=False)
222
- self.custom_format = tk.StringVar(value="%Y-%m-%d %H:%M:%S")
223
- self.show_relative = tk.BooleanVar(value=False)
224
-
225
- self.create_widgets()
226
- self.load_settings()
227
-
228
- def create_widgets(self):
229
- """Creates the widget interface."""
230
- # Input format
231
- input_frame = ttk.LabelFrame(self, text="Input Format", padding=10)
232
- input_frame.pack(fill=tk.X, padx=5, pady=5)
233
-
234
- formats = [("Unix Timestamp", "unix"), ("ISO 8601", "iso"),
235
- ("US Date", "us"), ("EU Date", "eu"), ("Auto-detect", "auto")]
236
-
237
- for i, (text, value) in enumerate(formats):
238
- ttk.Radiobutton(input_frame, text=text,
239
- variable=self.input_format, value=value,
240
- command=self.on_setting_change).grid(row=i//3, column=i%3, sticky=tk.W, padx=5)
241
-
242
- # Output format
243
- output_frame = ttk.LabelFrame(self, text="Output Format", padding=10)
244
- output_frame.pack(fill=tk.X, padx=5, pady=5)
245
-
246
- output_formats = [
247
- ("Unix Timestamp", "unix"), ("ISO 8601", "iso"), ("ISO Date", "iso_date"),
248
- ("US Format", "us"), ("EU Format", "eu"), ("Long", "long"),
249
- ("Short", "short"), ("RFC 2822", "rfc2822"), ("Custom", "custom")
250
- ]
251
-
252
- for i, (text, value) in enumerate(output_formats):
253
- ttk.Radiobutton(output_frame, text=text,
254
- variable=self.output_format, value=value,
255
- command=self.on_setting_change).grid(row=i//3, column=i%3, sticky=tk.W, padx=5)
256
-
257
- # Custom format
258
- custom_frame = ttk.Frame(self)
259
- custom_frame.pack(fill=tk.X, padx=5, pady=5)
260
-
261
- ttk.Label(custom_frame, text="Custom Format:").pack(side=tk.LEFT)
262
- ttk.Entry(custom_frame, textvariable=self.custom_format, width=25).pack(side=tk.LEFT, padx=5)
263
- ttk.Label(custom_frame, text="(e.g., %Y-%m-%d)", font=('TkDefaultFont', 8)).pack(side=tk.LEFT)
264
-
265
- # Options
266
- options_frame = ttk.Frame(self)
267
- options_frame.pack(fill=tk.X, padx=5, pady=5)
268
-
269
- ttk.Checkbutton(options_frame, text="Use UTC",
270
- variable=self.use_utc,
271
- command=self.on_setting_change).pack(side=tk.LEFT, padx=10)
272
- ttk.Checkbutton(options_frame, text="Show Relative Time",
273
- variable=self.show_relative,
274
- command=self.on_setting_change).pack(side=tk.LEFT, padx=10)
275
-
276
- # Buttons
277
- buttons_frame = ttk.Frame(self)
278
- buttons_frame.pack(fill=tk.X, padx=5, pady=10)
279
-
280
- ttk.Button(buttons_frame, text="Convert",
281
- command=self.convert).pack(side=tk.LEFT, padx=5)
282
- ttk.Button(buttons_frame, text="Insert Current Time",
283
- command=self.insert_current).pack(side=tk.LEFT, padx=5)
284
-
285
- def load_settings(self):
286
- """Load settings from the application."""
287
- settings = self.app.settings.get("tool_settings", {}).get("Timestamp Converter", {})
288
-
289
- self.input_format.set(settings.get("input_format", "unix"))
290
- self.output_format.set(settings.get("output_format", "iso"))
291
- self.use_utc.set(settings.get("use_utc", False))
292
- self.custom_format.set(settings.get("custom_format", "%Y-%m-%d %H:%M:%S"))
293
- self.show_relative.set(settings.get("show_relative", False))
294
-
295
- def save_settings(self):
296
- """Save current settings to the application."""
297
- if "Timestamp Converter" not in self.app.settings["tool_settings"]:
298
- self.app.settings["tool_settings"]["Timestamp Converter"] = {}
299
-
300
- self.app.settings["tool_settings"]["Timestamp Converter"].update({
301
- "input_format": self.input_format.get(),
302
- "output_format": self.output_format.get(),
303
- "use_utc": self.use_utc.get(),
304
- "custom_format": self.custom_format.get(),
305
- "show_relative": self.show_relative.get()
306
- })
307
-
308
- self.app.save_settings()
309
-
310
- def on_setting_change(self, *args):
311
- """Handle setting changes."""
312
- self.save_settings()
313
-
314
- def convert(self):
315
- """Convert timestamps."""
316
- active_input_tab = self.app.input_tabs[self.app.input_notebook.index(self.app.input_notebook.select())]
317
- input_text = active_input_tab.text.get("1.0", tk.END).rstrip('\n')
318
-
319
- if not input_text.strip():
320
- return
321
-
322
- result = TimestampConverterProcessor.convert_batch(
323
- input_text,
324
- self.input_format.get(),
325
- self.output_format.get(),
326
- self.use_utc.get(),
327
- self.custom_format.get(),
328
- self.show_relative.get()
329
- )
330
-
331
- active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
332
- active_output_tab.text.config(state="normal")
333
- active_output_tab.text.delete("1.0", tk.END)
334
- active_output_tab.text.insert("1.0", result)
335
- active_output_tab.text.config(state="disabled")
336
-
337
- self.app.update_all_stats()
338
-
339
- def insert_current(self):
340
- """Insert current timestamp."""
341
- if self.output_format.get() == "unix":
342
- result = TimestampConverterProcessor.get_current_timestamp()
343
- else:
344
- result = TimestampConverterProcessor.get_current_datetime(
345
- self.output_format.get(), self.custom_format.get()
346
- )
347
-
348
- active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
349
- active_output_tab.text.config(state="normal")
350
- active_output_tab.text.delete("1.0", tk.END)
351
- active_output_tab.text.insert("1.0", result)
352
- active_output_tab.text.config(state="disabled")
353
-
354
- self.app.update_all_stats()
355
-
356
-
357
- class TimestampConverter:
358
- """Main class for Timestamp Converter integration."""
359
-
360
- def __init__(self):
361
- self.processor = TimestampConverterProcessor()
362
-
363
- def create_widget(self, parent, app):
364
- """Create and return the Timestamp Converter widget."""
365
- return TimestampConverterWidget(parent, app)
366
-
367
- def get_default_settings(self):
368
- """Return default settings for Timestamp Converter."""
369
- return {
370
- "input_format": "unix",
371
- "output_format": "iso",
372
- "use_utc": False,
373
- "custom_format": "%Y-%m-%d %H:%M:%S",
374
- "show_relative": False
375
- }
376
-
377
-
378
- # BaseTool-compatible wrapper
379
- try:
380
- from tools.base_tool import ToolWithOptions
381
- from typing import Dict, Any
382
-
383
- class TimestampConverterV2(ToolWithOptions):
384
- """
385
- BaseTool-compatible version of TimestampConverter.
386
- """
387
-
388
- TOOL_NAME = "Timestamp Converter"
389
- TOOL_DESCRIPTION = "Convert between Unix timestamps and human-readable dates"
390
- TOOL_VERSION = "2.0.0"
391
-
392
- OPTIONS = [
393
- ("Unix to ISO", "unix_to_iso"),
394
- ("Unix to US Format", "unix_to_us"),
395
- ("Unix to EU Format", "unix_to_eu"),
396
- ("Date to Unix", "date_to_unix"),
397
- ("Current Time", "now"),
398
- ]
399
- OPTIONS_LABEL = "Conversion"
400
- USE_DROPDOWN = True
401
- DEFAULT_OPTION = "unix_to_iso"
402
-
403
- def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
404
- """Process text using the specified conversion."""
405
- mode = settings.get("mode", "unix_to_iso")
406
-
407
- if mode == "now":
408
- import time
409
- return str(int(time.time()))
410
- elif mode == "unix_to_iso":
411
- return TimestampConverterProcessor.convert_batch(input_text, "unix", "iso")
412
- elif mode == "unix_to_us":
413
- return TimestampConverterProcessor.convert_batch(input_text, "unix", "us")
414
- elif mode == "unix_to_eu":
415
- return TimestampConverterProcessor.convert_batch(input_text, "unix", "eu")
416
- elif mode == "date_to_unix":
417
- return TimestampConverterProcessor.convert_batch(input_text, "auto", "unix")
418
- else:
419
- return input_text
420
-
421
- except ImportError:
1
+ """
2
+ Timestamp Converter Module - Date/time conversion utilities
3
+
4
+ This module provides timestamp conversion functionality
5
+ for the Pomera AI Commander application.
6
+
7
+ Features:
8
+ - Unix timestamp to human-readable date
9
+ - Human-readable date to Unix timestamp
10
+ - Multiple date format options
11
+ - Relative time display
12
+ """
13
+
14
+ import tkinter as tk
15
+ from tkinter import ttk
16
+ from datetime import datetime, timezone
17
+ import time
18
+ import re
19
+
20
+
21
+ class TimestampConverterProcessor:
22
+ """Timestamp converter processor."""
23
+
24
+ DATE_FORMATS = {
25
+ "iso": "%Y-%m-%dT%H:%M:%S",
26
+ "iso_date": "%Y-%m-%d",
27
+ "us": "%m/%d/%Y %I:%M:%S %p",
28
+ "us_date": "%m/%d/%Y",
29
+ "eu": "%d/%m/%Y %H:%M:%S",
30
+ "eu_date": "%d/%m/%Y",
31
+ "long": "%B %d, %Y %H:%M:%S",
32
+ "short": "%b %d, %Y %H:%M",
33
+ "rfc2822": "%a, %d %b %Y %H:%M:%S",
34
+ "custom": None
35
+ }
36
+
37
+ @staticmethod
38
+ def unix_to_datetime(timestamp, use_utc=False):
39
+ """Convert Unix timestamp to datetime object."""
40
+ try:
41
+ ts = float(timestamp)
42
+ # Handle milliseconds
43
+ if ts > 1e12:
44
+ ts = ts / 1000
45
+
46
+ if use_utc:
47
+ return datetime.fromtimestamp(ts, tz=timezone.utc)
48
+ else:
49
+ return datetime.fromtimestamp(ts)
50
+ except (ValueError, OSError) as e:
51
+ return None
52
+
53
+ @staticmethod
54
+ def datetime_to_unix(dt):
55
+ """Convert datetime object to Unix timestamp."""
56
+ return int(dt.timestamp())
57
+
58
+ @staticmethod
59
+ def format_datetime(dt, format_name="iso", custom_format=None):
60
+ """Format datetime using specified format."""
61
+ if format_name == "custom" and custom_format:
62
+ fmt = custom_format
63
+ else:
64
+ fmt = TimestampConverterProcessor.DATE_FORMATS.get(format_name, "%Y-%m-%d %H:%M:%S")
65
+
66
+ try:
67
+ return dt.strftime(fmt)
68
+ except Exception:
69
+ return str(dt)
70
+
71
+ @staticmethod
72
+ def parse_datetime(text, format_name="iso", custom_format=None):
73
+ """Parse datetime from string."""
74
+ text = text.strip()
75
+
76
+ # Try to detect Unix timestamp
77
+ if re.match(r'^\d{10,13}$', text):
78
+ ts = float(text)
79
+ if ts > 1e12:
80
+ ts = ts / 1000
81
+ return datetime.fromtimestamp(ts)
82
+
83
+ # Try specified format
84
+ if format_name == "custom" and custom_format:
85
+ fmt = custom_format
86
+ else:
87
+ fmt = TimestampConverterProcessor.DATE_FORMATS.get(format_name)
88
+
89
+ if fmt:
90
+ try:
91
+ return datetime.strptime(text, fmt)
92
+ except ValueError:
93
+ pass
94
+
95
+ # Try common formats
96
+ common_formats = [
97
+ "%Y-%m-%dT%H:%M:%S",
98
+ "%Y-%m-%d %H:%M:%S",
99
+ "%Y-%m-%d",
100
+ "%m/%d/%Y %H:%M:%S",
101
+ "%m/%d/%Y",
102
+ "%d/%m/%Y %H:%M:%S",
103
+ "%d/%m/%Y",
104
+ "%B %d, %Y",
105
+ "%b %d, %Y",
106
+ ]
107
+
108
+ for fmt in common_formats:
109
+ try:
110
+ return datetime.strptime(text, fmt)
111
+ except ValueError:
112
+ continue
113
+
114
+ return None
115
+
116
+ @staticmethod
117
+ def relative_time(dt):
118
+ """Get relative time string (e.g., '2 hours ago')."""
119
+ now = datetime.now()
120
+ diff = now - dt
121
+
122
+ seconds = diff.total_seconds()
123
+
124
+ if seconds < 0:
125
+ # Future
126
+ seconds = abs(seconds)
127
+ suffix = "from now"
128
+ else:
129
+ suffix = "ago"
130
+
131
+ if seconds < 60:
132
+ return f"{int(seconds)} seconds {suffix}"
133
+ elif seconds < 3600:
134
+ minutes = int(seconds / 60)
135
+ return f"{minutes} minute{'s' if minutes != 1 else ''} {suffix}"
136
+ elif seconds < 86400:
137
+ hours = int(seconds / 3600)
138
+ return f"{hours} hour{'s' if hours != 1 else ''} {suffix}"
139
+ elif seconds < 2592000: # 30 days
140
+ days = int(seconds / 86400)
141
+ return f"{days} day{'s' if days != 1 else ''} {suffix}"
142
+ elif seconds < 31536000: # 365 days
143
+ months = int(seconds / 2592000)
144
+ return f"{months} month{'s' if months != 1 else ''} {suffix}"
145
+ else:
146
+ years = int(seconds / 31536000)
147
+ return f"{years} year{'s' if years != 1 else ''} {suffix}"
148
+
149
+ @staticmethod
150
+ def convert_timestamp(text, input_format="unix", output_format="iso",
151
+ use_utc=False, custom_format=None, show_relative=False):
152
+ """Convert timestamp between formats."""
153
+ text = text.strip()
154
+
155
+ if not text:
156
+ return ""
157
+
158
+ # Parse input
159
+ if input_format == "unix":
160
+ dt = TimestampConverterProcessor.unix_to_datetime(text, use_utc)
161
+ else:
162
+ dt = TimestampConverterProcessor.parse_datetime(text, input_format, custom_format)
163
+
164
+ if dt is None:
165
+ return f"Error: Could not parse '{text}'"
166
+
167
+ # Format output
168
+ if output_format == "unix":
169
+ result = str(TimestampConverterProcessor.datetime_to_unix(dt))
170
+ else:
171
+ result = TimestampConverterProcessor.format_datetime(dt, output_format, custom_format)
172
+
173
+ if show_relative:
174
+ relative = TimestampConverterProcessor.relative_time(dt)
175
+ result += f" ({relative})"
176
+
177
+ return result
178
+
179
+ @staticmethod
180
+ def convert_batch(text, input_format="unix", output_format="iso",
181
+ use_utc=False, custom_format=None, show_relative=False):
182
+ """Convert multiple timestamps."""
183
+ lines = text.strip().split('\n')
184
+ results = []
185
+
186
+ for line in lines:
187
+ line = line.strip()
188
+ if line:
189
+ result = TimestampConverterProcessor.convert_timestamp(
190
+ line, input_format, output_format, use_utc, custom_format, show_relative
191
+ )
192
+ results.append(result)
193
+ else:
194
+ results.append('')
195
+
196
+ return '\n'.join(results)
197
+
198
+ @staticmethod
199
+ def get_current_timestamp():
200
+ """Get current Unix timestamp."""
201
+ return str(int(time.time()))
202
+
203
+ @staticmethod
204
+ def get_current_datetime(format_name="iso", custom_format=None):
205
+ """Get current datetime in specified format."""
206
+ return TimestampConverterProcessor.format_datetime(
207
+ datetime.now(), format_name, custom_format
208
+ )
209
+
210
+
211
+ class TimestampConverterWidget(ttk.Frame):
212
+ """Widget for timestamp converter tool."""
213
+
214
+ def __init__(self, parent, app):
215
+ super().__init__(parent)
216
+ self.app = app
217
+ self.processor = TimestampConverterProcessor()
218
+
219
+ self.input_format = tk.StringVar(value="unix")
220
+ self.output_format = tk.StringVar(value="iso")
221
+ self.use_utc = tk.BooleanVar(value=False)
222
+ self.custom_format = tk.StringVar(value="%Y-%m-%d %H:%M:%S")
223
+ self.show_relative = tk.BooleanVar(value=False)
224
+
225
+ self.create_widgets()
226
+ self.load_settings()
227
+
228
+ def create_widgets(self):
229
+ """Creates the widget interface."""
230
+ # Input format
231
+ input_frame = ttk.LabelFrame(self, text="Input Format", padding=10)
232
+ input_frame.pack(fill=tk.X, padx=5, pady=5)
233
+
234
+ formats = [("Unix Timestamp", "unix"), ("ISO 8601", "iso"),
235
+ ("US Date", "us"), ("EU Date", "eu"), ("Auto-detect", "auto")]
236
+
237
+ for i, (text, value) in enumerate(formats):
238
+ ttk.Radiobutton(input_frame, text=text,
239
+ variable=self.input_format, value=value,
240
+ command=self.on_setting_change).grid(row=i//3, column=i%3, sticky=tk.W, padx=5)
241
+
242
+ # Output format
243
+ output_frame = ttk.LabelFrame(self, text="Output Format", padding=10)
244
+ output_frame.pack(fill=tk.X, padx=5, pady=5)
245
+
246
+ output_formats = [
247
+ ("Unix Timestamp", "unix"), ("ISO 8601", "iso"), ("ISO Date", "iso_date"),
248
+ ("US Format", "us"), ("EU Format", "eu"), ("Long", "long"),
249
+ ("Short", "short"), ("RFC 2822", "rfc2822"), ("Custom", "custom")
250
+ ]
251
+
252
+ for i, (text, value) in enumerate(output_formats):
253
+ ttk.Radiobutton(output_frame, text=text,
254
+ variable=self.output_format, value=value,
255
+ command=self.on_setting_change).grid(row=i//3, column=i%3, sticky=tk.W, padx=5)
256
+
257
+ # Custom format
258
+ custom_frame = ttk.Frame(self)
259
+ custom_frame.pack(fill=tk.X, padx=5, pady=5)
260
+
261
+ ttk.Label(custom_frame, text="Custom Format:").pack(side=tk.LEFT)
262
+ ttk.Entry(custom_frame, textvariable=self.custom_format, width=25).pack(side=tk.LEFT, padx=5)
263
+ ttk.Label(custom_frame, text="(e.g., %Y-%m-%d)", font=('TkDefaultFont', 8)).pack(side=tk.LEFT)
264
+
265
+ # Options
266
+ options_frame = ttk.Frame(self)
267
+ options_frame.pack(fill=tk.X, padx=5, pady=5)
268
+
269
+ ttk.Checkbutton(options_frame, text="Use UTC",
270
+ variable=self.use_utc,
271
+ command=self.on_setting_change).pack(side=tk.LEFT, padx=10)
272
+ ttk.Checkbutton(options_frame, text="Show Relative Time",
273
+ variable=self.show_relative,
274
+ command=self.on_setting_change).pack(side=tk.LEFT, padx=10)
275
+
276
+ # Buttons
277
+ buttons_frame = ttk.Frame(self)
278
+ buttons_frame.pack(fill=tk.X, padx=5, pady=10)
279
+
280
+ ttk.Button(buttons_frame, text="Convert",
281
+ command=self.convert).pack(side=tk.LEFT, padx=5)
282
+ ttk.Button(buttons_frame, text="Insert Current Time",
283
+ command=self.insert_current).pack(side=tk.LEFT, padx=5)
284
+
285
+ def load_settings(self):
286
+ """Load settings from the application."""
287
+ settings = self.app.settings.get("tool_settings", {}).get("Timestamp Converter", {})
288
+
289
+ self.input_format.set(settings.get("input_format", "unix"))
290
+ self.output_format.set(settings.get("output_format", "iso"))
291
+ self.use_utc.set(settings.get("use_utc", False))
292
+ self.custom_format.set(settings.get("custom_format", "%Y-%m-%d %H:%M:%S"))
293
+ self.show_relative.set(settings.get("show_relative", False))
294
+
295
+ def save_settings(self):
296
+ """Save current settings to the application."""
297
+ if "Timestamp Converter" not in self.app.settings["tool_settings"]:
298
+ self.app.settings["tool_settings"]["Timestamp Converter"] = {}
299
+
300
+ self.app.settings["tool_settings"]["Timestamp Converter"].update({
301
+ "input_format": self.input_format.get(),
302
+ "output_format": self.output_format.get(),
303
+ "use_utc": self.use_utc.get(),
304
+ "custom_format": self.custom_format.get(),
305
+ "show_relative": self.show_relative.get()
306
+ })
307
+
308
+ self.app.save_settings()
309
+
310
+ def on_setting_change(self, *args):
311
+ """Handle setting changes."""
312
+ self.save_settings()
313
+
314
+ def convert(self):
315
+ """Convert timestamps."""
316
+ active_input_tab = self.app.input_tabs[self.app.input_notebook.index(self.app.input_notebook.select())]
317
+ input_text = active_input_tab.text.get("1.0", tk.END).rstrip('\n')
318
+
319
+ if not input_text.strip():
320
+ return
321
+
322
+ result = TimestampConverterProcessor.convert_batch(
323
+ input_text,
324
+ self.input_format.get(),
325
+ self.output_format.get(),
326
+ self.use_utc.get(),
327
+ self.custom_format.get(),
328
+ self.show_relative.get()
329
+ )
330
+
331
+ active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
332
+ active_output_tab.text.config(state="normal")
333
+ active_output_tab.text.delete("1.0", tk.END)
334
+ active_output_tab.text.insert("1.0", result)
335
+ active_output_tab.text.config(state="disabled")
336
+
337
+ self.app.update_all_stats()
338
+
339
+ def insert_current(self):
340
+ """Insert current timestamp."""
341
+ if self.output_format.get() == "unix":
342
+ result = TimestampConverterProcessor.get_current_timestamp()
343
+ else:
344
+ result = TimestampConverterProcessor.get_current_datetime(
345
+ self.output_format.get(), self.custom_format.get()
346
+ )
347
+
348
+ active_output_tab = self.app.output_tabs[self.app.output_notebook.index(self.app.output_notebook.select())]
349
+ active_output_tab.text.config(state="normal")
350
+ active_output_tab.text.delete("1.0", tk.END)
351
+ active_output_tab.text.insert("1.0", result)
352
+ active_output_tab.text.config(state="disabled")
353
+
354
+ self.app.update_all_stats()
355
+
356
+
357
+ class TimestampConverter:
358
+ """Main class for Timestamp Converter integration."""
359
+
360
+ def __init__(self):
361
+ self.processor = TimestampConverterProcessor()
362
+
363
+ def create_widget(self, parent, app):
364
+ """Create and return the Timestamp Converter widget."""
365
+ return TimestampConverterWidget(parent, app)
366
+
367
+ def get_default_settings(self):
368
+ """Return default settings for Timestamp Converter."""
369
+ return {
370
+ "input_format": "unix",
371
+ "output_format": "iso",
372
+ "use_utc": False,
373
+ "custom_format": "%Y-%m-%d %H:%M:%S",
374
+ "show_relative": False
375
+ }
376
+
377
+
378
+ # BaseTool-compatible wrapper
379
+ try:
380
+ from tools.base_tool import ToolWithOptions
381
+ from typing import Dict, Any
382
+
383
+ class TimestampConverterV2(ToolWithOptions):
384
+ """
385
+ BaseTool-compatible version of TimestampConverter.
386
+ """
387
+
388
+ TOOL_NAME = "Timestamp Converter"
389
+ TOOL_DESCRIPTION = "Convert between Unix timestamps and human-readable dates"
390
+ TOOL_VERSION = "2.0.0"
391
+
392
+ OPTIONS = [
393
+ ("Unix to ISO", "unix_to_iso"),
394
+ ("Unix to US Format", "unix_to_us"),
395
+ ("Unix to EU Format", "unix_to_eu"),
396
+ ("Date to Unix", "date_to_unix"),
397
+ ("Current Time", "now"),
398
+ ]
399
+ OPTIONS_LABEL = "Conversion"
400
+ USE_DROPDOWN = True
401
+ DEFAULT_OPTION = "unix_to_iso"
402
+
403
+ def process_text(self, input_text: str, settings: Dict[str, Any]) -> str:
404
+ """Process text using the specified conversion."""
405
+ mode = settings.get("mode", "unix_to_iso")
406
+
407
+ if mode == "now":
408
+ import time
409
+ return str(int(time.time()))
410
+ elif mode == "unix_to_iso":
411
+ return TimestampConverterProcessor.convert_batch(input_text, "unix", "iso")
412
+ elif mode == "unix_to_us":
413
+ return TimestampConverterProcessor.convert_batch(input_text, "unix", "us")
414
+ elif mode == "unix_to_eu":
415
+ return TimestampConverterProcessor.convert_batch(input_text, "unix", "eu")
416
+ elif mode == "date_to_unix":
417
+ return TimestampConverterProcessor.convert_batch(input_text, "auto", "unix")
418
+ else:
419
+ return input_text
420
+
421
+ except ImportError:
422
422
  pass