ruby_todo 1.0.4 → 1.0.7
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/ruby_todo/commands/ai_assistant.rb +319 -216
- data/lib/ruby_todo/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37fb5cf86998fa9cf8832f85f97d6a842079d5a7c188797004be2c07c82124f0
|
4
|
+
data.tar.gz: 7fffd21364c1f52f65ac0209b1445068fb409f85eb2e9cf026774d83a407b9a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32eac5de70860e28c8fd6313cfb6d8cbe6ef5820ffa1328a29f1bb35bc248b2a7b0dcec0e2f6910114320e2c80e2513c11459e5021fd9cc43c6a2b815debf332
|
7
|
+
data.tar.gz: 9bfbb5a971db1822c4e080d10090f82910480dc1f4e9fe42329e074853634c9079c48c652e97a7e8dfab4dfca16e85cb68db319d6a6a3f7918b5ffd47a283b04
|
data/CHANGELOG.md
CHANGED
@@ -78,26 +78,30 @@ module RubyTodo
|
|
78
78
|
module StatusFilteringHelpers
|
79
79
|
# Helper method to process the status and delegate to handle_status_filtered_tasks
|
80
80
|
def handle_filtered_tasks(cli, status_text)
|
81
|
-
status
|
81
|
+
# Normalize the status by removing extra spaces and replacing dashes
|
82
|
+
status = status_text.to_s.downcase.strip
|
83
|
+
.gsub(/[-\s]+/, "_") # Replace dashes or spaces with underscore
|
84
|
+
.gsub(/^in_?_?progress$/, "in_progress") # Normalize in_progress variations
|
85
|
+
|
82
86
|
handle_status_filtered_tasks(cli, status)
|
83
87
|
end
|
84
88
|
|
85
89
|
# Status-based task filtering patterns
|
86
90
|
def tasks_with_status_regex
|
87
|
-
/(?:list|show|get|display).*(?:all)?\s*tasks\s+
|
88
|
-
(?:with|that\s+(?:are|have))\s+
|
89
|
-
(in\
|
91
|
+
/(?:list|show|get|display|see).*(?:all)?\s*tasks\s+
|
92
|
+
(?:with|that\s+(?:are|have)|having|in|that\s+are\s+in)\s+
|
93
|
+
(in[\s_-]?progress|todo|done|archived)(?:\s+status)?/ix
|
90
94
|
end
|
91
95
|
|
92
96
|
def tasks_by_status_regex
|
93
|
-
/(?:list|show|get|display).*(?:all)?\s*tasks\s+
|
94
|
-
(?:with)?\s*status\s+
|
95
|
-
(in\
|
97
|
+
/(?:list|show|get|display|see).*(?:all)?\s*tasks\s+
|
98
|
+
(?:with|by|having)?\s*status\s+
|
99
|
+
(in[\s_-]?progress|todo|done|archived)/ix
|
96
100
|
end
|
97
101
|
|
98
102
|
def status_prefix_tasks_regex
|
99
|
-
/(?:list|show|get|display).*(?:all)?\s*
|
100
|
-
(in\
|
103
|
+
/(?:list|show|get|display|see).*(?:all)?\s*
|
104
|
+
(in[\s_-]?progress|todo|done|archived)(?:\s+status)?\s+tasks/ix
|
101
105
|
end
|
102
106
|
|
103
107
|
# Helper method to handle tasks filtered by status
|
@@ -136,6 +140,292 @@ module RubyTodo
|
|
136
140
|
end
|
137
141
|
end
|
138
142
|
|
143
|
+
# Module for handling export-related functionality - Part 1: Patterns and Detection
|
144
|
+
module ExportPatternHelpers
|
145
|
+
def export_tasks_regex
|
146
|
+
/export.*tasks.*(?:done|in_progress|todo|archived)?.*last\s+\d+\s+weeks?/i
|
147
|
+
end
|
148
|
+
|
149
|
+
def export_done_tasks_regex
|
150
|
+
/export.*done.*tasks.*last\s+\d+\s+weeks?/i
|
151
|
+
end
|
152
|
+
|
153
|
+
def export_in_progress_tasks_regex
|
154
|
+
/export.*in[_\s-]?progress.*tasks/i
|
155
|
+
end
|
156
|
+
|
157
|
+
def export_todo_tasks_regex
|
158
|
+
/export.*todo.*tasks/i
|
159
|
+
end
|
160
|
+
|
161
|
+
def export_archived_tasks_regex
|
162
|
+
/export.*archived.*tasks/i
|
163
|
+
end
|
164
|
+
|
165
|
+
def export_all_done_tasks_regex
|
166
|
+
/export.*all.*done.*tasks/i
|
167
|
+
end
|
168
|
+
|
169
|
+
def export_tasks_with_status_regex
|
170
|
+
/export.*tasks.*(?:with|in).*(?:status|state)\s+(todo|in[_\s-]?progress|done|archived)/i
|
171
|
+
end
|
172
|
+
|
173
|
+
def export_tasks_to_csv_regex
|
174
|
+
/export.*tasks.*to.*csv/i
|
175
|
+
end
|
176
|
+
|
177
|
+
def export_tasks_to_json_regex
|
178
|
+
/export.*tasks.*to.*json/i
|
179
|
+
end
|
180
|
+
|
181
|
+
def export_tasks_to_file_regex
|
182
|
+
/export.*tasks.*to\s+[^\.]+\.(json|csv)/i
|
183
|
+
end
|
184
|
+
|
185
|
+
def save_tasks_to_file_regex
|
186
|
+
/save.*tasks.*to.*file/i
|
187
|
+
end
|
188
|
+
|
189
|
+
def handle_export_task_patterns(prompt)
|
190
|
+
# Determine the status to export based on the prompt
|
191
|
+
status = determine_export_status(prompt)
|
192
|
+
|
193
|
+
case
|
194
|
+
when prompt.match?(export_tasks_regex) ||
|
195
|
+
prompt.match?(export_done_tasks_regex) ||
|
196
|
+
prompt.match?(export_in_progress_tasks_regex) ||
|
197
|
+
prompt.match?(export_todo_tasks_regex) ||
|
198
|
+
prompt.match?(export_archived_tasks_regex) ||
|
199
|
+
prompt.match?(export_all_done_tasks_regex) ||
|
200
|
+
prompt.match?(export_tasks_with_status_regex) ||
|
201
|
+
prompt.match?(export_tasks_to_csv_regex) ||
|
202
|
+
prompt.match?(export_tasks_to_json_regex) ||
|
203
|
+
prompt.match?(export_tasks_to_file_regex) ||
|
204
|
+
prompt.match?(save_tasks_to_file_regex)
|
205
|
+
handle_export_tasks_by_status(prompt, status)
|
206
|
+
return true
|
207
|
+
end
|
208
|
+
false
|
209
|
+
end
|
210
|
+
|
211
|
+
# Determine which status to export based on the prompt
|
212
|
+
def determine_export_status(prompt)
|
213
|
+
case prompt
|
214
|
+
when /in[_\s-]?progress/i
|
215
|
+
"in_progress"
|
216
|
+
when /todo/i
|
217
|
+
"todo"
|
218
|
+
when /archived/i
|
219
|
+
"archived"
|
220
|
+
when export_tasks_with_status_regex
|
221
|
+
status_match = prompt.match(export_tasks_with_status_regex)
|
222
|
+
normalize_status(status_match[1])
|
223
|
+
else
|
224
|
+
"done" # Default to done if no specific status mentioned
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Normalize status string (convert "in progress" to "in_progress", etc.)
|
229
|
+
def normalize_status(status)
|
230
|
+
status.to_s.downcase.strip
|
231
|
+
.gsub(/[-\s]+/, "_") # Replace dashes or spaces with underscore
|
232
|
+
.gsub(/^in_?_?progress$/, "in_progress") # Normalize in_progress variations
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Module for handling export-related functionality - Part 2: Core Export Functions
|
237
|
+
module ExportCoreHelpers
|
238
|
+
# Handle exporting tasks with a specific status
|
239
|
+
def handle_export_tasks_by_status(prompt, status)
|
240
|
+
# Extract export parameters from prompt
|
241
|
+
export_params = extract_export_parameters(prompt)
|
242
|
+
|
243
|
+
say "Exporting tasks with status '#{status}'..."
|
244
|
+
|
245
|
+
# Collect and filter tasks by status
|
246
|
+
exported_data = collect_tasks_by_status(status, export_params[:weeks_ago])
|
247
|
+
|
248
|
+
if exported_data["notebooks"].empty?
|
249
|
+
say "No tasks with status '#{status}' found."
|
250
|
+
return
|
251
|
+
end
|
252
|
+
|
253
|
+
# Count tasks
|
254
|
+
total_tasks = exported_data["notebooks"].sum { |nb| nb["tasks"].size }
|
255
|
+
|
256
|
+
# Export data to file
|
257
|
+
export_data_to_file(exported_data, export_params[:filename], export_params[:format])
|
258
|
+
|
259
|
+
# Format the success message
|
260
|
+
success_msg = "Successfully exported #{total_tasks} '#{status}' tasks to #{export_params[:filename]}."
|
261
|
+
say success_msg
|
262
|
+
end
|
263
|
+
|
264
|
+
# This replaces the old handle_export_recent_done_tasks method
|
265
|
+
def handle_export_recent_done_tasks(prompt)
|
266
|
+
handle_export_tasks_by_status(prompt, "done")
|
267
|
+
end
|
268
|
+
|
269
|
+
# Collect tasks with a specific status
|
270
|
+
def collect_tasks_by_status(status, weeks_ago = nil)
|
271
|
+
# Collect all notebooks
|
272
|
+
notebooks = RubyTodo::Notebook.all
|
273
|
+
|
274
|
+
# Filter for tasks with the specified status
|
275
|
+
exported_data = {
|
276
|
+
"notebooks" => notebooks.map do |notebook|
|
277
|
+
notebook_tasks = notebook.tasks.select do |task|
|
278
|
+
if weeks_ago
|
279
|
+
task.status == status &&
|
280
|
+
task.updated_at &&
|
281
|
+
task.updated_at >= weeks_ago
|
282
|
+
else
|
283
|
+
task.status == status
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
{
|
288
|
+
"name" => notebook.name,
|
289
|
+
"created_at" => notebook.created_at,
|
290
|
+
"updated_at" => notebook.updated_at,
|
291
|
+
"tasks" => notebook_tasks.map { |task| task_to_hash(task) }
|
292
|
+
}
|
293
|
+
end
|
294
|
+
}
|
295
|
+
|
296
|
+
# Filter out notebooks with no matching tasks
|
297
|
+
exported_data["notebooks"].select! { |nb| nb["tasks"].any? }
|
298
|
+
|
299
|
+
exported_data
|
300
|
+
end
|
301
|
+
|
302
|
+
# Helper for task_to_hash in export context
|
303
|
+
def task_to_hash(task)
|
304
|
+
{
|
305
|
+
"id" => task.id,
|
306
|
+
"title" => task.title,
|
307
|
+
"description" => task.description,
|
308
|
+
"status" => task.status,
|
309
|
+
"priority" => task.priority,
|
310
|
+
"tags" => task.tags,
|
311
|
+
"due_date" => task.due_date&.iso8601,
|
312
|
+
"created_at" => task.created_at&.iso8601,
|
313
|
+
"updated_at" => task.updated_at&.iso8601
|
314
|
+
}
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
# Module for handling export-related functionality - Part 3: File and Parameter Handling
|
319
|
+
module ExportFileHelpers
|
320
|
+
# Update default export filename to reflect the status
|
321
|
+
def default_export_filename(current_time, format, status = "done")
|
322
|
+
"#{status}_tasks_export_#{current_time.strftime("%Y%m%d")}.#{format}"
|
323
|
+
end
|
324
|
+
|
325
|
+
def extract_export_parameters(prompt)
|
326
|
+
# Parse the number of weeks from the prompt
|
327
|
+
weeks_regex = /last\s+(\d+)\s+weeks?/i
|
328
|
+
weeks = prompt.match(weeks_regex) ? ::Regexp.last_match(1).to_i : 2 # Default to 2 weeks
|
329
|
+
|
330
|
+
# Allow specifying output format
|
331
|
+
format = prompt.match?(/csv/i) ? "csv" : "json"
|
332
|
+
|
333
|
+
# Check if a custom filename is specified
|
334
|
+
custom_filename = extract_custom_filename(prompt, format)
|
335
|
+
|
336
|
+
# Get current time
|
337
|
+
current_time = Time.now
|
338
|
+
|
339
|
+
# Calculate the time from X weeks ago
|
340
|
+
weeks_ago = current_time - (weeks * 7 * 24 * 60 * 60)
|
341
|
+
|
342
|
+
# Determine status for the filename
|
343
|
+
status = determine_export_status(prompt)
|
344
|
+
|
345
|
+
{
|
346
|
+
weeks: weeks,
|
347
|
+
format: format,
|
348
|
+
filename: custom_filename || default_export_filename(current_time, format, status),
|
349
|
+
weeks_ago: weeks_ago,
|
350
|
+
status: status
|
351
|
+
}
|
352
|
+
end
|
353
|
+
|
354
|
+
def extract_custom_filename(prompt, format)
|
355
|
+
if prompt.match(/to\s+(?:file\s+|filename\s+)?["']?([^"']+)["']?/i)
|
356
|
+
filename = ::Regexp.last_match(1).strip
|
357
|
+
# Ensure the filename has the correct extension
|
358
|
+
unless filename.end_with?(".#{format}")
|
359
|
+
filename = "#{filename}.#{format}"
|
360
|
+
end
|
361
|
+
return filename
|
362
|
+
end
|
363
|
+
nil
|
364
|
+
end
|
365
|
+
|
366
|
+
def export_data_to_file(exported_data, filename, format)
|
367
|
+
case format
|
368
|
+
when "json"
|
369
|
+
export_to_json(exported_data, filename)
|
370
|
+
when "csv"
|
371
|
+
export_to_csv(exported_data, filename)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def export_to_json(exported_data, filename)
|
376
|
+
File.write(filename, JSON.pretty_generate(exported_data))
|
377
|
+
end
|
378
|
+
|
379
|
+
def export_to_csv(exported_data, filename)
|
380
|
+
require "csv"
|
381
|
+
CSV.open(filename, "wb") do |csv|
|
382
|
+
# Add headers - Note: "Completed At" is the date when the task was moved to the "done" status
|
383
|
+
csv << ["Notebook", "ID", "Title", "Description", "Tags", "Priority", "Created At", "Completed At"]
|
384
|
+
|
385
|
+
# Add data rows
|
386
|
+
exported_data["notebooks"].each do |notebook|
|
387
|
+
notebook["tasks"].each do |task|
|
388
|
+
# Handle tags that might be arrays or comma-separated strings
|
389
|
+
tag_value = format_tags_for_csv(task["tags"])
|
390
|
+
|
391
|
+
csv << [
|
392
|
+
notebook["name"],
|
393
|
+
task["id"] || "N/A",
|
394
|
+
task["title"],
|
395
|
+
task["description"] || "",
|
396
|
+
tag_value,
|
397
|
+
task["priority"] || "normal",
|
398
|
+
task["created_at"],
|
399
|
+
task["updated_at"]
|
400
|
+
]
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def format_tags_for_csv(tags)
|
407
|
+
if tags.nil?
|
408
|
+
""
|
409
|
+
elsif tags.is_a?(Array)
|
410
|
+
tags.join(",")
|
411
|
+
else
|
412
|
+
tags.to_s
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
# Module for handling export-related functionality
|
418
|
+
module ExportProcessingHelpers
|
419
|
+
include ExportCoreHelpers
|
420
|
+
include ExportFileHelpers
|
421
|
+
end
|
422
|
+
|
423
|
+
# Combine export helpers for convenience
|
424
|
+
module ExportHelpers
|
425
|
+
include ExportPatternHelpers
|
426
|
+
include ExportProcessingHelpers
|
427
|
+
end
|
428
|
+
|
139
429
|
# Main AI Assistant command class
|
140
430
|
class AIAssistantCommand < Thor
|
141
431
|
include OpenAIIntegration
|
@@ -144,6 +434,7 @@ module RubyTodo
|
|
144
434
|
include AIAssistant::ParamExtractor
|
145
435
|
include AIAssistantHelpers
|
146
436
|
include StatusFilteringHelpers
|
437
|
+
include ExportHelpers
|
147
438
|
|
148
439
|
desc "ask [PROMPT]", "Ask the AI assistant to perform tasks using natural language"
|
149
440
|
method_option :api_key, type: :string, desc: "OpenAI API key"
|
@@ -253,38 +544,6 @@ module RubyTodo
|
|
253
544
|
/xi
|
254
545
|
end
|
255
546
|
|
256
|
-
def export_tasks_regex
|
257
|
-
/export.*tasks.*done.*last\s+\d+\s+weeks?/i
|
258
|
-
end
|
259
|
-
|
260
|
-
def export_done_tasks_regex
|
261
|
-
/export.*done.*tasks.*last\s+\d+\s+weeks?/i
|
262
|
-
end
|
263
|
-
|
264
|
-
def export_all_done_tasks_regex
|
265
|
-
/export.*all.*done.*tasks/i
|
266
|
-
end
|
267
|
-
|
268
|
-
def export_tasks_with_done_status_regex
|
269
|
-
/export.*tasks.*with.*done.*status/i
|
270
|
-
end
|
271
|
-
|
272
|
-
def export_tasks_to_csv_regex
|
273
|
-
/export.*tasks.*to.*csv/i
|
274
|
-
end
|
275
|
-
|
276
|
-
def export_tasks_to_json_regex
|
277
|
-
/export.*tasks.*to.*json/i
|
278
|
-
end
|
279
|
-
|
280
|
-
def export_tasks_to_file_regex
|
281
|
-
/export.*tasks.*to\s+[^\.]+\.[json|cv]/i
|
282
|
-
end
|
283
|
-
|
284
|
-
def save_done_tasks_to_file_regex
|
285
|
-
/save.*done.*tasks.*to.*file/i
|
286
|
-
end
|
287
|
-
|
288
547
|
def process_ai_query(prompt)
|
289
548
|
api_key = fetch_api_key
|
290
549
|
say "\nAPI key loaded successfully" if @options[:verbose]
|
@@ -457,22 +716,6 @@ module RubyTodo
|
|
457
716
|
false
|
458
717
|
end
|
459
718
|
|
460
|
-
def handle_export_task_patterns(prompt)
|
461
|
-
case
|
462
|
-
when prompt.match?(export_tasks_regex) ||
|
463
|
-
prompt.match?(export_done_tasks_regex) ||
|
464
|
-
prompt.match?(export_all_done_tasks_regex) ||
|
465
|
-
prompt.match?(export_tasks_with_done_status_regex) ||
|
466
|
-
prompt.match?(export_tasks_to_csv_regex) ||
|
467
|
-
prompt.match?(export_tasks_to_json_regex) ||
|
468
|
-
prompt.match?(export_tasks_to_file_regex) ||
|
469
|
-
prompt.match?(save_done_tasks_to_file_regex)
|
470
|
-
handle_export_recent_done_tasks(prompt)
|
471
|
-
return true
|
472
|
-
end
|
473
|
-
false
|
474
|
-
end
|
475
|
-
|
476
719
|
def handle_notebook_operations(prompt, cli)
|
477
720
|
# Check for notebook creation requests
|
478
721
|
if prompt.match?(notebook_create_regex)
|
@@ -493,6 +736,7 @@ module RubyTodo
|
|
493
736
|
|
494
737
|
def handle_task_operations(prompt, cli)
|
495
738
|
# Try to handle each type of operation
|
739
|
+
# Check status filtering first to ensure it captures the "tasks that are in todo" pattern
|
496
740
|
return true if handle_status_filtering(prompt, cli)
|
497
741
|
return true if handle_task_creation(prompt, cli)
|
498
742
|
return true if handle_task_listing(prompt, cli)
|
@@ -704,165 +948,6 @@ module RubyTodo
|
|
704
948
|
}
|
705
949
|
end
|
706
950
|
|
707
|
-
def handle_export_recent_done_tasks(prompt)
|
708
|
-
# Extract export parameters from prompt
|
709
|
-
export_params = extract_export_parameters(prompt)
|
710
|
-
|
711
|
-
say "Exporting tasks marked as 'done' from the last #{export_params[:weeks]} weeks..."
|
712
|
-
|
713
|
-
# Collect and filter tasks
|
714
|
-
exported_data = collect_done_tasks(export_params[:weeks_ago])
|
715
|
-
|
716
|
-
if exported_data["notebooks"].empty?
|
717
|
-
say "No 'done' tasks found from the last #{export_params[:weeks]} weeks."
|
718
|
-
return
|
719
|
-
end
|
720
|
-
|
721
|
-
# Count tasks
|
722
|
-
total_tasks = exported_data["notebooks"].sum { |nb| nb["tasks"].size }
|
723
|
-
|
724
|
-
# Export data to file
|
725
|
-
export_data_to_file(exported_data, export_params[:filename], export_params[:format])
|
726
|
-
|
727
|
-
# Format the success message
|
728
|
-
success_msg = "Successfully exported #{total_tasks} 'done' tasks from the last " \
|
729
|
-
"#{export_params[:weeks]} weeks to #{export_params[:filename]}."
|
730
|
-
say success_msg
|
731
|
-
end
|
732
|
-
|
733
|
-
def extract_export_parameters(prompt)
|
734
|
-
# Parse the number of weeks from the prompt
|
735
|
-
weeks_regex = /last\s+(\d+)\s+weeks?/i
|
736
|
-
weeks = prompt.match(weeks_regex) ? ::Regexp.last_match(1).to_i : 2 # Default to 2 weeks
|
737
|
-
|
738
|
-
# Allow specifying output format
|
739
|
-
format = prompt.match?(/csv/i) ? "csv" : "json"
|
740
|
-
|
741
|
-
# Check if a custom filename is specified
|
742
|
-
custom_filename = extract_custom_filename(prompt, format)
|
743
|
-
|
744
|
-
# Get current time
|
745
|
-
current_time = Time.now
|
746
|
-
|
747
|
-
# Calculate the time from X weeks ago
|
748
|
-
weeks_ago = current_time - (weeks * 7 * 24 * 60 * 60)
|
749
|
-
|
750
|
-
{
|
751
|
-
weeks: weeks,
|
752
|
-
format: format,
|
753
|
-
filename: custom_filename || default_export_filename(current_time, format),
|
754
|
-
weeks_ago: weeks_ago
|
755
|
-
}
|
756
|
-
end
|
757
|
-
|
758
|
-
def extract_custom_filename(prompt, format)
|
759
|
-
if prompt.match(/to\s+(?:file\s+|filename\s+)?["']?([^"']+)["']?/i)
|
760
|
-
filename = ::Regexp.last_match(1).strip
|
761
|
-
# Ensure the filename has the correct extension
|
762
|
-
unless filename.end_with?(".#{format}")
|
763
|
-
filename = "#{filename}.#{format}"
|
764
|
-
end
|
765
|
-
return filename
|
766
|
-
end
|
767
|
-
nil
|
768
|
-
end
|
769
|
-
|
770
|
-
def default_export_filename(current_time, format)
|
771
|
-
"done_tasks_export_#{current_time.strftime("%Y%m%d")}.#{format}"
|
772
|
-
end
|
773
|
-
|
774
|
-
def collect_done_tasks(weeks_ago)
|
775
|
-
# Collect all notebooks
|
776
|
-
notebooks = RubyTodo::Notebook.all
|
777
|
-
|
778
|
-
# Filter for done tasks within the time period
|
779
|
-
exported_data = {
|
780
|
-
"notebooks" => notebooks.map do |notebook|
|
781
|
-
notebook_tasks = notebook.tasks.select do |task|
|
782
|
-
task.status == "done" &&
|
783
|
-
task.updated_at &&
|
784
|
-
task.updated_at >= weeks_ago
|
785
|
-
end
|
786
|
-
|
787
|
-
{
|
788
|
-
"name" => notebook.name,
|
789
|
-
"created_at" => notebook.created_at,
|
790
|
-
"updated_at" => notebook.updated_at,
|
791
|
-
"tasks" => notebook_tasks.map { |task| task_to_hash(task) }
|
792
|
-
}
|
793
|
-
end
|
794
|
-
}
|
795
|
-
|
796
|
-
# Filter out notebooks with no matching tasks
|
797
|
-
exported_data["notebooks"].select! { |nb| nb["tasks"].any? }
|
798
|
-
|
799
|
-
exported_data
|
800
|
-
end
|
801
|
-
|
802
|
-
def export_data_to_file(exported_data, filename, format)
|
803
|
-
case format
|
804
|
-
when "json"
|
805
|
-
export_to_json(exported_data, filename)
|
806
|
-
when "csv"
|
807
|
-
export_to_csv(exported_data, filename)
|
808
|
-
end
|
809
|
-
end
|
810
|
-
|
811
|
-
def export_to_json(exported_data, filename)
|
812
|
-
File.write(filename, JSON.pretty_generate(exported_data))
|
813
|
-
end
|
814
|
-
|
815
|
-
def export_to_csv(exported_data, filename)
|
816
|
-
require "csv"
|
817
|
-
CSV.open(filename, "wb") do |csv|
|
818
|
-
# Add headers - Note: "Completed At" is the date when the task was moved to the "done" status
|
819
|
-
csv << ["Notebook", "ID", "Title", "Description", "Tags", "Priority", "Created At", "Completed At"]
|
820
|
-
|
821
|
-
# Add data rows
|
822
|
-
exported_data["notebooks"].each do |notebook|
|
823
|
-
notebook["tasks"].each do |task|
|
824
|
-
# Handle tags that might be arrays or comma-separated strings
|
825
|
-
tag_value = format_tags_for_csv(task["tags"])
|
826
|
-
|
827
|
-
csv << [
|
828
|
-
notebook["name"],
|
829
|
-
task["id"] || "N/A",
|
830
|
-
task["title"],
|
831
|
-
task["description"] || "",
|
832
|
-
tag_value,
|
833
|
-
task["priority"] || "normal",
|
834
|
-
task["created_at"],
|
835
|
-
task["updated_at"]
|
836
|
-
]
|
837
|
-
end
|
838
|
-
end
|
839
|
-
end
|
840
|
-
end
|
841
|
-
|
842
|
-
def format_tags_for_csv(tags)
|
843
|
-
if tags.nil?
|
844
|
-
""
|
845
|
-
elsif tags.is_a?(Array)
|
846
|
-
tags.join(",")
|
847
|
-
else
|
848
|
-
tags.to_s
|
849
|
-
end
|
850
|
-
end
|
851
|
-
|
852
|
-
def task_to_hash(task)
|
853
|
-
{
|
854
|
-
"id" => task.id,
|
855
|
-
"title" => task.title,
|
856
|
-
"description" => task.description,
|
857
|
-
"status" => task.status,
|
858
|
-
"priority" => task.priority,
|
859
|
-
"tags" => task.tags,
|
860
|
-
"due_date" => task.due_date&.iso8601,
|
861
|
-
"created_at" => task.created_at&.iso8601,
|
862
|
-
"updated_at" => task.updated_at&.iso8601
|
863
|
-
}
|
864
|
-
end
|
865
|
-
|
866
951
|
def handle_task_with_invalid_attributes(prompt, _cli)
|
867
952
|
# Extract task title and notebook from prompt
|
868
953
|
match = prompt.match(/add task\s+['"]([^'"]+)['"]\s+to\s+(\w+)/i)
|
@@ -910,5 +995,23 @@ module RubyTodo
|
|
910
995
|
false # Not matching our pattern
|
911
996
|
end
|
912
997
|
end
|
998
|
+
|
999
|
+
# Helper method to check if a pattern is status filtering or notebook filtering
|
1000
|
+
def is_status_filtering_pattern?(prompt)
|
1001
|
+
tasks_with_status_regex = /(?:list|show|get|display|see).*(?:all)?\s*tasks\s+
|
1002
|
+
(?:with|that\s+(?:are|have)|having|in|that\s+are)\s+
|
1003
|
+
(in[\s_-]?progress|todo|done|archived)(?:\s+status)?/ix
|
1004
|
+
|
1005
|
+
tasks_by_status_regex = /(?:list|show|get|display|see).*(?:all)?\s*tasks\s+
|
1006
|
+
(?:with|by|having)?\s*status\s+
|
1007
|
+
(in[\s_-]?progress|todo|done|archived)/ix
|
1008
|
+
|
1009
|
+
status_prefix_tasks_regex = /(?:list|show|get|display|see).*(?:all)?\s*
|
1010
|
+
(in[\s_-]?progress|todo|done|archived)(?:\s+status)?\s+tasks/ix
|
1011
|
+
|
1012
|
+
prompt.match?(tasks_with_status_regex) ||
|
1013
|
+
prompt.match?(tasks_by_status_regex) ||
|
1014
|
+
prompt.match?(status_prefix_tasks_regex)
|
1015
|
+
end
|
913
1016
|
end
|
914
1017
|
end
|
data/lib/ruby_todo/version.rb
CHANGED