watch_tower 0.0.0.1 → 0.0.1.beta1
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.
- data/.todo +3 -0
- data/.travis.yml +4 -0
- data/Gemfile +0 -4
- data/README.md +48 -2
- data/TODO +3 -0
- data/ci/travis.rb +4 -1
- data/lib/watch_tower/appscript.rb +65 -7
- data/lib/watch_tower/cli/install.rb +27 -3
- data/lib/watch_tower/cli/open.rb +2 -0
- data/lib/watch_tower/cli/start.rb +3 -1
- data/lib/watch_tower/cli.rb +2 -0
- data/lib/watch_tower/config.rb +2 -0
- data/lib/watch_tower/core_ext.rb +2 -0
- data/lib/watch_tower/editor/base_appscript.rb +28 -1
- data/lib/watch_tower/editor/base_ps.rb +25 -0
- data/lib/watch_tower/editor/textmate.rb +2 -0
- data/lib/watch_tower/editor/xcode.rb +2 -0
- data/lib/watch_tower/editor.rb +2 -0
- data/lib/watch_tower/errors.rb +2 -0
- data/lib/watch_tower/eye.rb +13 -2
- data/lib/watch_tower/file_tree.rb +78 -0
- data/lib/watch_tower/project/any_based.rb +2 -0
- data/lib/watch_tower/project/git_based.rb +2 -0
- data/lib/watch_tower/project/init.rb +2 -0
- data/lib/watch_tower/project/path_based.rb +2 -0
- data/lib/watch_tower/project.rb +2 -0
- data/lib/watch_tower/server/app.rb +37 -7
- data/lib/watch_tower/server/assets/javascripts/application.js +4 -0
- data/lib/watch_tower/server/assets/javascripts/datepicker.coffee +21 -0
- data/lib/watch_tower/server/assets/javascripts/file_tree.coffee +30 -0
- data/lib/watch_tower/server/assets/javascripts/percentage.coffee +15 -5
- data/lib/watch_tower/server/assets/stylesheets/application.css +1 -0
- data/lib/watch_tower/server/assets/stylesheets/date.sass +17 -0
- data/lib/watch_tower/server/assets/stylesheets/file_tree.sass +42 -0
- data/lib/watch_tower/server/assets/stylesheets/global.sass +7 -4
- data/lib/watch_tower/server/configurations/asset.rb +2 -0
- data/lib/watch_tower/server/configurations.rb +2 -0
- data/lib/watch_tower/server/database.rb +2 -0
- data/lib/watch_tower/server/db/migrate/001_create_projects.rb +2 -0
- data/lib/watch_tower/server/db/migrate/002_create_files.rb +2 -0
- data/lib/watch_tower/server/db/migrate/003_create_time_entries.rb +2 -0
- data/lib/watch_tower/server/db/migrate/004_create_durations.rb +2 -0
- data/lib/watch_tower/server/db/migrate/005_add_hash_to_time_entries.rb +2 -0
- data/lib/watch_tower/server/db/migrate/006_add_hash_to_files.rb +2 -0
- data/lib/watch_tower/server/db/migrate/007_add_editor_to_times_entries.rb +8 -0
- data/lib/watch_tower/server/db/migrate/008_rename_editor_to_editor_name_in_times_entries.rb +5 -0
- data/lib/watch_tower/server/db/migrate/009_remove_editor_index_from_time_entries.rb +7 -0
- data/lib/watch_tower/server/db/migrate/010_add_editor_version_to_times_entries.rb +7 -0
- data/lib/watch_tower/server/helpers/asset.rb +2 -0
- data/lib/watch_tower/server/helpers/improved_partials.rb +2 -0
- data/lib/watch_tower/server/helpers/presenters.rb +33 -0
- data/lib/watch_tower/server/helpers.rb +3 -0
- data/lib/watch_tower/server/models/duration.rb +9 -0
- data/lib/watch_tower/server/models/file.rb +17 -0
- data/lib/watch_tower/server/models/project.rb +30 -0
- data/lib/watch_tower/server/models/time_entry.rb +5 -0
- data/lib/watch_tower/server/presenters/application_presenter.rb +165 -0
- data/lib/watch_tower/server/presenters/file_presenter.rb +10 -0
- data/lib/watch_tower/server/presenters/project_presenter.rb +20 -0
- data/lib/watch_tower/server/presenters.rb +13 -0
- data/lib/watch_tower/server/public/assets/{WatchTower-4d6de11e1bd34165ad91ac46fb711bf3.jpg → WatchTower-58eff0713efffbc6054defddc879e0b1.jpg} +0 -0
- data/lib/watch_tower/server/public/assets/application-4e6971066e06aa53b0c8e52c764044d1.css +389 -0
- data/lib/watch_tower/server/public/assets/application-6a1be75d4fd6a545faceb638e47a2486.js +23778 -0
- data/lib/watch_tower/server/public/assets/calendar-379834cd6e6321a940b662ace47f3032.gif +0 -0
- data/lib/watch_tower/server/public/assets/calendar-blue-d6aa74feef7ee4287532761db99a6c0a.gif +0 -0
- data/lib/watch_tower/server/public/assets/calendar-green-3752fe2996091379c8d321f759039385.gif +0 -0
- data/lib/watch_tower/server/public/assets/jquery.datepick-9c8dfe3a4d40bcafc7b182e194c13836.css +227 -0
- data/lib/watch_tower/server/public/assets/{percentage-d8589e21a5fc85d32a445f531ff8ab95.png → percentage-d0176e99520c95e93eee63738ef5d487.png} +0 -0
- data/lib/watch_tower/server/vendor/assets/images/calendar-blue.gif +0 -0
- data/lib/watch_tower/server/vendor/assets/images/calendar-green.gif +0 -0
- data/lib/watch_tower/server/vendor/assets/images/calendar.gif +0 -0
- data/lib/watch_tower/server/vendor/assets/javascripts/jquery-datepick-ext.js +266 -0
- data/lib/watch_tower/server/vendor/assets/javascripts/jquery-datepick-validation.js +232 -0
- data/lib/watch_tower/server/vendor/assets/javascripts/jquery-datepick.js +2092 -0
- data/lib/watch_tower/server/vendor/assets/stylesheets/jquery.datepick.css +226 -0
- data/lib/watch_tower/server/views/_project.haml +10 -13
- data/lib/watch_tower/server/views/index.haml +9 -6
- data/lib/watch_tower/server/views/layout.haml +7 -4
- data/lib/watch_tower/server/views/project.haml +9 -11
- data/lib/watch_tower/server.rb +8 -1
- data/lib/watch_tower/templates/{watchtower.plist → watchtower.plist.erb} +5 -6
- data/lib/watch_tower/version.rb +12 -3
- data/lib/watch_tower.rb +20 -1
- data/spec/factories.rb +2 -0
- data/spec/watch_tower/appscript_spec.rb +36 -2
- data/spec/watch_tower/editor/base_appscript_spec.rb +83 -0
- data/spec/watch_tower/editor/textmate_spec.rb +37 -0
- data/spec/watch_tower/editor/xcode_spec.rb +6 -0
- data/spec/watch_tower/eye_spec.rb +21 -0
- data/spec/watch_tower/file_tree_spec.rb +156 -0
- data/spec/watch_tower/server/app_spec.rb +64 -20
- data/spec/watch_tower/server/models/file_spec.rb +93 -31
- data/spec/watch_tower/server/models/project_spec.rb +147 -18
- data/spec/watch_tower/server/models/time_entry_spec.rb +10 -0
- data/spec/watch_tower/server/{decorator/project_decorator_spec.rb → presenters/application_presenter_spec.rb} +24 -13
- data/spec/watch_tower/server/presenters/file_presenter_spec.rb +8 -0
- data/spec/watch_tower/server/presenters/project_presenter_spec.rb +130 -0
- data/watch_tower.gemspec +10 -4
- metadata +114 -74
- data/lib/watch_tower/server/decorator/application_decorator.rb +0 -91
- data/lib/watch_tower/server/decorator/file_decorator.rb +0 -38
- data/lib/watch_tower/server/decorator/project_decorator.rb +0 -51
- data/lib/watch_tower/server/decorator.rb +0 -21
- data/lib/watch_tower/server/public/assets/application-7829b53b5ece1a16d22dc3d00f329023.css +0 -107
- data/lib/watch_tower/server/public/assets/application-e0e6b7731aade460f680331e65cf0682.js +0 -9359
- data/lib/watch_tower/server/views/_file.haml +0 -9
data/.todo
CHANGED
@@ -12,6 +12,9 @@
|
|
12
12
|
<note priority="medium" time="1317889118" done="1317980852">
|
13
13
|
Add a ci/travis.rb script which runs the tests under Travis-CI, needed because the config file added in todo number 3 won't exist and the current setup (sqlite) does not work with JRuby
|
14
14
|
</note>
|
15
|
+
<note priority="veryhigh" time="1319041505">
|
16
|
+
The elapsed time of the project should change when the duration changes
|
17
|
+
</note>
|
15
18
|
<note priority="high" time="1318256016">
|
16
19
|
Figure out how to load assets from gems and remove the bundled jQuery files.
|
17
20
|
</note>
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Watch Tower [](http://travis-ci.org/TechnoGate/watch_tower) 
|
2
2
|
|
3
|
-
|
3
|
+
[](http://www.pledgie.com/campaigns/16123)
|
4
4
|
|
5
5
|
WatchTower helps you track the time you spend on each project.
|
6
6
|
|
@@ -35,4 +35,50 @@ http://localhost:9282 or using the command
|
|
35
35
|
|
36
36
|
```bash
|
37
37
|
$ watchtower open
|
38
|
-
```
|
38
|
+
```
|
39
|
+
|
40
|
+
# Screenshots
|
41
|
+
|
42
|
+
## Home page
|
43
|
+
|
44
|
+

|
45
|
+
|
46
|
+
## Project page
|
47
|
+
|
48
|
+

|
49
|
+
|
50
|
+
# Contributing
|
51
|
+
|
52
|
+
Please feel free to fork and send pull requests, but please follow the
|
53
|
+
following guidelines:
|
54
|
+
|
55
|
+
- Prefix each commit message with the filename or the module followed by a
|
56
|
+
colon and a space, for example 'README: fix a typo' or 'Server/Project: Fix
|
57
|
+
a typo'.
|
58
|
+
- Include tests.
|
59
|
+
- Do not change the version, We will take care of that.
|
60
|
+
|
61
|
+
# License
|
62
|
+
|
63
|
+
## This code is free to use under the terms of the MIT license.
|
64
|
+
|
65
|
+
Copyright (c) 2011 TechnoGate <support@technogate.fr>
|
66
|
+
|
67
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
68
|
+
a copy of this software and associated documentation files (the
|
69
|
+
"Software"), to deal in the Software without restriction, including
|
70
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
71
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
72
|
+
permit persons to whom the Software is furnished to do so, subject to
|
73
|
+
the following conditions:
|
74
|
+
|
75
|
+
The above copyright notice and this permission notice shall be
|
76
|
+
included in all copies or substantial portions of the Software.
|
77
|
+
|
78
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
79
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
80
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
81
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
82
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
83
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
84
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/TODO
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
- The elapsed time of the project should change when the duration changes
|
2
|
+
(added Wed Oct 19 18:25:05 2011, incomplete, priority veryhigh)
|
3
|
+
|
1
4
|
- Figure out how to load assets from gems and remove the bundled jQuery files.
|
2
5
|
(added Mon Oct 10 16:13:36 2011, incomplete, priority high)
|
3
6
|
|
data/ci/travis.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: utf-8 -*-
|
2
3
|
# This file has been taken from rails
|
3
4
|
# https://github.com/rails/rails/blob/master/ci/travis.rb
|
4
5
|
require 'fileutils'
|
5
6
|
include FileUtils
|
6
7
|
|
7
8
|
commands = [
|
9
|
+
'mysql -e "drop database if exists watch_tower_test;"',
|
8
10
|
'mysql -e "create database watch_tower_test;"',
|
11
|
+
'psql -c "drop database if exists watch_tower_test;" -U postgres',
|
9
12
|
'psql -c "create database watch_tower_test;" -U postgres'
|
10
13
|
]
|
11
14
|
|
@@ -97,6 +100,6 @@ if failures.empty?
|
|
97
100
|
else
|
98
101
|
puts
|
99
102
|
puts "WatchTower build FAILED"
|
100
|
-
puts "Failed adapters: #{failures.join(', ')}"
|
103
|
+
puts "Failed adapters: #{failures.keys.join(', ')}"
|
101
104
|
exit(false)
|
102
105
|
end
|
@@ -1,22 +1,80 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
begin
|
2
|
-
require 'rubygems'
|
3
4
|
require 'appscript'
|
4
5
|
rescue LoadError
|
5
|
-
|
6
|
-
if RbConfig::CONFIG['target_os'] =~ /darwin/i
|
7
|
-
STDERR.puts "Please install 'appscript' to use this gem with Textmate"
|
8
|
-
STDERR.puts "gem install appscript"
|
9
|
-
end
|
10
|
-
# Define a simple class so the gem works even if Appscript is not installed
|
6
|
+
# Define a part of the Appscript gem so WatchTower is fully operational
|
11
7
|
module ::Appscript
|
8
|
+
CommandError = Class.new(Exception)
|
9
|
+
|
12
10
|
class Application
|
11
|
+
|
12
|
+
MOCK_METHODS = [:name, :version, :unix_id, :by_pid]
|
13
|
+
|
13
14
|
def is_running?
|
14
15
|
false
|
15
16
|
end
|
17
|
+
|
18
|
+
def get
|
19
|
+
"Not available"
|
20
|
+
end
|
21
|
+
|
22
|
+
def processes
|
23
|
+
Process.new(self)
|
24
|
+
end
|
25
|
+
|
26
|
+
MOCK_METHODS.each do |method|
|
27
|
+
define_method method do |*args|
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Process
|
34
|
+
def initialize(klass)
|
35
|
+
@klass = klass
|
36
|
+
end
|
37
|
+
|
38
|
+
def [](*args)
|
39
|
+
[@klass]
|
40
|
+
end
|
16
41
|
end
|
17
42
|
|
18
43
|
def app(*args)
|
19
44
|
Application.new
|
20
45
|
end
|
46
|
+
|
47
|
+
def its
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def name
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def eq(*args)
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def Appscript.app(*args)
|
60
|
+
Application.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def Appscript.its
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def Appscript.name
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def Appscript.eq(*args)
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
module ::FindApp
|
78
|
+
ApplicationNotFoundError = Class.new(Exception)
|
21
79
|
end
|
22
80
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
module WatchTower
|
2
4
|
module CLI
|
3
5
|
module Install
|
@@ -25,6 +27,22 @@ module WatchTower
|
|
25
27
|
end
|
26
28
|
|
27
29
|
protected
|
30
|
+
# Taken from hub
|
31
|
+
# https://github.com/defunkt/hub/blob/master/lib/hub/context.rb#L186
|
32
|
+
# Cross-platform way of finding an executable in the $PATH.
|
33
|
+
#
|
34
|
+
# which('ruby') #=> /usr/bin/ruby
|
35
|
+
def which(cmd)
|
36
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
37
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
38
|
+
exts.each { |ext|
|
39
|
+
exe = "\#{path}/\#{cmd}\#{ext}"
|
40
|
+
return exe if File.executable? exe
|
41
|
+
}
|
42
|
+
end
|
43
|
+
return nil
|
44
|
+
end
|
45
|
+
|
28
46
|
# Install the configuration file
|
29
47
|
def install_config_file
|
30
48
|
self.class.source_root(TEMPLATE_PATH)
|
@@ -42,7 +60,7 @@ module WatchTower
|
|
42
60
|
WatchTower bootloader is not supported on your OS, you'd have to run it manually
|
43
61
|
for the time being. Support for many editors and many OSes is planned for the
|
44
62
|
future, if you would like to help, or drop in an issue please don't hesitate to
|
45
|
-
do so on the Github page: https://github.com/TechnoGate/watch_tower
|
63
|
+
do so on the project's Github page: https://github.com/TechnoGate/watch_tower
|
46
64
|
MSG
|
47
65
|
end
|
48
66
|
end
|
@@ -50,8 +68,14 @@ MSG
|
|
50
68
|
# Install bootloader on Mac OS X
|
51
69
|
def install_bootloader_on_mac
|
52
70
|
self.class.source_root(TEMPLATE_PATH)
|
53
|
-
copy_file 'watchtower.plist', File.join(ENV['HOME'], 'Library',
|
54
|
-
|
71
|
+
# copy_file 'watchtower.plist', File.join(ENV['HOME'], 'Library',
|
72
|
+
# 'LaunchAgents', 'fr.technogate.WatchTower.plist')
|
73
|
+
create_file File.join(ENV['HOME'], 'Library', 'LaunchAgents', 'fr.technogate.WatchTower.plist') do
|
74
|
+
ruby_binary = which('ruby')
|
75
|
+
watch_tower_binary = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'bin', 'watchtower'))
|
76
|
+
template = File.expand_path(find_in_source_paths('watchtower.plist.erb'))
|
77
|
+
ERB.new(File.read(template)).result(binding)
|
78
|
+
end
|
55
79
|
|
56
80
|
puts "\nCreated. Now run:\n launchctl load ~/Library/LaunchAgents/fr.technogate.WatchTower.plist\n\n"
|
57
81
|
end
|
data/lib/watch_tower/cli/open.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
module WatchTower
|
2
4
|
module CLI
|
3
5
|
module Start
|
@@ -40,7 +42,7 @@ module WatchTower
|
|
40
42
|
desc: "Set the server's port."
|
41
43
|
def start
|
42
44
|
if Config[:enabled] &&
|
43
|
-
(!options[:bootloader] || (options
|
45
|
+
(!options[:bootloader] || (options[:bootloader] && Config[:launch_on_boot]))
|
44
46
|
LOG.info "Starting WatchTower."
|
45
47
|
start!
|
46
48
|
else
|
data/lib/watch_tower/cli.rb
CHANGED
data/lib/watch_tower/config.rb
CHANGED
data/lib/watch_tower/core_ext.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
require 'watch_tower/appscript'
|
2
4
|
|
3
5
|
module WatchTower
|
@@ -13,16 +15,41 @@ module WatchTower
|
|
13
15
|
# Include AppScript
|
14
16
|
include ::Appscript
|
15
17
|
|
18
|
+
# Is the editor running ?
|
19
|
+
#
|
20
|
+
# @return [Boolean]
|
16
21
|
def is_running?
|
17
22
|
editor.is_running? if editor
|
18
23
|
end
|
19
24
|
|
25
|
+
# Returns the name of the editor
|
26
|
+
#
|
27
|
+
# Child class should implement this method
|
28
|
+
def name
|
29
|
+
editor.try(:name).try(:get)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the version of the editor
|
33
|
+
#
|
34
|
+
# Child class should implement this method
|
35
|
+
def version
|
36
|
+
editor.try(:version).try(:get)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return the path of the document being edited
|
40
|
+
# Child classes can override this method if the behaviour is different
|
41
|
+
#
|
42
|
+
# @return [String] path to the document currently being edited
|
20
43
|
def current_path
|
21
44
|
current_paths.try(:first)
|
22
45
|
end
|
23
46
|
|
47
|
+
# Return the pathes of the documents being edited
|
48
|
+
# Child classes can override this method if the behaviour is different
|
49
|
+
#
|
50
|
+
# @return [Array] pathes to the documents currently being edited
|
24
51
|
def current_paths
|
25
|
-
if is_running?
|
52
|
+
if is_running? && editor.respond_to?(:document)
|
26
53
|
editor.document.get.collect(&:path).collect(&:get)
|
27
54
|
end
|
28
55
|
end
|
@@ -1,6 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
module WatchTower
|
2
4
|
module Editor
|
3
5
|
module BasePs
|
6
|
+
def self.included(base)
|
7
|
+
base.send :include, InstanceMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
def self.included(base)
|
12
|
+
base.class_eval <<-END, __FILE__, __LINE__ + 1
|
13
|
+
# Returns the name of the editor
|
14
|
+
#
|
15
|
+
# Child class should implement this method
|
16
|
+
def name
|
17
|
+
raise NotImplementedError, "Please define this function in your class."
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the version of the editor
|
21
|
+
#
|
22
|
+
# Child class should implement this method
|
23
|
+
def version
|
24
|
+
raise NotImplementedError, "Please define this function in your class."
|
25
|
+
end
|
26
|
+
END
|
27
|
+
end
|
28
|
+
end
|
4
29
|
end
|
5
30
|
end
|
6
31
|
end
|
data/lib/watch_tower/editor.rb
CHANGED
data/lib/watch_tower/errors.rb
CHANGED
data/lib/watch_tower/eye.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
require 'digest/sha1'
|
2
4
|
|
3
5
|
module WatchTower
|
@@ -22,8 +24,10 @@ module WatchTower
|
|
22
24
|
LOG.debug("#{__FILE__}:#{__LINE__}: #{editor.to_s} is running")
|
23
25
|
# Get the currently being edited file from the editor
|
24
26
|
files_paths = editor.current_paths
|
27
|
+
# Iterate over the files to fill the database
|
25
28
|
files_paths.each do |file_path|
|
26
29
|
begin
|
30
|
+
next unless file_path && File.exists?(file_path)
|
27
31
|
# Get the file_hash of the file
|
28
32
|
file_hash = Digest::SHA1.file(file_path).hexdigest
|
29
33
|
# Create a project from the file_path
|
@@ -44,7 +48,10 @@ module WatchTower
|
|
44
48
|
file_model = project_model.files.find_or_create_by_path(file_path)
|
45
49
|
begin
|
46
50
|
# Create a time entry
|
47
|
-
file_model.time_entries.create!
|
51
|
+
file_model.time_entries.create! mtime: File.stat(file_path).mtime,
|
52
|
+
file_hash: file_hash,
|
53
|
+
editor_name: editor.name,
|
54
|
+
editor_version: editor.version
|
48
55
|
rescue ActiveRecord::RecordInvalid => e
|
49
56
|
# This should happen if the mtime is already present
|
50
57
|
end
|
@@ -59,7 +66,7 @@ module WatchTower
|
|
59
66
|
end
|
60
67
|
end
|
61
68
|
|
62
|
-
# If $stop global is set, please stop, otherwise sleep for
|
69
|
+
# If $stop global is set, please stop, otherwise sleep for 10 seconds.
|
63
70
|
if $close_eye
|
64
71
|
LOG.debug("#{__FILE__}:#{__LINE__}: Closing eye has been requested, end the loop")
|
65
72
|
break
|
@@ -73,6 +80,10 @@ module WatchTower
|
|
73
80
|
#
|
74
81
|
# @param [Hash] options
|
75
82
|
def start!(options = {})
|
83
|
+
# Signal handling
|
84
|
+
Signal.trap("INT") { $close_eye = true }
|
85
|
+
Signal.trap("TERM") { $close_eye = true }
|
86
|
+
|
76
87
|
start(options)
|
77
88
|
end
|
78
89
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module WatchTower
|
4
|
+
# This class is used by the server to provided a FileTree representation
|
5
|
+
# of the files and their elapsed time
|
6
|
+
class FileTree
|
7
|
+
|
8
|
+
attr_reader :base_path, :files, :nested_tree, :elapsed_time
|
9
|
+
|
10
|
+
# Initialize
|
11
|
+
#
|
12
|
+
# @param [String] base_path: The base path of all files, usually the project's path
|
13
|
+
# @param [Array] files: The files and their elapsed time
|
14
|
+
# The Array elements should be Hashes with two required keys
|
15
|
+
# :path and :elapsed_time
|
16
|
+
def initialize(base_path, files)
|
17
|
+
# Init with args
|
18
|
+
@base_path = base_path
|
19
|
+
@all_files = remove_base_path_from_files(@base_path, files)
|
20
|
+
# Init with defaults
|
21
|
+
@elapsed_time = 0
|
22
|
+
@files = Array.new
|
23
|
+
@nested_tree = Hash.new
|
24
|
+
# Process the tree
|
25
|
+
process
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
# Process the Tree
|
30
|
+
def process
|
31
|
+
# Parse files
|
32
|
+
parse_files
|
33
|
+
# Parse folders
|
34
|
+
parse_folders
|
35
|
+
end
|
36
|
+
|
37
|
+
# Removes the base_path from the files
|
38
|
+
def remove_base_path_from_files(base_path, files)
|
39
|
+
files.collect do |f|
|
40
|
+
f[:path] = f[:path].gsub(%r(#{base_path}#{File::SEPARATOR}), '')
|
41
|
+
f
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Parses only files under the current base_path
|
46
|
+
def parse_files
|
47
|
+
return if @files.any?
|
48
|
+
|
49
|
+
@all_files.each do |f|
|
50
|
+
unless f[:path] =~ %r(#{File::SEPARATOR})
|
51
|
+
@elapsed_time += f[:elapsed_time]
|
52
|
+
@files << f
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Parses only folders under the current base_path
|
58
|
+
def parse_folders
|
59
|
+
return if @nested_tree.any?
|
60
|
+
|
61
|
+
@all_files.each do |f|
|
62
|
+
if f[:path] =~ %r(#{File::SEPARATOR})
|
63
|
+
# Get the base_path
|
64
|
+
base_path = f[:path].split(File::SEPARATOR).first
|
65
|
+
# Do not continue if we already parsed this path
|
66
|
+
next if @nested_tree.has_key?(base_path)
|
67
|
+
# Get the nested files
|
68
|
+
nested_files = remove_base_path_from_files base_path,
|
69
|
+
@all_files.select { |f| f[:path] =~ %r(^#{base_path}#{File::SEPARATOR}) }
|
70
|
+
# Create a tree
|
71
|
+
@nested_tree[base_path] = self.class.new("#{@base_path}#{File::SEPARATOR}#{base_path}", nested_files)
|
72
|
+
# Add the elapsed_time of the tree
|
73
|
+
@elapsed_time += @nested_tree[base_path].elapsed_time
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/watch_tower/project.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
# Sinatra
|
2
4
|
require 'sinatra'
|
3
5
|
|
@@ -7,9 +9,7 @@ module WatchTower
|
|
7
9
|
# Helper
|
8
10
|
include Helpers::ImprovedPartials
|
9
11
|
include Helpers::Asset
|
10
|
-
|
11
|
-
# Include Decorator
|
12
|
-
include Decorator
|
12
|
+
include Helpers::Presenters
|
13
13
|
|
14
14
|
# Configurations
|
15
15
|
include Configurations::Asset
|
@@ -18,19 +18,49 @@ module WatchTower
|
|
18
18
|
paths :root => '/'
|
19
19
|
paths :project => '/project/:id'
|
20
20
|
|
21
|
+
# Enable sessions
|
22
|
+
enable :sessions
|
23
|
+
|
24
|
+
# Before filter
|
25
|
+
before do
|
26
|
+
# Parse the from/to date from params and add it to the session
|
27
|
+
if params[:from_date] && params[:to_date]
|
28
|
+
if params[:from_date].present? && params[:to_date].present?
|
29
|
+
session[:date_filtering] = {
|
30
|
+
from_date: params[:from_date],
|
31
|
+
to_date: params[:to_date]
|
32
|
+
}
|
33
|
+
else
|
34
|
+
session[:date_filtering] = nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Make sure we have a default date filtering
|
39
|
+
unless session.try(:[], :date_filtering).try(:[], :from_date) && session.try(:[], :date_filtering).try(:[], :to_date)
|
40
|
+
session[:date_filtering] = {
|
41
|
+
from_date: Time.now.to_date.beginning_of_month.strftime('%m/%d/%Y'),
|
42
|
+
to_date: Time.now.to_date.strftime('%m/%d/%Y')
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
21
47
|
# The index action
|
22
48
|
get :root do
|
23
49
|
@title = "Projects"
|
24
|
-
@
|
50
|
+
@durations = Duration.date_range(session[:date_filtering][:from_date], session[:date_filtering][:to_date])
|
51
|
+
@projects = @durations.collect(&:file).collect(&:project).uniq
|
25
52
|
|
26
|
-
haml :index
|
53
|
+
haml :index, layout: (request.xhr? ? false : :layout)
|
27
54
|
end
|
28
55
|
|
56
|
+
# The project action
|
29
57
|
get :project do
|
30
|
-
@project =
|
58
|
+
@project = Project.find(params[:id])
|
31
59
|
@title = "Project - #{@project.name.camelcase}"
|
60
|
+
@durations = @project.durations.date_range(session[:date_filtering][:from_date], session[:date_filtering][:to_date])
|
61
|
+
@files = @durations.collect(&:file).uniq
|
32
62
|
|
33
|
-
haml :project
|
63
|
+
haml :project, layout: (request.xhr? ? false : :layout)
|
34
64
|
end
|
35
65
|
end
|
36
66
|
end
|