watch_tower 0.0.0.1 → 0.0.1.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](http://travis-ci.org/TechnoGate/watch_tower.png)](http://travis-ci.org/TechnoGate/watch_tower) ![Still Maintained](http://stillmaintained.com/TechnoGate/watch_tower.png)
|
2
2
|
|
3
|
-
|
3
|
+
[![Click here to lend your support to: Open Source Projects and make a donation at www.pledgie.com !](http://www.pledgie.com/campaigns/16123.png?skin_name=chrome)](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
|
+
![Home page](http://cloud.github.com/downloads/TechnoGate/watch_tower/home_page.png)
|
45
|
+
|
46
|
+
## Project page
|
47
|
+
|
48
|
+
![Project page](http://cloud.github.com/downloads/TechnoGate/watch_tower/project_page.png)
|
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
|