Skip to content

Commit efb6a25

Browse files
authored
Refactor Calendar.py for reservables and SQL updates
Cleaning up some things in the Reservable view. Caution: this exposes details to anyone who can sign in regardless of their access roles. That will be addressed in the future.
1 parent 75b4bfb commit efb6a25

1 file changed

Lines changed: 114 additions & 41 deletions

File tree

Calendar/Calendar.py

Lines changed: 114 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,40 +24,69 @@ def get_reservables(typ):
2424
return q.QuerySql("""
2525
SELECT ReservableId,
2626
ParentId,
27-
BadgeColor as Color,
27+
COALESCE(BadgeColor, '#153aa8') as Color,
2828
Name,
2929
Description,
3030
IsReservable,
3131
IsEnabled,
3232
IsCountable,
3333
Quantity
3434
FROM Reservable
35-
WHERE ReservableTypeId = 1
35+
WHERE 1=1
36+
-- AND ReservableTypeId = 1
3637
AND IsDeleted = 0
3738
AND IsEnabled = 1
39+
40+
ORDER BY ReservableTypeId, Name
3841
;
3942
""")
4043

4144

4245
def get_reservations(typ, dt):
4346
return q.QuerySql("""
47+
-- Room things
4448
SELECT
4549
rb.ReservableId,
4650
rv.MeetingStart,
4751
COALESCE(rv.MeetingEnd, rv.MeetingStart) as MeetingEnd,
4852
COALESCE(NULLIF(rv.SetupMinutes, ''), 0) as SetupMinutes,
4953
COALESCE(NULLIF(rv.SetupMinutes, ''), 0) as TeardownMinutes,
5054
COALESCE(NULLIF(m.Description, ''), o.OrganizationName) as Name,
51-
m.MeetingId
55+
0 as Quantity,
56+
m.MeetingId,
57+
o.LeaderName
58+
INTO #reservables
5259
FROM Reservations rv
5360
JOIN Reservable rb ON rv.ReservableId = rb.ReservableId
5461
JOIN Meetings m ON rv.MeetingId = m.MeetingId
5562
JOIN Organizations o ON m.OrganizationId = o.OrganizationId
63+
WHERE rv.MeetingId IS NOT NULL
64+
AND rv.MeetingEnd > '{0}'
65+
AND rv.MeetingStart < DATEADD(day, 1, '{0}');
66+
67+
-- Returns reservable items (furniture and services, not reservables)
68+
SELECT
69+
ri.ReservableId,
70+
rv.MeetingStart,
71+
COALESCE(rv.MeetingEnd, rv.MeetingStart) as MeetingEnd,
72+
COALESCE(NULLIF(rv.SetupMinutes, ''), 0) as SetupMinutes,
73+
COALESCE(NULLIF(rv.SetupMinutes, ''), 0) as TeardownMinutes,
74+
COALESCE(NULLIF(m.Description, ''), o.OrganizationName) as Name,
75+
ri.Quantity,
76+
m.MeetingId,
77+
o.LeaderName
78+
INTO #Jawns
79+
FROM ReservationItems ri
80+
LEFT JOIN Reservations rv ON ri.ReservationId = rv.ReservationId
81+
JOIN Reservable rb ON ri.ReservableId = rb.ReservableId
82+
JOIN Meetings m ON rv.MeetingId = m.MeetingId
83+
JOIN Organizations o ON m.OrganizationId = o.OrganizationId
5684
WHERE rv.MeetingId IS NOT NULL
5785
AND rv.MeetingEnd > '{0}'
5886
AND rv.MeetingStart < DATEADD(day, 1, '{0}')
59-
ORDER BY rb.ReservableId, rv.MeetingStart
6087
;
88+
89+
SELECT * FROM #reservables UNION SELECT * FROM #Jawns;
6190
""".format(dt))
6291

6392
def generate_calendar_html(date):
@@ -115,7 +144,7 @@ def generate_calendar_html(date):
115144
html.append("<strong>Month</strong>")
116145
html.append(" | <a href='?v=w&%s'>Week</a>" % (curr_link))
117146
html.append(" | <a href='?v=d&%s'>Day</a>" % (curr_link))
118-
html.append(" | <a href='?v=r&%s'>Rooms</a>" % (curr_link))
147+
html.append(" | <a href='?v=r&%s'>Reservables</a>" % (curr_link))
119148
html.append("</div>")
120149
html.append("</div>")
121150

@@ -234,7 +263,7 @@ def generate_calendar_vert_html(start_date, dayCount):
234263
else:
235264
html.append(" | <a href='?v=d&%s'>Day</a>" % (curr_link))
236265

237-
html.append(" | <a href='?v=r&%s'>Rooms</a>" % (curr_link))
266+
html.append(" | <a href='?v=r&%s'>Reservables</a>" % (curr_link))
238267

239268
html.append("</div>")
240269

@@ -320,19 +349,17 @@ def generate_calendar_vert_html(start_date, dayCount):
320349

321350
return "\n".join(html)
322351

323-
324-
325352
def generate_room_gantt_html(date):
326-
rooms = get_reservables("r")
353+
reservables = get_reservables("r")
327354
reservations = get_reservations("r", date)
328355

329356
hour_width = 6 # vw per hour
330357
total_hours = 25
331358

332-
room_dict = {r.ReservableId: r for r in rooms}
333-
children = {r.ReservableId: [] for r in rooms}
334-
for r in rooms:
335-
if r.ParentId in room_dict:
359+
reservable_dict = {r.ReservableId: r for r in reservables}
360+
children = {r.ReservableId: [] for r in reservables}
361+
for r in reservables:
362+
if r.ParentId in reservable_dict:
336363
children[r.ParentId].append(r)
337364

338365
def time_to_vw(dt):
@@ -356,7 +383,8 @@ def time_to_vw(dt):
356383
html.append(".gantt-container { overflow-x: scroll; flex: 1; position: relative; height: fit-content; }")
357384
html.append(".gantt-chart { position: relative; width: %dvw; }" % (hour_width * (total_hours-1)))
358385
html.append(".room-row { position: relative; height: 2em; border-bottom: 1px solid #ccc; }")
359-
html.append(".bar { position: absolute; top: 2px; bottom: 2px; border-radius: 3px; color: #fff; padding: 2px; font-size: 0.8em; overflow: hidden; white-space: nowrap; }")
386+
html.append(".bar { position: absolute; border-radius: 3px; color: #fff; padding: 2px; font-size: 0.8em; overflow: hidden; white-space: nowrap; line-height:.8em; }")
387+
html.append(".bar small { opacity:.75; }")
360388
html.append(".setup { opacity: 0.5; }")
361389
html.append(".timegrid { position: absolute; top: 0; height: 100%; border-left: 1px dashed #ccc; font-size: 0.7em; color: #666; text-align: center; }")
362390

@@ -381,7 +409,7 @@ def time_to_vw(dt):
381409
html.append("<a href='?%s'>Month</a>" % (curr_link))
382410
html.append(" | <a href='?v=w&%s'>Week</a>" % (curr_link))
383411
html.append(" | <a href='?v=d&%s'>Day</a>" % (curr_link))
384-
html.append(" | <strong>Rooms</strong>")
412+
html.append(" | <strong>Reservables</strong>")
385413
html.append("</div>")
386414
html.append("</div>")
387415

@@ -391,24 +419,45 @@ def time_to_vw(dt):
391419

392420
html.append("<div class='gantt-wrapper'>")
393421

394-
def room_has_reservations(room):
395-
if len([r for r in reservations if r.ReservableId == room.ReservableId]) > 0:
422+
423+
def assign_lanes(reservations):
424+
lanes = []
425+
for res in reservations:
426+
placed = False
427+
for lane in lanes:
428+
if all(res.MeetingStart >= r.MeetingEnd or res.MeetingEnd <= r.MeetingStart for r in lane):
429+
lane.append(res)
430+
placed = True
431+
break
432+
if not placed:
433+
lanes.append([res])
434+
lane_map = {}
435+
for i, lane in enumerate(lanes):
436+
for res in lane:
437+
lane_map[res] = i
438+
return lane_map, max(1, len(lanes))
439+
440+
441+
def reservable_has_reservations(rbl):
442+
if len(rbl._reservations) > 0:
396443
return True
397444

398-
for child in children.get(room.ReservableId, []):
399-
if room_has_reservations(child):
445+
for child in children.get(rbl.ReservableId, []):
446+
if reservable_has_reservations(child):
400447
return True
401448

402449
return False
403450

404451

405452
# Room rows and bars
406-
def render_room_rows(room):
407-
if not room_has_reservations(room):
453+
def render_row(rbl):
454+
if not reservable_has_reservations(rbl):
408455
return
409456

410-
html.append("<div class='room-row'>")
411-
for res in [r for r in reservations if r.ReservableId == room.ReservableId]:
457+
row_height = rbl._lanes[1] * 2
458+
459+
html.append("<div class='room-row' style='height:%dem'>" % (row_height))
460+
for res in rbl._reservations:
412461
start = res.MeetingStart
413462
end = res.MeetingEnd
414463
setup_start = start.AddMinutes(-res.SetupMinutes)
@@ -419,36 +468,60 @@ def render_room_rows(room):
419468
setup_left = time_to_vw(setup_start)
420469
setup_width = time_to_vw(teardown_end) - setup_left
421470

422-
color = room_dict[res.ReservableId].Color
471+
color = reservable_dict[res.ReservableId].Color
423472

424473
html.append("<a href='/Meeting/MeetingDetails/%s'>" % res.MeetingId)
474+
475+
476+
lane = rbl._lanes[0][res]
477+
top_offset = round(lane / (.01 * rbl._lanes[1]), 2)
478+
bot_offset = round(((rbl._lanes[1] - 1 - lane) / (.01 * rbl._lanes[1])), 2)
425479

426480
if res.SetupMinutes > 0 or res.TeardownMinutes > 0:
427-
html.append("<div class='bar setup' style='left:%.2fvw; width:%.2fvw; background:%s;'></div>" % (
428-
setup_left, setup_width, color
481+
html.append("<div class='bar setup' style='left:%.2fvw; width:%.2fvw; background:%s; top:calc(%.2f%% + 1px); bottom:calc(%.2f%% + 1px);'></div>" % (
482+
setup_left, setup_width, color, top_offset, bot_offset
429483
))
430484

431-
html.append("<div class='bar' style='left:%.2fvw; width:%.2fvw; background:%s;' title=\"%s\">%s</div>" % (
432-
left, width, color, res.Name, res.Name
485+
html.append("<div class='bar' style='left:%.2fvw; width:%.2fvw; background:%s; top:calc(%.2f%% + 1px); bottom:calc(%.2f%% + 1px);' title=\"%s\">%s" % (
486+
left, width, color, top_offset, bot_offset, res.Name, res.Name
433487
))
488+
label = ""
489+
if res.Quantity > 0:
490+
label = "(%d) " % (res.Quantity)
491+
if res.LeaderName is not None:
492+
label = "%s%s" % (label, res.LeaderName)
493+
if label != "":
494+
html.append("<small><br />%s</small>" % (label))
495+
html.append("</div>")
434496
html.append("</a>")
435497
html.append("</div>")
436-
for child in children.get(room.ReservableId, []):
437-
render_room_rows(child)
498+
499+
for child in children.get(rbl.ReservableId, []):
500+
render_row(child)
501+
502+
503+
for rbl in reservables:
504+
rbl._reservations = [r for r in reservations if r.ReservableId == rbl.ReservableId]
505+
rbl._lanes = assign_lanes(rbl._reservations)
438506

439507
# Static room labels
440508
html.append("<div class='room-labels'>")
441-
def render_room_labels(room, depth=0):
442-
if not room_has_reservations(room):
509+
510+
def render_labels(rbl, depth=0):
511+
if not reservable_has_reservations(rbl):
443512
return
444513

445514
indent = "&nbsp;" * (depth * 4)
446-
html.append("<div class='room-label'>%s%s</div>" % (indent, room.Name))
447-
for child in children.get(room.ReservableId, []):
448-
render_room_labels(child, depth + 1)
449-
for room in rooms:
450-
if room.ParentId not in room_dict:
451-
render_room_labels(room)
515+
height = rbl._lanes[1] * 2
516+
html.append("<div class='room-label' style='height:%dem;'>%s%s</div>" % (height, indent, rbl.Name))
517+
for child in children.get(rbl.ReservableId, []):
518+
render_labels(child, depth + 1)
519+
520+
521+
for rbl in reservables:
522+
if rbl.ParentId not in reservable_dict:
523+
render_labels(rbl)
524+
452525
html.append("</div>")
453526

454527
# Scrollable Gantt chart
@@ -467,9 +540,9 @@ def render_room_labels(room, depth=0):
467540
half_left = left + hour_width / 2
468541
html.append("<div class='timegrid' style='left:%.2fvw; width:0; border-left: 1px dashed #ccc;'></div>" % half_left)
469542

470-
for room in rooms:
471-
if room.ParentId not in room_dict:
472-
render_room_rows(room)
543+
for room in reservables:
544+
if room.ParentId not in reservable_dict:
545+
render_row(room)
473546

474547
html.append("</div></div>") # end gantt-chart and container
475548
html.append("</div>") # end gantt-wrapper

0 commit comments

Comments
 (0)