tempo-cli 0.2.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/README.md +14 -6
- data/bin/tempo +28 -0
- data/features/step_definitions/tempo_steps.rb +7 -7
- data/lib/file_record/directory.rb +104 -1
- data/lib/file_record/file_utility.rb +47 -5
- data/lib/file_record/record.rb +10 -9
- data/lib/tempo/controllers/end_controller.rb +1 -1
- data/lib/tempo/controllers/records_controller.rb +55 -3
- data/lib/tempo/controllers/report_controller.rb +1 -1
- data/lib/tempo/controllers/start_controller.rb +2 -2
- data/lib/tempo/exceptions.rb +11 -0
- data/lib/tempo/models/log.rb +20 -3
- data/lib/tempo/models/time_record.rb +7 -1
- data/lib/tempo/version.rb +1 -1
- data/lib/tempo/views/base.rb +3 -3
- data/lib/tempo/views/formatters/interactive.rb +44 -0
- data/lib/tempo/views/formatters/screen.rb +9 -10
- data/lib/tempo/views/interactive.rb +32 -0
- data/lib/tempo/views/reporter.rb +15 -0
- data/lib/tempo/views/view_records/base.rb +24 -0
- data/lib/tempo/views/view_records/time_record.rb +1 -1
- data/test/lib/file_record/directory_test.rb +8 -5
- data/test/lib/file_record/record_test.rb +8 -5
- data/test/lib/tempo/models/log_test.rb +14 -4
- data/test/lib/tempo/models/time_record_test.rb +2 -2
- data/test/lib/tempo/views/formatters/console_test.rb +32 -0
- data/test/lib/tempo/views/reporter_test.rb +7 -1
- data/test/support/factories.rb +7 -15
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fea9fde6712531a15750a50792f48596161e24f7
|
4
|
+
data.tar.gz: 9cf7aa5a815d65f23fde2da454a274334d4d04e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e482886b1748a24e458f7c3b44ae2638f5746d25bbf8c5b4a5834fd878730f6f5f1bf2451bce3bc0e672f8908300c42a3ff92baf73d7df6fc2c8ac8721cbf1eb
|
7
|
+
data.tar.gz: eac361590d878b3414d557a0c672d8c5d139420e27efcf69c6022daabb268caa5cabab30daa1f6d88b2d4e440a86b96933caa2366d192824cfadd57cf370a563
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
tempo-cli (0.
|
4
|
+
tempo-cli (1.0.0)
|
5
5
|
chronic (~> 0.10)
|
6
6
|
gli (~> 2.10)
|
7
7
|
|
@@ -27,7 +27,7 @@ GEM
|
|
27
27
|
ffi (1.9.0)
|
28
28
|
gherkin (2.12.0)
|
29
29
|
multi_json (~> 1.3)
|
30
|
-
gli (2.
|
30
|
+
gli (2.12.0)
|
31
31
|
json (1.8.1)
|
32
32
|
method_source (0.8.1)
|
33
33
|
multi_json (1.7.7)
|
data/README.md
CHANGED
@@ -4,7 +4,13 @@ A command line interface for time tracking by project.
|
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
gem install tempo-cli
|
7
|
+
gem install tempo-cli
|
8
|
+
|
9
|
+
## Version 1.0
|
10
|
+
|
11
|
+
If you are updatging from version ~> 0.2.6 to 1.0, there is a new structure to the time records - time records are now kept in sub-folders by year.
|
12
|
+
|
13
|
+
Run `tempo clean` to make a backup of your old directory, move files into the proper folders, and check all earlier records for errors.
|
8
14
|
|
9
15
|
### binary
|
10
16
|
|
@@ -18,11 +24,9 @@ Tempo cli runs with the binary `tempo`, but you can alias a shorter binary for y
|
|
18
24
|
|
19
25
|
Tempo tracks time against projects. Projects can be nested, and tagged, and each time entry can have an additional description. Time reports are produced by day.
|
20
26
|
|
21
|
-
Future enhancements will include reports by project and time totals by day or by project.
|
22
|
-
|
23
27
|
### Records
|
24
28
|
|
25
|
-
All records are produced in YAML files. These
|
29
|
+
All records are produced in YAML files. These records can be edited by hand, but keep in mind that any invalid data could cause problems when it is read back into the app. Make sure time formatting is valid and that they don't overlap, and that all ids are unique per page.
|
26
30
|
|
27
31
|
Each day's time records are designed to work independently. When adding or updating time records, only the records for the days in question are loaded into the app. The most recent day's records will also always be read in, to assure no running records are created earlier than existing records.
|
28
32
|
|
@@ -235,8 +239,9 @@ The description, if passed in, will be used to replace the existing description.
|
|
235
239
|
|
236
240
|
##### command options
|
237
241
|
|
238
|
-
--from=time
|
239
|
-
--
|
242
|
+
-f, --from=time - begin time records on this date
|
243
|
+
-o, --order=[project, date] - order reports by date or project
|
244
|
+
-t, --to=time - end time records on this date
|
240
245
|
|
241
246
|
Reports time entries for a day or a period of days. By default, lists the last recorded day's entries. To list a different day, supply a day as the args. To list a series of records, include a --from and --to value
|
242
247
|
|
@@ -254,6 +259,9 @@ The description, if passed in, will be used to replace the existing description.
|
|
254
259
|
# report a period of days
|
255
260
|
$ tempo report -f 'last monday' -t 'last friday'
|
256
261
|
|
262
|
+
# organize reports as bullet lists under each project
|
263
|
+
$ tempo report --o p
|
264
|
+
|
257
265
|
## Assumptions and Limitations
|
258
266
|
|
259
267
|
Before you begin using tempo, you must have at least one project. Projects can be tagged, and organized as sub-projects. See project in the commands section below, for more information. To view your projects file, look at ~/tempo/tempo_projects.yaml
|
data/bin/tempo
CHANGED
@@ -464,6 +464,34 @@ command [:update, :u] do |c|
|
|
464
464
|
end
|
465
465
|
end
|
466
466
|
|
467
|
+
# COMMAND: CLEAN
|
468
|
+
|
469
|
+
desc 'Clean the tempo file structure'
|
470
|
+
long_desc "Creates a backup of the existing tempo directory,
|
471
|
+
and then clean all files in the current directory.
|
472
|
+
|
473
|
+
Cleaning the projects assures that:
|
474
|
+
|
475
|
+
1. All projects that have been renamed have been updated in the files.
|
476
|
+
|
477
|
+
2. Any files with time record conflicts are caught and can be corrected."
|
478
|
+
|
479
|
+
command [:clean] do |c|
|
480
|
+
require 'readline'
|
481
|
+
c.action do |global_options, options, args|
|
482
|
+
controller = Tempo::Controllers::Records
|
483
|
+
|
484
|
+
confirm = Tempo::Views::interactive_confirm_clean
|
485
|
+
|
486
|
+
if confirm.positive_response?
|
487
|
+
controller.backup_records options, args
|
488
|
+
controller.clean_records options, args
|
489
|
+
else
|
490
|
+
Tempo::Views::message "Cancelling clean"
|
491
|
+
end
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
467
495
|
pre do |global,command,options,args|
|
468
496
|
|
469
497
|
# add reporting of all arguments
|
@@ -53,9 +53,9 @@ Given /^an alternate directory and an existing project file$/ do
|
|
53
53
|
end
|
54
54
|
|
55
55
|
Given /^an existing time record file$/ do
|
56
|
-
@records_directory = File.join(ENV['HOME'], 'tempo/tempo_time_records')
|
56
|
+
@records_directory = File.join(ENV['HOME'], 'tempo/tempo_time_records', '2014')
|
57
57
|
FileUtils.rm_r(@records_directory) if File.exists?(@records_directory)
|
58
|
-
|
58
|
+
FileUtils.mkdir_p @records_directory
|
59
59
|
projects_file = File.join(@records_directory, '20140101.yaml')
|
60
60
|
|
61
61
|
File.open(projects_file,'w') do |f|
|
@@ -107,7 +107,7 @@ end
|
|
107
107
|
|
108
108
|
|
109
109
|
Then /^the time record (.*?) should contain "(.*?)" at line (\d+)$/ do |arg1, arg2, arg3|
|
110
|
-
file = File.join(ENV['HOME'], 'tempo/tempo_time_records', "#{arg1}.yaml")
|
110
|
+
file = File.join(ENV['HOME'], 'tempo/tempo_time_records', "#{arg1[0..3]}", "#{arg1}.yaml")
|
111
111
|
contents = []
|
112
112
|
File.open(file, "r") do |f|
|
113
113
|
f.readlines.each do |line|
|
@@ -118,7 +118,7 @@ Then /^the time record (.*?) should contain "(.*?)" at line (\d+)$/ do |arg1, ar
|
|
118
118
|
end
|
119
119
|
|
120
120
|
Then /^the time record (.*?) should not contain "(.*?)"$/ do |arg1, arg2|
|
121
|
-
file = File.join(ENV['HOME'], 'tempo/tempo_time_records', "#{arg1}.yaml")
|
121
|
+
file = File.join(ENV['HOME'], 'tempo/tempo_time_records', "#{arg1[0..3]}", "#{arg1}.yaml")
|
122
122
|
contents = []
|
123
123
|
File.open(file, "r") do |f|
|
124
124
|
f.readlines.each do |line|
|
@@ -129,7 +129,7 @@ Then /^the time record (.*?) should not contain "(.*?)"$/ do |arg1, arg2|
|
|
129
129
|
end
|
130
130
|
|
131
131
|
Then /^the time record (.*?) should not exist$/ do |arg1|
|
132
|
-
file = File.join(ENV['HOME'], 'tempo/tempo_time_records', "#{arg1}.yaml")
|
132
|
+
file = File.join(ENV['HOME'], 'tempo/tempo_time_records', "#{arg1[0..3]}", "#{arg1}.yaml")
|
133
133
|
File.exists?(file).should == false
|
134
134
|
end
|
135
135
|
|
@@ -200,7 +200,7 @@ Then /^the alternate directory (.*?) file should not contain "(.*?)"$/ do |arg1,
|
|
200
200
|
end
|
201
201
|
|
202
202
|
Then /^the alternate directory time record (\d+) should contain "(.*?)" at line (\d+)$/ do |arg1, arg2, arg3|
|
203
|
-
file = File.join(ENV['HOME'], 'alt_dir', 'tempo', 'tempo_time_records', "#{arg1}.yaml")
|
203
|
+
file = File.join(ENV['HOME'], 'alt_dir', 'tempo', 'tempo_time_records', "#{arg1[0..3]}", "#{arg1}.yaml")
|
204
204
|
contents = []
|
205
205
|
File.open(file, "r") do |f|
|
206
206
|
f.readlines.each do |line|
|
@@ -211,7 +211,7 @@ Then /^the alternate directory time record (\d+) should contain "(.*?)" at line
|
|
211
211
|
end
|
212
212
|
|
213
213
|
Then /^the alternate directory time record (.*?) should not exist$/ do |arg1|
|
214
|
-
file = File.join(ENV['HOME'], 'alt_dir', 'tempo/tempo_time_records', "#{arg1}.yaml")
|
214
|
+
file = File.join(ENV['HOME'], 'alt_dir', 'tempo/tempo_time_records', "#{arg1[0..3]}", "#{arg1}.yaml")
|
215
215
|
File.exists?(file).should == false
|
216
216
|
end
|
217
217
|
|
@@ -1,3 +1,9 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'rubygems/package'
|
3
|
+
require 'rubygems/package'
|
4
|
+
require 'zlib'
|
5
|
+
|
6
|
+
|
1
7
|
module FileRecord
|
2
8
|
class Directory
|
3
9
|
|
@@ -14,7 +20,7 @@ module FileRecord
|
|
14
20
|
|
15
21
|
def create_new(options={})
|
16
22
|
|
17
|
-
directory = options.fetch(
|
23
|
+
directory = options.fetch(:directory, Dir.home)
|
18
24
|
cwd = File.expand_path File.dirname(__FILE__)
|
19
25
|
source = File.join(cwd, "directory_structure/tempo")
|
20
26
|
if ! Dir.exists? directory
|
@@ -22,6 +28,103 @@ module FileRecord
|
|
22
28
|
end
|
23
29
|
FileUtils.cp_r source, directory
|
24
30
|
end
|
31
|
+
|
32
|
+
# Backup the tempo directory to tempo_backup/tempo_backup_20140101_HrMnS.tar.gz
|
33
|
+
# pass in an optional directory (see create_new)
|
34
|
+
# Pass in a timestamp, or default to 20140101_120000
|
35
|
+
def backup(options={})
|
36
|
+
directory = options.fetch(:directory, Dir.home)
|
37
|
+
timestamp = options.fetch(:timestamp, Time.new.strftime("%Y%m%d_%H%M%S"))
|
38
|
+
source = File.join(directory, "tempo")
|
39
|
+
dest = File.join(directory, "tempo_backups")
|
40
|
+
Dir.mkdir dest unless Dir.exists? dest
|
41
|
+
destination = File.join(directory, "tempo_backups", "tempo_backup_#{timestamp}.tar.gz")
|
42
|
+
|
43
|
+
io = tar(source)
|
44
|
+
gz = gzip(io)
|
45
|
+
|
46
|
+
File.open(destination,"w") do |file|
|
47
|
+
file.binmode
|
48
|
+
file.write gz.read
|
49
|
+
end
|
50
|
+
|
51
|
+
# return the new directory name
|
52
|
+
destination
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# COMPRESSION / DECOMPRESSION
|
58
|
+
# From: https://gist.github.com/sinisterchipmunk/1335041
|
59
|
+
|
60
|
+
# Creates a tar file in memory recursively
|
61
|
+
# from the given dir.
|
62
|
+
#
|
63
|
+
# Returns a StringIO whose underlying String
|
64
|
+
# is the contents of the tar file.
|
65
|
+
def tar(dir)
|
66
|
+
tarfile = StringIO.new("")
|
67
|
+
Gem::Package::TarWriter.new(tarfile) do |tar|
|
68
|
+
Dir[File.join(dir, "**/*")].each do |file|
|
69
|
+
mode = File.stat(file).mode
|
70
|
+
relative_file = file.sub /^#{Regexp::escape dir}\/?/, ''
|
71
|
+
|
72
|
+
if File.directory?(file)
|
73
|
+
tar.mkdir relative_file, mode
|
74
|
+
else
|
75
|
+
tar.add_file relative_file, mode do |tf|
|
76
|
+
File.open(file, "rb") { |f| tf.write f.read }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
tarfile.rewind
|
83
|
+
tarfile
|
84
|
+
end
|
85
|
+
|
86
|
+
# gzips the underlying string in the given StringIO,
|
87
|
+
# returning a new StringIO representing the
|
88
|
+
# compressed file.
|
89
|
+
def gzip(tarfile)
|
90
|
+
gz = StringIO.new("")
|
91
|
+
z = Zlib::GzipWriter.new(gz)
|
92
|
+
z.write tarfile.string
|
93
|
+
z.close # this is necessary!
|
94
|
+
|
95
|
+
# z was closed to write the gzip footer, so
|
96
|
+
# now we need a new StringIO
|
97
|
+
StringIO.new gz.string
|
98
|
+
end
|
99
|
+
|
100
|
+
# un-gzips the given IO, returning the
|
101
|
+
# decompressed version as a StringIO
|
102
|
+
def ungzip(tarfile)
|
103
|
+
z = Zlib::GzipReader.new(tarfile)
|
104
|
+
unzipped = StringIO.new(z.read)
|
105
|
+
z.close
|
106
|
+
unzipped
|
107
|
+
end
|
108
|
+
|
109
|
+
# untars the given IO into the specified
|
110
|
+
# directory
|
111
|
+
def untar(io, destination)
|
112
|
+
Gem::Package::TarReader.new io do |tar|
|
113
|
+
tar.each do |tarfile|
|
114
|
+
destination_file = File.join destination, tarfile.full_name
|
115
|
+
|
116
|
+
if tarfile.directory?
|
117
|
+
FileUtils.mkdir_p destination_file
|
118
|
+
else
|
119
|
+
destination_directory = File.dirname(destination_file)
|
120
|
+
FileUtils.mkdir_p destination_directory unless File.directory?(destination_directory)
|
121
|
+
File.open destination_file, "wb" do |f|
|
122
|
+
f.print tarfile.read
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
25
128
|
end
|
26
129
|
end
|
27
130
|
end
|
@@ -73,14 +73,28 @@ module FileRecord
|
|
73
73
|
"#{sn[0]}_#{sn[-1]}s"
|
74
74
|
end
|
75
75
|
|
76
|
-
|
76
|
+
|
77
|
+
def log_year_directory
|
78
|
+
if @time.kind_of? Time
|
79
|
+
@time.strftime("%Y")
|
80
|
+
else
|
81
|
+
@time[0..3]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Tempo::Model::Log -> Users/usrname/(alternate_directory/)tempo/tempo_logs'
|
77
86
|
# Will also create the directory if not found
|
78
|
-
|
79
|
-
def log_directory_path
|
87
|
+
def log_main_directory_path
|
80
88
|
dir = File.join(@directory, module_name, log_directory)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Tempo::Model::Log -> Users/usrname/(alternate_directory/)tempo/tempo_logs/20XX
|
92
|
+
# Will also create the directory if not found
|
93
|
+
def log_directory_path
|
94
|
+
dir = File.join(log_main_directory_path, log_year_directory)
|
81
95
|
|
82
96
|
if @create and !File.exists?(dir)
|
83
|
-
|
97
|
+
FileUtils.mkdir_p dir
|
84
98
|
end
|
85
99
|
|
86
100
|
dir
|
@@ -104,9 +118,37 @@ module FileRecord
|
|
104
118
|
clean_path File.join(dir, filename)
|
105
119
|
end
|
106
120
|
|
121
|
+
def old_style_log_records_exists?
|
122
|
+
return false if !File.exists?(log_main_directory_path)
|
123
|
+
Pathname.new(log_main_directory_path).children.each do |c|
|
124
|
+
return true if c.to_s.match(/\.yaml/)
|
125
|
+
end
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
def move_old_records
|
130
|
+
return false if !File.exists?(log_main_directory_path)
|
131
|
+
puts "moving files in #{log_main_directory_path}"
|
132
|
+
Pathname.new(log_main_directory_path).children.each do |c|
|
133
|
+
if c.to_s.match(/\.yaml/)
|
134
|
+
year = File.basename(c).match(/(^\d{4})/)[1]
|
135
|
+
raise Tempo::DuplicateRecordError.new(File.join(log_main_directory_path,year,File.basename(c))) if File.exists?(File.join(log_main_directory_path,year,File.basename(c)))
|
136
|
+
FileUtils.mkdir_p File.join(log_main_directory_path,year) if !File.exists? File.join(log_main_directory_path,year)
|
137
|
+
FileUtils.cp c, File.join(log_main_directory_path,year,File.basename(c))
|
138
|
+
FileUtils.rm c
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
107
143
|
# Returns the list of log records from a log directory
|
108
144
|
def log_records
|
109
|
-
|
145
|
+
records = []
|
146
|
+
return records if !File.exists?(log_main_directory_path)
|
147
|
+
years = Pathname.new(log_main_directory_path).children.select { |c| c.directory? }
|
148
|
+
years.each do |dir|
|
149
|
+
records = records | Dir[dir.to_s + "/*.yaml"]
|
150
|
+
end
|
151
|
+
records.sort!
|
110
152
|
end
|
111
153
|
|
112
154
|
# remove existing file when passed destroy:true in options
|
data/lib/file_record/record.rb
CHANGED
@@ -42,15 +42,6 @@ module FileRecord
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
# Used by read_model and read_log to load all instances from a file
|
46
|
-
#
|
47
|
-
def read_instances( model, file, options={} )
|
48
|
-
instances = YAML::load_stream( File.open( file ) )
|
49
|
-
instances.each do |i|
|
50
|
-
model.new( i )
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
45
|
# Read in all models instances from the model file
|
55
46
|
def read_model( model, options={} )
|
56
47
|
|
@@ -68,6 +59,16 @@ module FileRecord
|
|
68
59
|
read_instances model, file_path
|
69
60
|
end
|
70
61
|
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
# Used by read_model and read_log to load all instances from a file
|
66
|
+
def read_instances( model, file, options={} )
|
67
|
+
instances = YAML::load_stream( File.open( file ) )
|
68
|
+
instances.each do |i|
|
69
|
+
model.new( i )
|
70
|
+
end
|
71
|
+
end
|
71
72
|
end
|
72
73
|
end
|
73
74
|
end
|
@@ -7,17 +7,69 @@ module Tempo
|
|
7
7
|
|
8
8
|
def initialize_from_records(options, args)
|
9
9
|
|
10
|
-
dir =
|
10
|
+
dir = options.fetch( :directory, ENV['HOME'])
|
11
11
|
|
12
12
|
if File.exists?(File.join(dir, 'tempo'))
|
13
|
-
|
14
13
|
Tempo::Controllers::Projects.load directory: dir
|
15
|
-
|
16
14
|
else
|
17
15
|
FileRecord::Directory.create_new directory: dir
|
18
16
|
end
|
19
17
|
end
|
20
18
|
|
19
|
+
def backup_records(options, args)
|
20
|
+
dir = options.fetch( :directory, ENV['HOME'])
|
21
|
+
Views::interactive_progress "\nBacking up #{dir}/tempo"
|
22
|
+
|
23
|
+
if File.exists?(File.join(dir, 'tempo'))
|
24
|
+
dest = FileRecord::Directory.backup directory: dir
|
25
|
+
Views::interactive_progress "Sucessfully created #{dest}"
|
26
|
+
else
|
27
|
+
Views::no_items("directory #{dir}/tempo", :error)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def old_records_present?(options)
|
32
|
+
file_utility = FileRecord::FileUtility.new(Tempo::Model::TimeRecord, options)
|
33
|
+
file_utility.old_style_log_records_exists?
|
34
|
+
end
|
35
|
+
|
36
|
+
def move_old_records(options)
|
37
|
+
file_utility = FileRecord::FileUtility.new(Tempo::Model::TimeRecord, options)
|
38
|
+
file_utility.move_old_records
|
39
|
+
end
|
40
|
+
|
41
|
+
def clean_records(options, args)
|
42
|
+
dir = File.join( options.fetch( :directory, ENV['HOME']), "tempo", "tempo_time_records")
|
43
|
+
|
44
|
+
if old_records_present? options
|
45
|
+
|
46
|
+
confirm = Tempo::Views::interactive_confirm_move_old_records
|
47
|
+
|
48
|
+
if confirm.positive_response?
|
49
|
+
move_old_records options
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Views::interactive_progress "Loading records from #{dir}"
|
54
|
+
|
55
|
+
days = Model::TimeRecord.record_d_ids(options)
|
56
|
+
|
57
|
+
days.each do |d_id|
|
58
|
+
begin
|
59
|
+
date = "#{d_id[4..5].to_i}/#{d_id[6..7]}/#{d_id[0..3]}"
|
60
|
+
Views::interactive_progress_partial date
|
61
|
+
Model::TimeRecord.load_day_record(d_id, options)
|
62
|
+
Model::TimeRecord.save_to_file(options)
|
63
|
+
Model::TimeRecord.clear_all
|
64
|
+
rescue TimeConflictError => e
|
65
|
+
Views::message " exiting on error..."
|
66
|
+
Views::message "\nAn error occurred which prevented cleaning all the records on #{date}"
|
67
|
+
Views::message "Please repair the records in file #{dir}/#{Model::TimeRecord.file(d_id)}"
|
68
|
+
return Views::error e
|
69
|
+
end
|
70
|
+
end
|
71
|
+
Views::message "\nSuccess -- all files are clean!"
|
72
|
+
end
|
21
73
|
end #class << self
|
22
74
|
end
|
23
75
|
end
|
@@ -11,7 +11,7 @@ module Tempo
|
|
11
11
|
return Tempo::Views.project_assistance if Tempo::Model::Project.index.empty?
|
12
12
|
|
13
13
|
# A from flag has been supplied by the user
|
14
|
-
# and
|
14
|
+
# and possibly a to flag,
|
15
15
|
# so we return a period of day records
|
16
16
|
#
|
17
17
|
if options[:from] != "last record"
|
@@ -13,14 +13,14 @@ module Tempo
|
|
13
13
|
return Views.project_assistance if Model::Project.index.empty?
|
14
14
|
|
15
15
|
if not options[:at]
|
16
|
-
start_time = Time.new()
|
16
|
+
start_time = Time.new().round()
|
17
17
|
else
|
18
18
|
start_time = Time.parse options[:at]
|
19
19
|
end
|
20
20
|
|
21
21
|
return Views.no_match_error( "valid timeframe", options[:at], false ) if start_time.nil?
|
22
22
|
|
23
|
-
if start_time > Time.new()
|
23
|
+
if start_time > Time.new().round()
|
24
24
|
Views.warning("WARNING: logging time in the future may cause trouble maintaining running records")
|
25
25
|
end
|
26
26
|
|
data/lib/tempo/exceptions.rb
CHANGED
data/lib/tempo/models/log.rb
CHANGED
@@ -46,9 +46,13 @@ module Tempo
|
|
46
46
|
FileRecord::FileUtility.new(self, {time: time}).filename
|
47
47
|
end
|
48
48
|
|
49
|
+
def d_id_from_file(file)
|
50
|
+
/(\d+)\.yaml/.match(file)[1]
|
51
|
+
end
|
52
|
+
|
49
53
|
# Returns the immediate directory for the log
|
50
54
|
# Tempo::Model::MessageLog => tempo_message_logs
|
51
|
-
def dir
|
55
|
+
def dir(time)
|
52
56
|
FileRecord::FileUtility.new(self).log_directory
|
53
57
|
end
|
54
58
|
|
@@ -58,6 +62,10 @@ module Tempo
|
|
58
62
|
FileRecord::FileUtility.new(self, options).log_records
|
59
63
|
end
|
60
64
|
|
65
|
+
def record_d_ids(options={})
|
66
|
+
records(options).each_with_object(Array.new) {|file,d_ids| d_ids << d_id_from_file(file)}
|
67
|
+
end
|
68
|
+
|
61
69
|
# returns the loaded record with the latest start time
|
62
70
|
# Only loads records if options[:load] is true,
|
63
71
|
# otherwise assumes records are already loaded
|
@@ -102,10 +110,9 @@ module Tempo
|
|
102
110
|
|
103
111
|
# Return a Time object for the last record's date
|
104
112
|
def last_day(options={})
|
105
|
-
reg = /(\d+)\.yaml/
|
106
113
|
recs = records options
|
107
114
|
if recs.last
|
108
|
-
d_id =
|
115
|
+
d_id = d_id_from_file(recs.last)
|
109
116
|
time = day_id_to_time d_id if d_id
|
110
117
|
return time
|
111
118
|
end
|
@@ -131,6 +138,16 @@ module Tempo
|
|
131
138
|
FileRecord::FileUtility.new(self, options).file_path
|
132
139
|
end
|
133
140
|
|
141
|
+
# Normally not necessary to perform, only
|
142
|
+
# used when cleaning (and testing) records
|
143
|
+
def clear_all
|
144
|
+
@ids = {}
|
145
|
+
@index = []
|
146
|
+
@days_index = {}
|
147
|
+
@id_counter = {}
|
148
|
+
@current = nil
|
149
|
+
end
|
150
|
+
|
134
151
|
# takes and integer, and time or day_id
|
135
152
|
# and returns the instance that matches both
|
136
153
|
# the id and d_id
|
@@ -41,6 +41,12 @@ module Tempo
|
|
41
41
|
# super handles start_time, not end time
|
42
42
|
options[:start_time] ||= Time.now
|
43
43
|
@end_time = options.fetch :end_time, :running
|
44
|
+
|
45
|
+
if ! options[:exact_time]
|
46
|
+
options[:start_time] = options[:start_time].round unless options[:start_time] == :running
|
47
|
+
@end_time = @end_time.round unless @end_time == :running
|
48
|
+
end
|
49
|
+
|
44
50
|
verify_times options[:start_time], @end_time
|
45
51
|
|
46
52
|
super options
|
@@ -146,7 +152,7 @@ module Tempo
|
|
146
152
|
if @end_time.kind_of? Time
|
147
153
|
end_time = @end_time
|
148
154
|
else
|
149
|
-
end_time = Time.now()
|
155
|
+
end_time = Time.now().round
|
150
156
|
end
|
151
157
|
end_time.to_i - @start_time.to_i
|
152
158
|
end
|
data/lib/tempo/version.rb
CHANGED
data/lib/tempo/views/base.rb
CHANGED
@@ -48,13 +48,13 @@ module Tempo
|
|
48
48
|
def no_items(items, category=:info)
|
49
49
|
ViewRecords::Message.new "no #{items} exist", category: category
|
50
50
|
if items == "projects"
|
51
|
-
ViewRecords::Message.new "You must at least one project before you can begin tracking time"
|
51
|
+
ViewRecords::Message.new "You must create at least one project before you can begin tracking time"
|
52
52
|
ViewRecords::Message.new "run `tempo project --help` for more information"
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
def message(message)
|
57
|
-
ViewRecords::Message.new message, category:
|
56
|
+
def message(message, category=:info)
|
57
|
+
ViewRecords::Message.new message, category: category
|
58
58
|
end
|
59
59
|
|
60
60
|
def warning(message)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# The Console block handles interactive queries and progress reports.
|
2
|
+
# It is required when reporting must be done in 'real time' rather than compiled
|
3
|
+
# during runtime and then presented at the end (see Views::Screen). It is the only
|
4
|
+
# formatter that receives blocks as soon as they are handed to the Reporter
|
5
|
+
|
6
|
+
module Tempo
|
7
|
+
module Views
|
8
|
+
module Formatters
|
9
|
+
|
10
|
+
class Interactive < Tempo::Views::Formatters::Base
|
11
|
+
|
12
|
+
|
13
|
+
def message_block(record)
|
14
|
+
record.format do |m|
|
15
|
+
case m.category
|
16
|
+
when :immediate
|
17
|
+
puts "#{m.message}"
|
18
|
+
when :progress
|
19
|
+
puts "#{m.message}..."
|
20
|
+
when :progress_partial
|
21
|
+
$stdout.sync = true
|
22
|
+
print "#{m.message}..."
|
23
|
+
end
|
24
|
+
m.message
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def query_block(query)
|
29
|
+
query.format do |q|
|
30
|
+
puts q.query
|
31
|
+
response = Readline.readline('> ', true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_records_container(container)
|
36
|
+
# Pass through over-ride
|
37
|
+
# We don't allow interactive containers at this time because they
|
38
|
+
# would need to be able to detect when the container is complete.
|
39
|
+
# (report containers raised errors on nil durations).
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
|
-
# Tempo View Formatters are triggered by the View Reporter
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# see <TODO> for an example of proc blocks.
|
1
|
+
# Tempo View Formatters are triggered by the View Reporter, and all inherit from
|
2
|
+
# Views::Base
|
3
|
+
#
|
4
|
+
# The screen formatter is the primary formatter for reporting results back to the
|
5
|
+
# screen. All formatting is handled after the main processes, when the Reporter is
|
6
|
+
# invoked during the post block. (If immediate feedback is needed,
|
7
|
+
# see Formatters::Console)
|
9
8
|
|
10
9
|
module Tempo
|
11
10
|
module Views
|
@@ -31,7 +30,7 @@ module Tempo
|
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
34
|
-
# PARTIALS
|
33
|
+
# PARTIALS vv-----------------------------------------------------------------vv
|
35
34
|
|
36
35
|
# spacer for project titles, active project marked with *
|
37
36
|
def active_indicator(project)
|
@@ -56,7 +55,7 @@ module Tempo
|
|
56
55
|
@options[:id] ? "[#{id}] ".rjust(6, ' ') : ""
|
57
56
|
end
|
58
57
|
|
59
|
-
# PARTIALS
|
58
|
+
# PARTIALS ^^-----------------------------------------------------------------^^
|
60
59
|
|
61
60
|
|
62
61
|
def project_block(record)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Tempo
|
2
|
+
module Views
|
3
|
+
class << self
|
4
|
+
|
5
|
+
# Must be allowed to return results
|
6
|
+
def interactive_query(query)
|
7
|
+
ViewRecords::Query.new query
|
8
|
+
end
|
9
|
+
|
10
|
+
def interactive_progress(message)
|
11
|
+
ViewRecords::Message.new message, category: :progress
|
12
|
+
end
|
13
|
+
|
14
|
+
def interactive_progress_partial(message)
|
15
|
+
ViewRecords::Message.new message, category: :progress_partial
|
16
|
+
end
|
17
|
+
|
18
|
+
def interactive_confirm_clean
|
19
|
+
query = "\nCleaning Tempo records resaves all records, looking for errors.\n" +
|
20
|
+
"In the event that a record cannot be corrected, you wil be prompted to repair the record manually.\n" +
|
21
|
+
"A backup of the records will also be created before any changes are made.\n\n" +
|
22
|
+
"Do you wish to continue? [YyNn]"
|
23
|
+
interactive_query(query)
|
24
|
+
end
|
25
|
+
|
26
|
+
def interactive_confirm_move_old_records
|
27
|
+
query = "\nYou have files which match an older file structure, do you want to move them so they will be included in your records? [YyNn]"
|
28
|
+
interactive_query(query)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/tempo/views/reporter.rb
CHANGED
@@ -6,6 +6,11 @@
|
|
6
6
|
#
|
7
7
|
# class instance variables:
|
8
8
|
#
|
9
|
+
# @@console
|
10
|
+
# A shell formtter which receives view records as they are created and can
|
11
|
+
# intercept messages that should be displayed in real time, as well as interactive
|
12
|
+
# prompts
|
13
|
+
#
|
9
14
|
# @@formats
|
10
15
|
# an array of formatters, which will be passed the view records on exit
|
11
16
|
# Reporter will always run the error formater first, to check for errors in
|
@@ -20,6 +25,7 @@ module Tempo
|
|
20
25
|
module Views
|
21
26
|
|
22
27
|
class Reporter
|
28
|
+
@@console
|
23
29
|
@@formats
|
24
30
|
@@view_records
|
25
31
|
@@options
|
@@ -40,6 +46,12 @@ module Tempo
|
|
40
46
|
@@options ||= {}
|
41
47
|
end
|
42
48
|
|
49
|
+
# All records are sent directly to the console, so it can decide if
|
50
|
+
# action is required immediately based on the type of record
|
51
|
+
def console
|
52
|
+
@@console ||= Formatters::Interactive.new(options)
|
53
|
+
end
|
54
|
+
|
43
55
|
def add_options(options)
|
44
56
|
@@options ||= {}
|
45
57
|
@@options.merge! options
|
@@ -50,6 +62,9 @@ module Tempo
|
|
50
62
|
|
51
63
|
if /Views::ViewRecords/.match record.class.name
|
52
64
|
@@view_records << record
|
65
|
+
|
66
|
+
# console must be able to return a value
|
67
|
+
return console.report record
|
53
68
|
else
|
54
69
|
raise InvalidViewRecordError
|
55
70
|
end
|
@@ -46,6 +46,30 @@ module Tempo
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
# Query records are handled by the console formatter,
|
50
|
+
# returning results from a call to Readline
|
51
|
+
class Query
|
52
|
+
attr_accessor :type, :query, :match, :response
|
53
|
+
|
54
|
+
def initialize(query, options={})
|
55
|
+
@query = query
|
56
|
+
@type = "query"
|
57
|
+
@match = options.fetch(:match, /(y|Y)(es)?/)
|
58
|
+
@response = Reporter.add_view_record self
|
59
|
+
end
|
60
|
+
|
61
|
+
def positive_response?
|
62
|
+
true if @response.match(@match)
|
63
|
+
end
|
64
|
+
|
65
|
+
def format(&block)
|
66
|
+
# TODO: should we create an interactive default? using:
|
67
|
+
# confirm = Readline.readline('> ', true).match(/(y|Y)(es)?/)
|
68
|
+
block ||= lambda { |q| puts "#{q.query}"; Readline.readline('> ', true) }
|
69
|
+
response = block.call self
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
49
73
|
# Specifically for managing a time duration, nested in other
|
50
74
|
# view records. This can be used with a start and end time,
|
51
75
|
# or used to manage a sum of times.
|
@@ -27,7 +27,7 @@ module Tempo
|
|
27
27
|
@description = model.description
|
28
28
|
@description ||= ""
|
29
29
|
@duration = Duration.new model.duration
|
30
|
-
@end_time = model.end_time == :running ? Time.now() : model.end_time
|
30
|
+
@end_time = model.end_time == :running ? Time.now().round : model.end_time
|
31
31
|
@project = model.project_title
|
32
32
|
@running = model.running?
|
33
33
|
self.class.max_description_length @description.length
|
@@ -2,23 +2,22 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
describe FileRecord do
|
4
4
|
|
5
|
-
|
5
|
+
def setup
|
6
6
|
@dir = File.join(Dir.home,"tempo")
|
7
7
|
@alt_dir = File.join(Dir.home, "testdir", "tempo")
|
8
8
|
FileUtils.rm_r @dir if File.exists?(@dir)
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
def teardown
|
12
12
|
FileUtils.rm_r @dir if File.exists?(@dir)
|
13
13
|
FileUtils.rm_r @alt_dir if File.exists?(@alt_dir)
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
16
|
describe "Directory" do
|
18
17
|
|
19
18
|
describe "initialize" do
|
20
19
|
|
21
|
-
it "
|
20
|
+
it "initializes a new directory structure" do
|
22
21
|
project_file = File.join(@dir, "tempo_projects.yaml")
|
23
22
|
readme = File.join(@dir, "README.txt")
|
24
23
|
|
@@ -28,7 +27,7 @@ describe FileRecord do
|
|
28
27
|
File.exists?( readme ).must_equal true
|
29
28
|
end
|
30
29
|
|
31
|
-
it "
|
30
|
+
it "takes an optional directory parameter" do
|
32
31
|
project_file = File.join(@alt_dir, "tempo_projects.yaml")
|
33
32
|
readme = File.join(@alt_dir, "README.txt")
|
34
33
|
dir = File.join(Dir.home, "testdir")
|
@@ -38,6 +37,10 @@ describe FileRecord do
|
|
38
37
|
File.exists?( project_file ).must_equal true
|
39
38
|
File.exists?( readme ).must_equal true
|
40
39
|
end
|
40
|
+
|
41
|
+
it "creates a zipped backup directory" do
|
42
|
+
|
43
|
+
end
|
41
44
|
end
|
42
45
|
end
|
43
46
|
end
|
@@ -28,13 +28,13 @@ describe FileRecord do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
describe "
|
31
|
+
describe "recording a Tempo Log" do
|
32
32
|
|
33
33
|
it "should create daily records containing each instance" do
|
34
|
-
test_file_1 = File.join(ENV['HOME'],'tempo','tempo_message_logs', '20140101.yaml')
|
35
|
-
File.delete(
|
36
|
-
test_file_2 = File.join(ENV['HOME'],'tempo','tempo_message_logs', '20140102.yaml')
|
37
|
-
File.delete(
|
34
|
+
test_file_1 = File.join(ENV['HOME'],'tempo','tempo_message_logs', '2014', '20140101.yaml')
|
35
|
+
File.delete( test_file_1 ) if File.exists?( test_file_1 )
|
36
|
+
test_file_2 = File.join(ENV['HOME'],'tempo','tempo_message_logs', '2014', '20140102.yaml')
|
37
|
+
File.delete( test_file_2 ) if File.exists?( test_file_2 )
|
38
38
|
|
39
39
|
log_factory
|
40
40
|
FileRecord::Record.save_log(Tempo::Model::MessageLog)
|
@@ -67,5 +67,8 @@ describe FileRecord do
|
|
67
67
|
":message: day 2 water the bonsai"]
|
68
68
|
end
|
69
69
|
end
|
70
|
+
|
71
|
+
describe "reading a Tempo Log" do
|
72
|
+
end
|
70
73
|
end
|
71
74
|
end
|
@@ -34,8 +34,8 @@ describe Tempo do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
it "knows which directory to save to" do
|
37
|
-
|
38
|
-
Tempo::Model::MessageLog.dir.must_equal "tempo_message_logs"
|
37
|
+
date = Time.new(2021,1,1)
|
38
|
+
Tempo::Model::MessageLog.dir(date).must_equal "tempo_message_logs"
|
39
39
|
end
|
40
40
|
|
41
41
|
it "knows which file name to save to" do
|
@@ -44,13 +44,14 @@ describe Tempo do
|
|
44
44
|
Tempo::Model::MessageLog.file(date).must_equal "20140101.yaml"
|
45
45
|
end
|
46
46
|
|
47
|
+
#TODO-NEW-LOG-DIR
|
47
48
|
it "grants children the ability to write to a file" do
|
48
49
|
log_factory
|
49
50
|
test_dir = File.join(ENV['HOME'],'tempo','tempo_message_logs')
|
50
51
|
FileUtils.rm_r test_dir if File.exists?(test_dir)
|
51
52
|
Tempo::Model::MessageLog.save_to_file
|
52
|
-
test_file_1 = File.join(test_dir, "20140101.yaml")
|
53
|
-
test_file_2 = File.join(test_dir, "20140102.yaml")
|
53
|
+
test_file_1 = File.join(test_dir, "2014/20140101.yaml")
|
54
|
+
test_file_2 = File.join(test_dir, "2014/20140102.yaml")
|
54
55
|
contents = eval_file_as_array( test_file_1 )
|
55
56
|
# testing with regex because time zone will be different on different computers,
|
56
57
|
# ex: ":start_time: 2014-01-02 07:15:00.000000000 -05:00"
|
@@ -92,6 +93,15 @@ describe Tempo do
|
|
92
93
|
Tempo::Model::MessageLog.index[0].message.must_equal "day 1 pet the sheep"
|
93
94
|
end
|
94
95
|
|
96
|
+
it "can clear records out after load" do
|
97
|
+
log_record_factory
|
98
|
+
time = Time.new(2014, 1, 1)
|
99
|
+
Tempo::Model::MessageLog.load_day_record time
|
100
|
+
Tempo::Model::MessageLog.ids( time ).must_equal [1,2,3]
|
101
|
+
Tempo::Model::MessageLog.clear_all
|
102
|
+
Tempo::Model::MessageLog.ids( time ).must_equal []
|
103
|
+
end
|
104
|
+
|
95
105
|
it "knows the date of the last record" do
|
96
106
|
log_record_factory
|
97
107
|
last_day = Tempo::Model::MessageLog.last_day
|
@@ -275,8 +275,8 @@ describe Tempo do
|
|
275
275
|
it "saves to file a collection of projects" do
|
276
276
|
time_record_factory
|
277
277
|
Tempo::Model::TimeRecord.save_to_file
|
278
|
-
test_file_1 = File.join(ENV['HOME'],'tempo/tempo_time_records/20140101.yaml')
|
279
|
-
test_file_2 = File.join(ENV['HOME'],'tempo/tempo_time_records/20140102.yaml')
|
278
|
+
test_file_1 = File.join(ENV['HOME'],'tempo/tempo_time_records/2014/20140101.yaml')
|
279
|
+
test_file_2 = File.join(ENV['HOME'],'tempo/tempo_time_records/2014/20140102.yaml')
|
280
280
|
contents = eval_file_as_array( test_file_1 )
|
281
281
|
|
282
282
|
# testing with regex because time zone will be different on different computers,
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
describe Tempo do
|
4
|
+
describe "Views" do
|
5
|
+
describe "Formatters" do
|
6
|
+
describe "Console" do
|
7
|
+
|
8
|
+
before do
|
9
|
+
view_records_factory
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "Message View Records" do
|
13
|
+
|
14
|
+
it "outputs the message" do
|
15
|
+
record = @progress_message
|
16
|
+
formatter = Tempo::Views::Formatters::Interactive.new
|
17
|
+
out = capture_stdout { formatter.report record }
|
18
|
+
|
19
|
+
assert_equal "Making progress...\n", out.string
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "Interactive View Records" do
|
24
|
+
|
25
|
+
it "outputs the interaction with a prompt" do
|
26
|
+
# pending
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -22,6 +22,12 @@ describe Tempo do
|
|
22
22
|
proc { Tempo::Views::Reporter.add_view_record record }.must_raise Tempo::InvalidViewRecordError
|
23
23
|
end
|
24
24
|
|
25
|
+
it "sends the reports immediately to the console formatter" do
|
26
|
+
Tempo::Views::Reporter.clear_records
|
27
|
+
out = capture_stdout { Tempo::Views::ViewRecords::Message.new "an immediate message to report", category: :immediate }
|
28
|
+
assert_equal "an immediate message to report\n", out.string
|
29
|
+
end
|
30
|
+
|
25
31
|
it "sends the reports to the screen formatter on report" do
|
26
32
|
Tempo::Views::Reporter.clear_records
|
27
33
|
record_1 = Tempo::Views::ViewRecords::Message.new "a message to report"
|
@@ -37,4 +43,4 @@ describe Tempo do
|
|
37
43
|
end
|
38
44
|
end
|
39
45
|
end
|
40
|
-
end
|
46
|
+
end
|
data/test/support/factories.rb
CHANGED
@@ -27,16 +27,6 @@ module Tempo
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
class Log
|
31
|
-
def self.clear_all()
|
32
|
-
@ids = {}
|
33
|
-
@index = []
|
34
|
-
@days_index = {}
|
35
|
-
@id_counter = {}
|
36
|
-
@current = nil
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
30
|
class MessageLog < Tempo::Model::Log
|
41
31
|
attr_accessor :message
|
42
32
|
|
@@ -133,10 +123,10 @@ end
|
|
133
123
|
# For creating a file to load records from
|
134
124
|
def log_record_factory
|
135
125
|
Tempo::Model::MessageLog.clear_all
|
136
|
-
test_dir = File.join(ENV['HOME'],'tempo','tempo_message_logs')
|
126
|
+
test_dir = File.join(ENV['HOME'],'tempo','tempo_message_logs','2014')
|
137
127
|
|
138
128
|
FileUtils.rm_r test_dir if File.exists?(test_dir)
|
139
|
-
|
129
|
+
FileUtils.mkdir_p(test_dir) unless File.exists?(test_dir)
|
140
130
|
file_lines = ["---", ":start_time: 2014-01-01 07:15:00.000000000 -05:00",
|
141
131
|
":id: 1", ":message: day 1 pet the sheep",
|
142
132
|
"---", ":start_time: 2014-01-01 07:45:00.000000000 -05:00",
|
@@ -170,13 +160,15 @@ end
|
|
170
160
|
|
171
161
|
def view_records_factory
|
172
162
|
time_record_factory
|
173
|
-
@message_1 = Tempo::Views::ViewRecords::Message.new "All The Things I Did"
|
174
|
-
@message_2 = Tempo::Views::ViewRecords::Message.new "on a busy busy day"
|
163
|
+
@message_1 = Tempo::Views::ViewRecords::Message.new "All The Things I Did"#, class: :title
|
164
|
+
@message_2 = Tempo::Views::ViewRecords::Message.new "on a busy busy day"#, class: :title
|
175
165
|
@error = Tempo::Views::ViewRecords::Message.new "raising an error", category: :error
|
176
166
|
@duration = Tempo::Views::ViewRecords::Duration.new 9600 # 2 hours and 40 minutes
|
177
167
|
@project_1 = Tempo::Views::ViewRecords::Project.new @project_1
|
178
168
|
@project_2 = Tempo::Views::ViewRecords::Project.new @project_2
|
179
169
|
@time_record_1 = Tempo::Views::ViewRecords::TimeRecord.new @record_1
|
180
170
|
@time_record_6 = Tempo::Views::ViewRecords::TimeRecord.new @record_6
|
181
|
-
|
171
|
+
@progress_message = Tempo::Views::ViewRecords::Message.new "Making progress", category: :progress
|
172
|
+
# change to interaction:
|
173
|
+
@interaction = Tempo::Views::ViewRecords::Message.new "Making progress", category: :progress
|
182
174
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tempo-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Gabel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -158,7 +158,9 @@ files:
|
|
158
158
|
- lib/tempo/views/arrange_view.rb
|
159
159
|
- lib/tempo/views/base.rb
|
160
160
|
- lib/tempo/views/formatters/base.rb
|
161
|
+
- lib/tempo/views/formatters/interactive.rb
|
161
162
|
- lib/tempo/views/formatters/screen.rb
|
163
|
+
- lib/tempo/views/interactive.rb
|
162
164
|
- lib/tempo/views/projects_view.rb
|
163
165
|
- lib/tempo/views/report_view.rb
|
164
166
|
- lib/tempo/views/reporter.rb
|
@@ -183,6 +185,7 @@ files:
|
|
183
185
|
- test/lib/tempo/models/time_record_test.rb
|
184
186
|
- test/lib/tempo/views/base_test.rb
|
185
187
|
- test/lib/tempo/views/formatters/base_test.rb
|
188
|
+
- test/lib/tempo/views/formatters/console_test.rb
|
186
189
|
- test/lib/tempo/views/formatters/screen_test.rb
|
187
190
|
- test/lib/tempo/views/reporter_test.rb
|
188
191
|
- test/lib/tempo/views/view_records/base_test.rb
|