xteam_schedule 0.0.4 → 0.1.0
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/README.md +31 -1
- data/lib/xteam_schedule/facilitation/base.rb +18 -10
- data/lib/xteam_schedule/facilitation/composer.rb +65 -21
- data/lib/xteam_schedule/facilitation/parser.rb +76 -30
- data/lib/xteam_schedule/models/holiday.rb +16 -0
- data/lib/xteam_schedule/models/resource.rb +6 -5
- data/lib/xteam_schedule/models/schedule.rb +9 -14
- data/lib/xteam_schedule.rb +1 -0
- metadata +4 -3
data/README.md
CHANGED
@@ -4,6 +4,8 @@ xTeam Schedule is a gem that provides full control over schedules for use with [
|
|
4
4
|
|
5
5
|
It is capable of reading and writing schedules, whilst providing access to all of its components through the [ActiveRecord](http://api.rubyonrails.org/classes/ActiveRecord/Base.html) interface, which every Ruby on Rails developer will be familiar with. This is absolutely, **the best solution** for managing agile teams.
|
6
6
|
|
7
|
+
You can find a blog post explaining some of the thinking behind its implementation [here](https://www.unboxedconsulting.com/blog/gemnastics-with-activerecord).
|
8
|
+
|
7
9
|
<img src="http://www.adnx.com/i/uploads/xTeam1.jpg" width="820" alt="xTeam Schedule" />
|
8
10
|
|
9
11
|
### Features:
|
@@ -303,13 +305,41 @@ wednesday.update_attribute(:break_begin, nil)
|
|
303
305
|
**Defaults:**
|
304
306
|
The default weekly working schedule is identical to the same as the one set up above (except wednesday lunch). i.e. 9am-5pm Mon-Fri, with lunch from 12pm-1pm.
|
305
307
|
|
308
|
+
## Holidays
|
309
|
+
|
310
|
+
Holidays can either belong to a resource, or the entire schedule. Holidays on the schedule are shared by all resources. This is useful for bank holidays. You can leave off the end date for one day holidays.
|
311
|
+
|
312
|
+
```ruby
|
313
|
+
schedule.holidays.create!(:begin_date => Date.new(2012, 12, 25), :name => 'Christmas Day')
|
314
|
+
|
315
|
+
resource_group = schedule.resource_groups.create(:name => 'foo')
|
316
|
+
resource = resource_group.resources.create!(:name => 'bar')
|
317
|
+
resource.holidays.create!(
|
318
|
+
:begin_date => Date.new(2012, 07, 01),
|
319
|
+
:end_date => Date.new(2012, 07, 10),
|
320
|
+
:name => 'Visiting California'
|
321
|
+
)
|
322
|
+
```
|
323
|
+
|
324
|
+
**Required attributes:**
|
325
|
+
begin_date
|
326
|
+
|
327
|
+
**Example queries:**
|
328
|
+
|
329
|
+
```ruby
|
330
|
+
holiday_names = schedule.resources.map(&:holidays).flatten.map(&:name)
|
331
|
+
resources_without_holidays = schedule.resources.select { |r| r.holidays.empty? }
|
332
|
+
finished_holidays = schedule.resources.map(&:holidays).flatten.select { |h|
|
333
|
+
(h.end_date || h.begin_date) < Date.today
|
334
|
+
}
|
335
|
+
```
|
336
|
+
|
306
337
|
## Under Development
|
307
338
|
|
308
339
|
This gem is far from complete. The following is a list of features that are under development:
|
309
340
|
|
310
341
|
* Resource images
|
311
342
|
* Sort by
|
312
|
-
* Holidays
|
313
343
|
* Absences
|
314
344
|
* Remote access
|
315
345
|
* To assign
|
@@ -1,15 +1,15 @@
|
|
1
1
|
class XTeamSchedule::Base < ActiveRecord::Base
|
2
2
|
self.abstract_class = true
|
3
3
|
establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
4
|
-
|
4
|
+
|
5
5
|
def self.build_schema
|
6
6
|
XTeamSchedule::Schema.define do
|
7
|
-
|
7
|
+
|
8
8
|
create_table :schedules, :force => true do |table|
|
9
9
|
table.column :begin_date, :date, :default => 10.years.ago.to_date
|
10
10
|
table.column :end_date, :date, :default => 10.years.from_now.to_date
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
create_table :interfaces, :force => true do |table|
|
14
14
|
table.column :schedule_id, :integer
|
15
15
|
table.column :display_assignments_name, :boolean, :default => true
|
@@ -21,11 +21,11 @@ class XTeamSchedule::Base < ActiveRecord::Base
|
|
21
21
|
table.column :display_absences, :boolean, :default => true
|
22
22
|
table.column :time_granularity, :integer, :default => XTeamSchedule::Interface::TIME_GRANULARITIES[:month]
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
create_table :weekly_working_schedules, :force => true do |table|
|
26
26
|
table.column :schedule_id, :integer
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
create_table :working_days, :force => true do |table|
|
30
30
|
table.column :weekly_working_schedule_id, :integer
|
31
31
|
table.column :name, :string
|
@@ -34,13 +34,13 @@ class XTeamSchedule::Base < ActiveRecord::Base
|
|
34
34
|
table.column :break_begin, :string
|
35
35
|
table.column :break_end, :string
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
create_table :resource_groups, :force => true do |table|
|
39
39
|
table.column :schedule_id, :integer
|
40
40
|
table.column :expanded_in_library, :boolean, :default => true
|
41
41
|
table.column :name, :string
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
create_table :resources, :force => true do |table|
|
45
45
|
table.column :resource_group_id, :integer
|
46
46
|
table.column :displayed_in_planning, :boolean, :default => true
|
@@ -50,19 +50,19 @@ class XTeamSchedule::Base < ActiveRecord::Base
|
|
50
50
|
table.column :name, :string
|
51
51
|
table.column :phone, :string
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
create_table :assignment_groups, :force => true do |table|
|
55
55
|
table.column :schedule_id, :integer
|
56
56
|
table.column :expanded_in_library, :boolean, :default => true
|
57
57
|
table.column :name, :string
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
create_table :assignments, :force => true do |table|
|
61
61
|
table.column :assignment_group_id, :integer
|
62
62
|
table.column :name, :string
|
63
63
|
table.column :colour, :string
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
create_table :working_times, :force => true do |table|
|
67
67
|
table.column :resource_id, :integer
|
68
68
|
table.column :assignment_id, :integer
|
@@ -70,6 +70,14 @@ class XTeamSchedule::Base < ActiveRecord::Base
|
|
70
70
|
table.column :duration, :integer
|
71
71
|
table.column :notes, :string
|
72
72
|
end
|
73
|
+
|
74
|
+
create_table :holidays, :force => true do |table|
|
75
|
+
table.column :schedule_id, :integer
|
76
|
+
table.column :resource_id, :integer
|
77
|
+
table.column :begin_date, :date
|
78
|
+
table.column :end_date, :date
|
79
|
+
table.column :name, :string
|
80
|
+
end
|
73
81
|
end
|
74
82
|
end
|
75
83
|
end
|
@@ -1,17 +1,17 @@
|
|
1
1
|
class XTeamSchedule::Composer
|
2
|
-
|
2
|
+
|
3
3
|
attr_accessor :schedule, :hash
|
4
|
-
|
4
|
+
|
5
5
|
def self.compose(schedule)
|
6
6
|
new(schedule).compose
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(schedule)
|
10
10
|
schedule.save!
|
11
11
|
self.schedule = schedule
|
12
12
|
self.hash = {}
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def compose
|
16
16
|
compose_resource_groups!
|
17
17
|
compose_resources!
|
@@ -20,12 +20,13 @@ class XTeamSchedule::Composer
|
|
20
20
|
compose_working_times!
|
21
21
|
compose_interface!
|
22
22
|
compose_weekly_working_schedule!
|
23
|
+
compose_holidays!
|
23
24
|
compose_schedule!
|
24
25
|
hash
|
25
26
|
end
|
26
|
-
|
27
|
+
|
27
28
|
private
|
28
|
-
|
29
|
+
|
29
30
|
def compose_resource_groups!
|
30
31
|
hash['resource groups'] ||= []
|
31
32
|
resource_groups = schedule.resource_groups
|
@@ -36,15 +37,16 @@ private
|
|
36
37
|
}
|
37
38
|
end
|
38
39
|
end
|
39
|
-
|
40
|
+
|
40
41
|
def compose_resources!
|
41
42
|
hash['resources'] ||= []
|
42
43
|
resources = schedule.resource_groups.map(&:resources).flatten
|
43
44
|
resources.each do |r|
|
45
|
+
image = Base64.decode64(r.image) if r.image
|
44
46
|
hash['resources'] << {
|
45
47
|
'displayedInPlanning' => r.displayed_in_planning,
|
46
48
|
'email' => r.email,
|
47
|
-
'image' => (StringIO.new(
|
49
|
+
'image' => (StringIO.new(image) if image),
|
48
50
|
'mobile' => r.mobile,
|
49
51
|
'name' => r.name,
|
50
52
|
'phone' => r.phone,
|
@@ -52,7 +54,7 @@ private
|
|
52
54
|
}
|
53
55
|
end
|
54
56
|
end
|
55
|
-
|
57
|
+
|
56
58
|
def compose_assignment_groups!
|
57
59
|
hash['task categories'] ||= []
|
58
60
|
assignment_groups = schedule.assignment_groups
|
@@ -63,7 +65,7 @@ private
|
|
63
65
|
}
|
64
66
|
end
|
65
67
|
end
|
66
|
-
|
68
|
+
|
67
69
|
def compose_assignments!
|
68
70
|
hash['tasks'] ||= []
|
69
71
|
assignments = schedule.assignment_groups.map(&:assignments).flatten
|
@@ -76,7 +78,7 @@ private
|
|
76
78
|
}
|
77
79
|
end
|
78
80
|
end
|
79
|
-
|
81
|
+
|
80
82
|
def compose_working_times!
|
81
83
|
hash['objectsForResources'] ||= {}
|
82
84
|
resources = schedule.resource_groups.map(&:resources).flatten
|
@@ -95,7 +97,7 @@ private
|
|
95
97
|
end
|
96
98
|
end
|
97
99
|
end
|
98
|
-
|
100
|
+
|
99
101
|
def compose_interface!
|
100
102
|
interface = schedule.interface
|
101
103
|
hash['display task names'] = interface.display_assignments_name
|
@@ -107,15 +109,15 @@ private
|
|
107
109
|
hash['display absence cells'] = interface.display_absences
|
108
110
|
hash['interface status'] = { 'latest time navigation mode' => interface.time_granularity }
|
109
111
|
end
|
110
|
-
|
112
|
+
|
111
113
|
def compose_weekly_working_schedule!
|
112
114
|
weekly_working_schedule = schedule.weekly_working_schedule
|
113
115
|
working_days = weekly_working_schedule.working_days
|
114
|
-
|
116
|
+
|
115
117
|
hash['settings'] ||= {}
|
116
118
|
hash['settings']['days off'] ||= []
|
117
119
|
hash['settings']['working schedule'] ||= {}
|
118
|
-
|
120
|
+
|
119
121
|
working_days.each do |day|
|
120
122
|
day_name = day.name.downcase
|
121
123
|
hash['settings']['working schedule'][day_name] =
|
@@ -126,7 +128,7 @@ private
|
|
126
128
|
else
|
127
129
|
{ 'worked' => 'no' }
|
128
130
|
end
|
129
|
-
|
131
|
+
|
130
132
|
hash['settings']['working schedule']["pause_#{day_name}"] =
|
131
133
|
if day.day_begin.present? and day.break_begin.present?
|
132
134
|
{ 'worked' => 'yes',
|
@@ -138,16 +140,58 @@ private
|
|
138
140
|
end
|
139
141
|
end
|
140
142
|
end
|
141
|
-
|
143
|
+
|
144
|
+
def compose_holidays!
|
145
|
+
compose_schedule_holidays!
|
146
|
+
compose_resource_holidays!
|
147
|
+
end
|
148
|
+
|
149
|
+
def compose_schedule_holidays!
|
150
|
+
holidays = schedule.holidays
|
151
|
+
|
152
|
+
hash['settings'] ||= {}
|
153
|
+
hash['settings']['days off'] ||= []
|
154
|
+
|
155
|
+
holidays.each do |h|
|
156
|
+
h.end_date ||= h.begin_date
|
157
|
+
hash['settings']['days off'] << {
|
158
|
+
'begin date' => compose_date(h.begin_date),
|
159
|
+
'end date' => compose_date(h.end_date),
|
160
|
+
'name' => h.name
|
161
|
+
}
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def compose_resource_holidays!
|
166
|
+
resources = schedule.resource_groups.map(&:resources).flatten
|
167
|
+
resources.each do |r|
|
168
|
+
next unless r.holidays
|
169
|
+
index = hash['resources'].find_index { |h| h['name'] == r.name }
|
170
|
+
next unless index
|
171
|
+
|
172
|
+
hash['resources'][index]['settings'] ||= {}
|
173
|
+
hash['resources'][index]['settings']['days off'] ||= []
|
174
|
+
hash['resources'][index]['settings']['use custom days off'] = 1
|
175
|
+
r.holidays.each do |h|
|
176
|
+
h.end_date ||= h.begin_date
|
177
|
+
hash['resources'][index]['settings']['days off'] << {
|
178
|
+
'begin date' => compose_date(h.begin_date),
|
179
|
+
'end date' => compose_date(h.end_date),
|
180
|
+
'name' => h.name
|
181
|
+
}
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
142
186
|
def compose_schedule!
|
143
187
|
hash['begin date'] = compose_date(schedule.begin_date)
|
144
188
|
hash['end date'] = compose_date(schedule.end_date)
|
145
189
|
end
|
146
|
-
|
190
|
+
|
147
191
|
def compose_colour(colour_hash)
|
148
192
|
{ 'alpha' => 1 }.merge([:red, :green, :blue].inject({}) { |h, c| h[c.to_s] = colour_hash[c]; h })
|
149
193
|
end
|
150
|
-
|
194
|
+
|
151
195
|
def compose_date(date)
|
152
196
|
return unless date.present?
|
153
197
|
components = []
|
@@ -156,11 +200,11 @@ private
|
|
156
200
|
components << date.year
|
157
201
|
components.join('/')
|
158
202
|
end
|
159
|
-
|
203
|
+
|
160
204
|
def compose_time(time_string)
|
161
205
|
return unless time_string.present?
|
162
206
|
hours, minutes = time_string.split(':').map(&:to_i)
|
163
|
-
|
207
|
+
|
164
208
|
hours * 60 + minutes
|
165
209
|
end
|
166
210
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
class XTeamSchedule::Parser
|
2
|
-
|
2
|
+
|
3
3
|
attr_accessor :hash, :schedule
|
4
|
-
|
4
|
+
|
5
5
|
def self.parse(hash)
|
6
6
|
new(hash).parse
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(hash)
|
10
10
|
self.hash = hash
|
11
11
|
self.schedule = XTeamSchedule::Schedule.create!
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def parse
|
15
15
|
parse_resource_groups!
|
16
16
|
parse_resources!
|
@@ -19,27 +19,29 @@ class XTeamSchedule::Parser
|
|
19
19
|
parse_working_times!
|
20
20
|
parse_interface!
|
21
21
|
parse_weekly_working_schedule!
|
22
|
+
parse_holidays!
|
22
23
|
parse_schedule!
|
23
24
|
schedule
|
24
25
|
end
|
25
|
-
|
26
|
+
|
26
27
|
private
|
27
|
-
|
28
|
+
|
28
29
|
def parse_resource_groups!
|
30
|
+
return unless hash['resource groups'].present?
|
29
31
|
hash['resource groups'].each do |rg|
|
30
32
|
schedule.resource_groups.create!(
|
31
33
|
:name => rg['name'],
|
32
34
|
:expanded_in_library => rg['expanded in library']
|
33
35
|
)
|
34
36
|
end
|
35
|
-
rescue
|
36
37
|
end
|
37
|
-
|
38
|
+
|
38
39
|
def parse_resources!
|
40
|
+
return unless hash['resources'].present?
|
39
41
|
hash['resources'].each do |r|
|
40
42
|
resource_group = schedule.resource_groups.find_by_name(r['group'])
|
41
43
|
if resource_group
|
42
|
-
image = r['image'].class == StringIO ? r['image'].read : ''
|
44
|
+
image = r['image'].class == StringIO ? Base64.encode64(r['image'].read) : ''
|
43
45
|
resource_group.resources.create!(
|
44
46
|
:displayed_in_planning => r['displayedInPlanning'],
|
45
47
|
:email => r['email'],
|
@@ -50,20 +52,20 @@ private
|
|
50
52
|
)
|
51
53
|
end
|
52
54
|
end
|
53
|
-
rescue
|
54
55
|
end
|
55
|
-
|
56
|
+
|
56
57
|
def parse_assignment_groups!
|
58
|
+
return unless hash['task categories'].present?
|
57
59
|
hash['task categories'].each do |ag|
|
58
60
|
schedule.assignment_groups.create!(
|
59
61
|
:name => ag['name'],
|
60
62
|
:expanded_in_library => ag['expanded in library']
|
61
63
|
)
|
62
64
|
end
|
63
|
-
rescue
|
64
65
|
end
|
65
|
-
|
66
|
+
|
66
67
|
def parse_assignments!
|
68
|
+
return unless hash['tasks'].present?
|
67
69
|
hash['tasks'].each do |a|
|
68
70
|
assignment_group = schedule.assignment_groups.find_by_name(a['category'])
|
69
71
|
if assignment_group
|
@@ -73,13 +75,12 @@ private
|
|
73
75
|
)
|
74
76
|
end
|
75
77
|
end
|
76
|
-
rescue
|
77
78
|
end
|
78
|
-
|
79
|
+
|
79
80
|
def parse_working_times!
|
81
|
+
return unless hash['objectsForResources'].present?
|
80
82
|
resources = schedule.resource_groups.map(&:resources).flatten
|
81
83
|
assignments = schedule.assignment_groups.map(&:assignments).flatten
|
82
|
-
hash['objectsForResources'] ||= {}
|
83
84
|
hash['objectsForResources'].each do |r_name, wt_array|
|
84
85
|
resource = resources.detect { |r| r.name == r_name }
|
85
86
|
next unless resource
|
@@ -95,10 +96,10 @@ private
|
|
95
96
|
end
|
96
97
|
end
|
97
98
|
end
|
98
|
-
|
99
|
+
|
99
100
|
def parse_interface!
|
100
|
-
|
101
|
-
time_granularity =
|
101
|
+
return unless hash['interface status'].present?
|
102
|
+
time_granularity = hash['interface status']['latest time navigation mode']
|
102
103
|
schedule.interface.update_attributes!(
|
103
104
|
:display_assignments_name => hash['display task names'],
|
104
105
|
:display_resources_name => hash['display resource names'],
|
@@ -110,31 +111,31 @@ private
|
|
110
111
|
:time_granularity => time_granularity
|
111
112
|
)
|
112
113
|
end
|
113
|
-
|
114
|
+
|
114
115
|
def parse_weekly_working_schedule!
|
115
116
|
settings = hash['settings']
|
116
117
|
return unless settings.present?
|
117
118
|
working_schedule = settings['working schedule']
|
118
119
|
return unless working_schedule.present?
|
119
|
-
|
120
|
+
|
120
121
|
weekly_working_schedule = schedule.weekly_working_schedule
|
121
122
|
working_days = weekly_working_schedule.working_days
|
122
|
-
|
123
|
+
|
123
124
|
working_days.destroy_all
|
124
125
|
XTeamSchedule::WorkingDay::WORKING_DAY_NAMES.each do |name|
|
125
126
|
day = working_schedule[name.downcase]
|
126
127
|
pause = working_schedule["pause_#{name.downcase}"]
|
127
|
-
|
128
|
+
|
128
129
|
if day.present?
|
129
130
|
day_begin = parse_time(day['begin']) if day['worked'] == 'yes'
|
130
131
|
day_end = parse_time(day['end']) if day_begin
|
131
132
|
end
|
132
|
-
|
133
|
+
|
133
134
|
if pause.present?
|
134
135
|
break_begin = parse_time(pause['begin']) if pause['worked'] == 'yes'
|
135
136
|
break_end = parse_time(pause['end']) if break_begin
|
136
137
|
end
|
137
|
-
|
138
|
+
|
138
139
|
working_days << XTeamSchedule::WorkingDay.create!(
|
139
140
|
:name => name,
|
140
141
|
:day_begin => day_begin, :day_end => day_end,
|
@@ -142,29 +143,74 @@ private
|
|
142
143
|
)
|
143
144
|
end
|
144
145
|
end
|
145
|
-
|
146
|
+
|
147
|
+
def parse_holidays!
|
148
|
+
parse_schedule_holidays!
|
149
|
+
parse_resource_holidays!
|
150
|
+
end
|
151
|
+
|
152
|
+
def parse_schedule_holidays!
|
153
|
+
settings = hash['settings']
|
154
|
+
return unless settings.present?
|
155
|
+
holidays = settings['days off']
|
156
|
+
return unless holidays.present?
|
157
|
+
|
158
|
+
holidays.each do |h|
|
159
|
+
end_date = h['end date']
|
160
|
+
end_date = nil if h['begin date'] == h['end date']
|
161
|
+
schedule.holidays.create!(
|
162
|
+
:begin_date => parse_date(h['begin date']),
|
163
|
+
:end_date => parse_date(end_date),
|
164
|
+
:name => h['name']
|
165
|
+
)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def parse_resource_holidays!
|
170
|
+
resources = schedule.resource_groups.map(&:resources).flatten
|
171
|
+
hash['resources'] ||= []
|
172
|
+
hash['resources'].each do |resource|
|
173
|
+
settings = resource['settings']
|
174
|
+
next unless settings.present?
|
175
|
+
holidays = settings['days off']
|
176
|
+
next unless holidays.present?
|
177
|
+
resource = resources.detect { |r| r.name == resource['name'] }
|
178
|
+
next unless resource.present?
|
179
|
+
|
180
|
+
holidays.each do |h|
|
181
|
+
end_date = h['end date']
|
182
|
+
end_date = nil if h['begin date'] == h['end date']
|
183
|
+
resource.holidays.create!(
|
184
|
+
:begin_date => parse_date(h['begin date']),
|
185
|
+
:end_date => parse_date(end_date),
|
186
|
+
:name => h['name']
|
187
|
+
)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
146
192
|
def parse_schedule!
|
147
193
|
schedule.update_attributes!(
|
148
194
|
:begin_date => parse_date(hash['begin date']),
|
149
195
|
:end_date => parse_date(hash['end date'])
|
150
196
|
)
|
151
197
|
end
|
152
|
-
|
198
|
+
|
153
199
|
def parse_colour(colour_data)
|
154
200
|
[:red, :green, :blue].inject({}) { |h, c| h[c] = colour_data[c.to_s]; h }
|
155
201
|
end
|
156
|
-
|
202
|
+
|
157
203
|
def parse_date(date_string)
|
158
204
|
return unless date_string.present?
|
159
205
|
month, day, year = date_string.split('/').map(&:to_i)
|
160
206
|
Date.new(year, month, day)
|
161
207
|
end
|
162
|
-
|
208
|
+
|
163
209
|
def parse_time(seconds)
|
164
210
|
return unless seconds.present?
|
165
211
|
hours = seconds / 60
|
166
212
|
minutes = seconds % 60
|
167
|
-
|
213
|
+
|
168
214
|
hours = "%02d" % hours
|
169
215
|
minutes = "%02d" % minutes
|
170
216
|
[hours, minutes].join(':')
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class XTeamSchedule::Holiday < XTeamSchedule::Base
|
2
|
+
belongs_to :schedule
|
3
|
+
belongs_to :resource
|
4
|
+
|
5
|
+
validates_presence_of :begin_date
|
6
|
+
validate :can_not_belong_to_both_schedule_and_resource
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def can_not_belong_to_both_schedule_and_resource
|
11
|
+
if [schedule, resource].all?
|
12
|
+
errors.add(:base, 'Can not belong to both a schedule and a resource')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -1,22 +1,23 @@
|
|
1
1
|
class XTeamSchedule::Resource < XTeamSchedule::Base
|
2
2
|
belongs_to :resource_group
|
3
3
|
has_many :working_times, :dependent => :destroy
|
4
|
+
has_many :holidays, :dependent => :destroy
|
4
5
|
delegate :schedule, :to => :resource_group
|
5
|
-
|
6
|
+
|
6
7
|
validates_presence_of :name
|
7
8
|
validate :uniqueness_of_name_scoped_to_schedule
|
8
|
-
|
9
|
+
|
9
10
|
private
|
10
|
-
|
11
|
+
|
11
12
|
def uniqueness_of_name_scoped_to_schedule
|
12
13
|
return unless new_record?
|
13
14
|
resource_group = self.resource_group or return
|
14
15
|
schedule = resource_group.schedule or return
|
15
16
|
resources = schedule.resources or return
|
16
|
-
|
17
|
+
|
17
18
|
if resources.find_by_name(name).present?
|
18
19
|
errors.add(:name, 'must be unique within the schedule')
|
19
20
|
end
|
20
21
|
end
|
21
|
-
|
22
|
+
|
22
23
|
end
|
@@ -4,10 +4,11 @@ class XTeamSchedule::Schedule < XTeamSchedule::Base
|
|
4
4
|
has_many :assignment_groups, :dependent => :destroy
|
5
5
|
has_many :assignments, :through => :assignment_groups
|
6
6
|
has_many :working_days, :through => :weekly_working_schedule
|
7
|
-
|
7
|
+
has_many :holidays, :dependent => :destroy
|
8
|
+
|
8
9
|
has_one :interface
|
9
10
|
has_one :weekly_working_schedule
|
10
|
-
|
11
|
+
|
11
12
|
ActiveSupport::Deprecation.silence do
|
12
13
|
after_initialize :set_default_interface
|
13
14
|
after_initialize :set_default_weekly_working_schedule
|
@@ -16,26 +17,20 @@ class XTeamSchedule::Schedule < XTeamSchedule::Base
|
|
16
17
|
set_default_weekly_working_schedule
|
17
18
|
end
|
18
19
|
end
|
19
|
-
|
20
|
+
|
20
21
|
def working_times
|
21
|
-
ids =
|
22
|
-
"select distinct * from working_times as wt
|
23
|
-
join resources r on wt.resource_id = r.id
|
24
|
-
join resource_groups rg on r.resource_group_id = rg.id
|
25
|
-
where rg.schedule_id = ?", id
|
26
|
-
]).map(&:id)
|
27
|
-
|
22
|
+
ids = resources.map(&:working_times).flatten.map(&:id)
|
28
23
|
XTeamSchedule::WorkingTime.scoped(:conditions => { :id => ids })
|
29
24
|
end
|
30
|
-
|
25
|
+
|
31
26
|
private
|
32
|
-
|
27
|
+
|
33
28
|
def set_default_interface
|
34
29
|
self.interface ||= XTeamSchedule::Interface.new
|
35
30
|
end
|
36
|
-
|
31
|
+
|
37
32
|
def set_default_weekly_working_schedule
|
38
33
|
self.weekly_working_schedule ||= XTeamSchedule::WeeklyWorkingSchedule.new
|
39
34
|
end
|
40
|
-
|
35
|
+
|
41
36
|
end
|
data/lib/xteam_schedule.rb
CHANGED
@@ -19,5 +19,6 @@ require 'xteam_schedule/models/schedule'
|
|
19
19
|
require 'xteam_schedule/models/weekly_working_schedule'
|
20
20
|
require 'xteam_schedule/models/working_day'
|
21
21
|
require 'xteam_schedule/models/working_time'
|
22
|
+
require 'xteam_schedule/models/holiday'
|
22
23
|
|
23
24
|
XTeamSchedule::Base.build_schema
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xteam_schedule
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.4
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Christopher Patuzzo
|
@@ -110,6 +110,7 @@ files:
|
|
110
110
|
- lib/xteam_schedule/facilitation/schema.rb
|
111
111
|
- lib/xteam_schedule/models/assignment.rb
|
112
112
|
- lib/xteam_schedule/models/assignment_group.rb
|
113
|
+
- lib/xteam_schedule/models/holiday.rb
|
113
114
|
- lib/xteam_schedule/models/interface.rb
|
114
115
|
- lib/xteam_schedule/models/resource.rb
|
115
116
|
- lib/xteam_schedule/models/resource_group.rb
|