resources.rb 24.8 KB
Newer Older
Tyler Lemburg's avatar
Tyler Lemburg committed
1
2
3
4
5
6
7
8
require 'models/resource'
require 'models/reservation'
require 'models/event'
require 'models/event_type'
require 'models/event_signup'
require 'models/space_hour'

get '/:service_space_url_name/resources/?' do
Tyler Lemburg's avatar
Tyler Lemburg committed
9
	require_login
Tyler Lemburg's avatar
Tyler Lemburg committed
10
11
12
13
14
15
16
17
18
19
20
21
	load_service_space
	@breadcrumbs << {:text => 'Resources'}

	# show resources that the user is authorized to use, as well as all those that do not require authorization
	resources = Resource.where(:service_space_id => @space.id).all.to_a
	resources.reject! {|resource| resource.needs_authorization && !@user.authorized_resource_ids.include?(resource.id)}

	erb :resources, :layout => :fixed, :locals => {
		:available_resources => resources
	}
end

Tyler Lemburg's avatar
Tyler Lemburg committed
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# page to see the calendar for a resource
get '/:service_space_url_name/resources/:resource_id/calendar/?' do
	check_login
	load_service_space
	# check that the user has authorization to reserve this resource, if resource requires auth
	resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id])
	if resource.nil?
		flash(:alert, 'Not Found', 'That resource does not exist.')
		redirect @space.resources_href
	end

	@breadcrumbs << {:text => 'Resources', :href => @space.resources_href} << {:text => "#{resource.name} Calendar"}

	date = params[:date].nil? ? Time.now.midnight.in_time_zone : Time.parse(params[:date]).midnight.in_time_zone
	sunday = date.in_time_zone.week_start
	
	# get the reservations for this week
39
	reservations = Reservation.includes(:event, :user).where(:resource_id => resource.id).in_week(date).all
Tyler Lemburg's avatar
Tyler Lemburg committed
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

	# get the space's hours for the week
	hours = SpaceHour.where(:service_space_id => @space.id)
		.where('effective_date < ?', (sunday+1.week+1.hour).midnight.utc.strftime('%Y-%m-%d %H:%M:%S'))
		.order(:day_of_week, :effective_date => :desc, :id => :desc).all.to_a

	hours_days = hours.group_by do |space_hour|
		space_hour.day_of_week
	end

	week_hours = {}
	hours_days.each do |number_of_days, array|
		this_day = (sunday + number_of_days.days + 1.hour).midnight

		# find the correct hour record to use for this day
		array.each do |space_hour|
			if space_hour.effective_date.in_time_zone.midnight == this_day.in_time_zone.midnight || (!space_hour.one_off && space_hour.effective_date.in_time_zone.midnight <= this_day.in_time_zone.midnight)
				week_hours[number_of_days] = space_hour
				break
			end
		end
	end

	erb :resource_calendar, :layout => :fixed, :locals => {
		:date => date,
		:sunday => sunday,
		:reservations => reservations,
		:resource => resource,
Tyler Lemburg's avatar
Tyler Lemburg committed
68
69
		:week_hours => week_hours,
		:kiosk_mode => params[:kiosk_mode]
Tyler Lemburg's avatar
Tyler Lemburg committed
70
71
72
	}
end

Tyler Lemburg's avatar
Tyler Lemburg committed
73
74
# form for reserving a resource
get '/:service_space_url_name/resources/:resource_id/reserve/?' do
Tyler Lemburg's avatar
Tyler Lemburg committed
75
	require_login
Tyler Lemburg's avatar
Tyler Lemburg committed
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
	load_service_space
	@breadcrumbs << {:text => 'Resources', :href => @space.resources_href} << {:text => 'Reserve'}

	# check that the user has authorization to reserve this resource, if resource requires auth
	resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id])
	if resource.nil?
		flash(:alert, 'Not Found', 'That resource does not exist.')
		redirect @space.resources_href
	end

	date = params[:date].nil? ? Time.now.midnight.in_time_zone : Time.parse(params[:date]).midnight.in_time_zone
	# get the studio's hours for this day
	# is there a one_off
	space_hour = SpaceHour.where(:service_space_id => @space.id)
		.where('effective_date = ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
		.where(:day_of_week => date.wday).where(:one_off => true).first
	if space_hour.nil?
		space_hour = SpaceHour.where(:service_space_id => @space.id)
			.where('effective_date <= ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
			.where(:day_of_week => date.wday).where(:one_off => false)
			.order(:effective_date => :desc, :id => :desc).first
	end

	available_start_times = []
	# calculate the available start times for reservation
	if space_hour.nil?
		start = 0
Tyler Lemburg's avatar
Tyler Lemburg committed
103
		while start + (resource.minutes_per_reservation || resource.min_minutes_per_reservation || 15) <= 1440
Tyler Lemburg's avatar
Tyler Lemburg committed
104
			available_start_times << start
Tyler Lemburg's avatar
Tyler Lemburg committed
105
			start += (resource.minutes_per_reservation || resource.min_minutes_per_reservation || 15)
Tyler Lemburg's avatar
Tyler Lemburg committed
106
107
108
109
110
		end
	else
		space_hour.hours.sort{|x,y| x[:start] <=> y[:start]}.each do |record|
			if record[:status] == 'open'
				start = record[:start]
Tyler Lemburg's avatar
Tyler Lemburg committed
111
				while start + (resource.minutes_per_reservation || resource.min_minutes_per_reservation || 15) <= record[:end]
Tyler Lemburg's avatar
Tyler Lemburg committed
112
					available_start_times << start
Tyler Lemburg's avatar
Tyler Lemburg committed
113
					start += (resource.minutes_per_reservation || resource.min_minutes_per_reservation || 15)
Tyler Lemburg's avatar
Tyler Lemburg committed
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
				end
			end
		end
	end

	# filter out times when resource is reserved
	reservations = Reservation.includes(:event).where(:resource_id => resource.id).in_day(date).all
	available_start_times = available_start_times - reservations.map{|res|res.start_time.in_time_zone.minutes_after_midnight}

	erb :reserve, :layout => :fixed, :locals => {
		:resource => resource,
		:reservations => reservations,
		:available_start_times => available_start_times,
		:space_hour => space_hour,
		:day => date,
Tyler Lemburg's avatar
Tyler Lemburg committed
129
130
		:reservation => nil,
		:kiosk_mode => params[:kiosk_mode]
Tyler Lemburg's avatar
Tyler Lemburg committed
131
132
133
134
135
	}
end

# submit form for reserving a resource
post '/:service_space_url_name/resources/:resource_id/reserve/?' do
Tyler Lemburg's avatar
Tyler Lemburg committed
136
	require_login
Tyler Lemburg's avatar
Tyler Lemburg committed
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
	load_service_space

	resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id])
	if resource.nil?
		flash(:alert, 'Not Found', 'That resource does not exist.')
		redirect @space.resources_href
	end

	if params[:start_minutes].nil?
		flash(:alert, 'Please specify a start time', 'Please specify a start time for your reservation.')
		redirect back
	end

	hour = (params[:start_minutes].to_i / 60).floor
	am_pm = hour >= 12 ? 'pm' : 'am'
	hour = hour % 12
	hour += 12 if hour == 0
	minutes = params[:start_minutes].to_i % 60

	start_time = calculate_time(params[:date], hour, minutes, am_pm)
	end_time = start_time + params[:length].to_i.minutes

	date = start_time.midnight
	# validate that the requested time slot falls within the open hours of the day
	# get the studio's hours for this day
	# is there a one_off
	space_hour = SpaceHour.where(:service_space_id => @space.id)
		.where('effective_date = ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
		.where(:day_of_week => date.wday).where(:one_off => true).first
	if space_hour.nil?
		space_hour = SpaceHour.where(:service_space_id => @space.id)
			.where('effective_date <= ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
			.where(:day_of_week => date.wday).where(:one_off => false)
			.order(:effective_date => :desc, :id => :desc).first
	end

	unless space_hour.nil?
		# figure out where the closed sections need to be
        # we can assume that all records in this space_hour are non-intertwined
        closed_start = 0
        closed_end = 0
        starts = space_hour.hours.map{|record| record[:start]}
        ends = space_hour.hours.map{|record| record[:end]}
        closeds = []
        (0..1439).each do |j|
            if starts.include?(j)
                closed_end = j
                closeds << {:status => 'closed', :start => closed_start, :end => closed_end}
                closed_start = 0
                closed_end = 0
            end
            if ends.include?(j)
                closed_start = j
            end
        end 
        closed_end = 1440
        closeds << {:status => 'closed', :start => closed_start, :end => closed_end}

		# for each record, ensure that the time does not overlap if the record is not "open"
		(space_hour.hours + closeds).each do |record|
			if record[:status] != 'open'
				start_time_minutes = 60 * start_time.hour + start_time.min
				end_time_minutes = 60 * end_time.hour + end_time.min
				if (record[:start]+1..record[:end]-1).include?(start_time_minutes) || (record[:start]+1..record[:end]-1).include?(end_time_minutes) ||
						(start_time_minutes < record[:start] && end_time_minutes > record[:end])
					# there is an overlap, this time is invalid
					flash :alert, 'Invalid Time Slot', 'Sorry, that time slot is invalid for reservations.'
					redirect back
				end
			end
		end
	end
	# if no record studio is open

	# check for possible other reservations during this time period
	other_reservations = Reservation.where(:resource_id => params[:resource_id]).in_day(date).all
	other_reservations.each do |reservation|
		if (start_time >= reservation.start_time && start_time < reservation.end_time) ||
				(end_time > reservation.start_time && end_time <= reservation.end_time) ||
				(start_time < reservation.start_time && end_time > reservation.end_time)
Tyler Lemburg's avatar
Tyler Lemburg committed
217
			flash :alert, "Resource is being used.", "Sorry, that resource is reserved during that time period. Please try another time slot."
Tyler Lemburg's avatar
Tyler Lemburg committed
218
219
220
221
			redirect back
		end
	end

222
	recurring_reference_id = (Reservation.maximum(:recurring_reference_id) || 0) + 1
223
	if params.checked?('recurring')
224
225
226
227
228
229
230
231
232
233
		begin
			recurs_until_date = Time.strptime(params[:recurs_until_date], '%m/%d/%Y').midnight.in_time_zone
		rescue
			recurs_until_date = nil
		end

		if recurs_until_date.nil? || recurs_until_date - 365.days > Time.now || recurs_until_date < start_time.midnight.in_time_zone
			flash :error, 'Invalid Recurs Until Date', 'Your recurs-until date must be less than a year in the future.'
			redirect back
		end
234
235
236

		# use the recurring type to increment the date here
		starts = []
237
		if %w(daily weekly biweekly).include?(params[:recurring_type])
238
			inc = 7.days
239
240
			inc = 14.days if params[:recurring_type] == 'biweekly'
			inc = 1.day if params[:recurring_type] == 'daily'
Tyler Lemburg's avatar
Tyler Lemburg committed
241
			new_start = start_time.dup
242
			while (new_start = new_start + inc) <= recurs_until_date + 1.day
Tyler Lemburg's avatar
Tyler Lemburg committed
243
244
245
				# reset in case we moved past DST change
				new_start = (new_start + 1.hour).midnight.in_time_zone
				starts << new_start + start_time.minutes_after_midnight.minutes
246
			end
Tyler Lemburg's avatar
Tyler Lemburg committed
247
		elsif %w(first second third fourth).include?(params[:recurring_type])
248
249
			day_of_week = start_time.wday

Tyler Lemburg's avatar
Tyler Lemburg committed
250
			new_start = start_time.dup
251
252
253
254
255
256
257
258
259
260
261
			while (new_start <= recurs_until_date + 1.day)
				# calculate when this is next month
				year = new_start.year
				month = new_start.month + 1
				month = 1 and year += 1 if month == 13
				start_day = Time.new(year, month, 1).midnight.in_time_zone
				while (start_day.wday != day_of_week)
					start_day = start_day + 1.day
				end

				# now add weeks
Tyler Lemburg's avatar
Tyler Lemburg committed
262
263
264
265
266
267
268
269
270
271
				weeks = {
					"first" => 1,
					"second" => 2,
					"third" => 3,
					"fourth" => 4
				}

				start_day += (weeks[params[:recurring_type]] - 1).weeks
				# reset this to midnight in case of DST change
				start_day = (start_day + 1.hour).midnight.in_time_zone
272
273
274
275
276
277
278
279
280
281
				# and set the start time
				start_day += start_time.hour.hours + start_time.min.minutes

				if start_day <= recurs_until_date + 1.day
					starts << start_day
				end
				new_start = start_day
			end
		elsif params[:recurring_type] == 'last'
			day_of_week = start_time.wday
Tyler Lemburg's avatar
Tyler Lemburg committed
282
			new_start = start_time.dup
283
284
285
286
287
288
			while (new_start <= recurs_until_date + 1.day)
				# calculate when this is next month
				year = new_start.year
				month = new_start.month + 1
				month = 1 and year += 1 if month == 13
				start_day = Time.new(year, month, 1).midnight.in_time_zone
Tyler Lemburg's avatar
Tyler Lemburg committed
289

290
291
292
293
294
295
296
297
				while (start_day.wday != day_of_week)
					start_day = start_day + 1.day
				end

				# now add weeks (go until end of month)
				while (start_day + 1.week).month == month
					start_day += 1.week
				end
Tyler Lemburg's avatar
Tyler Lemburg committed
298
299
300

				# reset this to midnight in case of DST change
				start_day = (start_day + 1.hour).midnight.in_time_zone
301
302
303
304
305
306
307
308
309
				# and set the start time
				start_day += start_time.hour.hours + start_time.min.minutes

				if start_day <= recurs_until_date + 1.day
					starts << start_day
				end
				new_start = start_day
			end
		elsif params[:recurring_type] == 'day'
Tyler Lemburg's avatar
Tyler Lemburg committed
310
			new_start = start_time.dup
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
			while (new_start <= recurs_until_date + 1.day)
				# calculate when this is next month
				year = new_start.year
				month = new_start.month + 1
				month = 1 and year += 1 if month == 13
				start_day = Time.new(year, month, [start_time.day, Time.days_in_month(month, year)].min).midnight.in_time_zone

				# and set the start time
				start_day += start_time.hour.hours + start_time.min.minutes

				if start_day <= recurs_until_date + 1.day
					starts << start_day
				end
				new_start = start_day
			end
		else
			flash :error, 'Invalid Recurring Type', 'Please select a recurrence frequency below.'
			redirect back
		end

		messages = []
		successful = 0
		starts.each do |new_start|
			invalid = false
			new_end = new_start + params[:length].to_i.minutes

			date = new_start.midnight
			# validate that the requested time slot falls within the open hours of the day
			# get the studio's hours for this day
			# is there a one_off
			space_hour = SpaceHour.where(:service_space_id => @space.id)
				.where('effective_date = ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
				.where(:day_of_week => date.wday).where(:one_off => true).first
			if space_hour.nil?
				space_hour = SpaceHour.where(:service_space_id => @space.id)
					.where('effective_date <= ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
					.where(:day_of_week => date.wday).where(:one_off => false)
					.order(:effective_date => :desc, :id => :desc).first
			end

			unless space_hour.nil?
				# figure out where the closed sections need to be
		        # we can assume that all records in this space_hour are non-intertwined
		        closed_start = 0
		        closed_end = 0
		        starts = space_hour.hours.map{|record| record[:start]}
		        ends = space_hour.hours.map{|record| record[:end]}
		        closeds = []
		        (0..1439).each do |j|
		            if starts.include?(j)
		                closed_end = j
		                closeds << {:status => 'closed', :start => closed_start, :end => closed_end}
		                closed_start = 0
		                closed_end = 0
		            end
		            if ends.include?(j)
		                closed_start = j
		            end
		        end 
		        closed_end = 1440
		        closeds << {:status => 'closed', :start => closed_start, :end => closed_end}

				# for each record, ensure that the time does not overlap if the record is not "open"
				(space_hour.hours + closeds).each do |record|
					if record[:status] != 'open'
						start_time_minutes = 60 * new_start.hour + new_start.min
						end_time_minutes = 60 * new_end.hour + new_end.min
						if (record[:start]+1..record[:end]-1).include?(start_time_minutes) || (record[:start]+1..record[:end]-1).include?(end_time_minutes) ||
								(start_time_minutes < record[:start] && end_time_minutes > record[:end])
							# there is an overlap, this time is invalid
							messages << "Sorry, the time slot at #{new_start.strftime('%A, %B %d at %l:%M %P')} is invalid for reservations."
							invalid = true
						end
					end
				end
			end
			# if no record studio is open
			next if invalid

			# check for possible other reservations during this time period
			other_reservations = Reservation.where(:resource_id => params[:resource_id]).in_day(date).all
			other_reservations.each do |reservation|
				if (new_start >= reservation.start_time && new_start < reservation.end_time) ||
						(new_end > reservation.start_time && new_end <= reservation.end_time) ||
						(new_start < reservation.start_time && new_end > reservation.end_time)
					messages << "Sorry, that resource is reserved for the #{new_start.strftime('%A, %B %d at %l:%M %P')} attempt."
					invalid = true
				end
			end

			next if invalid

			Reservation.create(
				:resource_id => resource.id,
				:event_id => nil,
				:start_time => new_start,
				:end_time => new_end,
				:is_training => false,
				:user_id => @user.id,
410
411
				:title => params[:title],
				:recurring_reference_id => recurring_reference_id
412
413
414
415
416
417
418
			)
			successful += 1
		end
		if successful > 0
			flash :success, 'Recurring Reservations Created', "You have created #{successful+1} total reservations."
		end
		unless messages.empty?
419
420
421
422
423
			if params[:recurring_type] == 'daily'
				flash :alert, 'Some recurring reservations were not created', "#{messages.count} recurring reservations were not made. This may be just because the space is closed for certain days."
			else
				flash :alert, 'Some recurring reservations were not created', "<ul><li>#{messages.join('</li><li>')}</li></ul>"
			end
424
425
426
		end
	end

427
	res = Reservation.create(
Tyler Lemburg's avatar
Tyler Lemburg committed
428
429
430
431
432
		:resource_id => resource.id,
		:event_id => nil,
		:start_time => start_time,
		:end_time => end_time,
		:is_training => false,
Tyler Lemburg's avatar
Tyler Lemburg committed
433
434
		:user_id => @user.id,
		:title => params[:title]
Tyler Lemburg's avatar
Tyler Lemburg committed
435
	)
436
437
438
439
	if params.checked?('recurring')
		res.recurring_reference_id = recurring_reference_id
		res.save
	end
Tyler Lemburg's avatar
Tyler Lemburg committed
440
441

	flash(:success, 'Reservation Created', "You have successfully reserved #{resource.name} for #{params[:length]} minutes at #{start_time.in_time_zone.strftime('%A, %B %d at %l:%M %P')}")
Tyler Lemburg's avatar
Tyler Lemburg committed
442
	redirect "/#{@space.url_name}/resources/#{resource.id}/calendar/#{params[:kiosk_mode] ? '?kiosk_mode=true' : ''}"
Tyler Lemburg's avatar
Tyler Lemburg committed
443
444
445
end

get '/:service_space_url_name/resources/:resource_id/edit_reservation/:reservation_id/?' do
Tyler Lemburg's avatar
Tyler Lemburg committed
446
	require_login
Tyler Lemburg's avatar
Tyler Lemburg committed
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
	load_service_space
	@breadcrumbs << {:text => 'Resources', :href => @space.resources_href} << {:text => 'Edit Reservation'}

	resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id])
	if resource.nil?
		flash(:alert, 'Not Found', 'That resource does not exist.')
		redirect @space.resources_href
	end

	# check that this reservation exists
	reservation = Reservation.find(params[:reservation_id])
	if reservation.nil?
		flash(:alert, 'Not Found', 'That reservation does not exist.')
		redirect back
	end

	date = params[:date].nil? ? reservation.start_time.in_time_zone.midnight : Time.parse(params[:date]).midnight.in_time_zone
	# get the studio's hours for this day
	# is there a one_off
	space_hour = SpaceHour.where(:service_space_id => @space.id)
		.where('effective_date = ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
		.where(:day_of_week => date.wday).where(:one_off => true).first
	if space_hour.nil?
		space_hour = SpaceHour.where(:service_space_id => @space.id)
			.where('effective_date <= ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
			.where(:day_of_week => date.wday).where(:one_off => false)
			.order(:effective_date => :desc, :id => :desc).first
	end

	available_start_times = []
	# calculate the available start times for reservation
	if space_hour.nil?
		start = 0
		while start + (resource.minutes_per_reservation || 15) <= 1440
			available_start_times << start
			start += (resource.minutes_per_reservation || 15)
		end
	else
		space_hour.hours.sort{|x,y| x[:start] <=> y[:start]}.each do |record|
			if record[:status] == 'open'
				start = record[:start]
				while start + (resource.minutes_per_reservation || 15) <= record[:end]
					available_start_times << start
					start += (resource.minutes_per_reservation || 15)
				end
			end
		end
	end

	# filter out times when resource is reserved
	reservations = Reservation.includes(:event).where(:resource_id => resource.id).in_day(date).all
	available_start_times = (available_start_times - reservations.map{|res|res.start_time.in_time_zone.minutes_after_midnight})
	if date == reservation.start_time.in_time_zone.midnight
		available_start_times = available_start_times + [reservation.start_time.in_time_zone.minutes_after_midnight]
	end
	available_start_times.sort!

	erb :reserve, :layout => :fixed, :locals => {
		:resource => resource,
		:reservations => reservations,
		:available_start_times => available_start_times,
		:space_hour => space_hour,
		:day => date,
		:reservation => reservation
	}
end

post '/:service_space_url_name/resources/:resource_id/edit_reservation/:reservation_id/?' do
Tyler Lemburg's avatar
Tyler Lemburg committed
515
	require_login
Tyler Lemburg's avatar
Tyler Lemburg committed
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
	load_service_space

	resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id])
	if resource.nil?
		flash(:alert, 'Not Found', 'That resource does not exist.')
		redirect @space.resources_href
	end

	# check that this reservation exists
	reservation = Reservation.find(params[:reservation_id])
	if reservation.nil?
		flash(:alert, 'Not Found', 'That reservation does not exist.')
		redirect back
	end

	hour = (params[:start_minutes].to_i / 60).floor
	am_pm = hour >= 12 ? 'pm' : 'am'
	hour = hour % 12
	hour += 12 if hour == 0
	minutes = params[:start_minutes].to_i % 60

	start_time = calculate_time(params[:date], hour, minutes, am_pm)
	end_time = start_time + params[:length].to_i.minutes

	date = start_time.midnight
	# validate that the requested time slot falls within the open hours of the day
	# get the studio's hours for this day
	# is there a one_off
	space_hour = SpaceHour.where(:service_space_id => @space.id)
		.where('effective_date = ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
		.where(:day_of_week => date.wday).where(:one_off => true).first
	if space_hour.nil?
		space_hour = SpaceHour.where(:service_space_id => @space.id)
			.where('effective_date <= ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
			.where(:day_of_week => date.wday).where(:one_off => false)
			.order(:effective_date => :desc, :id => :desc).first
	end

	unless space_hour.nil?
		# figure out where the closed sections need to be
        # we can assume that all records in this space_hour are non-intertwined
        closed_start = 0
        closed_end = 0
        starts = space_hour.hours.map{|record| record[:start]}
        ends = space_hour.hours.map{|record| record[:end]}
        closeds = []
        (0..1439).each do |j|
            if starts.include?(j)
                closed_end = j
                closeds << {:status => 'closed', :start => closed_start, :end => closed_end}
                closed_start = 0
                closed_end = 0
            end
            if ends.include?(j)
                closed_start = j
            end
        end 
        closed_end = 1440
        closeds << {:status => 'closed', :start => closed_start, :end => closed_end}

		# for each record, ensure that the time does not overlap if the record is not "open"
		(space_hour.hours + closeds).each do |record|
			if record[:status] != 'open'
				start_time_minutes = 60 * start_time.hour + start_time.min
				end_time_minutes = 60 * end_time.hour + end_time.min
				if (record[:start]+1..record[:end]-1).include?(start_time_minutes) || (record[:start]+1..record[:end]-1).include?(end_time_minutes) ||
						(start_time_minutes < record[:start] && end_time_minutes > record[:end])
					# there is an overlap, this time is invalid
					flash :alert, 'Invalid Time Slot', 'Sorry, that time slot is invalid for reservations.'
					redirect back
				end
			end
		end
	end
	# if no record studio is open

	# check for possible other reservations during this time period
	other_reservations = Reservation.where(:resource_id => params[:resource_id]).where.not(:id => reservation.id).in_day(date).all
	other_reservations.each do |reservation|
		if (start_time >= reservation.start_time && start_time < reservation.end_time) ||
				(end_time >= reservation.start_time && end_time < reservation.end_time) ||
				(start_time < reservation.start_time && end_time > reservation.end_time)
Tyler Lemburg's avatar
Tyler Lemburg committed
598
			flash :alert, "Resource is being used.", "Sorry, that resource is reserved during that time period. Please try another time slot."
Tyler Lemburg's avatar
Tyler Lemburg committed
599
600
601
602
603
604
			redirect back
		end
	end

	reservation.update(
		:start_time => start_time,
Tyler Lemburg's avatar
Tyler Lemburg committed
605
606
		:end_time => end_time,
		:title => params[:title]
Tyler Lemburg's avatar
Tyler Lemburg committed
607
608
609
	)

	flash(:success, 'Reservation Updated', "You have successfully updated your reservation for #{resource.name}: it is now for #{params[:length]} minutes at #{start_time.in_time_zone.strftime('%A, %B %d at %l:%M %P')}")
Tyler Lemburg's avatar
Tyler Lemburg committed
610
	redirect "/#{@space.url_name}/resources/#{resource.id}/calendar/"
Tyler Lemburg's avatar
Tyler Lemburg committed
611
612
613
end

post '/:service_space_url_name/resources/:resource_id/cancel/:reservation_id/?' do
Tyler Lemburg's avatar
Tyler Lemburg committed
614
	require_login
Tyler Lemburg's avatar
Tyler Lemburg committed
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
	load_service_space

	# check that the user requesting cancel is the same as the one on the reservation
	reservation = Reservation.find(params[:reservation_id])
	if reservation.nil?
		flash :alert, 'Not Found', 'That reservation was not found.'
		redirect back
	end

	if reservation.user_id != @user.id
		flash :alert, 'Unauthorized', 'That is not your reservation.'
		redirect back
	end

	reservation.delete

	flash :success, 'Reservation Cancelled', 'Your reservation has been removed.'
	redirect back
end

635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
post '/:service_space_url_name/resources/:resource_id/cancel_all/:recurring_reference_id/?' do
	require_login
	load_service_space

	# check that the user requesting cancel is the same as the one on the reservation
	reservations = Reservation.where(:recurring_reference_id => params[:recurring_reference_id]).all
	count = reservations.count
	if reservations.empty?
		flash :alert, 'Not Found', 'Those reservations were not found.'
		redirect back
	end

	reservations.each do |reservation|
		if reservation.user_id != @user.id
			flash :alert, 'Unauthorized', 'That is not your reservation.'
			redirect back
		end
	end

	Reservation.where(:recurring_reference_id => params[:recurring_reference_id]).delete_all

	flash :success, 'Reservations Cancelled', "#{count} reservations have been removed."
	redirect back
end

660
661
662
get '/:service_space_url_name/resources/:resource_id/reservation/:reservation_id/reservation.ics' do 
	require_login
	load_service_space
Tyler Lemburg's avatar
Tyler Lemburg committed
663

664
665
666
667
668
669
670
671
672
673
674
675
676
	resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id])
	if resource.nil?
		raise Sinatra::NotFound
	end

	reservation = Reservation.find_by(:resource_id => resource.id, :id => params[:reservation_id])
	if reservation.nil?
		raise Sinatra::NotFound
	end

	attachment "#{reservation.title}.ics"
	reservation.to_ics
end