logtime 0.0.2
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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.gitignore +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +40 -0
- data/LICENSE.md +21 -0
- data/VERSION +1 -0
- data/bin/logtime +3 -0
- data/certs/paperback.pem +21 -0
- data/config/application.rb +6 -0
- data/config/database.rb +4 -0
- data/config/initialisation.rb +8 -0
- data/config/schema.rb +41 -0
- data/lib/app/commands/add.rb +41 -0
- data/lib/app/commands/list.rb +56 -0
- data/lib/app/commands/start.rb +28 -0
- data/lib/app/commands/stop.rb +29 -0
- data/lib/app/helpers/estimate.rb +13 -0
- data/lib/app/helpers/statistics.rb +45 -0
- data/lib/app/helpers/time_to_words.rb +33 -0
- data/lib/app/logtime.rb +67 -0
- data/lib/app/models/log.rb +42 -0
- data/lib/app/models/series.rb +35 -0
- data/lib/app/models/tag.rb +9 -0
- data/lib/app/subcommands/keyword.rb +214 -0
- data/lib/app/subcommands/series.rb +58 -0
- data/lib/app/subcommands/tags.rb +79 -0
- data/lib/commands/add.rb +41 -0
- data/lib/commands/list.rb +67 -0
- data/lib/commands/start.rb +28 -0
- data/lib/commands/stop.rb +29 -0
- data/lib/commands/version.rb +12 -0
- data/lib/helpers/estimate.rb +13 -0
- data/lib/helpers/statistics.rb +48 -0
- data/lib/helpers/time_difference.rb +20 -0
- data/lib/helpers/time_display.rb +5 -0
- data/lib/helpers/time_to_words.rb +33 -0
- data/lib/logtime.rb +68 -0
- data/lib/models/log.rb +47 -0
- data/lib/models/series.rb +35 -0
- data/lib/models/tag.rb +9 -0
- data/lib/subcommands/keyword.rb +214 -0
- data/lib/subcommands/series.rb +59 -0
- data/lib/subcommands/tags.rb +37 -0
- data/logtime.gemspec +23 -0
- data.tar.gz.sig +2 -0
- metadata +167 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 22377c5c1f038237e3b5999f1ceaf12a7b5df7e7
|
4
|
+
data.tar.gz: ab756e60d54c8b8cc12b8cc62ff9f48f113000f6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d01528ded70155208de1635016846bca56e6e859b88d03676eaff2870725831be54b85e95355fa3e836164bbc98bf7eb5043905c6296519fd36b48f95044ef79
|
7
|
+
data.tar.gz: 33a19b730e74c339d7f42706ce2c201e7c9ebf0474c32a415035b7559a782870ab05e95c3157a4f24ae1f70ac04b893a31502338b3502589a838fdd0ab4e4c98
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activemodel (4.1.10)
|
5
|
+
activesupport (= 4.1.10)
|
6
|
+
builder (~> 3.1)
|
7
|
+
activerecord (4.1.10)
|
8
|
+
activemodel (= 4.1.10)
|
9
|
+
activesupport (= 4.1.10)
|
10
|
+
arel (~> 5.0.0)
|
11
|
+
activesupport (4.1.10)
|
12
|
+
i18n (~> 0.6, >= 0.6.9)
|
13
|
+
json (~> 1.7, >= 1.7.7)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
thread_safe (~> 0.1)
|
16
|
+
tzinfo (~> 1.1)
|
17
|
+
arel (5.0.1.20140414130214)
|
18
|
+
builder (3.2.2)
|
19
|
+
chronic (0.10.2)
|
20
|
+
chronic_duration (0.10.6)
|
21
|
+
numerizer (~> 0.1.1)
|
22
|
+
i18n (0.7.0)
|
23
|
+
json (1.8.2)
|
24
|
+
minitest (5.6.1)
|
25
|
+
numerizer (0.1.1)
|
26
|
+
sqlite3 (1.3.10)
|
27
|
+
thor (0.19.1)
|
28
|
+
thread_safe (0.3.5)
|
29
|
+
tzinfo (1.2.2)
|
30
|
+
thread_safe (~> 0.1)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
activerecord (~> 4.1.6)
|
37
|
+
chronic (= 0.10.2)
|
38
|
+
chronic_duration (= 0.10.6)
|
39
|
+
sqlite3 (~> 1.3.9)
|
40
|
+
thor (~> 0.19.1)
|
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Sam 'Paperback' Sweeney
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
data/bin/logtime
ADDED
data/certs/paperback.pem
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDZDCCAkygAwIBAgIBATANBgkqhkiG9w0BAQUFADA8MQwwCgYDVQQDDANzYW0x
|
3
|
+
FzAVBgoJkiaJk/IsZAEZFgdzYW13aGF0MRMwEQYKCZImiZPyLGQBGRYDY29tMB4X
|
4
|
+
DTE1MDYwMjA5NTQxOVoXDTE2MDYwMTA5NTQxOVowPDEMMAoGA1UEAwwDc2FtMRcw
|
5
|
+
FQYKCZImiZPyLGQBGRYHc2Ftd2hhdDETMBEGCgmSJomT8ixkARkWA2NvbTCCASIw
|
6
|
+
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPKKZV2uNAV6W+IlPDriPQNMPp7C
|
7
|
+
Xw7yNlbhmrmgNxJotT4DP6Eyc1NzAJdfz5kQMqgrzxRWBbFD1wPFJB3IdgX1PGKi
|
8
|
+
VOV/BO6sFBFrje/2a364FtKkGjm5V+zyeNZO/DUMuFalnbs+OWvfktFrx2JdBWeU
|
9
|
+
Utmtx3+AcW91+OFZ8y7ZZjUd1FbNP6A+WTYh2odUDFCQXe4xk3bcSGa0Ca7uAfv8
|
10
|
+
VMm1cWu23n+cer1dUW0VspFprSoo/f1WGduZus8PjBFAgEQdDDmTbB49DrigilzT
|
11
|
+
swXvrzGfCKZ0OveVFmhJz1Zr9WUlFNs/GU5x2iQpIZjg1S1mbIfhRhw+H1MCAwEA
|
12
|
+
AaNxMG8wCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFOGu79hFpVdT
|
13
|
+
ALi15MDDorfHC7nPMBoGA1UdEQQTMBGBD3NhbUBzYW13aGF0LmNvbTAaBgNVHRIE
|
14
|
+
EzARgQ9zYW1Ac2Ftd2hhdC5jb20wDQYJKoZIhvcNAQEFBQADggEBAIQ69hz8tXeJ
|
15
|
+
YPTz23tOss7qcY9xo5HInXQXfQ8wZGNVaBPmtBognGO/Kqf5PxeoaZ3FfT8pE9qz
|
16
|
+
giuth+pA2j2EXCBwEuu6+I4J9lfIT030uVMaBIQ1JJxKZcXtRpOP2yd/gKodDVn/
|
17
|
+
KIGKFrb3RWI2VVKNYV7lBCtHyx4uhEBvHmOhHKm3vlkzc+IZWQtCHhJ9iSKSefZ+
|
18
|
+
fZbMJQmYpt4/9KimNcpD+k3mQwMEW947060mRhnpAGkwlIujltb5lQwlWQoKm/Z4
|
19
|
+
2/luIWFCIDih/t3xy/BezkXJLXfRZF2Xd6/Ax9+zEVss6iUJ6kcxwaZ/J5r28EDI
|
20
|
+
KS3vMuAGgck=
|
21
|
+
-----END CERTIFICATE-----
|
data/config/database.rb
ADDED
data/config/schema.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
|
3
|
+
#
|
4
|
+
# Logs
|
5
|
+
#
|
6
|
+
create_table :logs do |table|
|
7
|
+
table.column :name, :string
|
8
|
+
table.column :active, :string, default: false
|
9
|
+
table.column :estimation, :decimal
|
10
|
+
table.timestamps
|
11
|
+
end unless ActiveRecord::Base.connection.table_exists? 'logs'
|
12
|
+
|
13
|
+
|
14
|
+
#
|
15
|
+
# Logs Tags
|
16
|
+
#
|
17
|
+
create_table :logs_tags do |table|
|
18
|
+
table.column :log_id, :integer
|
19
|
+
table.column :tag_id, :integer
|
20
|
+
end unless ActiveRecord::Base.connection.table_exists? 'logs_tags'
|
21
|
+
|
22
|
+
|
23
|
+
#
|
24
|
+
# Tags
|
25
|
+
#
|
26
|
+
create_table :tags do |table|
|
27
|
+
table.column :tag, :string
|
28
|
+
table.timestamps
|
29
|
+
end unless ActiveRecord::Base.connection.table_exists? 'tags'
|
30
|
+
|
31
|
+
#
|
32
|
+
# Series
|
33
|
+
#
|
34
|
+
create_table :series do |table|
|
35
|
+
table.column :log_id, :integer
|
36
|
+
table.column :start, :timestamp
|
37
|
+
table.column :end, :timestamp
|
38
|
+
table.timestamps
|
39
|
+
end unless ActiveRecord::Base.connection.table_exists? 'series'
|
40
|
+
|
41
|
+
end unless File.exists?(File.join($config_path, 'database.db'))
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Commands
|
2
|
+
module Add
|
3
|
+
def self.included(thor)
|
4
|
+
thor.class_eval do
|
5
|
+
option :tag, :type => :string, :alias => '-t', default: ""
|
6
|
+
option :estimate, :type => :string, :alias => '-e', default: false
|
7
|
+
option :start, :type => :boolean, default: true
|
8
|
+
desc "add [NAME]", "create new log"
|
9
|
+
def add(name)
|
10
|
+
log = Log.new(name: name).tag(options[:tag])
|
11
|
+
|
12
|
+
# estimate option
|
13
|
+
if options[:estimate]
|
14
|
+
estimation = ChronicDuration.parse(options[:estimate]) if !estimation
|
15
|
+
if !estimation
|
16
|
+
say "could not parse estimation time: " + options[:estimate], :red
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
log.estimation = estimation
|
20
|
+
end
|
21
|
+
|
22
|
+
# save log
|
23
|
+
if !log.save
|
24
|
+
log.errors.full_messages.each do |error|
|
25
|
+
say error, :red
|
26
|
+
end
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
# start option
|
31
|
+
if options[:start]
|
32
|
+
log.activate if Series.begin(log_id: log.id)
|
33
|
+
say name.to_s + " added and started", :green
|
34
|
+
else
|
35
|
+
say name.to_s + " added", :green
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Commands
|
2
|
+
module List
|
3
|
+
def self.included(thor)
|
4
|
+
thor.class_eval do
|
5
|
+
option :active, :type => :boolean, :default => true
|
6
|
+
option :tag, :type => :boolean, :alias => '-t'
|
7
|
+
desc "ls", "list time series"
|
8
|
+
def ls()
|
9
|
+
logs = Log.where(active: options[:active]).includes(:series)
|
10
|
+
# logs = Tag.where(tag: options[:tag]).logs if options[:tag]
|
11
|
+
|
12
|
+
|
13
|
+
creep_stats = Statistics.new
|
14
|
+
table = [['#', 'name', 'active', 'tags', 'series', 'time', 'estimate', 'creep %']] # header
|
15
|
+
logs.each do |log|
|
16
|
+
|
17
|
+
# tags
|
18
|
+
tags = log.tags.map do |t|
|
19
|
+
t.tag
|
20
|
+
end
|
21
|
+
|
22
|
+
# active text
|
23
|
+
active = "ACTIVE" if log.active == "t"
|
24
|
+
|
25
|
+
# total counter
|
26
|
+
total_time = (log.total/3600).round(2)
|
27
|
+
|
28
|
+
# estimation creep
|
29
|
+
if log.estimation
|
30
|
+
creep = (total_time/log.estimation)*100
|
31
|
+
creep_stats << creep
|
32
|
+
creep = creep.round(3)
|
33
|
+
estimation = (log.estimation/3600).round(3) # log estimation to hours
|
34
|
+
end
|
35
|
+
|
36
|
+
table << [log.id,
|
37
|
+
log.name,
|
38
|
+
active || '',
|
39
|
+
tags,
|
40
|
+
log.series.count || '',
|
41
|
+
total_time || '',
|
42
|
+
estimation || '',
|
43
|
+
creep || '']
|
44
|
+
end
|
45
|
+
puts ""
|
46
|
+
print_table table
|
47
|
+
puts ""
|
48
|
+
|
49
|
+
say [logs.count.to_s, "logs out of", Log.count.to_s].join(' '), :cyan
|
50
|
+
say [creep_stats.count, "estimations"].join(' '), :cyan
|
51
|
+
say [creep_stats.mean.round(3), "avg creep"].join(' '), :cyan
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Commands
|
2
|
+
module Start
|
3
|
+
def self.included(thor)
|
4
|
+
thor.class_eval do
|
5
|
+
desc "start [NAME]", "start new timer"
|
6
|
+
def start(name)
|
7
|
+
|
8
|
+
# not found
|
9
|
+
if Log.where(name: name).count == 0
|
10
|
+
say name.to_s + " not found", :red
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
log = Log.find_by(name: name)
|
15
|
+
|
16
|
+
# already active?
|
17
|
+
if log.active == "t"
|
18
|
+
say name.to_s + " already active", :red
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
# begin new series, activate log
|
23
|
+
series = Series.begin(log_id: log.id) and log.activate
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Commands
|
2
|
+
module Stop
|
3
|
+
def self.included(thor)
|
4
|
+
thor.class_eval do
|
5
|
+
desc "stop [NAME]", "stop active timer"
|
6
|
+
def stop(name)
|
7
|
+
if Log.where(name: name).count == 0
|
8
|
+
say name.to_s + " not found", :red
|
9
|
+
exit
|
10
|
+
end
|
11
|
+
|
12
|
+
log = Log.find_by(name: name)
|
13
|
+
if log.active == "f"
|
14
|
+
say name.to_s + " not active", :red
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
|
18
|
+
series = log.series.last
|
19
|
+
if series.stop and log.deactivate
|
20
|
+
say name + " stopped!", :green
|
21
|
+
|
22
|
+
# this_series = log.series.last
|
23
|
+
say (series.total/3600).round(3).to_s + " hours out of " + (log.total/3600).round(3).to_s, :cyan
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module StatisticsHelper
|
2
|
+
class Series < Array
|
3
|
+
def sum
|
4
|
+
inject(0.0) { |result, el| result + el }
|
5
|
+
end
|
6
|
+
|
7
|
+
def mean
|
8
|
+
sum / size
|
9
|
+
end
|
10
|
+
|
11
|
+
alias :average :mean
|
12
|
+
end
|
13
|
+
|
14
|
+
class Statistics
|
15
|
+
def initialize
|
16
|
+
@statistics = Series.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(statistic)
|
20
|
+
@statistics << statistic
|
21
|
+
end
|
22
|
+
|
23
|
+
def count
|
24
|
+
@statistics.count
|
25
|
+
end
|
26
|
+
|
27
|
+
def mean
|
28
|
+
@statistics.mean
|
29
|
+
end
|
30
|
+
|
31
|
+
def <<(statistic)
|
32
|
+
add(statistic)
|
33
|
+
end
|
34
|
+
|
35
|
+
def +(statistics)
|
36
|
+
@statistics = @statistics + statistics.data
|
37
|
+
end
|
38
|
+
|
39
|
+
def data
|
40
|
+
@statistics
|
41
|
+
end
|
42
|
+
|
43
|
+
alias :average :mean
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module TimeToWordsHelper
|
2
|
+
def time_to_words_ago(time)
|
3
|
+
a = (Time.now-time).to_i
|
4
|
+
case a
|
5
|
+
when 0 then 'just now'
|
6
|
+
when 1 then 'a second ago'
|
7
|
+
when 2..59 then a.to_s+' seconds ago'
|
8
|
+
when 60..119 then 'a minute ago' # 120 = 2 minutes
|
9
|
+
when 120..3540 then (a/60).to_i.to_s+' minutes ago'
|
10
|
+
when 3541..7100 then 'an hour ago' # 3600 = 1 hour
|
11
|
+
when 7101..82800 then ((a+99)/3600).to_i.to_s+' hours ago'
|
12
|
+
when 82801..172000 then 'a day ago' # 86400 = 1 day
|
13
|
+
when 172001..518400 then ((a+800)/(60*60*24)).to_i.to_s+' days ago'
|
14
|
+
when 518400..1036800 then 'a week ago'
|
15
|
+
else ((a+180000)/(60*60*24*7)).to_i.to_s+' weeks ago'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def time_to_words(seconds)
|
20
|
+
seconds
|
21
|
+
case a
|
22
|
+
when 0..60 then 'seconds'
|
23
|
+
when 60..69 then 'minute'
|
24
|
+
when 70..3600 then 'minutes'
|
25
|
+
when 3600..7199 then 'day'
|
26
|
+
when 7200..107999 then 'days' # 3600 = 1 hour
|
27
|
+
when 108000..215999 then 'month'
|
28
|
+
when 216000..1296000 then 'months'
|
29
|
+
when 1296000..1296000 then 'year'
|
30
|
+
else 'years'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/app/logtime.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require '../config/initialisation'
|
2
|
+
|
3
|
+
# require models
|
4
|
+
models_path = '../app/models'
|
5
|
+
if Dir.exists?(models_path)
|
6
|
+
Dir.entries(models_path).each do |file|
|
7
|
+
require File.join(models_path, file) if file =~ /^(.*)\.rb$/
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# require helpers
|
12
|
+
helpers_path = '../app/helpers'
|
13
|
+
if Dir.exists?(helpers_path)
|
14
|
+
Dir.entries(helpers_path).each do |file|
|
15
|
+
next unless file =~ /^(.*)\.rb$/
|
16
|
+
|
17
|
+
require File.join(helpers_path, file)
|
18
|
+
|
19
|
+
# helper name
|
20
|
+
load = file.split '_'
|
21
|
+
load = load.each { |h| h.capitalize! }
|
22
|
+
load = load.join
|
23
|
+
load = load[0..-4] # '.rb'
|
24
|
+
load = load + "Helper"
|
25
|
+
|
26
|
+
# load helper
|
27
|
+
# begin
|
28
|
+
send(:include, load.constantize)
|
29
|
+
# rescue
|
30
|
+
# ActiveRecord::Base.logger.warn "Failed to load " + load
|
31
|
+
# end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# require commands
|
36
|
+
commands_path = '../app/commands'
|
37
|
+
if Dir.exists?(commands_path)
|
38
|
+
Dir.entries(commands_path).each do |file|
|
39
|
+
require File.join(commands_path, file) if file =~ /^(.*)\.rb$/
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# require subcommands
|
44
|
+
subcommands_path = '../app/subcommands'
|
45
|
+
if Dir.exists?(subcommands_path)
|
46
|
+
Dir.entries(subcommands_path).each do |file|
|
47
|
+
require File.join(subcommands_path, file) if file =~ /^(.*)\.rb$/
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Application
|
52
|
+
class LogTime < Thor
|
53
|
+
|
54
|
+
# commands
|
55
|
+
include Commands::List
|
56
|
+
include Commands::Add
|
57
|
+
# include Commands::Remove
|
58
|
+
include Commands::Start
|
59
|
+
include Commands::Stop
|
60
|
+
|
61
|
+
# subcommands
|
62
|
+
desc "series", ""
|
63
|
+
subcommand "series", SubCommands::Series
|
64
|
+
|
65
|
+
desc "tags", ""
|
66
|
+
subcommand "tags", SubCommands::Tags
|
67
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#
|
2
|
+
# Logs
|
3
|
+
#
|
4
|
+
|
5
|
+
class Log < ActiveRecord::Base
|
6
|
+
scope :active, -> { where(active: true) }
|
7
|
+
scope :inactive, -> { where(active: false) }
|
8
|
+
scope :estimated, -> { where("estimation is not null") }
|
9
|
+
|
10
|
+
validates :name, uniqueness: true
|
11
|
+
|
12
|
+
has_many :series
|
13
|
+
has_and_belongs_to_many :tags
|
14
|
+
|
15
|
+
def tag(tags)
|
16
|
+
tags.split(',').each do |t|
|
17
|
+
self.tags << Tag.where(tag: t).first_or_create
|
18
|
+
end
|
19
|
+
|
20
|
+
return self
|
21
|
+
end
|
22
|
+
|
23
|
+
def activate
|
24
|
+
self.active = true
|
25
|
+
self.save
|
26
|
+
end
|
27
|
+
|
28
|
+
def deactivate
|
29
|
+
self.active = false
|
30
|
+
self.save
|
31
|
+
end
|
32
|
+
|
33
|
+
def total
|
34
|
+
difference = 0
|
35
|
+
self.series.each do |s|
|
36
|
+
ending = s.end || Time.now
|
37
|
+
# next unless s.start and s.end
|
38
|
+
difference = difference + s.start.difference(ending)
|
39
|
+
end
|
40
|
+
difference
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#
|
2
|
+
# Series
|
3
|
+
#
|
4
|
+
|
5
|
+
class Series < ActiveRecord::Base
|
6
|
+
belongs_to :log
|
7
|
+
|
8
|
+
def self.begin(options = {})
|
9
|
+
attributes = {
|
10
|
+
start: Time.now
|
11
|
+
}.merge! options
|
12
|
+
|
13
|
+
create(attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
def begin
|
17
|
+
self.start = Time.now
|
18
|
+
self.save
|
19
|
+
end
|
20
|
+
|
21
|
+
def stop
|
22
|
+
self.end = Time.now
|
23
|
+
self.save
|
24
|
+
end
|
25
|
+
|
26
|
+
def finished?
|
27
|
+
!self.end.blank?
|
28
|
+
end
|
29
|
+
|
30
|
+
def total
|
31
|
+
return false if self.end.blank? or self.start.blank?
|
32
|
+
|
33
|
+
self.start.difference(self.end)
|
34
|
+
end
|
35
|
+
end
|