openall_time_applet 0.0.21 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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