harvest-worklog 0.9.0 → 0.10.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ead4ee3da24a3522c5b5e09e694de573b4e10c2736b7e328377197efd349dcb
4
- data.tar.gz: 41cce9ae00a04e151fd68914ddbb3200ae8451c837f93e95a8bd5b601ea1fc27
3
+ metadata.gz: 59f6ddf4acd09bb36f6f508719fccdd81db151107203367cd85deec4fb4bb6da
4
+ data.tar.gz: ebabb8af8524ca17fa2e1972582853eaaef6c721a62df4ad68671091cd63a65f
5
5
  SHA512:
6
- metadata.gz: 2e2592d645f2153fab30539d0ad0fd103b8008c0025950c9222298fc1e06abf4cbd759f3b1a6e4a8eeba5ecf985670d13b994d430b870144afdf825a0ea44018
7
- data.tar.gz: 11f9e301054fc944a9abf5a4509777cb9e6e538d3cd5be2fd50fc06b2a88feaf577b7ae22265ffe7d0a45972f5a7d25f58b99fb9ea36fd54b9eaee2edce0cd64
6
+ metadata.gz: 637f2eebaad54a58e481b9b860f6af18af769fb573c0af55843e7197a6e02101b4b8aeea998f99a0a2b81bde1d4606c0725715bcf676d51fabe9a025b58fb688
7
+ data.tar.gz: 2ce2096247b4b779a6e88615748d9df27c67841ffaf2dc6f8d70874deaa8ceafc8e7f4ff84f196bb970863932f2ad8eb5c97b314c5564667a068ae93119f0a1c
data/README.md CHANGED
@@ -21,7 +21,9 @@ Personal Access Tokens are the intended Harvest authentication method for person
21
21
 
22
22
  ```text
23
23
  harvest-worklog time-off FROM TO --project NAME --task NAME [options]
24
+ harvest-worklog time-off FROM TO --project-id ID --task-id ID [options]
24
25
  harvest-worklog work-entry DATE --project NAME --task NAME --hours HOURS --notes NOTES [options]
26
+ harvest-worklog work-entry DATE --project-id ID --task-id ID --hours HOURS --notes NOTES [options]
25
27
  harvest-worklog aggregate FROM TO [--project NAME] [--task NAME]
26
28
  harvest-worklog timesheet DATE --project NAME [--task NAME]
27
29
  ```
@@ -42,6 +44,8 @@ The default holiday region is `ca_yt`. Add `--holiday-region REGION` or set `HAR
42
44
 
43
45
  A time-off block never creates weekend entries. When names are supplied, it resolves them from your active personal Harvest assignments, so a Project Manager role is not required.
44
46
 
47
+ The `harvest_record_time_off` OMP tool accepts the same name pair or ID pair. Its optional `holidayRegions` array adds per-call regions to the configured defaults.
48
+
45
49
  ### Reviewed ordinary work
46
50
 
47
51
  The `work-entry` command checks for existing or locked entries for its project, task, and date before creating a new duration entry. Use `--dry-run` to complete that preflight without a write. OMP Project Time uses the same path.
@@ -60,15 +64,16 @@ The `harvest_time_aggregates` OMP tool exposes the same optional filters and is
60
64
 
61
65
  ### Daily timesheets
62
66
 
63
- The `timesheet` command reads the authenticated user's compact daily Harvest view: task totals, entry durations when a task has multiple entries, and multiline notes. `DATE` accepts `today`, `yesterday`, or an ISO date in the CLI's local timezone.
67
+ The CLI `timesheet` command and `harvest_time_sheet` OMP tool read the authenticated user's compact daily Harvest view. They show task totals, entry durations, and notes already recorded in Harvest.
64
68
 
65
69
  ```bash
66
70
  harvest-worklog timesheet today --project WRAP
67
71
  ```
68
72
 
69
- OMP exposes the same CLI-only path through `/harvest-worklog today WRAP` and the read-only `harvest_time_sheet` tool. The wrapper never calls Harvest directly. A later, separate feature may upload reviewed content through the Harvest API; keep that write path separate from this read-only formatter.
73
+ The `/harvest-worklog timesheet today --project wrap` slash command instead reads local OMP Project Time and reports separate `Human active` and `Agent elapsed` totals. It does not call Harvest or require `projectTimeMappings`; mappings remain specific to previewing or recording Harvest entries.
74
+ `/project-time history` reports all logged dates, so its totals can be larger than a single-day timesheet.
70
75
 
71
- Type `/harvest-worklog ` in OMP to discover the compact and explicit timesheet forms, relative date tokens, and context-valid `--project`/`--task` options. Completion keeps filter values as editable text, and the slash command delegates the selected timesheet argument vector to the CLI.
76
+ Type `/harvest-worklog ` in OMP to discover `timesheet`, then select `today`, `yesterday`, or an ISO date before the contextual `--project` option. The editable project name must exactly match the case-sensitive local Project Time name.
72
77
 
73
78
  ## OMP settings
74
79
 
@@ -8,7 +8,7 @@ module HarvestWorklog
8
8
  options = {}
9
9
  parser = option_parser(options)
10
10
  dates = parser.parse(arguments)
11
- validate!(dates)
11
+ validate!(dates, options)
12
12
 
13
13
  from = Date.iso8601(dates[0])
14
14
  to = Date.iso8601(dates[1])
@@ -36,8 +36,10 @@ module HarvestWorklog
36
36
  end
37
37
  end
38
38
 
39
- def self.validate!(dates)
39
+ def self.validate!(dates, options = {})
40
40
  raise Error, "FROM and TO are required" unless dates.length == 2
41
+ raise Error, "--project must not be blank" if options[:project]&.strip&.empty?
42
+ raise Error, "--task must not be blank" if options[:task]&.strip&.empty?
41
43
  end
42
44
 
43
45
  def self.fetch_entries(client, from:, to:, user_id: nil)
@@ -46,7 +46,8 @@ module HarvestWorklog
46
46
 
47
47
  def self.validate!(dates, options)
48
48
  raise Error, "DATE is required" unless dates.length == 1
49
- raise Error, "--project is required" unless options[:project] && !options[:project].empty?
49
+ raise Error, "--project is required" unless options[:project] && !options[:project].strip.empty?
50
+ raise Error, "--task must not be blank" if options[:task]&.strip&.empty?
50
51
  end
51
52
 
52
53
  def self.print_timesheet(output, entries, spent_date:, requested_project:)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HarvestWorklog
4
- VERSION = "0.9.0"
4
+ VERSION = "0.10.1"
5
5
  end
@@ -42,7 +42,9 @@ module HarvestWorklog
42
42
  end
43
43
 
44
44
  if options[:dry_run]
45
- output.puts "Would create #{spent_date.iso8601}: #{HarvestWorklog.display_hours(options[:hours])}h on #{options[:project]} / #{options[:task]}; #{options[:notes]}"
45
+ project = options[:project] || "project ##{options[:project_id]}"
46
+ task = options[:task] || "task ##{options[:task_id]}"
47
+ output.puts "Would create #{spent_date.iso8601}: #{HarvestWorklog.display_hours(options[:hours])}h on #{project} / #{task}; #{options[:notes]}"
46
48
  return 0
47
49
  end
48
50
 
@@ -63,7 +65,10 @@ module HarvestWorklog
63
65
 
64
66
  def self.option_parser(options)
65
67
  OptionParser.new do |opts|
66
- opts.banner = "Usage: harvest-worklog work-entry DATE --project NAME --task NAME --hours HOURS --notes NOTES [--dry-run]"
68
+ opts.banner = <<~USAGE
69
+ Usage: harvest-worklog work-entry DATE --project NAME --task NAME --hours HOURS --notes NOTES [options]
70
+ harvest-worklog work-entry DATE --project-id ID --task-id ID --hours HOURS --notes NOTES [options]
71
+ USAGE
67
72
  opts.on("--project NAME", "Harvest project name") { |value| options[:project] = value }
68
73
  opts.on("--task NAME", "Harvest task name") { |value| options[:task] = value }
69
74
  opts.on("--project-id ID", Integer, "Harvest project ID") { |value| options[:project_id] = value }
@@ -82,12 +87,12 @@ module HarvestWorklog
82
87
  def self.validate!(dates, options)
83
88
  raise Error, "DATE is required" unless dates.length == 1
84
89
  raise Error, "--hours must be a positive finite number" unless options[:hours]&.positive? && options[:hours].finite?
85
- raise Error, "--notes is required" unless options[:notes] && !options[:notes].empty?
90
+ raise Error, "--notes is required" unless options[:notes] && !options[:notes].strip.empty?
86
91
 
87
92
  names_provided = options[:project] || options[:task]
88
93
  ids_provided = options[:project_id] || options[:task_id]
89
- valid_names = options[:project] && options[:task]
90
- valid_ids = options[:project_id] && options[:task_id]
94
+ valid_names = options[:project] && !options[:project].strip.empty? && options[:task] && !options[:task].strip.empty?
95
+ valid_ids = options[:project_id]&.positive? && options[:task_id]&.positive?
91
96
  raise Error, "supply --project and --task, or --project-id and --task-id" unless valid_names || valid_ids
92
97
  raise Error, "do not combine project/task names with IDs" if names_provided && ids_provided
93
98
  end
@@ -52,7 +52,9 @@ module HarvestWorklog
52
52
  <<~USAGE
53
53
  Usage:
54
54
  harvest-worklog time-off FROM TO --project NAME --task NAME [options]
55
+ harvest-worklog time-off FROM TO --project-id ID --task-id ID [options]
55
56
  harvest-worklog work-entry DATE --project NAME --task NAME --hours HOURS --notes NOTES [options]
57
+ harvest-worklog work-entry DATE --project-id ID --task-id ID --hours HOURS --notes NOTES [options]
56
58
  harvest-worklog aggregate FROM TO [--project NAME] [--task NAME]
57
59
  harvest-worklog timesheet DATE --project NAME [--task NAME]
58
60
 
@@ -70,6 +72,7 @@ module HarvestWorklog
70
72
  options = { hours: 7.0, dry_run: false, holiday_regions: holiday_regions_from_environment }
71
73
  parser = option_parser(options)
72
74
  dates = parser.parse(arguments)
75
+ normalize_holiday_regions!(options[:holiday_regions])
73
76
  validate!(dates, options)
74
77
 
75
78
  from = Date.iso8601(dates[0])
@@ -133,11 +136,12 @@ module HarvestWorklog
133
136
  raise Error, "FROM and TO are required" unless dates.length == 2
134
137
  raise Error, "--hours must be a positive finite number" unless options[:hours].positive? && options[:hours].finite?
135
138
  raise Error, "--holiday-region or HARVEST_HOLIDAY_REGIONS is required" if options[:holiday_regions].empty?
139
+ raise Error, "--notes must not be blank" if options[:notes]&.strip&.empty?
136
140
 
137
141
  names_provided = options[:project] || options[:task]
138
142
  ids_provided = options[:project_id] || options[:task_id]
139
- valid_names = options[:project] && options[:task]
140
- valid_ids = options[:project_id] && options[:task_id]
143
+ valid_names = options[:project] && !options[:project].strip.empty? && options[:task] && !options[:task].strip.empty?
144
+ valid_ids = options[:project_id]&.positive? && options[:task_id]&.positive?
141
145
  raise Error, "supply --project and --task, or --project-id and --task-id" unless valid_names || valid_ids
142
146
  raise Error, "do not combine project/task names with IDs" if names_provided && ids_provided
143
147
  end
@@ -146,6 +150,13 @@ module HarvestWorklog
146
150
  ENV.fetch("HARVEST_HOLIDAY_REGIONS", "ca_yt").split(",").map { |region| region.strip.downcase }.reject(&:empty?)
147
151
  end
148
152
 
153
+ def self.normalize_holiday_regions!(regions)
154
+ regions.map! { |region| region.strip.downcase }
155
+ regions.reject!(&:empty?)
156
+ regions.uniq!
157
+ regions
158
+ end
159
+
149
160
  def self.resolve_assignment(client, project_name, task_name)
150
161
  # ponytail: one 2,000-item page covers personal assignments; follow cursor pagination if that ceiling is exceeded.
151
162
  matches = client.active_personal_task_assignments.select do |assignment|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harvest-worklog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marlen Brunner