Skip to content
This repository has been archived by the owner on Jan 2, 2024. It is now read-only.

Commit

Permalink
Mann reservable lcd (#73)
Browse files Browse the repository at this point in the history
* First go at study room availability for LCD

Using two tiers of mocked data:

* first result set is hardcoded into a mocked components

* second result set is loading a mocked JSON file based on actual LibCal
  API response

Very much a work in progress.

* Iterate bookings, add available slots btw & after

First pass at this. Not accounting for leading available slots since
we're still working with mock data and unsure whether the API
drops/excludes bookings once current time is greater than the `toDate`.

Early testing indicates this is not the case (any booking with a
datetime for the requested date remains), which will mean more logic
needs to be introduced to handle open and current availability.

* Update data mock based on LibCal test reservations

* Refactor & simplify logic for room availability

Leaning heavily on lodash chaining [1] to filter and map the original
bookings array returned by LibCal API. Big hat tip to Pete from this SO
thread [2].

[1] https://blog.mariusschulz.com/2015/05/14/implicit-function-chains-in-lodash
[2] https://codereview.stackexchange.com/questions/82596/lodash-chain-implementation

* Leading padding for first booking when necessary

While implementing this, realized that the filter for removing past
bookings needs to be moved to the end of the chain, after the logic for
determining availability padding.

* Enforce open/close times

Can't hurt to double check. Don't assume that LibCal spaces API returns
bookings that abide to current/correct hours.

* Overhaul opening/closing handling

* Ensure moment object is returned

* Split out check for early morning closing (DRY)

* Dedicated function to format time for display

* Down with this keyword

* Note to self: DRY variables & util styles

* Drop mock study room component

Served admirably...

* Refactor to store space schedules in Vuex

Mocked data is no longer needed, so drop it along with baggage. Only
fetching data via LibCal API on initial load for now (no interval).

TODO: Use dotenv to handle sensitive LibCal oAuth data

* Note to self about unique bookId for avail slots

* Generalize dynamic routing

Allow for more flexibility to better accommodate future use cases with
minimal effort.

* Move _location (aka library) to first segment

* Pass category as route param for spaces

> For now, moving Mann signs (consultation desks and study rooms) but
> leaving OKU untouched to avoid downtime. TODO: coordinate new URLs
> with Olin & Uris circ at later date.

* Extract LibCal schema & generalize hour functions

First step in cleaning house and modularizing LibCal utils. Potential to
eventually extract into separate package.

Also generalize the hours related functions to use `location` in place
of 'desk'. Still a bit awkward with passing of the `isDesk` param to
handle different lookup in the schema, but wanted to keep the
consultation desks grouped together. Most likely will revisit this down
the road.

* Generalize component & requested spaces as param

No longer hardcoded for Mann study rooms.

* Prime store with requested spaces

Necessary since LibCal returns empty array if no bookings exist.

* Generalize hours store for desks & spaces

Still a bit of a work in progress in terms of:

* logic for space open/close

* assumption that all requested spaces follow same hours
  (applicable for the Mann reservable study room pilot)

* Use location for retrieving LibCal space bookings

Still a work in progress. Need to fine tune the filtering (down to
requested spaces). Also started on simplifying opening/closing via hours
store, but needs work.

* Filter LibCal bookings to requested spaces

* Keep LibCal API creds secret

All together now...

* dotenv [1]
* axios module [2]
* proxy module [3]
* serverMiddleware [4]
* body-parser [5]

Go team.

Heavily based on modify-post example [6] from http-proxy-middleware. The
need to restream the parsed body prior to proxying [7 - 9] was a special
treat.

[1] https://github.com/motdotla/dotenv
[2] https://axios.nuxtjs.org/
[3] https://github.com/nuxt-community/proxy-module
[4] https://nuxtjs.org/api/configuration-servermiddleware
[5] https://github.com/expressjs/body-parser
[6] https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/modify-post.md
[7] chimurai/http-proxy-middleware#40 (comment)
[8] http-party/node-http-proxy#1027
[9] http-party/node-http-proxy#1264

* Extract restream function from proxy config

Quick refactor in attempt to maintain overall clarity for Nuxt config.

* Closed status with next opening time

Rename existing `formatFutureOpening()` used for consultation signs to
`formatStatusChange()`.

* Remove jsonp-promise

No longer needed now since CORS is a non issue thanks to proxying
implemented in cc90db6. All axios all the time.

> jsonp-promise first introduced via 41

* Sync/update for spaces sign via setInterval

* time every 10s
* hours every 30s
* reservations every 1m

* Fix lodash import for libcal util

Doh...cherry-picking on the import was not intended here.

Was causing trouble when fetching reservations from the client via
setInterval. Specifically when attempting to build the schedule using,
encountering `undefined` errors for the lodash methods.

* Available til closing for spaces with no schedule

* Reservation URL in the footer #72

* FA icons to indicate indiv vs group rooms

Quick-n-dirty for now with Font Awesome 5.2 via CDN.

WIll revisit to:

* Use packages & official Vue.js components

* Clean up logic for determining which icon applies for each space

* Account for early AM closings within nextOpen()

Doh. Never encountered this with the consultation desks but today with
the Fall semester in full swing, moment's isBetween() was always
returning false for reservable study spaces.

Probably should consider refactoring the closingTime() method to pass
hours as optional argument instead of always fetching them from the
LibCal API. This would allow further DRYing.

* Https for reserve link

* Quick-n-dirty font scaling for launch

Go time...will be revisited.

* Drop lightweight font

As per feedback from working group. Chromebox is having difficulty
rendering lightweight Lato. Keeping it for meridiem on block start time
since it appears to be legible on the photos sent by the group.

* Bump limit on LibCal bookings API to 100 (max)

Default is 20. Noticed that a reservation for 270 was dropped from the
sign during the day. While bumping to the max provides us some leeway,
we will have to keep our eye on this and see if we still hit the ceiling
during peak semester activity.

* Display 12am as midnight

* Reserve link to top, consistent wght for datetime

And more breathing room for header. Go team!

* Sort each space's reservation by start time

Not sure if something changed in LibCal's API response, but this hadn't
been necessary until noticing the issue today.

* Unique ID for available time slots

Via unique-string package.

* Improve test for empty schedule

There are occasions when the object exists but the schedule array is
empty due to the fact that the API returns all bookings (even canceled
by either party) and only those bookings with a `confirmed` status
remain after filtering.

* Remove debugging holdouts & rename bookingsParser
  • Loading branch information
cappadona authored Sep 14, 2018
1 parent cd59085 commit 771f109
Show file tree
Hide file tree
Showing 14 changed files with 937 additions and 124 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copy this file to .env before editing any values

# LibCal API 1.1 credentials
LIBCAL_CLIENT_ID=CHANGEME
LIBCAL_CLIENT_SECRET=CHANGEME
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ npm-debug.log

# Nuxt generate
dist

# Environment variables via dotenv
.env
130 changes: 130 additions & 0 deletions components/SpaceAvailability.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<template>
<div class="space">
<h1 class="space__number">{{ space }}</h1>

<!-- TODO: Refactor logic for FA icons -->
<i class="fas space__type" aria-hidden="true"
:class="{ 'fa-users': space == 270, 'fa-user': space != 270 }"
></i>

<ul class="space__slot-list">
<li v-if="hours.status === 'closed'" class="space__slot">
<span class="space__status--closed">{{ hours.status }}</span>
<span class="space__closing">until {{ relativeStatusChange }}</span>
</li>
<li v-else
v-for="booking in spaceSchedule"
:key="booking.bookId"
:class="['space__slot', {'space__slot--available': booking.isAvailable}]"
>
<time class="slot__start">
{{ booking.startTime.hour }}
<div class="start__stack">
<span class="start__minutes">{{ booking.startTime.minute }}</span>
<span class="start__meridiem">{{ booking.startTime.meridiem }}</span>
</div>
</time>
<span v-if="!booking.isAvailable">
{{ booking.firstName }} {{ booking.lastName[0] }}.
</span>
<span v-else>
Available
</span>
<span v-if="booking.lastUp" class="space__closing">until closing at {{ relativeStatusChange }}</span>
</li>
</ul>
</div>
</template>

<script>
import Robin from '~/utils/libcal.js'
export default {
computed: {
spaceSchedule () {
return this.$store.state.spaces[this.space].schedule
},
relativeStatusChange () {
return Robin.formatStatusChange(this.hours.statusChange)
}
},
props: [
'hours',
'space'
]
}
</script>

<style lang="scss">
// TODO: Revisit font scaling and pull variables into global include (DRY)
$sf: 1.43vw;
.space {
text-align: center;
}
.space__closing {
display: block;
color: #fff;
font-size: 1 * $sf;
}
.space__number {
margin: 0;
font-size: 4 * $sf;
line-height: .7em;
}
.space__slot {
position: relative;
margin: .4em 0;
padding: .5em;
font-size: 2 * $sf;
color: #d93663;
border-radius: .3em;
background: #192639;
&--available {
color: #7edafe;
}
}
.space__slot-list {
margin: 0;
padding-left: 0;
list-style-type: none;
}
.space__status--closed {
text-transform: capitalize;
}
.space__type {
position: absolute;
margin-left: -.43em;
line-height: 1.4em;
font-size: 1.6 * $sf;
color: #657c8a;
z-index: 10000;
}
.fa-users {
margin-left: -.63em;
line-height: 1.2em;
font-size: 1.9 * $sf;
}
.slot__start {
position: absolute;
left: 5px;
color: #fff;
display: flex;
font-size: 2.2 * $sf;
}
.start__stack {
margin-top: .1em;
display: inline-flex;
flex-direction: column;
}
.start__meridiem,
.start__minutes {
align-self: flex-end;
font-size: .5em;
}
.start__meridiem {
font-weight: 100;
line-height: .3em;
}
</style>
74 changes: 74 additions & 0 deletions data-mocks/libcal-space-avail.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
[
{
"bookId": "cs_d49ELk2",
"eid": 20087,
"cid": 5395,
"lid": 2939,
"fromDate": "2018-03-20T08:00:00-04:00",
"toDate": "2018-03-20T11:00:00-04:00",
"firstName": "Chris",
"lastName": "Terreri",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_vp92j77",
"eid": 20087,
"cid": 5395,
"lid": 2939,
"fromDate": "2018-03-20T11:00:00-04:00",
"toDate": "2018-03-20T15:00:00-04:00",
"firstName": "Claude",
"lastName": "Lemieux",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_m5s8Tyr",
"eid": 20089,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-20T12:00:00-04:00",
"toDate": "2018-03-20T16:00:00-04:00",
"firstName": "Scott",
"lastName": "Niedermayer",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_1OZO2ir",
"eid": 20088,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-20T18:00:00-04:00",
"toDate": "2018-03-20T20:00:00-04:00",
"firstName": "Scott",
"lastName": "Stevens",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_kwMreUK",
"eid": 20089,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-20T19:00:00-04:00",
"toDate": "2018-03-20T21:00:00-04:00",
"firstName": "Martin",
"lastName": "Brodeur",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_1ji9Rgp",
"eid": 20088,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-20T22:00:00-04:00",
"toDate": "2018-03-21T00:00:00-04:00",
"firstName": "Patrick",
"lastName": "Elias",
"email": "[email protected]",
"status": "Confirmed"
}
]
146 changes: 146 additions & 0 deletions data-mocks/libcal-studyroom-bookings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
[
{
"bookId": "cs_XnM9XSd",
"eid": 20089,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-26T22:00:00-04:00",
"toDate": "2018-03-27T00:00:00-04:00",
"firstName": "Nick",
"lastName": "Cappadona",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_dvrZBU8",
"eid": 20088,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-27T08:00:00-04:00",
"toDate": "2018-03-27T09:00:00-04:00",
"firstName": "Nick",
"lastName": "Cappadona",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_x1w6mf2",
"eid": 20089,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-27T09:00:00-04:00",
"toDate": "2018-03-27T10:00:00-04:00",
"firstName": "Ella",
"lastName": "Fitzgerald",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_zxnYPfO",
"eid": 20087,
"cid": 5395,
"lid": 3182,
"fromDate": "2018-03-27T09:00:00-04:00",
"toDate": "2018-03-27T11:00:00-04:00",
"firstName": "Bobby",
"lastName": "Bland",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_G0L2Vij",
"eid": 20088,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-27T10:00:00-04:00",
"toDate": "2018-03-27T13:00:00-04:00",
"firstName": "Charles",
"lastName": "Mingus",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_1NJ6esr",
"eid": 20089,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-27T14:00:00-04:00",
"toDate": "2018-03-27T15:00:00-04:00",
"firstName": "Esperanza",
"lastName": "Spalding",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_AaVk3f6",
"eid": 20087,
"cid": 5395,
"lid": 3182,
"fromDate": "2018-03-27T14:00:00-04:00",
"toDate": "2018-03-27T16:00:00-04:00",
"firstName": "Cyrus",
"lastName": "Chestnut",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_9GEaOSB",
"eid": 20088,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-27T16:00:00-04:00",
"toDate": "2018-03-27T19:00:00-04:00",
"firstName": "Art",
"lastName": "Blakey",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_Pz6JbfJ",
"eid": 20087,
"cid": 5395,
"lid": 3182,
"fromDate": "2018-03-27T18:00:00-04:00",
"toDate": "2018-03-27T19:00:00-04:00",
"firstName": "Jaco",
"lastName": "Pastorius",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_W1OXYHP",
"eid": 20087,
"cid": 5395,
"lid": 3182,
"fromDate": "2018-03-27T20:00:00-04:00",
"toDate": "2018-03-27T21:00:00-04:00",
"firstName": "Quincy",
"lastName": "Jones",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_mVdBbF3",
"eid": 20089,
"cid": 5396,
"lid": 3182,
"fromDate": "2018-03-27T21:00:00-04:00",
"toDate": "2018-03-28T00:00:00-04:00",
"firstName": "Nina",
"lastName": "Simone",
"email": "[email protected]",
"status": "Confirmed"
},
{
"bookId": "cs_p3A0KUk",
"eid": 20087,
"cid": 5395,
"lid": 3182,
"fromDate": "2018-03-27T22:00:00-04:00",
"toDate": "2018-03-28T00:00:00-04:00",
"firstName": "Shemekia",
"lastName": "Copeland",
"email": "[email protected]",
"status": "Confirmed"
}
]
Loading

0 comments on commit 771f109

Please sign in to comment.