openall_time_applet 0.0.21 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -8,6 +8,8 @@ gem "sqlite3"
8
8
  gem "gettext"
9
9
  gem "json"
10
10
  gem "rmagick"
11
+ gem "datet"
12
+ gem "http2"
11
13
 
12
14
  # Add dependencies to develop your gem here.
13
15
  # Include everything needed to run rake, tests, features, etc.
@@ -5,6 +5,7 @@ GEM
5
5
  glib2 (>= 1.1.3)
6
6
  cairo (1.12.2)
7
7
  pkg-config
8
+ datet (0.0.4)
8
9
  diff-lcs (1.1.3)
9
10
  gdk_pixbuf2 (1.1.3)
10
11
  glib2 (>= 1.1.3)
@@ -17,6 +18,8 @@ GEM
17
18
  atk (>= 1.1.3)
18
19
  gdk_pixbuf2 (>= 1.1.3)
19
20
  pango (>= 1.1.3)
21
+ http2 (0.0.0)
22
+ knjrbfw
20
23
  jeweler (1.8.4)
21
24
  bundler (~> 1.0)
22
25
  git (>= 1.2.5)
@@ -53,8 +56,10 @@ PLATFORMS
53
56
 
54
57
  DEPENDENCIES
55
58
  bundler (>= 1.0.0)
59
+ datet
56
60
  gettext
57
61
  gtk2
62
+ http2
58
63
  jeweler (~> 1.8.3)
59
64
  json
60
65
  knjrbfw
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.21
1
+ 0.0.22
@@ -4,7 +4,7 @@ require "json"
4
4
  class Openall_time_applet::Connection
5
5
  def initialize(args)
6
6
  @args = args
7
- @http = Knj::Http2.new(
7
+ @http = Http2.new(
8
8
  :host => @args[:host],
9
9
  :port => @args[:port],
10
10
  :follow_redirects => false,
@@ -17,10 +17,10 @@ class Openall_time_applet::Connection
17
17
 
18
18
  def login
19
19
  #For some weird reason OpenAll seems to only accept multipart-post-requests??
20
- @http.post_multipart("index.php?c=Auth&m=validateLogin", {"username" => @args[:username], "password" => @args[:password]})
20
+ @http.post_multipart(:url => "index.php?c=Auth&m=validateLogin", :post => {"username" => @args[:username], "password" => @args[:password]})
21
21
 
22
22
  #Verify login by reading dashboard HTML.
23
- res = @http.get("index.php?c=Dashboard")
23
+ res = @http.get(:url => "index.php?c=Dashboard")
24
24
 
25
25
  if !res.body.match(/<ul\s*id="webticker"\s*>/) and !res.body.index("<a href=\"index.php?c=Dashboard&m=editDashboard\">") == nil
26
26
  tmp_path = "#{Knj::Os.tmpdir}/openall_login_debug.txt"
@@ -44,9 +44,9 @@ class Openall_time_applet::Connection
44
44
 
45
45
  #Send request to OpenAll via HTTP.
46
46
  if args[:post]
47
- res = @http.post_multipart(args[:url], args[:post])
47
+ res = @http.post_multipart(:url => args[:url], :post => args[:post])
48
48
  else
49
- res = @http.get(args[:url])
49
+ res = @http.get(:url => args[:url])
50
50
  end
51
51
 
52
52
  raise _("Empty body returned from OpenAll.") if res.body.to_s.strip.length <= 0
@@ -146,59 +146,29 @@
146
146
  </packing>
147
147
  </child>
148
148
  <child>
149
- <object class="GtkTable" id="tableRunning">
149
+ <object class="GtkHButtonBox" id="hbuttonbox3">
150
150
  <property name="visible">True</property>
151
151
  <property name="can_focus">False</property>
152
- <property name="n_columns">3</property>
153
152
  <child>
154
- <object class="GtkLabel" id="labRunningTimelog">
153
+ <object class="GtkButton" id="btnSwitch">
154
+ <property name="label" translatable="yes">Switch</property>
155
155
  <property name="visible">True</property>
156
- <property name="can_focus">False</property>
157
- <property name="xalign">0</property>
158
- </object>
159
- </child>
160
- <child>
161
- <object class="GtkLabel" id="labRunningTask">
162
- <property name="visible">True</property>
163
- <property name="can_focus">False</property>
164
- <property name="xalign">0</property>
165
- </object>
166
- <packing>
167
- <property name="left_attach">1</property>
168
- <property name="right_attach">2</property>
169
- </packing>
170
- </child>
171
- <child>
172
- <object class="GtkLabel" id="labRunningTime">
173
- <property name="visible">True</property>
174
- <property name="can_focus">False</property>
175
- <property name="xalign">0</property>
156
+ <property name="can_focus">True</property>
157
+ <property name="receives_default">True</property>
158
+ <property name="use_action_appearance">False</property>
159
+ <signal name="clicked" handler="on_btnSwitch_clicked" swapped="no"/>
176
160
  </object>
177
161
  <packing>
178
- <property name="left_attach">2</property>
179
- <property name="right_attach">3</property>
162
+ <property name="expand">False</property>
163
+ <property name="fill">True</property>
164
+ <property name="position">0</property>
180
165
  </packing>
181
166
  </child>
182
167
  </object>
183
- <packing>
184
- <property name="expand">True</property>
185
- <property name="fill">True</property>
186
- <property name="position">2</property>
187
- </packing>
188
- </child>
189
- <child>
190
- <object class="GtkButton" id="btnSwitch">
191
- <property name="label" translatable="yes">Switch</property>
192
- <property name="visible">True</property>
193
- <property name="can_focus">True</property>
194
- <property name="receives_default">True</property>
195
- <property name="use_action_appearance">False</property>
196
- <signal name="clicked" handler="on_btnSwitch_clicked" swapped="no"/>
197
- </object>
198
168
  <packing>
199
169
  <property name="expand">False</property>
200
170
  <property name="fill">True</property>
201
- <property name="position">3</property>
171
+ <property name="position">2</property>
202
172
  </packing>
203
173
  </child>
204
174
  </object>
@@ -349,6 +319,119 @@
349
319
  <property name="position">2</property>
350
320
  </packing>
351
321
  </child>
322
+ <child>
323
+ <object class="GtkVBox" id="vboxPrepareTransfer">
324
+ <property name="visible">True</property>
325
+ <property name="can_focus">False</property>
326
+ <child>
327
+ <object class="GtkFrame" id="frame2">
328
+ <property name="visible">True</property>
329
+ <property name="can_focus">False</property>
330
+ <property name="label_xalign">0</property>
331
+ <property name="shadow_type">none</property>
332
+ <child>
333
+ <object class="GtkAlignment" id="alignment3">
334
+ <property name="visible">True</property>
335
+ <property name="can_focus">False</property>
336
+ <property name="left_padding">12</property>
337
+ <child>
338
+ <object class="GtkVBox" id="vbox4">
339
+ <property name="visible">True</property>
340
+ <property name="can_focus">False</property>
341
+ <property name="spacing">3</property>
342
+ <child>
343
+ <object class="GtkViewport" id="viewport2">
344
+ <property name="visible">True</property>
345
+ <property name="can_focus">False</property>
346
+ <child>
347
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
348
+ <property name="visible">True</property>
349
+ <property name="can_focus">True</property>
350
+ <property name="hscrollbar_policy">automatic</property>
351
+ <property name="vscrollbar_policy">automatic</property>
352
+ <child>
353
+ <object class="GtkTreeView" id="tvTimelogsPrepareTransfer">
354
+ <property name="visible">True</property>
355
+ <property name="can_focus">True</property>
356
+ </object>
357
+ </child>
358
+ </object>
359
+ </child>
360
+ </object>
361
+ <packing>
362
+ <property name="expand">True</property>
363
+ <property name="fill">True</property>
364
+ <property name="position">0</property>
365
+ </packing>
366
+ </child>
367
+ <child>
368
+ <object class="GtkHButtonBox" id="hbuttonbox4">
369
+ <property name="visible">True</property>
370
+ <property name="can_focus">False</property>
371
+ <property name="spacing">3</property>
372
+ <property name="layout_style">end</property>
373
+ <child>
374
+ <object class="GtkButton" id="btnSyncPrepareTransfer">
375
+ <property name="label">gtk-harddisk</property>
376
+ <property name="visible">True</property>
377
+ <property name="can_focus">True</property>
378
+ <property name="receives_default">True</property>
379
+ <property name="use_action_appearance">False</property>
380
+ <property name="use_stock">True</property>
381
+ <signal name="clicked" handler="on_btnSyncPrepareTransfer_clicked" swapped="no"/>
382
+ </object>
383
+ <packing>
384
+ <property name="expand">False</property>
385
+ <property name="fill">False</property>
386
+ <property name="position">0</property>
387
+ </packing>
388
+ </child>
389
+ </object>
390
+ <packing>
391
+ <property name="expand">False</property>
392
+ <property name="fill">True</property>
393
+ <property name="position">1</property>
394
+ </packing>
395
+ </child>
396
+ </object>
397
+ </child>
398
+ </object>
399
+ </child>
400
+ <child type="label">
401
+ <object class="GtkLabel" id="label3">
402
+ <property name="visible">True</property>
403
+ <property name="can_focus">False</property>
404
+ <property name="label" translatable="yes">&lt;b&gt;Prepare transfer&lt;/b&gt;</property>
405
+ <property name="use_markup">True</property>
406
+ </object>
407
+ </child>
408
+ </object>
409
+ <packing>
410
+ <property name="expand">True</property>
411
+ <property name="fill">True</property>
412
+ <property name="position">0</property>
413
+ </packing>
414
+ </child>
415
+ <child>
416
+ <object class="GtkLabel" id="labTotal">
417
+ <property name="visible">True</property>
418
+ <property name="can_focus">False</property>
419
+ <property name="xalign">0</property>
420
+ <property name="label" translatable="yes">[totals]</property>
421
+ </object>
422
+ <packing>
423
+ <property name="expand">False</property>
424
+ <property name="fill">True</property>
425
+ <property name="position">1</property>
426
+ </packing>
427
+ </child>
428
+ </object>
429
+ <packing>
430
+ <property name="expand">True</property>
431
+ <property name="fill">True</property>
432
+ <property name="position">3</property>
433
+ </packing>
434
+ </child>
352
435
  <child>
353
436
  <object class="GtkStatusbar" id="statusbar">
354
437
  <property name="visible">True</property>
@@ -358,7 +441,7 @@
358
441
  <packing>
359
442
  <property name="expand">False</property>
360
443
  <property name="fill">True</property>
361
- <property name="position">3</property>
444
+ <property name="position">4</property>
362
445
  </packing>
363
446
  </child>
364
447
  </object>
@@ -39,7 +39,7 @@ class Openall_time_applet::Gui::Trayicon
39
39
 
40
40
  #Calculate minutes tracked and generate variables.
41
41
  secs = Time.now.to_i - @args[:oata].timelog_active_time.to_i + @args[:oata].timelog_active.time_total
42
- mins = (secs.to_f / 60.0)
42
+ mins = (secs.to_f / 60.0).floor
43
43
 
44
44
  if mins >= 60
45
45
  hours = mins / 60
@@ -118,7 +118,7 @@ class Openall_time_applet::Gui::Win_main
118
118
  :model_class => :Timelog,
119
119
  :renderers => init_data[:renderers],
120
120
  :change_before => proc{|d|
121
- if (d[:col_no] == 11 or d[:col_no] == 3 or d[:col_no] == 2) and @args[:oata].timelog_active and @args[:oata].timelog_active.id == d[:model].id
121
+ if d[:col_no] == 3 and @args[:oata].timelog_active and @args[:oata].timelog_active.id == d[:model].id
122
122
  raise _("You cannot edit the time for the active timelog.")
123
123
  end
124
124
 
@@ -127,6 +127,12 @@ class Openall_time_applet::Gui::Win_main
127
127
  :change_after => proc{
128
128
  @dont_reload = false
129
129
  },
130
+ :on_edit => proc{|d|
131
+ @tv_editting = true
132
+ },
133
+ :on_edit_done => proc{|d|
134
+ @tv_editting = nil
135
+ },
130
136
  :cols => {
131
137
  1 => :descr,
132
138
  2 => {:col => :timestamp, :type => :datetime},
@@ -160,10 +166,7 @@ class Openall_time_applet::Gui::Win_main
160
166
 
161
167
 
162
168
  #Connect certain column renderers to the editingStarted-method, so editing can be canceled, if the user tries to edit forbidden data on the active timelog.
163
- init_data[:renderers][1].signal_connect_after("editing-started", :descr, &self.method(:on_cell_editingStarted))
164
- init_data[:renderers][2].signal_connect_after("editing-started", :timestamp, &self.method(:on_cell_editingStarted))
165
169
  init_data[:renderers][3].signal_connect_after("editing-started", :time, &self.method(:on_cell_editingStarted))
166
- init_data[:renderers][11].signal_connect_after("editing-started", :task, &self.method(:on_cell_editingStarted))
167
170
 
168
171
 
169
172
  #Fills the timelogs-treeview with data.
@@ -194,13 +197,179 @@ class Openall_time_applet::Gui::Win_main
194
197
  end
195
198
 
196
199
 
200
+
201
+ #Initializes sync-box.
202
+ @gui["btnSyncPrepareTransfer"].label = _("Transfer")
203
+
204
+ #Generate list-store containing tasks for the task-column.
205
+ task_ls = Gtk::ListStore.new(String, String)
206
+ iter = task_ls.append
207
+ iter[0] = _("None")
208
+ iter[1] = 0.to_s
209
+
210
+ tasks = [_("Choose:")]
211
+ @args[:oata].ob.list(:Task, {"orderby" => "title"}) do |task|
212
+ iter = task_ls.append
213
+ iter[0] = task[:title]
214
+ iter[1] = task.id.to_s
215
+ tasks << task
216
+ end
217
+
218
+ #Initialize timelog treeview.
219
+ init_data = Knj::Gtk2::Tv.init(@gui["tvTimelogsPrepareTransfer"], [
220
+ _("ID"),
221
+ _("Description"),
222
+ _("Timestamp"),
223
+ _("Time"),
224
+ _("Transport"),
225
+ _("Length"),
226
+ _("Transport descr."),
227
+ _("Transport costs"),
228
+ {
229
+ :title => _("Fixed travel"),
230
+ :type => :toggle
231
+ },
232
+ {
233
+ :title => _("Int. work"),
234
+ :type => :toggle
235
+ },
236
+ {
237
+ :title => _("Sync?"),
238
+ :type => :toggle
239
+ },
240
+ {
241
+ :title => _("Task"),
242
+ :type => :combo,
243
+ :model => task_ls,
244
+ :has_entry => false
245
+ },
246
+ _("Sync time")
247
+ ])
248
+
249
+ #Make columns editable.
250
+ Knj::Gtk2::Tv.editable_text_renderers_to_model(
251
+ :ob => @args[:oata].ob,
252
+ :tv => @gui["tvTimelogsPrepareTransfer"],
253
+ :model_class => :Timelog,
254
+ :renderers => init_data[:renderers],
255
+ :change_before => proc{ @dont_reload_sync = true },
256
+ :change_after => proc{ @dont_reload_sync = false; self.update_sync_totals },
257
+ :cols => {
258
+ 1 => :descr,
259
+ 2 => {:col => :timestamp, :type => :datetime},
260
+ 3 => {:col => :time, :type => :time_as_sec},
261
+ 4 => {:col => :time_transport, :type => :time_as_sec},
262
+ 5 => {:col => :transportlength, :type => :int},
263
+ 6 => {:col => :transportdescription},
264
+ 7 => {:col => :transportcosts, :type => :human_number, :decimals => 2},
265
+ 8 => {:col => :travelfixed},
266
+ 9 => {:col => :workinternal},
267
+ 10 => {:col => :sync_need},
268
+ 11 => {
269
+ :col => :task_id,
270
+ :value_callback => lambda{ |data|
271
+ task = @args[:oata].ob.get_by(:Task, {"title" => data[:value]})
272
+
273
+ if !task
274
+ return 0
275
+ else
276
+ return task.id
277
+ end
278
+ },
279
+ :value_set_callback => proc{ |data| data[:model].task_name }
280
+ },
281
+ 12 => {:col => :time_sync, :type => :time_as_sec}
282
+ }
283
+ )
284
+ @gui["tvTimelogsPrepareTransfer"].columns[0].visible = false
285
+ @gui["vboxPrepareTransfer"].hide
286
+ @reload_preparetransfer_id = @args[:oata].ob.connect("object" => :Timelog, "signals" => ["add", "update", "delete"], &self.method(:reload_timelogs))
287
+
288
+
289
+
197
290
  #Show the window.
198
- @gui["window"].show_all
291
+ @gui["window"].show
199
292
  self.timelog_info_trigger
200
293
  width = @gui["window"].size[0]
201
294
  @gui["window"].resize(width, 1)
202
295
  end
203
296
 
297
+ def reload_timelogs_preparetransfer
298
+ return nil if @dont_reload_sync or @gui["tvTimelogsPrepareTransfer"].destroyed?
299
+ @gui["tvTimelogsPrepareTransfer"].model.clear
300
+ @timelogs_sync_count = 0
301
+
302
+ @args[:oata].ob.list(:Timelog, {"sync_need" => 1, "task_id_not" => ["", 0], "orderby" => "timestamp"}) do |timelog|
303
+ #Read time and transport from timelog.
304
+ time = timelog[:time].to_i
305
+ transport = timelog[:time_transport].to_i
306
+
307
+ #If transport is logged, then the work if offsite. It should be rounded up by 30 min. Else 15 min. round up.
308
+ if transport > 0
309
+ roundup = 1800
310
+ else
311
+ roundup = 900
312
+ end
313
+
314
+ #Do the actual counting.
315
+ count_rounded_time = 0
316
+ loop do
317
+ break if count_rounded_time >= time
318
+ count_rounded_time += roundup
319
+ end
320
+
321
+ #Set sync-time on timelog.
322
+ timelog[:time_sync] = count_rounded_time
323
+
324
+ Knj::Gtk2::Tv.append(@gui["tvTimelogsPrepareTransfer"], [
325
+ timelog.id,
326
+ timelog[:descr],
327
+ timelog.timestamp_str,
328
+ timelog.time_as_human,
329
+ timelog.time_transport_as_human,
330
+ Knj::Locales.number_out(timelog[:transportlength], 0),
331
+ timelog.transport_descr_short,
332
+ Knj::Locales.number_out(timelog[:transportcosts], 2),
333
+ Knj::Strings.yn_str(timelog[:travelfixed], true, false),
334
+ Knj::Strings.yn_str(timelog[:workinternal], true, false),
335
+ Knj::Strings.yn_str(timelog[:sync_need], true, false),
336
+ timelog.task_name,
337
+ Knj::Strings.secs_to_human_time_str(count_rounded_time)
338
+ ])
339
+ @timelogs_sync_count += 1
340
+ end
341
+ end
342
+
343
+ def on_btnSync_clicked
344
+ self.reload_timelogs_preparetransfer
345
+
346
+ if @timelogs_sync_count <= 0
347
+ #Show error-message and destroy the window, if no timelogs was to be synced.
348
+ Knj::Gtk2.msgbox("msg" => _("There is nothing to sync at this time."), "run" => false)
349
+ else
350
+ #...else show the window.
351
+ @gui["expOverview"].hide
352
+ self.update_sync_totals
353
+ @gui["vboxPrepareTransfer"].show
354
+ end
355
+ end
356
+
357
+ def update_sync_totals
358
+ total_secs = 0
359
+
360
+ @gui["tvTimelogsPrepareTransfer"].model.each do |model, path, iter|
361
+ time_val = @gui["tvTimelogsPrepareTransfer"].model.get_value(iter, 12)
362
+
363
+ begin
364
+ total_secs += Knj::Strings.human_time_str_to_secs(time_val)
365
+ rescue
366
+ #ignore - user is properly entering stuff.
367
+ end
368
+ end
369
+
370
+ @gui["labTotal"].markup = "<b>#{_("Total hours:")}</b> #{Knj::Strings.secs_to_human_time_str(total_secs)}"
371
+ end
372
+
204
373
  #This method is called, when editting starts in a description-, time- or task-cell. If it is the active timelog, then editting is canceled.
205
374
  def on_cell_editingStarted(renderer, editable, path, col_title)
206
375
  iter = @gui["tvTimelogs"].model.get_iter(path)
@@ -285,6 +454,8 @@ class Openall_time_applet::Gui::Win_main
285
454
  def on_window_destroy
286
455
  #Unconnect reload-event. Else it will crash on call to destroyed object. Also frees up various ressources.
287
456
  @args[:oata].ob.unconnect("object" => :Timelog, "conn_id" => @reload_id)
457
+ @args[:oata].ob.unconnect("object" => :Timelog, "conn_id" => @reload_preparetransfer_id)
458
+
288
459
  @args[:oata].events.disconnect(:timelog_active_changed, @event_timelog_active_changed) if @event_timelog_active_changed
289
460
  @event_timelog_active_changed = nil
290
461
  Gtk.timeout_remove(@timeout_id)
@@ -306,27 +477,8 @@ class Openall_time_applet::Gui::Win_main
306
477
  #This method handles the "Timelog info"-frame. Hides, shows and updates the info in it.
307
478
  def timelog_info_trigger
308
479
  if tlog = @args[:oata].timelog_active
309
- task = tlog.task
310
- if !task
311
- task_text = "[#{_("no task sat on the timelog")}]"
312
- else
313
- task_text = task.name
314
- end
315
-
316
- @gui["labRunningTimelog"].label = tlog[:descr]
317
- @gui["labRunningTask"].label = task_text
318
-
319
480
  time_tracked = Knj::Strings.secs_to_human_time_str(@args[:oata].timelog_active_time_tracked + tlog.time_total)
320
-
321
- @gui["labRunningTime"].label = time_tracked
322
-
323
- @gui["txtDescr"].hide
324
- @gui["cbTask"].hide
325
- @gui["tableRunning"].show_all
326
- else
327
- @gui["txtDescr"].show
328
- @gui["cbTask"].show
329
- @gui["tableRunning"].hide
481
+ @gui["btnSwitch"].label = time_tracked[0..4]
330
482
  end
331
483
  end
332
484
 
@@ -362,14 +514,31 @@ class Openall_time_applet::Gui::Win_main
362
514
  if tlog_act
363
515
  but.image = Gtk::Image.new(Gtk::Stock::MEDIA_STOP, Gtk::IconSize::BUTTON)
364
516
  but.label = _("Stop")
517
+
518
+ @gui["txtDescr"].text = tlog_act[:descr]
519
+
520
+ if task = tlog_act.task
521
+ @gui["cbTask"].sel = task.name
522
+ else
523
+ @gui["cbTask"].sel = _("Choose:")
524
+ end
525
+
526
+ @gui["txtDescr"].sensitive = false
527
+ @gui["cbTask"].sensitive = false
365
528
  else
366
529
  but.image = Gtk::Image.new(Gtk::Stock::MEDIA_RECORD, Gtk::IconSize::BUTTON)
367
530
  but.label = _("Start")
531
+
532
+ @gui["txtDescr"].text = ""
533
+ @gui["cbTask"].sel = _("Choose:")
534
+ @gui["txtDescr"].sensitive = true
535
+ @gui["cbTask"].sensitive = true
368
536
  end
369
537
  end
370
538
 
371
539
  #This method runs through all rows in the treeview and checks if a row should be marked with bold. It also increases the time in the time-column for the tracked timelog.
372
540
  def check_rows
541
+ return nil if @tv_editting
373
542
  act_timelog = @args[:oata].timelog_active
374
543
 
375
544
  if act_timelog
@@ -403,10 +572,6 @@ class Openall_time_applet::Gui::Win_main
403
572
  end
404
573
  end
405
574
 
406
- def on_btnSync_clicked
407
- @args[:oata].show_prepare_sync
408
- end
409
-
410
575
  def on_miSyncStatic_activate
411
576
  @args[:oata].sync_static("transient_for" => @gui["window"])
412
577
  end
@@ -446,4 +611,14 @@ class Openall_time_applet::Gui::Win_main
446
611
  def on_txtDescr_activate(*args)
447
612
  self.on_btnSwitch_clicked
448
613
  end
614
+
615
+ def on_btnSyncPrepareTransfer_clicked
616
+ begin
617
+ #Destroy this window and start syncing for real.
618
+ @gui["window"].destroy
619
+ @args[:oata].sync_real
620
+ rescue => e
621
+ Knj::Gtk2.msgbox(Knj::Errors.error_str(e))
622
+ end
623
+ end
449
624
  end
@@ -9,7 +9,7 @@ class Openall_time_applet::Gui::Win_worktime_overview
9
9
  @gui.translate
10
10
  @gui.connect_signals{|h| method(h)}
11
11
 
12
- @date = Knj::Datet.new
12
+ @date = Datet.new
13
13
  self.build_week
14
14
 
15
15
  @gui["window"].show_all
@@ -66,7 +66,7 @@ class Openall_time_applet::Gui::Win_worktime_overview
66
66
 
67
67
  #Draw all the days.
68
68
  stats[:days_total].keys.sort.each do |day_no|
69
- date = Knj::Datet.in(Time.new(date.year, date.month, day_no))
69
+ date = Datet.in(Time.new(date.year, date.month, day_no))
70
70
 
71
71
  day_title = Gtk::Label.new
72
72
  day_title.markup = "<b>#{date.day_str(:short => true)} #{date.time.strftime("%d/%m")} - #{stats[:days_total][day_no][:first_time].time.strftime("%H:%M")}</b>"
@@ -2,13 +2,19 @@
2
2
  require "rubygems"
3
3
 
4
4
  #For secs-to-human-string (MySQL-format), model-framework, database-framework, options-framework, date-framework and more.
5
- if ENV["HOME"] == "/home/kaspernj" and File.exists?("/home/kaspernj/Dev/Ruby/knjrbfw")
6
- #For development.
7
- require "/home/kaspernj/Dev/Ruby/knjrbfw/lib/knjrbfw"
8
- else
9
- require "knjrbfw"
5
+ gems = ["wref", "datet", "http2", "knjrbfw"]
6
+ gems.each do |gem|
7
+ fpath = "#{File.dirname(__FILE__)}/../../#{gem}/lib/#{gem}.rb"
8
+ if File.exists?(fpath)
9
+ puts "Require custom Gem-path: '#{fpath}'."
10
+ require fpath
11
+ else
12
+ puts "Require Gem normally: '#{gem}'."
13
+ require gem
14
+ end
10
15
  end
11
16
 
17
+
12
18
  require "sqlite3"
13
19
  require "gettext"
14
20
  require "base64"
@@ -185,7 +191,7 @@ class Openall_time_applet
185
191
  loop do
186
192
  enabled = Knj::Strings.yn_str(Knj::Opts.get("reminder_enabled"), true, false)
187
193
  if enabled and !@reminder_next
188
- @reminder_next = Knj::Datet.new
194
+ @reminder_next = Datet.new
189
195
  @reminder_next.mins + Knj::Opts.get("reminder_every_minute").to_i
190
196
  elsif enabled and @reminder_next and Time.now >= @reminder_next
191
197
  self.reminding_exec
@@ -14,7 +14,7 @@ class Openall_time_applet::Models::Timelog < Knj::Datarow
14
14
 
15
15
  #Fix default time-type (SQLite3 doesnt support this).
16
16
  self[:timetype] = "normal" if self[:timetype].to_s == ""
17
- self[:timestamp] = Knj::Datet.new.dbstr if self[:timestamp].to_s == ""
17
+ self[:timestamp] = Datet.new.dbstr if self[:timestamp].to_s == ""
18
18
  end
19
19
 
20
20
  #Treat data before inserting into database.
@@ -24,11 +24,43 @@ class Openall_time_applet::Models::Timelog < Knj::Datarow
24
24
  d.data[:parent_timelog_id] = 0 if !d.data.key?(:parent_timelog_id)
25
25
  end
26
26
 
27
+ #This method is called from objects-framework when it wants to delete this object. It checks for various cases where the timelog shouldnt be deleted.
28
+ def delete
29
+ #Ensure that child timelogs with logged time do not get deleted automatically!
30
+ self.child_timelogs do |child_timelog|
31
+ if child_timelog.time_total > 0
32
+ raise sprintf(_("Child timelog '%1$s' (%2$s) has logged time (%3$s) and this timelog cannot be deleted."), child_timelog[:descr], child_timelog.id, child_timelog.time_as_human)
33
+ end
34
+
35
+ if child_timelog.time_total(:transport => true) > 0
36
+ raise _("A child timelog has logged transport time and this timelog cannot be deleted.")
37
+ end
38
+ end
39
+ end
40
+
41
+ #This method proxies updates to child timelogs as well (not time though).
42
+ def update(hash)
43
+ #Update the data on this object.
44
+ super(hash)
45
+
46
+ #Certain data should be updated on child timelogs as well.
47
+ hash.each do |key, val|
48
+ key = key.to_sym
49
+
50
+ case key
51
+ when :descr, :task_id, :timetype, :transportdescription, :travelfixed, :workinternal, :sync_need
52
+ self.child_timelogs do |child_timelog|
53
+ child_timelog[key] = val
54
+ end
55
+ end
56
+ end
57
+ end
58
+
27
59
  #Pushes timelogs and time to OpenAll.
28
60
  def self.push_time_updates(d, args)
29
61
  args[:oata].oa_conn do |conn|
30
62
  #Go through timelogs that needs syncing and has a task set.
31
- self.ob.list(:Timelog, {"sync_need" => 1, "task_id_not" => 0}) do |timelog|
63
+ self.ob.list(:Timelog, {"sync_need" => 1, "task_id_not" => ["", 0]}) do |timelog|
32
64
  secs_sum = timelog[:time_sync].to_i + timelog[:time_transport].to_i
33
65
  next if secs_sum <= 0 or !timelog.task
34
66
 
@@ -50,8 +82,23 @@ class Openall_time_applet::Models::Timelog < Knj::Datarow
50
82
  }
51
83
  )
52
84
 
85
+ parent_timelog = timelog.parent_timelog
86
+ timelog.delete_empty_children
87
+
53
88
  #Delete timelog.
54
- d.ob.delete(timelog)
89
+ if timelog.child_timelogs("count" => true) <= 0
90
+ d.ob.delete(timelog)
91
+ else
92
+ timelog[:time] = 0
93
+ timelog[:time_transport] = 0
94
+ end
95
+
96
+ if parent_timelog
97
+ parent_timelog_child_timelogs = parent_timelog.child_timelogs("count" => true)
98
+ if parent_timelog_child_timelogs <= 0
99
+ d.ob.delete(parent_timelog)
100
+ end
101
+ end
55
102
  end
56
103
  end
57
104
  end
@@ -97,4 +144,15 @@ class Openall_time_applet::Models::Timelog < Knj::Datarow
97
144
  def time_transport_as_human
98
145
  return Knj::Strings.secs_to_human_time_str(self.time_total(:transport => true))
99
146
  end
147
+
148
+ def delete_empty_children
149
+ self.child_timelogs do |child_timelog|
150
+ time = child_timelog.time_total
151
+ time_transport = child_timelog.time_total(:transport => true)
152
+
153
+ if time <= 0 and time_transport <= 0
154
+ self.ob.delete(child_timelog)
155
+ end
156
+ end
157
+ end
100
158
  end
@@ -18,7 +18,7 @@ class Openall_time_applet::Models::Worktime < Knj::Datarow
18
18
  save_hash = {
19
19
  :openall_uid => wt_d["uid"],
20
20
  :task_id => task.id,
21
- :timestamp => Knj::Datet.in(wt_d["timestamp"]),
21
+ :timestamp => Datet.in(wt_d["timestamp"]),
22
22
  :worktime => Knj::Strings.human_time_str_to_secs(wt_d["worktime"]),
23
23
  :transporttime => Knj::Strings.human_time_str_to_secs(wt_d["transporttime"]),
24
24
  :comment => wt_d["comment"]
@@ -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.21"
8
+ s.version = "0.0.22"
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-07-10}
12
+ s.date = %q{2012-07-16}
13
13
  s.description = %q{Off-line time-tracking for OpenAll with syncing when online.}
14
14
  s.email = %q{k@spernj.org}
15
15
  s.executables = ["OpenAll Timelogging", "openall_time_applet.rb"]
@@ -39,12 +39,10 @@ Gem::Specification.new do |s|
39
39
  "glade/win_main.glade",
40
40
  "glade/win_overview.glade",
41
41
  "glade/win_preferences.glade",
42
- "glade/win_sync_overview.glade",
43
42
  "glade/win_worktime_overview.glade",
44
43
  "gui/trayicon.rb",
45
44
  "gui/win_main.rb",
46
45
  "gui/win_preferences.rb",
47
- "gui/win_sync_overview.rb",
48
46
  "gui/win_worktime_overview.rb",
49
47
  "lib/openall_time_applet.rb",
50
48
  "locales/da_DK/LC_MESSAGES/default.mo",
@@ -74,6 +72,8 @@ Gem::Specification.new do |s|
74
72
  s.add_runtime_dependency(%q<gettext>, [">= 0"])
75
73
  s.add_runtime_dependency(%q<json>, [">= 0"])
76
74
  s.add_runtime_dependency(%q<rmagick>, [">= 0"])
75
+ s.add_runtime_dependency(%q<datet>, [">= 0"])
76
+ s.add_runtime_dependency(%q<http2>, [">= 0"])
77
77
  s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
78
78
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
79
79
  s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
@@ -86,6 +86,8 @@ Gem::Specification.new do |s|
86
86
  s.add_dependency(%q<gettext>, [">= 0"])
87
87
  s.add_dependency(%q<json>, [">= 0"])
88
88
  s.add_dependency(%q<rmagick>, [">= 0"])
89
+ s.add_dependency(%q<datet>, [">= 0"])
90
+ s.add_dependency(%q<http2>, [">= 0"])
89
91
  s.add_dependency(%q<rspec>, ["~> 2.8.0"])
90
92
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
91
93
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
@@ -99,6 +101,8 @@ Gem::Specification.new do |s|
99
101
  s.add_dependency(%q<gettext>, [">= 0"])
100
102
  s.add_dependency(%q<json>, [">= 0"])
101
103
  s.add_dependency(%q<rmagick>, [">= 0"])
104
+ s.add_dependency(%q<datet>, [">= 0"])
105
+ s.add_dependency(%q<http2>, [">= 0"])
102
106
  s.add_dependency(%q<rspec>, ["~> 2.8.0"])
103
107
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
104
108
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
@@ -8,7 +8,7 @@ describe "OpenallTimeApplet" do
8
8
  end
9
9
 
10
10
  it "should be able to clone timelogs" do
11
- date = Knj::Datet.new
11
+ date = Datet.new
12
12
  date.days - 1
13
13
 
14
14
  timelog = $oata.ob.add(:Timelog, {
@@ -32,6 +32,11 @@ describe "OpenallTimeApplet" do
32
32
 
33
33
  timelogs = $oata.ob.list(:Timelog, "orderby" => "timestamp").to_a
34
34
  raise "Expected amount of timelogs to be 2 but it wasnt: #{timelogs.length}" if timelogs.length != 2
35
+
36
+ #Or else it wont be possible to delete main timelog.
37
+ timelog.child_timelogs do |child_timelog|
38
+ child_timelog[:time] = 0
39
+ end
35
40
  end
36
41
 
37
42
  it "should automatically delete sub-timelogs" do
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.21
5
+ version: 0.0.22
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-07-10 00:00:00 +02:00
13
+ date: 2012-07-16 00:00:00 +02:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -80,8 +80,30 @@ dependencies:
80
80
  prerelease: false
81
81
  version_requirements: *id006
82
82
  - !ruby/object:Gem::Dependency
83
- name: rspec
83
+ name: datet
84
84
  requirement: &id007 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: "0"
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: http2
95
+ requirement: &id008 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ type: :runtime
102
+ prerelease: false
103
+ version_requirements: *id008
104
+ - !ruby/object:Gem::Dependency
105
+ name: rspec
106
+ requirement: &id009 !ruby/object:Gem::Requirement
85
107
  none: false
86
108
  requirements:
87
109
  - - ~>
@@ -89,10 +111,10 @@ dependencies:
89
111
  version: 2.8.0
90
112
  type: :development
91
113
  prerelease: false
92
- version_requirements: *id007
114
+ version_requirements: *id009
93
115
  - !ruby/object:Gem::Dependency
94
116
  name: rdoc
95
- requirement: &id008 !ruby/object:Gem::Requirement
117
+ requirement: &id010 !ruby/object:Gem::Requirement
96
118
  none: false
97
119
  requirements:
98
120
  - - ~>
@@ -100,10 +122,10 @@ dependencies:
100
122
  version: "3.12"
101
123
  type: :development
102
124
  prerelease: false
103
- version_requirements: *id008
125
+ version_requirements: *id010
104
126
  - !ruby/object:Gem::Dependency
105
127
  name: bundler
106
- requirement: &id009 !ruby/object:Gem::Requirement
128
+ requirement: &id011 !ruby/object:Gem::Requirement
107
129
  none: false
108
130
  requirements:
109
131
  - - ">="
@@ -111,10 +133,10 @@ dependencies:
111
133
  version: 1.0.0
112
134
  type: :development
113
135
  prerelease: false
114
- version_requirements: *id009
136
+ version_requirements: *id011
115
137
  - !ruby/object:Gem::Dependency
116
138
  name: jeweler
117
- requirement: &id010 !ruby/object:Gem::Requirement
139
+ requirement: &id012 !ruby/object:Gem::Requirement
118
140
  none: false
119
141
  requirements:
120
142
  - - ~>
@@ -122,10 +144,10 @@ dependencies:
122
144
  version: 1.8.3
123
145
  type: :development
124
146
  prerelease: false
125
- version_requirements: *id010
147
+ version_requirements: *id012
126
148
  - !ruby/object:Gem::Dependency
127
149
  name: rcov
128
- requirement: &id011 !ruby/object:Gem::Requirement
150
+ requirement: &id013 !ruby/object:Gem::Requirement
129
151
  none: false
130
152
  requirements:
131
153
  - - ">="
@@ -133,7 +155,7 @@ dependencies:
133
155
  version: "0"
134
156
  type: :development
135
157
  prerelease: false
136
- version_requirements: *id011
158
+ version_requirements: *id013
137
159
  description: Off-line time-tracking for OpenAll with syncing when online.
138
160
  email: k@spernj.org
139
161
  executables:
@@ -166,12 +188,10 @@ files:
166
188
  - glade/win_main.glade
167
189
  - glade/win_overview.glade
168
190
  - glade/win_preferences.glade
169
- - glade/win_sync_overview.glade
170
191
  - glade/win_worktime_overview.glade
171
192
  - gui/trayicon.rb
172
193
  - gui/win_main.rb
173
194
  - gui/win_preferences.rb
174
- - gui/win_sync_overview.rb
175
195
  - gui/win_worktime_overview.rb
176
196
  - lib/openall_time_applet.rb
177
197
  - locales/da_DK/LC_MESSAGES/default.mo
@@ -198,7 +218,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
198
218
  requirements:
199
219
  - - ">="
200
220
  - !ruby/object:Gem::Version
201
- hash: 1036250711865852589
221
+ hash: -3066886317205834200
202
222
  segments:
203
223
  - 0
204
224
  version: "0"
@@ -1,121 +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">Prepare transfer</property>
8
- <property name="window_position">center</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"/>
12
- <child>
13
- <object class="GtkVBox" id="vbox1">
14
- <property name="visible">True</property>
15
- <property name="can_focus">False</property>
16
- <child>
17
- <object class="GtkFrame" id="frame1">
18
- <property name="visible">True</property>
19
- <property name="can_focus">False</property>
20
- <property name="label_xalign">0</property>
21
- <property name="shadow_type">none</property>
22
- <child>
23
- <object class="GtkAlignment" id="alignment1">
24
- <property name="visible">True</property>
25
- <property name="can_focus">False</property>
26
- <property name="left_padding">12</property>
27
- <child>
28
- <object class="GtkVBox" id="vbox2">
29
- <property name="visible">True</property>
30
- <property name="can_focus">False</property>
31
- <property name="spacing">3</property>
32
- <child>
33
- <object class="GtkViewport" id="viewport1">
34
- <property name="visible">True</property>
35
- <property name="can_focus">False</property>
36
- <child>
37
- <object class="GtkScrolledWindow" id="scrolledwindow1">
38
- <property name="visible">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>
48
- </object>
49
- </child>
50
- </object>
51
- <packing>
52
- <property name="expand">True</property>
53
- <property name="fill">True</property>
54
- <property name="position">0</property>
55
- </packing>
56
- </child>
57
- <child>
58
- <object class="GtkHButtonBox" id="hbuttonbox1">
59
- <property name="visible">True</property>
60
- <property name="can_focus">False</property>
61
- <property name="spacing">3</property>
62
- <property name="layout_style">end</property>
63
- <child>
64
- <object class="GtkButton" id="btnSync">
65
- <property name="label">gtk-harddisk</property>
66
- <property name="visible">True</property>
67
- <property name="can_focus">True</property>
68
- <property name="receives_default">True</property>
69
- <property name="use_action_appearance">False</property>
70
- <property name="use_stock">True</property>
71
- <signal name="clicked" handler="on_btnSync_clicked" swapped="no"/>
72
- </object>
73
- <packing>
74
- <property name="expand">False</property>
75
- <property name="fill">False</property>
76
- <property name="position">0</property>
77
- </packing>
78
- </child>
79
- </object>
80
- <packing>
81
- <property name="expand">False</property>
82
- <property name="fill">True</property>
83
- <property name="position">1</property>
84
- </packing>
85
- </child>
86
- </object>
87
- </child>
88
- </object>
89
- </child>
90
- <child type="label">
91
- <object class="GtkLabel" id="label2">
92
- <property name="visible">True</property>
93
- <property name="can_focus">False</property>
94
- <property name="label" translatable="yes">&lt;b&gt;Prepare transfer&lt;/b&gt;</property>
95
- <property name="use_markup">True</property>
96
- </object>
97
- </child>
98
- </object>
99
- <packing>
100
- <property name="expand">True</property>
101
- <property name="fill">True</property>
102
- <property name="position">0</property>
103
- </packing>
104
- </child>
105
- <child>
106
- <object class="GtkLabel" id="labTotal">
107
- <property name="visible">True</property>
108
- <property name="can_focus">False</property>
109
- <property name="xalign">0</property>
110
- <property name="label" translatable="yes">[totals]</property>
111
- </object>
112
- <packing>
113
- <property name="expand">False</property>
114
- <property name="fill">True</property>
115
- <property name="position">1</property>
116
- </packing>
117
- </child>
118
- </object>
119
- </child>
120
- </object>
121
- </interface>
@@ -1,213 +0,0 @@
1
- #This class handels the window that will be shown before the actual sync takes place.
2
- class Openall_time_applet::Gui::Win_sync_overview
3
- def initialize(args)
4
- @args = args
5
-
6
- @gui = Gtk::Builder.new.add("../glade/win_sync_overview.glade")
7
- @gui.translate
8
- @gui.connect_signals{|h|method(h)}
9
- @gui["btnSync"].label = _("Transfer")
10
-
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
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
95
- rowc = 1
96
- @args[:oata].ob.list(:Timelog, {"sync_need" => 1, "task_id_not" => 0}) do |timelog|
97
-
98
-
99
- #Spawn widgets.
100
- timelog_label = Gtk::Label.new(timelog[:descr])
101
- timelog_label.xalign = 0
102
- timelog_label.selectable = true
103
-
104
- logged_time_label = Gtk::Label.new(Knj::Strings.secs_to_human_time_str(timelog[:time]))
105
- logged_time_label.xalign = 0
106
- logged_time_label.selectable = true
107
-
108
- sync_time_text = Gtk::Entry.new
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))
111
- @sync_time_text_widgets[timelog.id] = sync_time_text
112
-
113
- #Attach widgets in table.
114
- @gui["tableTimelogs"].attach(timelog_label, 0, 1, rowc, rowc + 1)
115
- @gui["tableTimelogs"].attach(logged_time_label, 1, 2, rowc, rowc + 1)
116
- @gui["tableTimelogs"].attach(sync_time_text, 2, 3, rowc, rowc + 1)
117
-
118
- rowc += 1
119
- end
120
- =end
121
-
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
128
- #Show error-message and destroy the window, if no timelogs was to be synced.
129
- Knj::Gtk2.msgbox("msg" => _("There is nothing to sync at this time."), "run" => false)
130
- @gui["window"].destroy
131
- else
132
- #...else show the window.
133
- @gui["window"].show_all
134
- end
135
- end
136
-
137
- def reload_timelogs
138
- return nil if @dont_reload or @gui["tvTimelogs"].destroyed?
139
- @gui["tvTimelogs"].model.clear
140
- @timelogs_count = 0
141
-
142
- @args[:oata].ob.list(:Timelog, {"sync_need" => 1, "task_id_not" => ["", 0], "orderby" => "timestamp"}) do |timelog|
143
- #Read time and transport from timelog.
144
- time = timelog[:time].to_i
145
- transport = timelog[:time_transport].to_i
146
-
147
- #If transport is logged, then the work if offsite. It should be rounded up by 30 min. Else 15 min. round up.
148
- if transport > 0
149
- roundup = 1800
150
- else
151
- roundup = 900
152
- end
153
-
154
- #Do the actual counting.
155
- count_rounded_time = 0
156
- loop do
157
- break if count_rounded_time >= time
158
- count_rounded_time += roundup
159
- end
160
-
161
- #Set sync-time on timelog.
162
- timelog[:time_sync] = count_rounded_time
163
-
164
- Knj::Gtk2::Tv.append(@gui["tvTimelogs"], [
165
- timelog.id,
166
- timelog[:descr],
167
- timelog.timestamp_str,
168
- timelog.time_as_human,
169
- timelog.time_transport_as_human,
170
- Knj::Locales.number_out(timelog[:transportlength], 0),
171
- timelog.transport_descr_short,
172
- Knj::Locales.number_out(timelog[:transportcosts], 2),
173
- Knj::Strings.yn_str(timelog[:travelfixed], true, false),
174
- Knj::Strings.yn_str(timelog[:workinternal], true, false),
175
- Knj::Strings.yn_str(timelog[:sync_need], true, false),
176
- timelog.task_name,
177
- Knj::Strings.secs_to_human_time_str(count_rounded_time)
178
- ])
179
- @timelogs_count += 1
180
- end
181
- end
182
-
183
- def on_btnSync_clicked
184
- begin
185
- #Destroy this window and start syncing for real.
186
- @gui["window"].destroy
187
- @args[:oata].sync_real
188
- rescue => e
189
- Knj::Gtk2.msgbox(Knj::Errors.error_str(e))
190
- end
191
- end
192
-
193
- def update_totals
194
- total_secs = 0
195
-
196
- @gui["tvTimelogs"].model.each do |model, path, iter|
197
- time_val = @gui["tvTimelogs"].model.get_value(iter, 12)
198
-
199
- begin
200
- total_secs += Knj::Strings.human_time_str_to_secs(time_val)
201
- rescue
202
- #ignore - user is properly entering stuff.
203
- end
204
- end
205
-
206
- @gui["labTotal"].markup = "<b>#{_("Total hours:")}</b> #{Knj::Strings.secs_to_human_time_str(total_secs)}"
207
- end
208
-
209
- def on_window_destroy
210
- #Unconnect reload-event. Else it will crash on call to destroyed object. Also frees up various ressources.
211
- @args[:oata].ob.unconnect("object" => :Timelog, "conn_id" => @reload_id)
212
- end
213
- end