Gotta scrape 'n send that waitlist! Allowing University of Vermont students to subscribe for notification of when their course of interest has availability.
npm i require axios nodemailer twilio dotenv googleapis@39 google-spreadsheet
With config info for twilio
# email creds
EMAIL_USER=youemail@domain.com
EMAIL_PASS=***
# twilio auth
TWILIO_SID=***
TWILIO_TOKEN=***
# g-sheets auth
type=***
project_id=***
private_key_id=***
private_key=-----BEGIN PRIVATE KEY-----\***\n-----END PRIVATE KEY-----\n
client_email=***
client_id=***
auth_uri=https://accounts.google.com/o/oauth2/auth
token_uri=https://oauth2.googleapis.com/token
auth_provider_x509_cert_url=https://www.googleapis.com/oauth2/v1/certs
client_x509_cert_url=***
- Here's a Google Spreadsheet for testing: this google sheet
- To start the program, you can run the following command, thanks to NPM Scripts
npm run unwait
- Update all link pre-fixes that get sent to users via email. Based off UVM's public facing course catalog.
- Update the link to the registrar's page.
- Make sure Twilio has sufficient funds to continue messaging users
- Download the current courses (either fall or spring) as an Excel file (or use text-splitting in gSheets). Then copy/paste them into the Course Data tab of the Google Spreadsheet.
Issues requiring immediate attention are flagged in the code by "TODO"
- List all classes being checked each time user requests another course. For example, if their third request was phil, the follow-up email would confirm phil and list the other two pending courses.
- On initial request, check if the course has other cross listed CRNs that also should be getting checked.
- Timer function
- Install dependencies under the console tab found at the bottom of your Azure function portal
- KUDU debug console for adding dependencies
- select function
- platform features
- advanced tools (Kudu)
- debug console (located in navbar)
- run npm installs for dependencies
- Using Environment Variables in Azure
- select function
- platform features
- configuration
- add as application setting (as deployment)
- Quick start
- For Gmail, go into Google Settings and set account as available to "Less Secure" apps
- Nodemailer message configuration
- Outbound call quickstart
- Text to speech docs
- Twimlet Echo, URL builder - be sure to manually write out and nesting
- make promise based http requests
- won't render any javascript based changes
- Create .env file in root directory
- Declare sensative values as, "NAME=VALUE" (without quotes & each assignment receiving its own line)
- Access these values like, "process.env.NAME"
- https://www.npmjs.com/package/dotenv
- bug in newline support
- Online, visit the Google Dev Console and create a service worker
- Share your spreadsheet with the service worker's email
- Here's a Google Spreadsheet for testing: this google sheet
- Reading can be done by cells or rows, but rows seems to make the context more easily understood
- Writing is done simply with the assignment operator to a given cell, but this must be followed by
row.save()
- Caution: don't fully understand this, but it seems to ensure a save ONLY when it is the last statement to be evaluated in a function. You can force a save to happen immediately with
await promisify(row.save)()
instead ofrow.save()
. My experience otherwise has been that it will end the function wherever you call promisify from.
- Cyclomatic Complexity - number of different possible paths, level of nesting, indentation
Example Good: Cyclomatic Complexity = 1 (with Early Termination (ET))
if (!someCondition) return
if (!newCondition) return
if (!nextCondition) return
// do something
Example Bad: Cyclomatic Complexity = 3
if (someCondition) {
if (newCondition) {
if (nextCondition) {
// do something
}
}
}
- Variable name length can get smaller as you limit the scope of that variable
Example:
cosnt USER_ROW_INFO = ""
let row = allRows.find(r => r.id === ID)
For guarded if statements, use brackets (just as style convention)
if (!duplicateRow) return true // bad
if (!duplicateRow) { return true } // good
Try to place the target of the investigation as the first term/expression in a comparison operator
-
Good:
x === 7
- reads as "Does 'x' equal 7?" -
Bad:
7 === x
- reads as "Does 7 equal x?"
- Boolean Values should always be verb questions that have yes/no answer (ex.
isRegNumInvalid
)- Preference for interpretation should be default is false
- State things in the affirmative ex. prefer
IsTrue
toIsUntrue
var array = [{compnum: "13113"}, {compnum: "45345"}, {compnum: "34534"}]
// map values in array to return processed value for each item
array.map(i => i.compnum)
// filter list of values to matching criteria
array.filter(i => i.compnum === "45345")
// filter list of values to matching criteria - but just find the first record - return record
array.find(i => i.compnum === "45345")
// see if any values in array meet criteria
array.some(i => i.compnum === "45345")