ptf 0.1.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.
@@ -0,0 +1,67 @@
1
+ require 'ptf/commands/task/create'
2
+ require 'ptf/commands/task/edit'
3
+
4
+ module Ptf
5
+ module Commands
6
+ module Task
7
+ class << self
8
+
9
+ def show(id)
10
+ if !Ptf::Utilities::FileSystem.id_exist?(id.to_i)
11
+ puts "Task #{id} does not exist."
12
+ return
13
+ end
14
+
15
+ metadata_file = Ptf::Utilities::FileSystem.find_file id.to_i
16
+
17
+ if metadata_file.nil?
18
+ puts "Task #{id} has been closed. Reopen it with ptf task reopen ###."
19
+ return
20
+ end
21
+
22
+ info = Ptf::Data::MetadataFile.new(metadata_file)
23
+
24
+ data_file = File.join(Ptf::Utilities::FileSystem.data_dir, info.hash)
25
+ puts `cat #{data_file}`
26
+ end
27
+
28
+ def close(id)
29
+ if !Ptf::Utilities::FileSystem.id_exist?(id.to_i)
30
+ puts "Task #{id} does not exist."
31
+ return
32
+ end
33
+
34
+ metadata_file = Ptf::Utilities::FileSystem.find_file id.to_i
35
+
36
+ if metadata_file.nil?
37
+ puts "Task #{id} has been closed. Reopen it with ptf task reopen ###."
38
+ return
39
+ end
40
+
41
+ info = Ptf::Data::MetadataFile.create_from_file(metadata_file)
42
+
43
+ info.complete_now
44
+ end
45
+
46
+ def reopen(id)
47
+ if !Ptf::Utilities::FileSystem.id_exist?(id.to_i)
48
+ puts "Task #{id} does not exist."
49
+ return
50
+ end
51
+
52
+ metadata_file = Ptf::Utilities::FileSystem.find_file id.to_i, false
53
+
54
+ if metadata_file.nil?
55
+ puts "Task #{id} is already open. Close it with ptf task close ###."
56
+ return
57
+ end
58
+
59
+ info = Ptf::Data::MetadataFile.create_from_file(metadata_file)
60
+
61
+ info.reopen
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,13 @@
1
+ require 'ptf/commands/init'
2
+ require 'ptf/commands/list'
3
+ require 'ptf/commands/task'
4
+ require 'ptf/commands/group'
5
+
6
+ module Ptf
7
+ module Commands
8
+ class << self
9
+
10
+ end
11
+ end
12
+ end
13
+
data/lib/ptf/config.rb ADDED
@@ -0,0 +1,42 @@
1
+ require 'yaml'
2
+
3
+ module Ptf
4
+ module Config
5
+ YAML_LOCATION = File.join(Dir.home, ".ptfconfig")
6
+
7
+ EDITABLE = {
8
+ :default_group => "unassigned:NONE",
9
+ :base_dir => File.join(Dir.home, ".ptf")
10
+ }
11
+
12
+ STATIC = {
13
+ :file_permission => 0700,
14
+ :id_counter_start => "1",
15
+ :task_metadata_dir => "info",
16
+ :task_data_dir => "data",
17
+ :in_progress_dir => "open",
18
+ :completed_dir => "closed",
19
+ :tmp_dir => "tmp",
20
+ :task_counter_file => "id_counter",
21
+ :group_list_file => "group_list"
22
+ }
23
+
24
+ def self.get_config
25
+ yaml_settings = {}
26
+
27
+ yaml_settings = YAML.load(File.read(YAML_LOCATION)) if File.exist?(YAML_LOCATION)
28
+
29
+ EDITABLE.merge(yaml_settings).merge(STATIC)
30
+ end
31
+
32
+ def self.write_config(config)
33
+ new_config = EDITABLE.merge config.select { |k| EDITABLE.keys.include? k }
34
+
35
+ yaml_file = File.new(YAML_LOCATION, "w")
36
+ yaml_file.puts new_config.to_yaml
37
+ yaml_file.close
38
+ end
39
+
40
+ end
41
+ end
42
+
@@ -0,0 +1,42 @@
1
+ module Ptf
2
+ module Data
3
+ class DataFile
4
+
5
+ DIR_PATH = Ptf::Utilities::FileSystem.data_dir
6
+
7
+ # Initialize a new DataFile.
8
+ #
9
+ # @param metadata_file [Ptf::Data::MetadataFile] the associated metadata file.
10
+ #
11
+ # @raise [ArgumntError] if a MetadataFile is not given.
12
+ def initialize(metadata_file)
13
+ raise ArgumentError, "Metadata file not given." unless metadata_file.is_a? Ptf::Data::MetadataFile
14
+
15
+ @metadata_file = metadata_file
16
+ end
17
+
18
+ # Returns the full path to the data file.
19
+ #
20
+ # @return [String] the full path to the data file.
21
+ def data_file_path
22
+ File.join(DIR_PATH, @metadata_file.hash)
23
+ end
24
+
25
+ # Returns the header string for the file.
26
+ #
27
+ # @return [String] the header string for the data file.
28
+ def header_string
29
+ "##{@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"
30
+ end
31
+
32
+ # Write the header string to the data file. Overwrites any data file for the same task.
33
+ def write_to_file
34
+ File.open(data_file_path, "w") do |f|
35
+ f.write header_string
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,107 @@
1
+ module Ptf
2
+ module Data
3
+ class Group
4
+
5
+ def initialize(name, abbrev)
6
+ @name = name
7
+ @abbreviation = abbrev
8
+ end
9
+
10
+ def name
11
+ @name
12
+ end
13
+
14
+ def abbreviation
15
+ @abbreviation
16
+ end
17
+
18
+ def to_s
19
+ "#{name}:#{abbreviation}"
20
+ end
21
+
22
+ class << self
23
+ def from_name(group_name)
24
+ group_file = Ptf::Utilities::FileSystem.group_list_file
25
+ File.open(group_file, "r") do |f|
26
+ f.each_line do |l|
27
+ name, abbrev = l.gsub(/\s+/, "").split(":")
28
+
29
+ if group_name == name
30
+ return Group.new(name, abbrev)
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+ nil
37
+ end
38
+
39
+ def from_abbrev(group_abbrev)
40
+ group_file = Ptf::Utilities::FileSystem.group_list_file
41
+ File.open(group_file, "r") do |f|
42
+ f.each_line do |l|
43
+ name, abbrev = l.gsub(/\s+/, "").split(":")
44
+
45
+ if group_abbrev == abbrev
46
+ return Group.new(name, abbrev)
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+ nil
53
+ end
54
+
55
+ def get_group(group)
56
+ if(group.length <= 4 && group == group.upcase)
57
+ from_abbrev(group)
58
+ else
59
+ from_name(group)
60
+ end
61
+ end
62
+
63
+ def group_exist?(g)
64
+ group_file = Ptf::Utilities::FileSystem.group_list_file
65
+
66
+ File.open(group_file, "r") do |f|
67
+ f.each_line do |l|
68
+ name, abbrev = l.gsub(/\s+/, "").split(":")
69
+
70
+ if name == g || abbrev == g
71
+ return true
72
+ end
73
+
74
+ end
75
+ end
76
+
77
+ false
78
+ end
79
+
80
+ def all_groups
81
+ group_arr = []
82
+
83
+ File.open(Ptf::Utilities::FileSystem.group_list_file, "r") do |f|
84
+ f.each_line do |l|
85
+ name, abbrev = l.gsub(/\s+/, "").split(":")
86
+
87
+ group_arr.push (Ptf::Data::Group.new(name, abbrev))
88
+ end
89
+ end
90
+
91
+ group_arr
92
+ end
93
+
94
+ def default_group
95
+ config = Ptf::Config.get_config
96
+ default_group = config[:default_group]
97
+
98
+ name, abbrev = default_group.gsub(/\s+/, "").split(":")
99
+ Ptf::Data::Group.new(name, abbrev)
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+ end
106
+ end
107
+
@@ -0,0 +1,237 @@
1
+ module Ptf
2
+ module Data
3
+ # Represents a single Metadata file.
4
+ #
5
+ # @author Austin Blatt
6
+ # @since 0.1.0
7
+ class MetadataFile
8
+
9
+ # Create a MetadataFile object from the given input.
10
+ #
11
+ # @param title [String] the task's title.
12
+ # @param group [Ptf::Data::Group] the Group the task is associated with.
13
+ # @param due_date [DateTime, nil] the due date given for the task.
14
+ # @param estimate [Numeric, nil] the estimated time to complete the task.
15
+ # @param id [Integer] the unique ID number for this task.
16
+ #
17
+ # @return [Ptf::Data::MetadataFile] the metadata file for the task.
18
+ def self.create_from_input(title, group, due_date, estimate, id)
19
+ data = {
20
+ :title => title,
21
+ :group => group,
22
+ :due_date => due_date,
23
+ :estimate => estimate,
24
+ :id => id,
25
+ :created_at => DateTime.now,
26
+ :completed_at => nil
27
+ }
28
+
29
+ # Get the filepath for the infofile
30
+ open_dir = Ptf::Utilities::FileSystem.metadata_open_dir
31
+ group_dir = File.join(open_dir, group.name)
32
+ filepath = File.join(group_dir, id.to_s)
33
+
34
+ self.new(filepath, data)
35
+ end
36
+
37
+ # Create a MetadataFile object from the given filepath.
38
+ #
39
+ # @param filepath [String] the full file path to the metadata file.
40
+ #
41
+ # @raise [ArgumentError] if the given filepath does not exist.
42
+ # @raise [ArgumentError] if the given file is not a metadata file.
43
+ #
44
+ # @return [Ptf::Data::MetadataFile] the object representation of the metadata file.
45
+ def self.create_from_file(filepath)
46
+ raise ArgumentError, "File #{filepath} does not exist." unless Ptf::Utilities::FileSystem.file_exist?(filepath)
47
+
48
+ data = {}
49
+ # parse file
50
+ File.open(filepath, "r") do |f|
51
+ f.each_line do |l|
52
+ key, val = l.gsub(/\s+/, "").split(":")
53
+
54
+ if key.nil? || !(key.is_a? String)
55
+ raise ArgumentError, "Error parsing file."
56
+ end
57
+
58
+ # Convert values to appropriate types
59
+ case key
60
+ when "group"
61
+ val = Ptf::Data::Group.from_name(val)
62
+ when "due_date", "created_at", "completed_at"
63
+ val = (val.nil? ? val : Ptf::Utilities::Date.str_to_datetime(val))
64
+ when "id"
65
+ val = val.to_i
66
+ when "estimate"
67
+ val = (val.nil? ? nil : val.to_i)
68
+ end
69
+
70
+ data[key.to_sym] = val
71
+ end
72
+ end
73
+
74
+ return self.new(filepath, data)
75
+ end
76
+
77
+ # Initialize a new metadata file.
78
+ #
79
+ # @param filepath [String] the full path to the file.
80
+ # @param data [Hash] the data in the file.
81
+ # @option data [String] :title the task's title.
82
+ # @option data [Ptf::Data::Group] :group the Group the task is associated with.
83
+ # @option data [DateTime, nil] :due_date the due date given for the task.
84
+ # @option data [Integer, nil] :estimate the estimated time to complete the task.
85
+ # @option data [Integer] :id the unique ID number for this task.
86
+ # @option data [DateTime] :created_at the time the task was created.
87
+ # @option data [DateTime, nil] :completed_at the time the task was completed (nil if it has not been completed).
88
+ # @option data [String] :hash the file hash (the filepath of the data file).
89
+ def initialize(filepath, data)
90
+ @filepath = filepath
91
+ @data = data
92
+ end
93
+
94
+ # Add content to the metadata file.
95
+ #
96
+ # @param key [Symbol] the key for the new content.
97
+ # @param val [Object] the value for the new content.
98
+ #
99
+ # @raise [ArgumentError] if the key is not a symbol.
100
+ def add_content(key, val)
101
+ raise ArgumentError, "The key #{key} is not a symbol." unless key.is_a? Symbol
102
+
103
+ @data[key] = val
104
+ end
105
+
106
+ # Returns the title of the task.
107
+ #
108
+ # @return [String] the title of the task.
109
+ def title
110
+ @data[:title]
111
+ end
112
+
113
+ # Returns the group the task is associated with.
114
+ #
115
+ # @return [Ptf::Data::Group] the group the task is associated with.
116
+ def group
117
+ @data[:group]
118
+ end
119
+
120
+ # Returns the due date of the task.
121
+ #
122
+ # @return [DateTime, nil] the due date of the task or nil if no due date exists.
123
+ def due_date
124
+ @data[:due_date]
125
+ end
126
+
127
+ # Returns the due date of the task as a String.
128
+ #
129
+ # @return [String] the due date of the task as a string.
130
+ def due_date_str
131
+ return "" if due_date.nil?
132
+
133
+ Ptf::Utilities::Date.datetime_to_str(due_date)
134
+ end
135
+
136
+ # Returns the due date of the task for the ptf list command.
137
+ #
138
+ # @return [String] the due date in the form 'Mon Dec 19 - 11AM -' or 'Mon Dec 9 - 2PM -'.
139
+ def due_date_list_format
140
+ return "" if due_date.nil?
141
+
142
+ due_date.strftime("%a %b %_d - %l%p -")
143
+ end
144
+
145
+ # Returns the estimated time to complete the task.
146
+ #
147
+ # @return [Numeric] the estimated time to compelte the task.
148
+ def estimate
149
+ @data[:estimate]
150
+ end
151
+
152
+ # Returns the date and time that the task was created at.
153
+ #
154
+ # @return [DateTime] the date and time that the task was created at.
155
+ def created_at
156
+ @data[:created_at]
157
+ end
158
+
159
+ # Returns the date and time that the task was created at as a String.
160
+ #
161
+ # @return [String] the date and time represented as a string.
162
+ def created_at_str
163
+ Ptf::Utilities::Date.datetime_to_str(@data[:created_at])
164
+ end
165
+
166
+ # Return the id of the task.
167
+ #
168
+ # @return [Integer] the ID number of the task.
169
+ def id
170
+ @data[:id]
171
+ end
172
+
173
+ # Return the hash of the task.
174
+ #
175
+ # @return [String] the hash of the metadata file (the name of the data file).
176
+ def hash
177
+ @data[:hash]
178
+ end
179
+
180
+ # Completes the task.
181
+ #
182
+ # Fills in the completed_at field with the current DateTime.
183
+ # Removes the metadata file from the in progress directory and writes it to the completed directory.
184
+ def complete_now
185
+ @data[:completed_at] = DateTime.now
186
+
187
+ `rm #{@filepath}`
188
+ @filepath = File.join(File.join(Ptf::Utilities::FileSystem.metadata_closed_dir, group.name), id.to_s)
189
+
190
+ write_to_file
191
+ end
192
+
193
+ def reopen
194
+ `rm #{@filepath}`
195
+ @filepath = File.join(File.join(Ptf::Utilities::FileSystem.metadata_open_dir, group.name), id.to_s)
196
+
197
+ write_to_file
198
+ end
199
+
200
+ def key_val_to_str(key, val)
201
+ str_val = val
202
+ case key
203
+ when :group
204
+ str_val = val.name
205
+ when :due_date, :created_at, :completed_at
206
+ str_val = (val.nil? ? "" : Ptf::Utilities::Date.datetime_to_str(val))
207
+ end
208
+
209
+ "#{key}:#{str_val}"
210
+ end
211
+
212
+ # Returns the data in the file as a String.
213
+ #
214
+ # @return [String] the entire Metadata file as a String.
215
+ def file_string
216
+ return_str = ""
217
+ @data.each do |key, val|
218
+ return_str += "#{key_val_to_str(key, val)}\n"
219
+ end
220
+
221
+ return_str
222
+ end
223
+
224
+ # Writes the metadata file. Overwrites any previous metadata file for the same task.
225
+ def write_to_file
226
+ file = File.new(@filepath, "w")
227
+
228
+ @data.each do |key, val|
229
+ file.puts "#{key_val_to_str(key, val)}"
230
+ end
231
+ file.close
232
+ end
233
+
234
+ end
235
+ end
236
+ end
237
+
data/lib/ptf/data.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'ptf/data/group'
2
+ require 'ptf/data/metadata_file'
3
+ require 'ptf/data/data_file'
4
+
5
+ module Ptf
6
+ module Data
7
+ end
8
+ end
9
+
@@ -0,0 +1,63 @@
1
+ module Ptf
2
+ module Utilities
3
+ module Date
4
+ class << self
5
+
6
+ def datetime_to_str(datetime)
7
+ raise ArgumentError, "DateTime expected. Received #{datetime.class} instead." unless datetime.is_a? DateTime
8
+
9
+ datetime.strftime('%Y%m%d%H%M%S')
10
+ end
11
+
12
+ def create_datetime_from_str(str)
13
+
14
+ begin
15
+ dt = str_to_datetime(str)
16
+ return dt
17
+ rescue
18
+ end
19
+
20
+ time_regex = /([0-2]?\d):([0-5]\d):?([0-5]\d)?/
21
+ # Check if given time 12:21(:21)
22
+ match = time_regex.match(str).to_a
23
+
24
+ if !match.nil? && (match.length == 3 || match.length == 4)
25
+ now = DateTime.now
26
+ return DateTime.new(now.year, now.month, now.day, match[1].to_i, match[2].to_i, 0, now.zone)
27
+ end
28
+
29
+ # Check if given date 12/27(/[15|2015])
30
+ date_regex = /^([01]?\d)\/([0-3]?\d)\/?(\d\d\d\d|\d\d)?$/
31
+ match = date_regex.match(str)
32
+
33
+ if !match.nil? && (match.length == 3 || match.length == 4)
34
+ now = DateTime.now
35
+
36
+ if !match[3].nil? && match[3].length == 2
37
+ year = 2000 + match[3].to_i
38
+ elsif !match[3].nil?
39
+ year = match[3].to_i
40
+ end
41
+
42
+ return DateTime.new((match[3].nil? ? now.year : year), match[1].to_i, match[2].to_i, 12, 0, 0, now.zone)
43
+ end
44
+
45
+ nil
46
+ end
47
+
48
+ def str_to_datetime(str)
49
+ date_regex = /^(\d\d\d\d)([01]\d)([0-3]\d)([0-2]\d)([0-5]\d)([0-5]\d)?$/
50
+
51
+ regex_match = date_regex.match(str)
52
+
53
+ raise ArgumentError, "Improperly formatted datetime string, #{str}." unless regex_match.to_a.length == 7
54
+
55
+ _, year, month, day, hour, minute, second = regex_match.to_a
56
+
57
+ DateTime.new(year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, second.to_i)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,83 @@
1
+ module Ptf
2
+ module Utilities
3
+ module FileSystem
4
+ class << self
5
+
6
+ def file_exist?(filepath)
7
+ File.file?(filepath)
8
+ end
9
+
10
+ def config
11
+ @config = Ptf::Config.get_config if @config.nil?
12
+
13
+ @config
14
+ end
15
+
16
+ def file_permission
17
+ @config[:file_permission]
18
+ end
19
+
20
+ def base_dir
21
+ config[:base_dir]
22
+ end
23
+
24
+ def id_counter_file
25
+ File.join(base_dir, config[:task_counter_file])
26
+ end
27
+
28
+ def id_counter_starting_content
29
+ config[:id_counter_start]
30
+ end
31
+
32
+ def group_list_file
33
+ File.join(base_dir, config[:group_list_file])
34
+ end
35
+
36
+ def metadata_dir
37
+ File.join(base_dir, config[:task_metadata_dir])
38
+ end
39
+
40
+ def metadata_open_dir
41
+ File.join(metadata_dir, config[:in_progress_dir])
42
+ end
43
+
44
+ def metadata_closed_dir
45
+ File.join(metadata_dir, config[:completed_dir])
46
+ end
47
+
48
+ def data_dir
49
+ File.join(base_dir, config[:task_data_dir])
50
+ end
51
+
52
+ def tmp_dir
53
+ File.join(base_dir, config[:tmp_dir])
54
+ end
55
+
56
+ def id_exist?(id)
57
+ next_id = File.read(id_counter_file).to_i
58
+
59
+ (id < next_id && id > 0)
60
+ end
61
+
62
+ def find_file(id, open = true)
63
+ raise ArgumentError, "No task with id #{id} exists." unless id_exist?(id)
64
+
65
+ search_dir = (open ? metadata_open_dir : metadata_closed_dir)
66
+ groups = Ptf::Data::Group.all_groups
67
+
68
+ groups.each do |g|
69
+ dir = File.join(search_dir, g.name)
70
+ possible_id_file = File.join(dir, id.to_s)
71
+
72
+ if file_exist?(possible_id_file)
73
+ return possible_id_file
74
+ end
75
+ end
76
+
77
+ nil
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,28 @@
1
+ require 'ptf/utilities/date'
2
+ require 'ptf/utilities/file_system'
3
+
4
+ module Ptf
5
+ module Utilities
6
+ class << self
7
+
8
+ def datetime_to_str(datetime)
9
+ raise ArgumentError, "DateTime expected. Received #{datetime.class} instead." unless datetime.is_a? DateTime
10
+
11
+ datetime.strftime('%Y%m%d%H%M%S')
12
+ end
13
+
14
+ def str_to_datetime(str)
15
+ date_regex = /^([0-9][0-9][0-9][0-9])([01][0-9])([0-3][0-9])?([0-2][0-9])([0-5][0-9])([0-5][0-9])$/
16
+
17
+ regex_match = date_regex.match(str)
18
+
19
+ raise ArgumentError, "Improperly formatted datetime string." unless regex_match.to_a.length == 7
20
+
21
+ _, year, month, day, hour, minute, second = regex_match.to_a
22
+
23
+ DateTime.new(year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, second.to_i)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module Ptf
2
+ VERSION = "0.1.0"
3
+ end