ptf 0.1.0 → 0.2.0
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/lib/ptf.rb +5 -2
- data/lib/ptf/client.rb +4 -2
- data/lib/ptf/client/group.rb +5 -10
- data/lib/ptf/client/summary.rb +3 -3
- data/lib/ptf/client/task.rb +10 -10
- data/lib/ptf/commands/group.rb +1 -0
- data/lib/ptf/commands/group/add.rb +13 -18
- data/lib/ptf/commands/group/remove.rb +52 -0
- data/lib/ptf/commands/init.rb +19 -20
- data/lib/ptf/commands/list.rb +76 -21
- data/lib/ptf/commands/task.rb +19 -23
- data/lib/ptf/commands/task/create.rb +11 -11
- data/lib/ptf/commands/task/edit.rb +104 -20
- data/lib/ptf/data_file.rb +40 -0
- data/lib/ptf/date.rb +61 -0
- data/lib/ptf/file_system.rb +81 -0
- data/lib/ptf/group.rb +115 -0
- data/lib/ptf/metadata_file.rb +301 -0
- data/lib/ptf/version.rb +1 -1
- metadata +8 -9
- data/lib/ptf/data.rb +0 -9
- data/lib/ptf/data/data_file.rb +0 -42
- data/lib/ptf/data/group.rb +0 -107
- data/lib/ptf/data/metadata_file.rb +0 -237
- data/lib/ptf/utilities.rb +0 -28
- data/lib/ptf/utilities/date.rb +0 -63
- data/lib/ptf/utilities/file_system.rb +0 -83
data/lib/ptf/commands/task.rb
CHANGED
@@ -7,58 +7,54 @@ module Ptf
|
|
7
7
|
class << self
|
8
8
|
|
9
9
|
def show(id)
|
10
|
-
if !Ptf::
|
11
|
-
|
12
|
-
return
|
10
|
+
if !Ptf::FileSystem.id_exist?(id.to_i)
|
11
|
+
return "Task #{id} does not exist."
|
13
12
|
end
|
14
13
|
|
15
|
-
metadata_file = Ptf::
|
14
|
+
metadata_file = Ptf::FileSystem.find_file id.to_i
|
16
15
|
|
17
16
|
if metadata_file.nil?
|
18
|
-
|
19
|
-
return
|
17
|
+
return "Task #{id} has been closed. Reopen it with ptf task reopen ###."
|
20
18
|
end
|
21
19
|
|
22
|
-
info = Ptf::
|
20
|
+
info = Ptf::MetadataFile.create_from_file(metadata_file)
|
23
21
|
|
24
|
-
data_file = File.join(Ptf::
|
25
|
-
|
22
|
+
data_file = File.join(Ptf::FileSystem.data_dir, info.hash)
|
23
|
+
return File.read data_file
|
26
24
|
end
|
27
25
|
|
28
26
|
def close(id)
|
29
|
-
if !Ptf::
|
30
|
-
|
31
|
-
return
|
27
|
+
if !Ptf::FileSystem.id_exist?(id.to_i)
|
28
|
+
return "Task #{id} does not exist."
|
32
29
|
end
|
33
30
|
|
34
|
-
metadata_file = Ptf::
|
31
|
+
metadata_file = Ptf::FileSystem.find_file id.to_i
|
35
32
|
|
36
33
|
if metadata_file.nil?
|
37
|
-
|
38
|
-
return
|
34
|
+
return "Task #{id} has been closed. Reopen it with ptf task reopen ###."
|
39
35
|
end
|
40
36
|
|
41
|
-
info = Ptf::
|
37
|
+
info = Ptf::MetadataFile.create_from_file(metadata_file)
|
42
38
|
|
43
39
|
info.complete_now
|
40
|
+
return "Task #{info.id} is closed."
|
44
41
|
end
|
45
42
|
|
46
43
|
def reopen(id)
|
47
|
-
if !Ptf::
|
48
|
-
|
49
|
-
return
|
44
|
+
if !Ptf::FileSystem.id_exist?(id.to_i)
|
45
|
+
return "Task #{id} does not exist."
|
50
46
|
end
|
51
47
|
|
52
|
-
metadata_file = Ptf::
|
48
|
+
metadata_file = Ptf::FileSystem.find_file id.to_i, false
|
53
49
|
|
54
50
|
if metadata_file.nil?
|
55
|
-
|
56
|
-
return
|
51
|
+
return "Task #{id} is already open. Close it with ptf task close ###."
|
57
52
|
end
|
58
53
|
|
59
|
-
info = Ptf::
|
54
|
+
info = Ptf::MetadataFile.create_from_file(metadata_file)
|
60
55
|
|
61
56
|
info.reopen
|
57
|
+
return "Task #{info.id} is reopened."
|
62
58
|
end
|
63
59
|
|
64
60
|
end
|
@@ -11,7 +11,7 @@ module Ptf
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def new_id_number
|
14
|
-
id_file_path = Ptf::
|
14
|
+
id_file_path = Ptf::FileSystem.id_counter_file
|
15
15
|
next_id = File.read(id_file_path).to_i
|
16
16
|
|
17
17
|
id_file = File.new(id_file_path, "w")
|
@@ -29,38 +29,38 @@ module Ptf
|
|
29
29
|
|
30
30
|
# Select a group for the task
|
31
31
|
if group.nil?
|
32
|
-
group = Ptf::
|
32
|
+
group = Ptf::Group.default_group
|
33
33
|
else
|
34
|
-
if Ptf::
|
35
|
-
group = Ptf::
|
34
|
+
if Ptf::Group.group_exist? group
|
35
|
+
group = Ptf::Group.get_group group
|
36
36
|
else
|
37
|
-
|
38
|
-
return
|
37
|
+
return "Group #{p} does not exist. Create it with ptf group add GROUP ABBREVIATION"
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
42
41
|
# Verify the due date
|
43
42
|
if !due_date.nil?
|
44
|
-
due_date = Ptf::
|
43
|
+
due_date = Ptf::Date.create_datetime_from_str(due_date)
|
45
44
|
if due_date.nil?
|
46
|
-
|
47
|
-
return
|
45
|
+
return "Improperly formatted due date '#{d}' given."
|
48
46
|
end
|
49
47
|
end
|
50
48
|
|
51
49
|
id = new_id_number
|
52
50
|
|
53
|
-
info_file = Ptf::
|
51
|
+
info_file = Ptf::MetadataFile.create_from_input(title, group, due_date, estimate, id)
|
54
52
|
|
55
53
|
task_info_hash = generate_hash info_file.file_string
|
56
54
|
|
57
55
|
# Append hash to the file
|
58
56
|
info_file.add_content(:hash, task_info_hash)
|
59
57
|
|
60
|
-
data_file = Ptf::
|
58
|
+
data_file = Ptf::DataFile.new(info_file)
|
61
59
|
|
62
60
|
info_file.write_to_file
|
63
61
|
data_file.write_to_file
|
62
|
+
|
63
|
+
return "Task #{info_file.id} created successfully."
|
64
64
|
end
|
65
65
|
|
66
66
|
end
|
@@ -4,47 +4,131 @@ module Ptf
|
|
4
4
|
module Edit
|
5
5
|
class << self
|
6
6
|
|
7
|
-
def verify_file(file)
|
8
|
-
|
9
|
-
|
7
|
+
def verify_file(file, metadata)
|
8
|
+
title_line, due_date_line, estimate_line = File.foreach(file).first(3)
|
9
|
+
|
10
|
+
did_succeed, updated_metadata = verify_title_line title_line, metadata
|
11
|
+
return false unless did_succeed
|
12
|
+
|
13
|
+
did_succeed, updated_metadata = verify_due due_date_line, updated_metadata
|
14
|
+
return false unless did_succeed
|
15
|
+
|
16
|
+
did_succeed, updated_metadata = verify_estimate estimate_line, updated_metadata
|
17
|
+
return false unless did_succeed
|
18
|
+
|
19
|
+
updated_metadata.write_to_file
|
20
|
+
did_succeed
|
21
|
+
end
|
22
|
+
|
23
|
+
def verify_title_line(line, metadata)
|
24
|
+
title_line_regex = /^#([A-Z]+)--(\d+)\s([ a-zA-Z0-9]+)$/
|
25
|
+
|
26
|
+
_, group, id, title = title_line_regex.match(line).to_a
|
27
|
+
|
28
|
+
is_title_valid, updated_metadata = verify_title title, metadata
|
29
|
+
[(verify_group group, metadata) && (verify_id id, metadata) && is_title_valid, updated_metadata]
|
30
|
+
end
|
31
|
+
|
32
|
+
def verify_group(str, metadata)
|
33
|
+
return false unless !str.nil? && str.is_a?(String)
|
34
|
+
|
35
|
+
Ptf::Group.group_exist?(str) && metadata.group.abbreviation == str
|
36
|
+
end
|
37
|
+
|
38
|
+
def verify_id(str, metadata)
|
39
|
+
return false unless !str.nil? && str.is_a?(String)
|
40
|
+
|
41
|
+
str.to_i == metadata.id
|
42
|
+
end
|
43
|
+
|
44
|
+
def verify_title(str, metadata)
|
45
|
+
return false unless !str.nil? && str.is_a?(String)
|
46
|
+
|
47
|
+
if str != metadata.title
|
48
|
+
metadata.set_title str
|
49
|
+
end
|
50
|
+
|
51
|
+
[true, metadata]
|
52
|
+
end
|
53
|
+
|
54
|
+
def verify_due(line, metadata)
|
55
|
+
due_line_regex = /^#Due: ?(\d\d\d\d[01]\d[0-3]\d[0-2]\d[0-5]\d[0-5]\d)?$/
|
56
|
+
|
57
|
+
base, date = due_line_regex.match(line).to_a
|
58
|
+
|
59
|
+
if !base.nil? && date.nil?
|
60
|
+
metadata.set_due_date nil
|
61
|
+
return [true, metadata]
|
62
|
+
end
|
63
|
+
|
64
|
+
begin
|
65
|
+
datetime = Ptf::Date.str_to_datetime date
|
66
|
+
rescue
|
67
|
+
return [false, metadata]
|
68
|
+
end
|
69
|
+
|
70
|
+
metadata.set_due_date datetime
|
71
|
+
[!datetime.nil?, metadata]
|
72
|
+
end
|
73
|
+
|
74
|
+
def verify_estimate(line, metadata)
|
75
|
+
estimate_line_regex = /^#Estimate: ?(\d+)?$/
|
76
|
+
|
77
|
+
base, estimate = estimate_line_regex.match(line).to_a
|
78
|
+
|
79
|
+
if !base.nil? && estimate.nil?
|
80
|
+
metadata.set_estimate nil
|
81
|
+
return [true, metadata]
|
82
|
+
elsif base.nil?
|
83
|
+
return [false, metadata]
|
84
|
+
end
|
85
|
+
|
86
|
+
estimate = estimate.to_i
|
87
|
+
if estimate != 0
|
88
|
+
metadata.set_estimate estimate
|
89
|
+
return [true, metadata]
|
90
|
+
else
|
91
|
+
return [false, metadata]
|
92
|
+
end
|
10
93
|
end
|
11
94
|
|
12
95
|
def edit(id)
|
13
|
-
unless Ptf::
|
14
|
-
|
15
|
-
return
|
96
|
+
unless Ptf::FileSystem.id_exist? (id)
|
97
|
+
return "The task #{id} does not exist"
|
16
98
|
end
|
17
99
|
|
18
|
-
metadata_file = Ptf::
|
100
|
+
metadata_file = Ptf::FileSystem.find_file id
|
19
101
|
|
20
102
|
if metadata_file.nil?
|
21
|
-
|
22
|
-
return
|
103
|
+
return "The task #{id} has been closed. Reopen it with ptf task reopen ###."
|
23
104
|
end
|
24
105
|
|
25
106
|
# Get hash from file
|
26
|
-
|
27
|
-
|
107
|
+
metadata = Ptf::MetadataFile.create_from_file(metadata_file)
|
108
|
+
data_file_name = metadata.hash
|
109
|
+
data_file = File.join(Ptf::FileSystem.data_dir, data_file_name)
|
28
110
|
|
29
111
|
# Create a copy in tmp foldere
|
30
|
-
tmp_file_path = File.join(Ptf::
|
112
|
+
tmp_file_path = File.join(Ptf::FileSystem.tmp_dir, data_file_name)
|
31
113
|
FileUtils.copy data_file, tmp_file_path
|
32
114
|
edit_succeeded = system("vim #{data_file}")
|
33
115
|
|
34
116
|
if !edit_succeeded
|
35
117
|
# Move tmp file back, then remove it
|
36
|
-
|
37
|
-
|
118
|
+
|
119
|
+
FileUtils.copy tmp_file_path, data_file
|
120
|
+
FileUtils.remove_file tmp_file_path
|
121
|
+
return "Vim edit failed. Aborting changes."
|
38
122
|
end
|
39
123
|
|
40
|
-
if verify_file(data_file)
|
41
|
-
|
124
|
+
if verify_file(data_file, metadata)
|
125
|
+
FileUtils.remove_file tmp_file_path
|
126
|
+
return "Edit successful!"
|
42
127
|
else
|
43
|
-
|
128
|
+
FileUtils.copy tmp_file_path, data_file
|
129
|
+
FileUtils.remove_file tmp_file_path
|
130
|
+
return "File verification failed. Aborting changes."
|
44
131
|
end
|
45
|
-
|
46
|
-
# remove tmp file
|
47
|
-
FileUtils.remove_file tmp_file_path
|
48
132
|
end
|
49
133
|
|
50
134
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Ptf
|
2
|
+
class DataFile
|
3
|
+
|
4
|
+
DIR_PATH = Ptf::FileSystem.data_dir
|
5
|
+
|
6
|
+
# Initialize a new DataFile.
|
7
|
+
#
|
8
|
+
# @param metadata_file [Ptf::MetadataFile] the associated metadata file.
|
9
|
+
#
|
10
|
+
# @raise [ArgumntError] if a MetadataFile is not given.
|
11
|
+
def initialize(metadata_file)
|
12
|
+
raise ArgumentError, "Metadata file not given." unless metadata_file.is_a? Ptf::MetadataFile
|
13
|
+
|
14
|
+
@metadata_file = metadata_file
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the full path to the data file.
|
18
|
+
#
|
19
|
+
# @return [String] the full path to the data file.
|
20
|
+
def data_file_path
|
21
|
+
File.join(DIR_PATH, @metadata_file.hash)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the header string for the file.
|
25
|
+
#
|
26
|
+
# @return [String] the header string for the data file.
|
27
|
+
def header_string
|
28
|
+
"##{@metadata_file.group.abbreviation}--#{@metadata_file.id} #{@metadata_file.title}\n#Due: #{@metadata_file.due_date_str}\n#Estimate: #{@metadata_file.estimate}\n\n\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Write the header string to the data file. Overwrites any data file for the same task.
|
32
|
+
def write_to_file
|
33
|
+
File.open(data_file_path, "w") do |f|
|
34
|
+
f.write header_string
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
data/lib/ptf/date.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Ptf
|
2
|
+
module Date
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def datetime_to_str(datetime)
|
6
|
+
raise ArgumentError, "DateTime expected. Received #{datetime.class} instead." unless datetime.is_a? DateTime
|
7
|
+
|
8
|
+
datetime.strftime('%Y%m%d%H%M%S')
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_datetime_from_str(str)
|
12
|
+
|
13
|
+
begin
|
14
|
+
dt = str_to_datetime(str)
|
15
|
+
return dt
|
16
|
+
rescue
|
17
|
+
end
|
18
|
+
|
19
|
+
time_regex = /([0-2]?\d):([0-5]\d):?([0-5]\d)?/
|
20
|
+
# Check if given time 12:21(:21)
|
21
|
+
match = time_regex.match(str).to_a
|
22
|
+
|
23
|
+
if !match.nil? && (match.length == 3 || match.length == 4)
|
24
|
+
now = DateTime.now
|
25
|
+
return DateTime.new(now.year, now.month, now.day, match[1].to_i, match[2].to_i, 0, now.zone)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Check if given date 12/27(/[15|2015])
|
29
|
+
date_regex = /^([01]?\d)\/([0-3]?\d)\/?(\d\d\d\d|\d\d)?$/
|
30
|
+
match = date_regex.match(str)
|
31
|
+
|
32
|
+
if !match.nil? && (match.length == 3 || match.length == 4)
|
33
|
+
now = DateTime.now
|
34
|
+
|
35
|
+
if !match[3].nil? && match[3].length == 2
|
36
|
+
year = 2000 + match[3].to_i
|
37
|
+
elsif !match[3].nil?
|
38
|
+
year = match[3].to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
return DateTime.new((match[3].nil? ? now.year : year), match[1].to_i, match[2].to_i, 12, 0, 0, now.zone)
|
42
|
+
end
|
43
|
+
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def str_to_datetime(str)
|
48
|
+
date_regex = /^(\d\d\d\d)([01]\d)([0-3]\d)([0-2]\d)([0-5]\d)([0-5]\d)?$/
|
49
|
+
|
50
|
+
regex_match = date_regex.match(str)
|
51
|
+
|
52
|
+
raise ArgumentError, "Improperly formatted datetime string, #{str}." unless regex_match.to_a.length == 7
|
53
|
+
|
54
|
+
_, year, month, day, hour, minute, second = regex_match.to_a
|
55
|
+
|
56
|
+
DateTime.new(year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, second.to_i)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Ptf
|
2
|
+
module FileSystem
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def file_exist?(filepath)
|
6
|
+
File.file?(filepath)
|
7
|
+
end
|
8
|
+
|
9
|
+
def config
|
10
|
+
@config = Ptf::Config.get_config if @config.nil?
|
11
|
+
|
12
|
+
@config
|
13
|
+
end
|
14
|
+
|
15
|
+
def file_permission
|
16
|
+
@config[:file_permission]
|
17
|
+
end
|
18
|
+
|
19
|
+
def base_dir
|
20
|
+
config[:base_dir]
|
21
|
+
end
|
22
|
+
|
23
|
+
def id_counter_file
|
24
|
+
File.join(base_dir, config[:task_counter_file])
|
25
|
+
end
|
26
|
+
|
27
|
+
def id_counter_starting_content
|
28
|
+
config[:id_counter_start]
|
29
|
+
end
|
30
|
+
|
31
|
+
def group_list_file
|
32
|
+
File.join(base_dir, config[:group_list_file])
|
33
|
+
end
|
34
|
+
|
35
|
+
def metadata_dir
|
36
|
+
File.join(base_dir, config[:task_metadata_dir])
|
37
|
+
end
|
38
|
+
|
39
|
+
def metadata_open_dir
|
40
|
+
File.join(metadata_dir, config[:in_progress_dir])
|
41
|
+
end
|
42
|
+
|
43
|
+
def metadata_closed_dir
|
44
|
+
File.join(metadata_dir, config[:completed_dir])
|
45
|
+
end
|
46
|
+
|
47
|
+
def data_dir
|
48
|
+
File.join(base_dir, config[:task_data_dir])
|
49
|
+
end
|
50
|
+
|
51
|
+
def tmp_dir
|
52
|
+
File.join(base_dir, config[:tmp_dir])
|
53
|
+
end
|
54
|
+
|
55
|
+
def id_exist?(id)
|
56
|
+
next_id = File.read(id_counter_file).to_i
|
57
|
+
|
58
|
+
(id < next_id && id > 0)
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_file(id, open = true)
|
62
|
+
raise ArgumentError, "No task with id #{id} exists." unless id_exist?(id)
|
63
|
+
|
64
|
+
search_dir = (open ? metadata_open_dir : metadata_closed_dir)
|
65
|
+
groups = Ptf::Group.all_groups
|
66
|
+
|
67
|
+
groups.each do |g|
|
68
|
+
dir = File.join(search_dir, g.name)
|
69
|
+
possible_id_file = File.join(dir, id.to_s)
|
70
|
+
|
71
|
+
if file_exist?(possible_id_file)
|
72
|
+
return possible_id_file
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|