openall_time_applet 0.0.14 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.14
1
+ 0.0.15
data/conf/db_schema.rb CHANGED
@@ -35,6 +35,7 @@ Openall_time_applet::DB_SCHEMA = {
35
35
  {"name" => "task_id", "type" => "int"},
36
36
  {"name" => "timestamp", "type" => "datetime"},
37
37
  {"name" => "time", "type" => "int"},
38
+ {"name" => "time_sync", "type" => "varchar"},
38
39
  {"name" => "timetype", "type" => "enum", "maxlength" => "'normal','overtime150','overtime200'", "default" => "normal"},
39
40
  {"name" => "time_transport", "type" => "int"},
40
41
  {"name" => "transportlength", "type" => "int"},
@@ -50,6 +51,17 @@ Openall_time_applet::DB_SCHEMA = {
50
51
  "task_id"
51
52
  ]
52
53
  },
54
+ "Timelog_logged_time" => {
55
+ "columns" => [
56
+ {"name" => "id", "type" => "int", "autoincr" => true, "primarykey" => true},
57
+ {"name" => "timelog_id", "type" => "int"},
58
+ {"name" => "timestamp_start", "type" => "datetime"},
59
+ {"name" => "timestamp_end", "type" => "datetime"}
60
+ ],
61
+ "indexes" => [
62
+ "timelog_id"
63
+ ]
64
+ },
53
65
  "Worktime" => {
54
66
  "columns" => [
55
67
  {"name" => "id", "type" => "int", "autoincr" => true, "primarykey" => true},
data/glade/win_main.glade CHANGED
@@ -14,6 +14,7 @@
14
14
  </object>
15
15
  <object class="GtkWindow" id="window">
16
16
  <property name="can_focus">False</property>
17
+ <property name="title" translatable="yes">OpenAll Time Applet</property>
17
18
  <property name="window_position">center</property>
18
19
  <property name="default_width">640</property>
19
20
  <signal name="destroy" handler="on_window_destroy" swapped="no"/>
@@ -6,7 +6,9 @@
6
6
  <property name="can_focus">False</property>
7
7
  <property name="title" translatable="yes">Overview of sync</property>
8
8
  <property name="window_position">center</property>
9
- <property name="default_width">640</property>
9
+ <property name="default_width">850</property>
10
+ <property name="default_height">480</property>
11
+ <signal name="destroy" handler="on_window_destroy" swapped="no"/>
10
12
  <child>
11
13
  <object class="GtkVBox" id="vbox1">
12
14
  <property name="visible">True</property>
@@ -28,44 +30,22 @@
28
30
  <property name="can_focus">False</property>
29
31
  <property name="spacing">3</property>
30
32
  <child>
31
- <object class="GtkTable" id="tableTimelogs">
33
+ <object class="GtkViewport" id="viewport1">
32
34
  <property name="visible">True</property>
33
35
  <property name="can_focus">False</property>
34
- <property name="n_columns">3</property>
35
36
  <child>
36
- <object class="GtkLabel" id="label3">
37
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
37
38
  <property name="visible">True</property>
38
- <property name="can_focus">False</property>
39
- <property name="xalign">0</property>
40
- <property name="label" translatable="yes">&lt;b&gt;Timelog&lt;/b&gt;</property>
41
- <property name="use_markup">True</property>
42
- </object>
43
- </child>
44
- <child>
45
- <object class="GtkLabel" id="label4">
46
- <property name="visible">True</property>
47
- <property name="can_focus">False</property>
48
- <property name="xalign">0</property>
49
- <property name="label" translatable="yes">&lt;b&gt;Logged time&lt;/b&gt;</property>
50
- <property name="use_markup">True</property>
51
- </object>
52
- <packing>
53
- <property name="left_attach">1</property>
54
- <property name="right_attach">2</property>
55
- </packing>
56
- </child>
57
- <child>
58
- <object class="GtkLabel" id="label5">
59
- <property name="visible">True</property>
60
- <property name="can_focus">False</property>
61
- <property name="xalign">0</property>
62
- <property name="label" translatable="yes">&lt;b&gt;Sync. as time&lt;/b&gt;</property>
63
- <property name="use_markup">True</property>
39
+ <property name="can_focus">True</property>
40
+ <property name="hscrollbar_policy">automatic</property>
41
+ <property name="vscrollbar_policy">automatic</property>
42
+ <child>
43
+ <object class="GtkTreeView" id="tvTimelogs">
44
+ <property name="visible">True</property>
45
+ <property name="can_focus">True</property>
46
+ </object>
47
+ </child>
64
48
  </object>
65
- <packing>
66
- <property name="left_attach">2</property>
67
- <property name="right_attach">3</property>
68
- </packing>
69
49
  </child>
70
50
  </object>
71
51
  <packing>
@@ -98,7 +78,7 @@
98
78
  </child>
99
79
  </object>
100
80
  <packing>
101
- <property name="expand">True</property>
81
+ <property name="expand">False</property>
102
82
  <property name="fill">True</property>
103
83
  <property name="position">1</property>
104
84
  </packing>
@@ -111,24 +91,26 @@
111
91
  <object class="GtkLabel" id="label2">
112
92
  <property name="visible">True</property>
113
93
  <property name="can_focus">False</property>
114
- <property name="label" translatable="yes">&lt;b&gt;Sync with OpenAll&lt;/b&gt;</property>
94
+ <property name="label" translatable="yes">&lt;b&gt;Prepare synchronization&lt;/b&gt;</property>
115
95
  <property name="use_markup">True</property>
116
96
  </object>
117
97
  </child>
118
98
  </object>
119
99
  <packing>
120
- <property name="expand">False</property>
100
+ <property name="expand">True</property>
121
101
  <property name="fill">True</property>
122
102
  <property name="position">0</property>
123
103
  </packing>
124
104
  </child>
125
105
  <child>
126
- <object class="GtkLabel" id="label1">
106
+ <object class="GtkLabel" id="labTotal">
127
107
  <property name="visible">True</property>
128
108
  <property name="can_focus">False</property>
109
+ <property name="xalign">0</property>
110
+ <property name="label" translatable="yes">[totals]</property>
129
111
  </object>
130
112
  <packing>
131
- <property name="expand">True</property>
113
+ <property name="expand">False</property>
132
114
  <property name="fill">True</property>
133
115
  <property name="position">1</property>
134
116
  </packing>
data/gui/win_main.rb CHANGED
@@ -128,16 +128,6 @@ class Openall_time_applet::Gui::Win_main
128
128
  end
129
129
  end
130
130
 
131
- def on_tvTimelogs_row_activated(*args)
132
- row = @gui["tvTimelogs"].sel
133
- return nil if !row
134
-
135
- timelog = @args[:oata].ob.get(:Timelog, row[0])
136
- win_timelog_edit = @args[:oata].show_timelog_edit(timelog)
137
- win_timelog_edit.gui["window"].modal = @gui["window"]
138
- win_timelog_edit.gui["window"].transient_for = @gui["window"]
139
- end
140
-
141
131
  def on_imiQuit_activate
142
132
  @gui["window"].destroy
143
133
  end
@@ -180,7 +170,7 @@ class Openall_time_applet::Gui::Win_main
180
170
  end
181
171
 
182
172
  def on_btnSync_clicked
183
- @args[:oata].sync_static do
173
+ @args[:oata].sync_static("transient_for" => @gui["window"]) do
184
174
  @args[:oata].sync
185
175
  end
186
176
  end
@@ -6,30 +6,95 @@ class Openall_time_applet::Gui::Win_sync_overview
6
6
  @gui = Gtk::Builder.new.add("../glade/win_sync_overview.glade")
7
7
  @gui.translate
8
8
  @gui.connect_signals{|h|method(h)}
9
- @gui["btnSync"].label = _("Synchronize")
9
+ @gui["btnSync"].label = _("Transfer")
10
10
 
11
- #This hash holds the text-widgets which time, which are used to update the timelogs later.
12
- @sync_time_text_widgets = {}
11
+ #Generate list-store containing tasks for the task-column.
12
+ task_ls = Gtk::ListStore.new(String, String)
13
+ iter = task_ls.append
14
+ iter[0] = _("None")
15
+ iter[1] = 0.to_s
13
16
 
17
+ tasks = [_("Choose:")]
18
+ @args[:oata].ob.list(:Task, {"orderby" => "title"}) do |task|
19
+ iter = task_ls.append
20
+ iter[0] = task[:title]
21
+ iter[1] = task.id.to_s
22
+ tasks << task
23
+ end
24
+
25
+ #Initialize timelog treeview.
26
+ init_data = Knj::Gtk2::Tv.init(@gui["tvTimelogs"], [
27
+ _("ID"),
28
+ _("Description"),
29
+ _("Timestamp"),
30
+ _("Time"),
31
+ _("Transport"),
32
+ _("Length"),
33
+ _("Transport descr."),
34
+ _("Transport costs"),
35
+ {
36
+ :title => _("Fixed travel"),
37
+ :type => :toggle
38
+ },
39
+ {
40
+ :title => _("Int. work"),
41
+ :type => :toggle
42
+ },
43
+ {
44
+ :title => _("Sync?"),
45
+ :type => :toggle
46
+ },
47
+ {
48
+ :title => _("Task"),
49
+ :type => :combo,
50
+ :model => task_ls,
51
+ :has_entry => false
52
+ },
53
+ _("Sync time")
54
+ ])
55
+
56
+ #Make columns editable.
57
+ Knj::Gtk2::Tv.editable_text_renderers_to_model(
58
+ :ob => @args[:oata].ob,
59
+ :tv => @gui["tvTimelogs"],
60
+ :model_class => :Timelog,
61
+ :renderers => init_data[:renderers],
62
+ :change_before => proc{ @dont_reload = true },
63
+ :change_after => proc{ @dont_reload = false; self.update_totals },
64
+ :cols => {
65
+ 1 => :descr,
66
+ 2 => {:col => :timestamp, :type => :datetime},
67
+ 3 => {:col => :time, :type => :time_as_sec},
68
+ 4 => {:col => :time_transport, :type => :time_as_sec},
69
+ 5 => {:col => :transportlength, :type => :int},
70
+ 6 => {:col => :transportdescription},
71
+ 7 => {:col => :transportcosts, :type => :human_number, :decimals => 2},
72
+ 8 => {:col => :travelfixed},
73
+ 9 => {:col => :workinternal},
74
+ 10 => {:col => :sync_need},
75
+ 11 => {
76
+ :col => :task_id,
77
+ :value_callback => lambda{ |data|
78
+ task = @args[:oata].ob.get_by(:Task, {"title" => data[:value]})
79
+
80
+ if !task
81
+ return 0
82
+ else
83
+ return task.id
84
+ end
85
+ },
86
+ :value_set_callback => proc{ |data| data[:model].task_name }
87
+ },
88
+ 12 => {:col => :time_sync, :type => :time_as_sec}
89
+ }
90
+ )
91
+ @gui["tvTimelogs"].columns[0].visible = false
92
+
93
+
94
+ =begin
14
95
  rowc = 1
15
96
  @args[:oata].ob.list(:Timelog, {"sync_need" => 1, "task_id_not" => 0}) do |timelog|
16
- #Read time and transport from timelog.
17
- time = timelog[:time].to_i
18
- transport = timelog[:time_transport].to_i
19
-
20
- #If transport is logged, then the work if offsite. It should be rounded up by 30 min. Else 15 min. round up.
21
- if transport > 0
22
- roundup = 1800
23
- else
24
- roundup = 900
25
- end
26
97
 
27
- #Do the actual counting.
28
- count_rounded_time = 0
29
- loop do
30
- break if count_rounded_time >= time
31
- count_rounded_time += roundup
32
- end
33
98
 
34
99
  #Spawn widgets.
35
100
  timelog_label = Gtk::Label.new(timelog[:descr])
@@ -42,6 +107,7 @@ class Openall_time_applet::Gui::Win_sync_overview
42
107
 
43
108
  sync_time_text = Gtk::Entry.new
44
109
  sync_time_text.text = Knj::Strings.secs_to_human_time_str(count_rounded_time)
110
+ sync_time_text.signal_connect(:changed, &self.method(:on_syncTimeText_changed))
45
111
  @sync_time_text_widgets[timelog.id] = sync_time_text
46
112
 
47
113
  #Attach widgets in table.
@@ -51,8 +117,14 @@ class Openall_time_applet::Gui::Win_sync_overview
51
117
 
52
118
  rowc += 1
53
119
  end
120
+ =end
54
121
 
55
- if rowc == 1
122
+ self.reload_timelogs
123
+ self.update_totals
124
+
125
+ @reload_id = @args[:oata].ob.connect("object" => :Timelog, "signals" => ["add", "update", "delete"], &self.method(:reload_timelogs))
126
+
127
+ if @timelogs_count <= 0
56
128
  #Show error-message and destroy the window, if no timelogs was to be synced.
57
129
  Knj::Gtk2.msgbox("msg" => _("There is nothing to sync at this time."), "run" => false)
58
130
  @gui["window"].destroy
@@ -62,20 +134,53 @@ class Openall_time_applet::Gui::Win_sync_overview
62
134
  end
63
135
  end
64
136
 
65
- def on_btnSync_clicked
66
- begin
67
- #Go through the shown timelogs and update their time to the entered and rounded time.
68
- @sync_time_text_widgets.each do |timelog_id, sync_time_text|
69
- timelog = @args[:oata].ob.get(:Timelog, timelog_id)
70
- secs = Knj::Strings.human_time_str_to_secs(sync_time_text.text)
71
- if !Knj::Php.is_numeric(secs)
72
- raise sprintf(_("Time was not numeric for: '%s'."), timelog[:descr])
73
- end
74
-
75
- #Update the time so that is what will be synchronized.
76
- timelog[:time] = secs
137
+ def reload_timelogs
138
+ return nil if @dont_reload or @gui["tvTimelogs"].destroyed?
139
+ @gui["tvTimelogs"].model.clear
140
+ @timelogs_count = 0
141
+ @args[:oata].ob.list(:Timelog, {"sync_need" => 1, "task_id_not" => 0, "orderby" => "timestamp"}) do |timelog|
142
+ #Read time and transport from timelog.
143
+ time = timelog[:time].to_i
144
+ transport = timelog[:time_transport].to_i
145
+
146
+ #If transport is logged, then the work if offsite. It should be rounded up by 30 min. Else 15 min. round up.
147
+ if transport > 0
148
+ roundup = 1800
149
+ else
150
+ roundup = 900
151
+ end
152
+
153
+ #Do the actual counting.
154
+ count_rounded_time = 0
155
+ loop do
156
+ break if count_rounded_time >= time
157
+ count_rounded_time += roundup
77
158
  end
78
159
 
160
+ #Set sync-time on timelog.
161
+ timelog[:time_sync] = count_rounded_time
162
+
163
+ Knj::Gtk2::Tv.append(@gui["tvTimelogs"], [
164
+ timelog.id,
165
+ timelog[:descr],
166
+ timelog.timestamp_str,
167
+ timelog.time_as_human,
168
+ timelog.time_transport_as_human,
169
+ Knj::Locales.number_out(timelog[:transportlength], 0),
170
+ timelog.transport_descr_short,
171
+ Knj::Locales.number_out(timelog[:transportcosts], 2),
172
+ Knj::Strings.yn_str(timelog[:travelfixed], true, false),
173
+ Knj::Strings.yn_str(timelog[:workinternal], true, false),
174
+ Knj::Strings.yn_str(timelog[:sync_need], true, false),
175
+ timelog.task_name,
176
+ Knj::Strings.secs_to_human_time_str(count_rounded_time)
177
+ ])
178
+ @timelogs_count += 1
179
+ end
180
+ end
181
+
182
+ def on_btnSync_clicked
183
+ begin
79
184
  #Destroy this window and start syncing for real.
80
185
  @gui["window"].destroy
81
186
  @args[:oata].sync_real
@@ -83,4 +188,25 @@ class Openall_time_applet::Gui::Win_sync_overview
83
188
  Knj::Gtk2.msgbox(Knj::Errors.error_str(e))
84
189
  end
85
190
  end
191
+
192
+ def update_totals
193
+ total_secs = 0
194
+
195
+ @gui["tvTimelogs"].model.each do |model, path, iter|
196
+ time_val = @gui["tvTimelogs"].model.get_value(iter, 12)
197
+
198
+ begin
199
+ total_secs += Knj::Strings.human_time_str_to_secs(time_val)
200
+ rescue
201
+ #ignore - user is properly entering stuff.
202
+ end
203
+ end
204
+
205
+ @gui["labTotal"].markup = "<b>#{_("Total hours:")}</b> #{Knj::Strings.secs_to_human_time_str(total_secs)}"
206
+ end
207
+
208
+ def on_window_destroy
209
+ #Unconnect reload-event. Else it will crash on call to destroyed object. Also frees up various ressources.
210
+ @args[:oata].ob.unconnect("object" => :Timelog, "conn_id" => @reload_id)
211
+ end
86
212
  end
@@ -239,18 +239,6 @@ class Openall_time_applet
239
239
  end
240
240
  end
241
241
 
242
- def show_timelog_new
243
- Knj::Gtk2::Window.unique!("timelog_new") do
244
- Openall_time_applet::Gui::Win_timelog_edit.new(:oata => self)
245
- end
246
- end
247
-
248
- def show_timelog_edit(timelog)
249
- Knj::Gtk2::Window.unique!("timelog_edit_#{timelog.id}") do
250
- Openall_time_applet::Gui::Win_timelog_edit.new(:oata => self, :timelog => timelog)
251
- end
252
- end
253
-
254
242
  def show_overview
255
243
  Knj::Gtk2::Window.unique!("overview") do
256
244
  Openall_time_applet::Gui::Win_overview.new(:oata => self)
@@ -284,7 +272,7 @@ class Openall_time_applet
284
272
 
285
273
  #Synchronizes organisations, tasks and worktimes.
286
274
  def sync_static(args = nil)
287
- sw = Knj::Gtk2::StatusWindow.new
275
+ sw = Knj::Gtk2::StatusWindow.new("transient_for" => args["transient_for"])
288
276
 
289
277
  return Knj::Thread.new do
290
278
  begin
@@ -301,11 +289,11 @@ class Openall_time_applet
301
289
  sw.percent = 1
302
290
 
303
291
  sleep 1 if !block_given?
304
- sw.destroy
292
+ sw.destroy if sw
305
293
  sw = nil
306
294
  yield if block_given?
307
295
  rescue => e
308
- sw.destroy
296
+ sw.destroy if sw
309
297
  sw = nil
310
298
  Knj::Gtk2.msgbox("msg" => Knj::Errors.error_str(e), "type" => "warning", "title" => _("Error"), "run" => false)
311
299
  ensure
@@ -353,6 +341,7 @@ class Openall_time_applet
353
341
  :time => @timelog_active[:time].to_i + secs_passed,
354
342
  :sync_need => 1
355
343
  )
344
+ @timelog_logged_time[:timestamp_end] = Time.now
356
345
  end
357
346
 
358
347
  @timelog_active = nil
@@ -362,10 +351,33 @@ class Openall_time_applet
362
351
 
363
352
  #Sets a new timelog to track. Stops tracking of previous timelog if already tracking.
364
353
  def timelog_active=(timelog)
365
- self.timelog_stop_tracking
354
+ begin
355
+ #Check if there has been logged time another day - if so, clone it and begin logging on the clone instead.
356
+ tlt = @ob.list(:Timelog_logged_time, {
357
+ "timelog" => timelog,
358
+ "timestamp_start_day_not" => Time.now,
359
+ "limit" => 1
360
+ })
361
+ if !tlt.empty?
362
+ timelog = @ob.add(:Timelog, {
363
+ :task_id => timelog[:task_id],
364
+ :timestamp => Time.now,
365
+ :descr => timelog[:descr],
366
+ :transportdescription => timelog[:transportdescription],
367
+ :workinternal => timelog[:workinternal],
368
+ :timetype => timelog[:timetype]
369
+ })
370
+ #raise sprintf(_("You have already logged on this timelog on %s"), tlt.first.timestamp_start.out)
371
+ end
372
+
373
+ self.timelog_stop_tracking
374
+ @timelog_logged_time = @ob.add(:Timelog_logged_time, {:timelog_id => timelog.id})
375
+ @timelog_active = timelog
376
+ @timelog_active_time = Time.new
377
+ rescue => e
378
+ Knj::Gtk2.msgbox("msg" => Knj::Errors.error_str(e), "type" => "warning", "title" => _("Error"), "run" => false)
379
+ end
366
380
 
367
- @timelog_active = timelog
368
- @timelog_active_time = Time.new
369
381
  @ti.update_icon if @ti
370
382
  end
371
383
 
data/models/timelog.rb CHANGED
@@ -3,6 +3,10 @@ class Openall_time_applet::Models::Timelog < Knj::Datarow
3
3
  :Task
4
4
  ]
5
5
 
6
+ has_many [
7
+ {:class => :Timelog_logged_time, :col => :timelog_id, :method => :logged_times, :autodelete => true}
8
+ ]
9
+
6
10
  def initialize(*args, &block)
7
11
  super(*args, &block)
8
12
 
@@ -22,7 +26,7 @@ class Openall_time_applet::Models::Timelog < Knj::Datarow
22
26
  args[:oata].oa_conn do |conn|
23
27
  #Go through timelogs that needs syncing and has a task set.
24
28
  self.ob.list(:Timelog, {"sync_need" => 1, "task_id_not" => 0}) do |timelog|
25
- secs_sum = timelog[:time].to_i + timelog[:time_transport].to_i
29
+ secs_sum = timelog[:time_sync].to_i + timelog[:time_transport].to_i
26
30
  next if secs_sum <= 0
27
31
 
28
32
  #The timelog has not yet been created in OpenAll - create it!
@@ -0,0 +1,9 @@
1
+ class Openall_time_applet::Models::Timelog_logged_time < Knj::Datarow
2
+ has_one [
3
+ {:class => :Timelog, :col => :timelog_id, :method => :timelog, :depends => true}
4
+ ]
5
+
6
+ def self.add(d)
7
+ d.data[:timestamp_start] = Time.now if !d.data[:timestamp_start]
8
+ end
9
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{openall_time_applet}
8
- s.version = "0.0.14"
8
+ s.version = "0.0.15"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Kasper Johansen"]
12
- s.date = %q{2012-06-18}
12
+ s.date = %q{2012-06-25}
13
13
  s.default_executable = %q{openall_time_applet.rb}
14
14
  s.description = %q{Off-line time-tracking for OpenAll with syncing when online.}
15
15
  s.email = %q{k@spernj.org}
@@ -40,14 +40,11 @@ Gem::Specification.new do |s|
40
40
  "glade/win_overview.glade",
41
41
  "glade/win_preferences.glade",
42
42
  "glade/win_sync_overview.glade",
43
- "glade/win_timelog_edit.glade",
44
43
  "glade/win_worktime_overview.glade",
45
44
  "gui/trayicon.rb",
46
45
  "gui/win_main.rb",
47
- "gui/win_overview.rb",
48
46
  "gui/win_preferences.rb",
49
47
  "gui/win_sync_overview.rb",
50
- "gui/win_timelog_edit.rb",
51
48
  "gui/win_worktime_overview.rb",
52
49
  "lib/openall_time_applet.rb",
53
50
  "locales/da_DK/LC_MESSAGES/default.mo",
@@ -55,6 +52,7 @@ Gem::Specification.new do |s|
55
52
  "models/organisation.rb",
56
53
  "models/task.rb",
57
54
  "models/timelog.rb",
55
+ "models/timelog_logged_time.rb",
58
56
  "models/worktime.rb",
59
57
  "openall_time_applet.gemspec",
60
58
  "spec/openall_time_applet_spec.rb",
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: openall_time_applet
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.14
5
+ version: 0.0.15
6
6
  platform: ruby
7
7
  authors:
8
8
  - Kasper Johansen
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-06-18 00:00:00 +02:00
13
+ date: 2012-06-25 00:00:00 +02:00
14
14
  default_executable: openall_time_applet.rb
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -165,14 +165,11 @@ files:
165
165
  - glade/win_overview.glade
166
166
  - glade/win_preferences.glade
167
167
  - glade/win_sync_overview.glade
168
- - glade/win_timelog_edit.glade
169
168
  - glade/win_worktime_overview.glade
170
169
  - gui/trayicon.rb
171
170
  - gui/win_main.rb
172
- - gui/win_overview.rb
173
171
  - gui/win_preferences.rb
174
172
  - gui/win_sync_overview.rb
175
- - gui/win_timelog_edit.rb
176
173
  - gui/win_worktime_overview.rb
177
174
  - lib/openall_time_applet.rb
178
175
  - locales/da_DK/LC_MESSAGES/default.mo
@@ -180,6 +177,7 @@ files:
180
177
  - models/organisation.rb
181
178
  - models/task.rb
182
179
  - models/timelog.rb
180
+ - models/timelog_logged_time.rb
183
181
  - models/worktime.rb
184
182
  - openall_time_applet.gemspec
185
183
  - spec/openall_time_applet_spec.rb
@@ -198,7 +196,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
198
196
  requirements:
199
197
  - - ">="
200
198
  - !ruby/object:Gem::Version
201
- hash: 2439131216522647471
199
+ hash: 1181471258583995936
202
200
  segments:
203
201
  - 0
204
202
  version: "0"
@@ -1,440 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <interface>
3
- <requires lib="gtk+" version="2.24"/>
4
- <!-- interface-naming-policy project-wide -->
5
- <object class="GtkWindow" id="window">
6
- <property name="can_focus">False</property>
7
- <property name="title" translatable="yes">Timelog</property>
8
- <property name="window_position">center</property>
9
- <property name="default_width">360</property>
10
- <child>
11
- <object class="GtkVBox" id="vbox1">
12
- <property name="visible">True</property>
13
- <property name="can_focus">False</property>
14
- <child>
15
- <object class="GtkFrame" id="frame1">
16
- <property name="visible">True</property>
17
- <property name="can_focus">False</property>
18
- <property name="label_xalign">0</property>
19
- <property name="shadow_type">none</property>
20
- <child>
21
- <object class="GtkAlignment" id="alignment1">
22
- <property name="visible">True</property>
23
- <property name="can_focus">False</property>
24
- <property name="left_padding">12</property>
25
- <child>
26
- <object class="GtkTable" id="table1">
27
- <property name="visible">True</property>
28
- <property name="can_focus">False</property>
29
- <property name="n_rows">13</property>
30
- <property name="n_columns">2</property>
31
- <property name="column_spacing">3</property>
32
- <property name="row_spacing">3</property>
33
- <child>
34
- <object class="GtkLabel" id="label3">
35
- <property name="visible">True</property>
36
- <property name="can_focus">False</property>
37
- <property name="xalign">0</property>
38
- <property name="label" translatable="yes">Description</property>
39
- </object>
40
- <packing>
41
- <property name="x_options">GTK_FILL</property>
42
- <property name="y_options">GTK_FILL</property>
43
- </packing>
44
- </child>
45
- <child>
46
- <object class="GtkLabel" id="label4">
47
- <property name="visible">True</property>
48
- <property name="can_focus">False</property>
49
- <property name="xalign">0</property>
50
- <property name="label" translatable="yes">Time</property>
51
- </object>
52
- <packing>
53
- <property name="top_attach">1</property>
54
- <property name="bottom_attach">2</property>
55
- <property name="x_options">GTK_FILL</property>
56
- <property name="y_options">GTK_FILL</property>
57
- </packing>
58
- </child>
59
- <child>
60
- <object class="GtkEntry" id="txtTime">
61
- <property name="visible">True</property>
62
- <property name="can_focus">True</property>
63
- <property name="invisible_char">•</property>
64
- <property name="primary_icon_activatable">False</property>
65
- <property name="secondary_icon_activatable">False</property>
66
- <property name="primary_icon_sensitive">True</property>
67
- <property name="secondary_icon_sensitive">True</property>
68
- </object>
69
- <packing>
70
- <property name="left_attach">1</property>
71
- <property name="right_attach">2</property>
72
- <property name="top_attach">1</property>
73
- <property name="bottom_attach">2</property>
74
- </packing>
75
- </child>
76
- <child>
77
- <object class="GtkLabel" id="label5">
78
- <property name="visible">True</property>
79
- <property name="can_focus">False</property>
80
- <property name="xalign">0</property>
81
- <property name="label" translatable="yes">Transport</property>
82
- </object>
83
- <packing>
84
- <property name="top_attach">2</property>
85
- <property name="bottom_attach">3</property>
86
- <property name="x_options">GTK_FILL</property>
87
- <property name="y_options">GTK_FILL</property>
88
- </packing>
89
- </child>
90
- <child>
91
- <object class="GtkEntry" id="txtTimeTransport">
92
- <property name="visible">True</property>
93
- <property name="can_focus">True</property>
94
- <property name="invisible_char">•</property>
95
- <property name="primary_icon_activatable">False</property>
96
- <property name="secondary_icon_activatable">False</property>
97
- <property name="primary_icon_sensitive">True</property>
98
- <property name="secondary_icon_sensitive">True</property>
99
- </object>
100
- <packing>
101
- <property name="left_attach">1</property>
102
- <property name="right_attach">2</property>
103
- <property name="top_attach">2</property>
104
- <property name="bottom_attach">3</property>
105
- </packing>
106
- </child>
107
- <child>
108
- <object class="GtkLabel" id="label6">
109
- <property name="visible">True</property>
110
- <property name="can_focus">False</property>
111
- <property name="xalign">0</property>
112
- <property name="label" translatable="yes">Task</property>
113
- </object>
114
- <packing>
115
- <property name="top_attach">10</property>
116
- <property name="bottom_attach">11</property>
117
- <property name="x_options">GTK_FILL</property>
118
- <property name="y_options">GTK_FILL</property>
119
- </packing>
120
- </child>
121
- <child>
122
- <object class="GtkComboBox" id="cbTask">
123
- <property name="visible">True</property>
124
- <property name="can_focus">False</property>
125
- </object>
126
- <packing>
127
- <property name="left_attach">1</property>
128
- <property name="right_attach">2</property>
129
- <property name="top_attach">10</property>
130
- <property name="bottom_attach">11</property>
131
- </packing>
132
- </child>
133
- <child>
134
- <object class="GtkCheckButton" id="cbShouldSync">
135
- <property name="label" translatable="yes">Should sync</property>
136
- <property name="visible">True</property>
137
- <property name="can_focus">True</property>
138
- <property name="receives_default">False</property>
139
- <property name="use_action_appearance">False</property>
140
- <property name="draw_indicator">True</property>
141
- </object>
142
- <packing>
143
- <property name="right_attach">2</property>
144
- <property name="top_attach">11</property>
145
- <property name="bottom_attach">12</property>
146
- <property name="x_options">GTK_FILL</property>
147
- <property name="y_options">GTK_FILL</property>
148
- </packing>
149
- </child>
150
- <child>
151
- <object class="GtkCheckButton" id="cbStartTracking">
152
- <property name="label" translatable="yes">Start tracking after saving</property>
153
- <property name="visible">True</property>
154
- <property name="can_focus">True</property>
155
- <property name="receives_default">False</property>
156
- <property name="use_action_appearance">False</property>
157
- <property name="draw_indicator">True</property>
158
- </object>
159
- <packing>
160
- <property name="right_attach">2</property>
161
- <property name="top_attach">12</property>
162
- <property name="bottom_attach">13</property>
163
- </packing>
164
- </child>
165
- <child>
166
- <object class="GtkEntry" id="txtDescr">
167
- <property name="visible">True</property>
168
- <property name="can_focus">True</property>
169
- <property name="invisible_char">•</property>
170
- <property name="primary_icon_activatable">False</property>
171
- <property name="secondary_icon_activatable">False</property>
172
- <property name="primary_icon_sensitive">True</property>
173
- <property name="secondary_icon_sensitive">True</property>
174
- </object>
175
- <packing>
176
- <property name="left_attach">1</property>
177
- <property name="right_attach">2</property>
178
- </packing>
179
- </child>
180
- <child>
181
- <object class="GtkLabel" id="label7">
182
- <property name="visible">True</property>
183
- <property name="can_focus">False</property>
184
- <property name="xalign">0</property>
185
- <property name="label" translatable="yes">Transport length</property>
186
- </object>
187
- <packing>
188
- <property name="top_attach">3</property>
189
- <property name="bottom_attach">4</property>
190
- <property name="x_options">GTK_FILL</property>
191
- <property name="y_options">GTK_FILL</property>
192
- </packing>
193
- </child>
194
- <child>
195
- <object class="GtkLabel" id="label8">
196
- <property name="visible">True</property>
197
- <property name="can_focus">False</property>
198
- <property name="xalign">0</property>
199
- <property name="label" translatable="yes">Transport costs</property>
200
- </object>
201
- <packing>
202
- <property name="top_attach">4</property>
203
- <property name="bottom_attach">5</property>
204
- <property name="x_options">GTK_FILL</property>
205
- <property name="y_options">GTK_FILL</property>
206
- </packing>
207
- </child>
208
- <child>
209
- <object class="GtkEntry" id="txtTransportLength">
210
- <property name="visible">True</property>
211
- <property name="can_focus">True</property>
212
- <property name="invisible_char">•</property>
213
- <property name="primary_icon_activatable">False</property>
214
- <property name="secondary_icon_activatable">False</property>
215
- <property name="primary_icon_sensitive">True</property>
216
- <property name="secondary_icon_sensitive">True</property>
217
- </object>
218
- <packing>
219
- <property name="left_attach">1</property>
220
- <property name="right_attach">2</property>
221
- <property name="top_attach">3</property>
222
- <property name="bottom_attach">4</property>
223
- </packing>
224
- </child>
225
- <child>
226
- <object class="GtkEntry" id="txtTransportCosts">
227
- <property name="visible">True</property>
228
- <property name="can_focus">True</property>
229
- <property name="invisible_char">•</property>
230
- <property name="primary_icon_activatable">False</property>
231
- <property name="secondary_icon_activatable">False</property>
232
- <property name="primary_icon_sensitive">True</property>
233
- <property name="secondary_icon_sensitive">True</property>
234
- </object>
235
- <packing>
236
- <property name="left_attach">1</property>
237
- <property name="right_attach">2</property>
238
- <property name="top_attach">4</property>
239
- <property name="bottom_attach">5</property>
240
- </packing>
241
- </child>
242
- <child>
243
- <object class="GtkLabel" id="label9">
244
- <property name="visible">True</property>
245
- <property name="can_focus">False</property>
246
- <property name="xalign">0</property>
247
- <property name="label" translatable="yes">Transport description</property>
248
- </object>
249
- <packing>
250
- <property name="top_attach">5</property>
251
- <property name="bottom_attach">6</property>
252
- <property name="x_options">GTK_FILL</property>
253
- <property name="y_options">GTK_FILL</property>
254
- </packing>
255
- </child>
256
- <child>
257
- <object class="GtkEntry" id="txtTransportDescr">
258
- <property name="visible">True</property>
259
- <property name="can_focus">True</property>
260
- <property name="invisible_char">•</property>
261
- <property name="primary_icon_activatable">False</property>
262
- <property name="secondary_icon_activatable">False</property>
263
- <property name="primary_icon_sensitive">True</property>
264
- <property name="secondary_icon_sensitive">True</property>
265
- </object>
266
- <packing>
267
- <property name="left_attach">1</property>
268
- <property name="right_attach">2</property>
269
- <property name="top_attach">5</property>
270
- <property name="bottom_attach">6</property>
271
- </packing>
272
- </child>
273
- <child>
274
- <object class="GtkCheckButton" id="cbWorkInternal">
275
- <property name="label" translatable="yes">Internal work</property>
276
- <property name="visible">True</property>
277
- <property name="can_focus">True</property>
278
- <property name="receives_default">False</property>
279
- <property name="use_action_appearance">False</property>
280
- <property name="draw_indicator">True</property>
281
- </object>
282
- <packing>
283
- <property name="right_attach">2</property>
284
- <property name="top_attach">6</property>
285
- <property name="bottom_attach">7</property>
286
- </packing>
287
- </child>
288
- <child>
289
- <object class="GtkCheckButton" id="cbTravelFixed">
290
- <property name="label" translatable="yes">Fixed travel</property>
291
- <property name="visible">True</property>
292
- <property name="can_focus">True</property>
293
- <property name="receives_default">False</property>
294
- <property name="use_action_appearance">False</property>
295
- <property name="draw_indicator">True</property>
296
- </object>
297
- <packing>
298
- <property name="right_attach">2</property>
299
- <property name="top_attach">7</property>
300
- <property name="bottom_attach">8</property>
301
- <property name="x_options">GTK_FILL</property>
302
- <property name="y_options">GTK_FILL</property>
303
- </packing>
304
- </child>
305
- <child>
306
- <object class="GtkLabel" id="label10">
307
- <property name="visible">True</property>
308
- <property name="can_focus">False</property>
309
- <property name="xalign">0</property>
310
- <property name="label" translatable="yes">Time type</property>
311
- </object>
312
- <packing>
313
- <property name="top_attach">8</property>
314
- <property name="bottom_attach">9</property>
315
- <property name="x_options">GTK_FILL</property>
316
- <property name="y_options">GTK_FILL</property>
317
- </packing>
318
- </child>
319
- <child>
320
- <object class="GtkComboBox" id="cbTimeType">
321
- <property name="visible">True</property>
322
- <property name="can_focus">False</property>
323
- </object>
324
- <packing>
325
- <property name="left_attach">1</property>
326
- <property name="right_attach">2</property>
327
- <property name="top_attach">8</property>
328
- <property name="bottom_attach">9</property>
329
- </packing>
330
- </child>
331
- <child>
332
- <object class="GtkLabel" id="label11">
333
- <property name="visible">True</property>
334
- <property name="can_focus">False</property>
335
- <property name="xalign">0</property>
336
- <property name="label" translatable="yes">Timestamp</property>
337
- </object>
338
- <packing>
339
- <property name="top_attach">9</property>
340
- <property name="bottom_attach">10</property>
341
- <property name="x_options">GTK_FILL</property>
342
- <property name="y_options">GTK_FILL</property>
343
- </packing>
344
- </child>
345
- <child>
346
- <object class="GtkEntry" id="txtTimestamp">
347
- <property name="visible">True</property>
348
- <property name="can_focus">True</property>
349
- <property name="invisible_char">•</property>
350
- <property name="primary_icon_activatable">False</property>
351
- <property name="secondary_icon_activatable">False</property>
352
- <property name="primary_icon_sensitive">True</property>
353
- <property name="secondary_icon_sensitive">True</property>
354
- </object>
355
- <packing>
356
- <property name="left_attach">1</property>
357
- <property name="right_attach">2</property>
358
- <property name="top_attach">9</property>
359
- <property name="bottom_attach">10</property>
360
- </packing>
361
- </child>
362
- </object>
363
- </child>
364
- </object>
365
- </child>
366
- <child type="label">
367
- <object class="GtkLabel" id="label1">
368
- <property name="visible">True</property>
369
- <property name="can_focus">False</property>
370
- <property name="label" translatable="yes">&lt;b&gt;Timelog&lt;/b&gt;</property>
371
- <property name="use_markup">True</property>
372
- </object>
373
- </child>
374
- </object>
375
- <packing>
376
- <property name="expand">False</property>
377
- <property name="fill">True</property>
378
- <property name="position">0</property>
379
- </packing>
380
- </child>
381
- <child>
382
- <object class="GtkHButtonBox" id="hbuttonbox1">
383
- <property name="visible">True</property>
384
- <property name="can_focus">False</property>
385
- <property name="spacing">3</property>
386
- <property name="layout_style">end</property>
387
- <child>
388
- <object class="GtkButton" id="btnRemove">
389
- <property name="label">gtk-remove</property>
390
- <property name="visible">True</property>
391
- <property name="can_focus">True</property>
392
- <property name="receives_default">True</property>
393
- <property name="use_action_appearance">False</property>
394
- <property name="use_stock">True</property>
395
- <signal name="clicked" handler="on_btnRemove_clicked" swapped="no"/>
396
- </object>
397
- <packing>
398
- <property name="expand">False</property>
399
- <property name="fill">False</property>
400
- <property name="position">0</property>
401
- </packing>
402
- </child>
403
- <child>
404
- <object class="GtkButton" id="btnSave">
405
- <property name="label">gtk-save</property>
406
- <property name="visible">True</property>
407
- <property name="can_focus">True</property>
408
- <property name="receives_default">True</property>
409
- <property name="use_action_appearance">False</property>
410
- <property name="use_stock">True</property>
411
- <signal name="clicked" handler="on_btnSave_clicked" swapped="no"/>
412
- </object>
413
- <packing>
414
- <property name="expand">False</property>
415
- <property name="fill">False</property>
416
- <property name="position">1</property>
417
- </packing>
418
- </child>
419
- </object>
420
- <packing>
421
- <property name="expand">False</property>
422
- <property name="fill">True</property>
423
- <property name="position">1</property>
424
- </packing>
425
- </child>
426
- <child>
427
- <object class="GtkLabel" id="label2">
428
- <property name="visible">True</property>
429
- <property name="can_focus">False</property>
430
- </object>
431
- <packing>
432
- <property name="expand">True</property>
433
- <property name="fill">True</property>
434
- <property name="position">2</property>
435
- </packing>
436
- </child>
437
- </object>
438
- </child>
439
- </object>
440
- </interface>
data/gui/win_overview.rb DELETED
@@ -1,5 +0,0 @@
1
- class Openall_time_applet::Gui::Win_overview
2
- def initialize(args)
3
-
4
- end
5
- end
@@ -1,153 +0,0 @@
1
- class Openall_time_applet::Gui::Win_timelog_edit
2
- attr_reader :args, :gui
3
-
4
- def initialize(args)
5
- @args = args
6
-
7
- @gui = Gtk::Builder.new.add("../glade/win_timelog_edit.glade")
8
- @gui.translate
9
- @gui.connect_signals{|h| method(h)}
10
-
11
- tasks_opts = [_("None")] + @args[:oata].ob.list(:Task, {"orderby" => "openall_uid"})
12
- @gui["cbTask"].init(tasks_opts)
13
-
14
- Knj::Gtk2::Cb.init(
15
- "cb" => @gui["cbTimeType"],
16
- "items" => {
17
- "normal" => _("Normal"),
18
- "overtime150" => sprintf(_("Overtime %s"), 150),
19
- "overtime200" => sprintf(_("Overtime %s"), 200)
20
- }
21
- )
22
-
23
-
24
- #Set up completion for description entry.
25
- ec = Gtk::EntryCompletion.new
26
- ec.model = Gtk::ListStore.new(String)
27
- ec.text_column = 0
28
- @gui["txtDescr"].completion = ec
29
-
30
- added = {}
31
- @args[:oata].ob.list(:Worktime, {"orderby" => "timestamp"}) do |worktime|
32
- next if added.key?(worktime[:comment])
33
- added[worktime[:comment]] = true
34
- ec.model.append[0] = worktime[:comment]
35
- end
36
-
37
- @args[:oata].ob.list(:Timelog, {"orderby" => "descr"}) do |timelog|
38
- next if added.key?(timelog[:descr])
39
- added[timelog[:descr]] = true
40
- ec.model.append[0] = timelog[:descr]
41
- end
42
-
43
- ec.signal_connect("match-selected") do |me, model, iter|
44
- text = model.get_value(iter, 0)
45
- me.entry.text = text
46
- true
47
- end
48
-
49
-
50
- #We are editting a timelog - set widget-values.
51
- @timelog = @args[:timelog]
52
-
53
- if @timelog
54
- @gui["txtDescr"].text = @timelog[:descr]
55
- @gui["txtTime"].text = @timelog.time_as_human
56
- @gui["txtTimeTransport"].text = @timelog.time_transport_as_human
57
- @gui["txtTransportLength"].text = Knj::Locales.number_out(@timelog[:transportlength], 0)
58
- @gui["txtTransportCosts"].text = Knj::Locales.number_out(@timelog[:transportcosts], 0)
59
- @gui["txtTransportDescr"].text = @timelog[:transportdescription]
60
- @gui["cbTask"].sel = @timelog.task if @timelog.task
61
- @gui["cbShouldSync"].active = Knj::Strings.yn_str(@timelog[:sync_need], true, false)
62
- @gui["cbTravelFixed"].active = Knj::Strings.yn_str(@timelog[:travelfixed], true, false)
63
- @gui["cbWorkInternal"].active = Knj::Strings.yn_str(@timelog[:workinternal], true, false)
64
- @gui["cbTimeType"].sel = @timelog[:timetype]
65
- @gui["txtTimestamp"].text = Knj::Datet.in(@timelog[:timestamp]).out
66
- else
67
- @gui["btnRemove"].visible = false
68
- @gui["txtTimestamp"].text = Knj::Datet.new.out
69
- end
70
-
71
- #Show the window.
72
- @gui["window"].show
73
- end
74
-
75
- def on_btnSave_clicked(*args)
76
- #Generate task-ID based on widget-value.
77
- task = @gui["cbTask"].sel
78
- if task.respond_to?(:is_knj?)
79
- task_id = task.id
80
- else
81
- task_id = 0
82
- end
83
-
84
- #Get times as integers based on widget-values.
85
- if @gui["txtTime"].text == ""
86
- time_secs = 0
87
- else
88
- begin
89
- time_secs = Knj::Strings.human_time_str_to_secs(@gui["txtTime"].text)
90
- rescue => e
91
- Knj::Gtk2.msgbox(_("You have entered an invalid time-format.") + "\n\n" + Knj::Errors.error_str(e))
92
- return nil
93
- end
94
- end
95
-
96
- if @gui["txtTimeTransport"].text == ""
97
- time_transport_secs = 0
98
- else
99
- begin
100
- time_transport_secs = Knj::Strings.human_time_str_to_secs(@gui["txtTimeTransport"].text)
101
- rescue => e
102
- Knj::Gtk2.msgbox(_("You have entered an invalid transport-time-format.") + "\n\n" + Knj::Errors.error_str(e))
103
- return nil
104
- end
105
- end
106
-
107
- begin
108
- timestamp_dbstr = Knj::Datet.in(@gui["txtTimestamp"].text).dbstr
109
- rescue
110
- Knj::Gtk2.msgbox(_("You have entered an invalid timestamp."))
111
- return nil
112
- end
113
-
114
- #Generate hash for updating dataabase.
115
- save_hash = {
116
- :descr => @gui["txtDescr"].text,
117
- :timestamp => timestamp_dbstr,
118
- :time => time_secs,
119
- :timetype => @gui["cbTimeType"].sel,
120
- :time_transport => time_transport_secs,
121
- :transportdescription => @gui["txtTransportDescr"].text,
122
- :transportlength => Knj::Locales.number_in(@gui["txtTransportLength"].text),
123
- :transportcosts => Knj::Locales.number_in(@gui["txtTransportCosts"].text),
124
- :task_id => task_id,
125
- :sync_need => Knj::Strings.yn_str(@gui["cbShouldSync"].active?, 1, 0),
126
- :workinternal => Knj::Strings.yn_str(@gui["cbWorkInternal"].active?, 1, 0),
127
- :travelfixed => Knj::Strings.yn_str(@gui["cbTravelFixed"].active?, 1, 0)
128
- }
129
-
130
- #Update or add the timelog.
131
- if @timelog
132
- @timelog.update(save_hash)
133
- else
134
- @timelog = @args[:oata].ob.add(:Timelog, save_hash)
135
- end
136
-
137
- #Start tracking the current timelog if the checkbox has been checked.
138
- if @gui["cbStartTracking"].active?
139
- @args[:oata].timelog_active = @timelog
140
- end
141
-
142
- @gui["window"].destroy
143
- end
144
-
145
- def on_btnRemove_clicked(*args)
146
- if Knj::Gtk2.msgbox(_("Do you want to remove this timelog? This will not delete the timelog on OpenAll."), "yesno") != "yes"
147
- return nil
148
- end
149
-
150
- @args[:oata].ob.delete(@timelog)
151
- @gui["window"].destroy
152
- end
153
- end