wlog 1.1.1 → 1.1.5

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -48
  3. data/Rakefile +1 -0
  4. data/lib/wlog/commands/archive_finished_issues.rb +2 -2
  5. data/lib/wlog/commands/archive_issues.rb +1 -1
  6. data/lib/wlog/commands/bootstrap_templates.rb +37 -0
  7. data/lib/wlog/commands/concat_description.rb +7 -11
  8. data/lib/wlog/commands/delete_issue.rb +6 -5
  9. data/lib/wlog/commands/innit_db.rb +62 -6
  10. data/lib/wlog/commands/make_csv.rb +2 -6
  11. data/lib/wlog/commands/new_entry.rb +10 -7
  12. data/lib/wlog/commands/replace_pattern.rb +7 -5
  13. data/lib/wlog/domain/attachment.rb +50 -75
  14. data/lib/wlog/domain/invoice.rb +8 -2
  15. data/lib/wlog/domain/issue.rb +32 -132
  16. data/lib/wlog/domain/key_value.rb +10 -35
  17. data/lib/wlog/domain/log_entry.rb +18 -95
  18. data/lib/wlog/domain/static_configurations.rb +6 -0
  19. data/lib/wlog/domain/sys_config.rb +2 -5
  20. data/lib/wlog/migrations/make_standard_tables.rb +59 -0
  21. data/lib/wlog/ui/cli_interface.rb +55 -44
  22. data/lib/wlog/ui/commands/create_issue.rb +4 -8
  23. data/lib/wlog/ui/edit_handler.rb +74 -0
  24. data/lib/wlog/ui/invoice_ui.rb +133 -0
  25. data/lib/wlog/ui/issue_ui.rb +21 -77
  26. data/lib/wlog/ui/template_ui.rb +66 -0
  27. data/lib/wlog/version.rb +1 -1
  28. data/spec/domain/attachment_spec.rb +58 -59
  29. data/spec/domain/commands/concat_desc_spec.rb +10 -13
  30. data/spec/domain/commands/new_entry_spec.rb +10 -13
  31. data/spec/domain/commands/replace_pattern_spec.rb +11 -12
  32. data/spec/domain/issue_spec.rb +35 -38
  33. data/spec/domain/key_value_spec.rb +5 -8
  34. data/spec/domain/log_entry_spec.rb +20 -31
  35. data/spec/domain/sys_config_spec.rb +4 -7
  36. data/spec/make_db.rb +30 -4
  37. data/spec/tech/wlog_string_spec.rb +14 -14
  38. data/wlog.gemspec +2 -1
  39. metadata +26 -13
  40. data/lib/wlog/db_registry.rb +0 -38
  41. data/lib/wlog/domain/sql_modules/attachment_sql.rb +0 -21
  42. data/lib/wlog/domain/sql_modules/issue_sql.rb +0 -37
  43. data/lib/wlog/domain/sql_modules/key_value_sql.rb +0 -20
  44. data/lib/wlog/domain/sql_modules/log_entry_sql.rb +0 -35
  45. data/lib/wlog/ui/commands/attach_to_issue.rb +0 -0
@@ -1,5 +1,5 @@
1
- require 'wlog/db_registry'
2
- require 'wlog/domain/sql_modules/issue_sql'
1
+ require 'active_record'
2
+
3
3
  require 'wlog/domain/log_entry'
4
4
  require 'wlog/domain/timelog_helper'
5
5
  require 'wlog/domain/sys_config'
@@ -9,164 +9,64 @@ module Wlog
9
9
  # This aggregates log entries. The total time spent on this issue is
10
10
  # calculated from checking out said log entries.
11
11
  # @author Simon Symeonidis
12
- class Issue
13
- include IssueSql
14
-
15
- def initialize(db_handle)
16
- @reported_date = Time.now
17
- @log_entries = Array.new
18
- @status = @seconds = 0
19
- @db = db_handle
20
- @strmaker = SysConfig.string_decorator
21
- end
22
-
23
- # Calculate the total time that someone has wasted on all the
24
- # issues in the current database
25
- def self.total_time
26
- # issues = Issue.find_all
27
- end
28
-
29
- def self.find(db, id)
30
- issue = Issue.new(db)
31
- ret = db.execute(SelectSql, id).first
32
- if ret.nil? || ret.empty?
33
- issue = nil
34
- else
35
- issue.quick_assign! ret
36
- end
37
- issue end
38
-
39
- def self.find_all(db)
40
- self.generic_find_all(db, SelectAllSql)
41
- end
42
-
43
- def self.find_all_finished(db)
44
- self.generic_find_all(db, SelectFinishedSql)
45
- end
46
-
47
- def self.find_in_time_range(db, from, to)
48
- arr = Array.new
49
- db.execute(SelectTimeRange, from, to).each do |row|
50
- tmp = Issue.new(@db)
51
- tmp.quick_assign!(row)
52
- arr.push tmp
53
- end
54
- arr end
55
-
56
- def self.delete_by_id(db, id); db.execute(DeleteSql, id) end
57
-
58
- # inserts the entry into the database if it has not been stored before.
59
- def insert
60
- unless @id
61
- @db.execute(InsertSql, @description,
62
- @reported_date.to_i, @due_date.to_i, @status, @long_description)
63
- @id = @db.last_row_from(TableName).first[0]
64
- end
65
- end
12
+ class Issue < ActiveRecord::Base
66
13
 
67
- def delete; @db.execute(DeleteSql, @id) end
14
+ has_many :log_entries, dependent: :delete_all
68
15
 
69
- def update
70
- @db.execute(UpdateSql, @description, @reported_date.to_i,
71
- @due_date.to_i, @status, @seconds, @id)
72
- end
73
-
74
- # Add a log entry object to the issue
75
- # TODO this does nothing / used for nothing yet
76
- def add_log_entry(le)
77
- @log_entries.push le
78
- end
16
+ StatusNew = 0
17
+ StatusStarted = 1
18
+ StatusFinished = 2
19
+ StatusArchived = 3
79
20
 
80
- def quick_assign!(row)
81
- @id, @description, @reported_date, @due_date, @status, @seconds,
82
- @long_description =\
83
- row[0], row[1], Time.at(row[2]), Time.at(row[3]), row[4],
84
- row[5] || 0, row[6]
85
- nil end
21
+ # Anything which is not archived (eg: new, started work, finished)
22
+ def self.find_not_archived; where("status NOT IN (?)", StatusArchived) end
86
23
 
87
24
  # Log the seconds into the issue
88
- def log_time(seconds)
89
- @seconds += seconds
90
- update
25
+ def log_time(sec)
26
+ self.timelog += sec
27
+ save
91
28
  end
92
29
 
93
30
  def to_s
94
- "#{@strmaker.yellow('Issue')} ##{@id}#{$/}"\
95
- " #{@strmaker.blue('Reported')} : #{@reported_date.asctime}#{$/}"\
96
- " #{@strmaker.blue('Due')} : #{@due_date.asctime}#{$/}"\
97
- " #{@strmaker.blue('Entries')} : #{@log_entries.count}#{$/}"\
98
- " #{@strmaker.blue('Status')} : #{Statuses[@status]}#{$/}"\
99
- " #{@strmaker.blue('Time')} : #{TimelogHelper.time_to_s(@seconds)}#{$/}"\
31
+ le_count = self.log_entries.count
32
+ @strmaker = SysConfig.string_decorator
33
+ "#{@strmaker.yellow('Issue')} ##{id}#{$/}"\
34
+ " #{@strmaker.blue('Reported')} : #{created_at.asctime}#{$/}"\
35
+ " #{@strmaker.blue('Due')} : #{due_date.asctime}#{$/}"\
36
+ " #{@strmaker.blue('Entries')} : #{le_count} #{$/}"\
37
+ " #{@strmaker.blue('Status')} : #{Statuses[status]}#{$/}"\
38
+ " #{@strmaker.blue('Time')} : #{TimelogHelper.time_to_s(timelog)}#{$/}"\
100
39
  "#{$/}"\
101
40
  "#{@strmaker.yellow('Summary')} #{$/}"\
102
- " #{@description}#{$/ + $/}"\
41
+ " #{description}#{$/ + $/}"\
103
42
  "#{@strmaker.yellow('Description')} #{$/}"\
104
- " #{Helpers.break_string(@long_description, 80)}#{$/ + $/}"
43
+ " #{Helpers.break_string(long_description, 80)}#{$/ + $/}"
105
44
  end
106
45
 
107
46
  # Mark issue as started
108
- def mark_started!; @status = 0 end
47
+ def mark_started!; self.status = 0 end
109
48
 
110
49
  # Mark the issue as working
111
- def mark_working!; @status = 1 end
50
+ def mark_working!; self.status = 1 end
112
51
 
113
52
  # Mark the issue as finished
114
- def mark_finished!; @status = 2 end
53
+ def mark_finished!; self.status = 2 end
115
54
 
116
55
  # Archive the issue
117
- def archive!; @status = 3 end
56
+ def archive!; self.status = 3 end
118
57
 
119
58
  # Get the status as a string
120
- def status_s; Statuses[@status] end
121
-
122
- # [String] Description of the issue at hand
123
- attr_accessor :description
124
-
125
- # A longer description that can provide more details as opposed to a simple
126
- # title as suggested by @description.
127
- attr_accessor :long_description
128
-
129
- # [Time] The due date of the issue
130
- attr_accessor :due_date
131
-
132
- # [Time] The reported date of the issue
133
- attr_accessor :reported_date
134
-
135
- # [Array<LogEntry>] an array containing the log entries that are specific
136
- # to this issue
137
- attr_accessor :log_entries
138
-
139
- # [Fixnum] is the identifier of this object
140
- attr_accessor :id
141
-
142
- # [Fixnum] Status of the current issue (0 is for not started, 1 working on,
143
- # 2 for finished)
144
- attr_accessor :status
145
-
146
- # The seconds that you have wasted your life on in order to get something
147
- # done
148
- attr_accessor :seconds
149
-
150
- # The database handle for this AR
151
- attr_accessor :db
59
+ def status_s; Statuses[status] end
152
60
 
153
61
  private
154
-
155
62
  Statuses = {
156
- 0 => "new", 1 => "started work",
157
- 2 => "finished", 3 => "archived"}
63
+ StatusNew => "new",
64
+ StatusStarted => "started work",
65
+ StatusFinished => "finished",
66
+ StatusArchived => "archived"}
158
67
 
159
68
  private_class_method
160
69
 
161
- def self.generic_find_all(db, sql)
162
- arr = Array.new
163
- db.execute(sql).each do |row|
164
- issue = Issue.new(db)
165
- issue.quick_assign! row
166
- arr.push issue
167
- end
168
- arr end
169
-
170
70
  end # class Issue
171
71
  end # module Wlog
172
72
 
@@ -1,5 +1,4 @@
1
- require 'wlog/db_registry'
2
- require 'wlog/domain/sql_modules/key_value_sql'
1
+ require 'active_record'
3
2
  module Wlog
4
3
  # An active record that stores keys with values. Keys and values are strings.
5
4
  # convert as you need them.
@@ -9,51 +8,27 @@ module Wlog
9
8
  # '42', when looking up 'jon' you will retrieve only the latter (42).
10
9
  #
11
10
  # @author Simon Symeonidis
12
- class KeyValue
13
- include KeyValueSql
14
-
15
- def initialize(db_handle)
16
- @db = db_handle
17
- end
11
+ class KeyValue < ActiveRecord::Base
18
12
 
19
13
  # Insert a key in the storage. If exists, replace the value with new one
20
14
  # @return nil
21
- def put!(key, value)
22
- if get(key).nil?
23
- create!(key, value)
15
+ def self.put!(key, value)
16
+ if ret = KeyValue.find_by_key(key)
17
+ ret.value = value
24
18
  else
25
- update(key, value)
19
+ ret = KeyValue.new(:key => key, :value => value)
26
20
  end
21
+ ret.save
27
22
  end
28
23
 
29
24
  # Get a certain value by key
30
25
  # @return the value given the key. nil if not found
31
- def get(key)
32
- ret = @db.execute(Select, key)
33
- ret = ret.empty? ? nil : ret.first[1]
26
+ def self.get(key)
27
+ ret = find_by_key(key)
28
+ ret = ret ? ret.value : nil
34
29
  end
35
30
 
36
- # destroy by key
37
- def destroy!(key)
38
- @db.execute(DeleteSql, key)
39
- nil end
40
-
41
- # The db handle
42
- attr_accessor :db
43
-
44
31
  private
45
-
46
- # We want to provide a simple interface to the kv store, so the user should
47
- # not really care about updates
48
- def update(key,value)
49
- @db.execute(UpdateSql, value, key)
50
- end
51
-
52
- # Create a pair... ;)
53
- def create!(key, value)
54
- @db.execute(InsertSql, key, value)
55
- end
56
-
57
32
  end
58
33
  end # module Wlog
59
34
 
@@ -1,114 +1,37 @@
1
- require 'wlog/db_registry'
1
+ require 'active_record'
2
+
2
3
  require 'wlog/domain/helpers'
3
4
  require 'wlog/domain/attachment'
4
- require 'wlog/domain/sql_modules/log_entry_sql'
5
5
 
6
6
  module Wlog
7
7
  # Author :: Simon Symeonidis
8
8
  # Active Record Domain object for a log entry
9
- class LogEntry
10
- include LogEntrySql
11
-
12
- def initialize(db_handle)
13
- @date = Time.new
14
- @db = db_handle
15
- end
16
-
17
- def self.find(db, id)
18
- row = db.execute(Select,id).first
19
- le = nil
20
- if row && !row.empty?
21
- le = LogEntry.new(db)
22
- le.quick_assign!(row[0], row[1], Time.at(row[2]), row[3])
23
- end
24
- le end
25
-
26
- def self.find_all(db)
27
- self.generic_find_all(db, SelectAll)
28
- end
29
-
30
- def self.find_all_by_issue_id(db, id)
31
- self.generic_find_all(db, SelectAllByIssue, id)
32
- end
33
-
34
- # Delete a log entry with a given id.
35
- # @example Simple usage
36
- # # Since this is a class method:
37
- # LogEntry.delete(12)
38
- def self.delete_by_id(db, id)
39
- db.execute(DeleteSql,id)
40
- end
41
-
42
- # update the entry
43
- def update
44
- @db.execute(UpdateSql,@description,@id)
45
- end
46
-
47
- # Search by string to find a matching description with 'LIKE'.
48
- def self.search_descriptions(db, term)
49
- all = Array.new
50
- db.execute(SelectDescriptionLike,"%#{term}%").each do |row|
51
- le = LogEntry.new
52
- le.quick_assign!(row[0], row[1], Time.at(row[2]))
53
- all.push le
54
- end
55
- all end
56
-
57
- def insert
58
- raise 'Need issue_id' unless @issue_id
59
- unless @id
60
- @db.execute(InsertSql, @description, @date.to_i, @issue_id)
61
- @id = @db.last_row_from(TableName).first[0].to_i
62
- end
63
- end
64
-
65
- # Delete the loaded log entry currently in memory, by passing its id
66
- def delete
67
- if @id
68
- LogEntry.delete_by_id(@db, @id)
69
- @id = nil
70
- end
71
- end
72
-
73
- def quick_assign!(id,desc,date,issue_id)
74
- @id, @description, @date, @issue_id = id, desc, date, issue_id
75
- end
76
-
9
+ class LogEntry < ActiveRecord::Base
10
+
11
+ belongs_to :issue
12
+
13
+ # Search by string to find a matching description with 'LIKE'.
14
+ # def self.search_descriptions(db, term)
15
+ # all = Array.new
16
+ # db.execute(SelectDescriptionLike,"%#{term}%").each do |row|
17
+ # le = LogEntry.new
18
+ # le.quick_assign!(row[0], row[1], Time.at(row[2]))
19
+ # all.push le
20
+ # end
21
+ # all end
22
+
77
23
  # Print things nicely formmated no more than 80 cars (well, unless you stick
78
24
  # the time in the end which is not counted for).
79
25
  def to_s
80
- str = "[#{@id}] "
81
- tmp = "#{@description} [#{@date.strftime("%H:%M:%S")}]"
26
+ str = "[#{id}] "
27
+ tmp = "#{description} [#{created_at.strftime("%H:%M:%S")}]"
82
28
  desc = Helpers.break_string(tmp,80)
83
29
  indent = " " * (id.to_s.split('').count + 5)
84
30
  desc.gsub!(/#{$/}/, "#{$/}#{indent}")
85
31
  str.concat(desc)
86
32
  str end
87
33
 
88
- # The identity field for the log entry DO
89
- attr_accessor :id
90
-
91
- # Text description for the log entry
92
- attr_accessor :description
93
-
94
- # Date the entry was created
95
- attr_accessor :date
96
-
97
- # The issue id (parent of this log entry)
98
- attr_accessor :issue_id
99
-
100
- # The db handle
101
- attr_accessor :db
102
-
103
34
  private
104
- def self.generic_find_all(db, sql, *params)
105
- all = Array.new
106
- db.execute(sql, *params).each do |row|
107
- le = LogEntry.new(db)
108
- le.quick_assign!(row[0], row[1], Time.at(row[2]), row[3])
109
- all.push le
110
- end
111
- all end
112
35
  end
113
36
  end # module Wlog
114
37
 
@@ -20,6 +20,12 @@ module StaticConfigurations
20
20
  # Where the template files exist
21
21
  TemplateDir = "#{AppDirectory}templates/"
22
22
 
23
+ # Sample file to provide the user with
24
+ TemplateSampleFile = "#{TemplateDir}/default.erb"
25
+
26
+ # In the future if someone wants to code an alternative, go ahead
27
+ TemplateOutputDir = "#{ENV['HOME']}/Documents/wlog/"
28
+
23
29
  # Default database name (when unspecified)
24
30
  DefaultDb = "default"
25
31
 
@@ -8,9 +8,8 @@ module Wlog
8
8
  # @author Simon Symeonidis
9
9
  class SysConfig
10
10
 
11
- def initialize(db)
12
- @db = db
13
- @key_value = KeyValue.new(@db)
11
+ def initialize
12
+ @key_value = KeyValue
14
13
  end
15
14
 
16
15
  # load the last focused issue
@@ -59,8 +58,6 @@ class SysConfig
59
58
  end
60
59
  end
61
60
 
62
- attr_accessor :db
63
-
64
61
  # terms is a hash -> {:a => :b, :c => :d}
65
62
  # write each key value to a file like this:
66
63
  # a:b
@@ -0,0 +1,59 @@
1
+ require 'active_record'
2
+
3
+ module Wlog
4
+ # Migrations to replace the raw sql from turntables
5
+ # @author Simon Symeonidis
6
+ class MakeStandardTables < ActiveRecord::Migration
7
+ def change
8
+ create_table :issues do |t|
9
+ t.text :description
10
+ t.datetime :due_date
11
+ t.integer :status
12
+ t.integer :timelog, :limit => 8
13
+ t.text :long_description
14
+ t.datetime :created_at
15
+ t.datetime :updated_at
16
+ end
17
+
18
+ create_table :log_entries do |t|
19
+ t.text :description
20
+ t.integer :issue_id
21
+ t.datetime :created_at
22
+ t.datetime :updated_at
23
+ end
24
+
25
+ create_table :attachments do |t|
26
+ t.text :filename
27
+ t.text :given_name
28
+ t.text :data
29
+ t.datetime :created_at
30
+ t.datetime :updated_at
31
+ end
32
+
33
+ create_table :key_values do |t|
34
+ t.text :key
35
+ t.text :value
36
+ end
37
+
38
+ create_table :invoices do |t|
39
+ t.datetime :from
40
+ t.datetime :to
41
+ t.text :description
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+
48
+ =begin
49
+ TODO
50
+ -- A polymorphic relationship for attachments. So pretty much anything that
51
+ -- wants to have something attached, uses the discriminator in order to
52
+ -- specify itself, as well as its id.
53
+ CREATE TABLE polymorphic_attachments (
54
+ discriminator TEXT,
55
+ discriminator_id INTEGER,
56
+ attachment_id INTEGER
57
+ );
58
+
59
+ =end