xteam_schedule 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|