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 +2 -0
- data/Gemfile.lock +5 -0
- data/VERSION +1 -1
- data/classes/connection.rb +5 -5
- data/glade/win_main.glade +125 -42
- data/gui/trayicon.rb +1 -1
- data/gui/win_main.rb +204 -29
- data/gui/win_worktime_overview.rb +2 -2
- data/lib/openall_time_applet.rb +12 -6
- data/models/timelog.rb +61 -3
- data/models/worktime.rb +1 -1
- data/openall_time_applet.gemspec +8 -4
- data/spec/openall_time_applet_spec.rb +6 -1
- metadata +35 -15
- data/glade/win_sync_overview.glade +0 -121
- data/gui/win_sync_overview.rb +0 -213
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -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.
|
1
|
+
0.0.22
|
data/classes/connection.rb
CHANGED
@@ -4,7 +4,7 @@ require "json"
|
|
4
4
|
class Openall_time_applet::Connection
|
5
5
|
def initialize(args)
|
6
6
|
@args = args
|
7
|
-
@http =
|
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
|
data/glade/win_main.glade
CHANGED
@@ -146,59 +146,29 @@
|
|
146
146
|
</packing>
|
147
147
|
</child>
|
148
148
|
<child>
|
149
|
-
<object class="
|
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="
|
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">
|
157
|
-
<property name="
|
158
|
-
|
159
|
-
|
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="
|
179
|
-
<property name="
|
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">
|
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"><b>Prepare transfer</b></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">
|
444
|
+
<property name="position">4</property>
|
362
445
|
</packing>
|
363
446
|
</child>
|
364
447
|
</object>
|
data/gui/trayicon.rb
CHANGED
@@ -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
|
data/gui/win_main.rb
CHANGED
@@ -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
|
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"].
|
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 =
|
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 =
|
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>"
|
data/lib/openall_time_applet.rb
CHANGED
@@ -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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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 =
|
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
|
data/models/timelog.rb
CHANGED
@@ -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] =
|
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
|
-
|
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
|
data/models/worktime.rb
CHANGED
@@ -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 =>
|
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"]
|
data/openall_time_applet.gemspec
CHANGED
@@ -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.
|
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-
|
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 =
|
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.
|
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-
|
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:
|
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: *
|
114
|
+
version_requirements: *id009
|
93
115
|
- !ruby/object:Gem::Dependency
|
94
116
|
name: rdoc
|
95
|
-
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: *
|
125
|
+
version_requirements: *id010
|
104
126
|
- !ruby/object:Gem::Dependency
|
105
127
|
name: bundler
|
106
|
-
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: *
|
136
|
+
version_requirements: *id011
|
115
137
|
- !ruby/object:Gem::Dependency
|
116
138
|
name: jeweler
|
117
|
-
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: *
|
147
|
+
version_requirements: *id012
|
126
148
|
- !ruby/object:Gem::Dependency
|
127
149
|
name: rcov
|
128
|
-
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: *
|
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:
|
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"><b>Prepare transfer</b></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>
|
data/gui/win_sync_overview.rb
DELETED
@@ -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
|