logtime 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|