openall_time_applet 0.0.14 → 0.0.15

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