The finished code from our session can be found here:
https://github.com/gabeabrams/imhere-itsummit-2019
- Clone a git repo, initialize it as an npm project using
npm init
- Clean out sandbox course
- Add LTI button to sandbox course so when we deploy, we can launch immediately
- Reserve a Heroku app and leave it unchanged
- Navigate to project:
cd
into the folder - Initialize caccl using our wizard: run
npm init caccl
- Choose app type: choose EJS + Express App
- Set up dev enironment: choose "y" and set up dev environment
- Add test course link:
https://canvas.harvard.edu/courses...
(copy your course link) - Add access token (create an access token: try googling it)
- Start Canvas simulator: open new terminal tab, run
npm run dev:canvas
- Start server: open new terminal tab, run
npm run dev:server
- Simulate launch: go to the tab where you ran
npm run dev:canvas
and copy/paste link from canvas simulator into browser to demo the simulated launch process - Point out resources and note that we will start by editing
views/index.ejs
androutes.js
- Delete instructions in
views/index
...
<body>
<!-- Bootstrap alert -->
<div class="alert alert-dark text-center m-5">
<!-- Title -->
<h3>It's great to meet you, <%= name %>!</h3>
<!-- Instructions --> /* Remove */
<ul class="list-group"> /* Remove */
<li class="list-group-item"> /* Remove */
Start by editing <code>rou.../* Remove */
</li> /* Remove */
<li class="list-group-item"> /* Remove */
Read the <a href="https://.../* Remove */
</li> /* Remove */
<li class="list-group-item"> /* Remove */
Check out the <a href="htt.../* Remove */
</li> /* Remove */
</ul> /* Remove */
</div>
</body>
- Change title to "Hi there, <%= name %>!"
...
<body>
<!-- Bootstrap alert -->
<div class="alert alert-dark text-center m-5">
<!-- Title -->
<h3>Hi There, <%= name %>!</h3> /* Change */
</div>
</body>
- Refresh browser to show the update. Note the title change and the lack of instructions
-
Remove starter content: open
routes.js
and delete the contents of the main functionconst path = require('path'); module.exports = (app) => { // TODO: replace placehold... /* Remove */ app.get('/', async (req, r... /* Remove */ if (!req.api) { /* Remove */ // Not logged in /* Remove */ return res.send('Pleas... /* Remove */ } /* Remove */ /* Remove */ try { /* Remove */ // Get user profile. /* Remove */ const profile = await ... /* Remove */ /* Remove */ // Render the index pa... /* Remove */ return res.render(path... /* Remove */ name: profile.name, /* Remove */ }); /* Remove */ } catch (err) { /* Remove */ return res.send(`An er... /* Remove */ } /* Remove */ }); /* Remove */ };
-
Refresh everyone on the 4 routes (visit slides)
-
Add routes to
routes.js
:const path = require('path'); module.exports = (app) => { app.get('/', async (req, res) => { /* Add */ // Show the Create Event page /* Add */ }); /* Add */ /* Add */ app.post('/', async (req, res) => { /* Add */ // Capture input from Create Event page /* Add */ }); /* Add */ /* Add */ app.get('/events/:id', async (req, res) => { /* Add */ // Show Scan to Attend page /* Add */ }); /* Add */ /* Add */ app.post('/events/:id', async (req, res) => { /* Add */ // Capture input from Scan to Attend page /* Add */ }); /* Add */ };
- Render the Create Event page: edit
routes.js
...
app.get('/', async (req, res) => {
// Show the Create Event page
res.render(__dirname + '/views/index'); /* Add */
});
...
- Add content to the Create Event: edit
views/index
a. Change title to "Create Event"
```html
<body>
<!-- Bootstrap alert -->
<div class="alert alert-dark text-center m-5">
<!-- Title -->
<h3>Create Event</h3> /* Change */
</div>
</body>
```
b. Create the create event form
```html
<body>
<!-- Bootstrap alert -->
<div class="alert alert-dark text-center m-5">
<!-- Title -->
<h3>Create Event</h3>
<form method="POST"> /* Add */
</form> /* Add */
</div>
</body>
```
c. Add an input field in the form for the event title
```html
<body>
<!-- Bootstrap alert -->
<div class="alert alert-dark text-center m-5">
<!-- Title -->
<h3>Create Event</h3>
<form method="POST">
<input type="text" name="eventTitle" placeholder="Event Title" autofocus> /* Add */
</form>
</div>
</body>
```
- Restart the server (it's been updated): kill the server process using
ctrl + c
, start it again usingnpm run dev:server
- Simulate launch: go to the tab where you ran
npm run dev:canvas
and copy/paste link from canvas simulator into browser to demo the simulated launch process - Look at the Create Event page and see that it matches our design
- Back in
routes.js
, create a new 1-point assignment
app.post('/', async (req, res) => {
// Capture input from Create Event page
const assignment = req.api.course.assignment.create({ /* Add */
name: ____, /* Add */
pointsPossible: 1, /* Add */
published: true, /* Add */
courseId: _____, /* Add */
}); /* Add */
});
- Get title from form: let's get the title from the body of the form submission
app.post('/', async (req, res) => {
// Capture input from Create Event page
const assignment = req.api.course.assignment.create({
name: req.body.eventTitle, /* Change */
pointsPossible: 1,
published: true,
courseId: _____,
});
});
- Get course id from launch info: when we launch the app, in that launch info, we get course information. CACCL adds that to
req.session.launchInfo
app.post('/', async (req, res) => {
// Capture input from Create Event page
const assignment = req.api.course.assignment.create({
name: req.body.eventTitle,
pointsPossible: 1,
published: true,
courseId: req.session.launchInfo.courseId, /* Change */
});
});
- Wait for task to finish: this an asynchronous request, so let's wait for it to finish (add
await
)
app.post('/', async (req, res) => {
// Capture input from Create Event page
const assignment = await req.api.course.assignment.create({ /* Change */
name: req.body.eventTitle,
pointsPossible: 1,
published: true,
courseId: req.session.launchInfo.courseId,
});
});
- Redirect user to the Scan to Attend Event page
app.post('/', async (req, res) => {
// Capture input from Create Event page
const assignment = await req.api.course.assignment.create({
name: req.body.eventTitle,
pointsPossible: 1,
published: true,
courseId: req.session.launchInfo.courseId,
});
// Redirect user to event page /* Add */
res.redirect('/events/' + assignment.id); /* Add */
});
- Render the event page: edit
routes.js
app.get('/events/:id', async (req, res) => {
// Show Scan to Attend page
res.render(__dirname + '/views/event'); /* Add */
});
-
Duplicate
views/index.ejs
=>views/event.ejs
-
Add jQuery to
<head>
of the page
<head>
<title>CACCL App</title>
<!-- Import Bootstrap -->
<link
href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
crossorigin="anonymous"
>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script> /* Add */
</head>
...
- Change title to "Scan to Attend":
...
<body>
<!-- Bootstrap alert -->
<div class="alert alert-dark text-center m-5">
<!-- Title -->
<h3>Scan to Attend</h3> /* Change */
</div>
<form method="POST">
<input type="text" name="eventTitle" placeholder="Event Title" autofocus>
</form>
</body>
- Update input field: change the name to "studentId", get rid of placeholder
...
<body>
<!-- Bootstrap alert -->
<div class="alert alert-dark text-center m-5">
<!-- Title -->
<h3>Scan to Attend</h3>
</div>
<form method="POST">
<input type="text" name="studentId" autofocus> /* Change */
</form>
</body>
- Restart the server (it's been updated): kill the server process using
ctrl + c
, start it again usingnpm run dev:server
- Simulate launch: go to the tab where you ran
npm run dev:canvas
and copy/paste link from canvas simulator into browser to demo the simulated launch process - Test the app so far:
a. Enter "Lab 1" as the title and press enter
b. See that the "Scan to Attend" page appears. Note that we can't enter someone's ID into the field because we haven't set up the app to capture the input from the Scan to Attend page. That's next!
c. Visit Canvas assignments to see that the assignment "Lab 1" was created
- Add a friendly welcome message in
views/event.ejs
...
<body>
<!-- Bootstrap alert -->
<div class="alert alert-dark text-center m-5">
<!-- Title -->
<h3>Scan to Attend</h3>
<form method="POST">
<input type="text" name="studentId" autofocus>
</form>
</div>
<h3 id="welcome" class="alert alert-success text-center m-5"> /* Add */
Welcome! /* Add */
</h3> /* Add */
</body>
- Hide the welcome message after 1s
...
<body>
<!-- Bootstrap alert -->
<div class="alert alert-dark text-center m-5">
<!-- Title -->
<h3>Scan to Attend</h3>
<form method="POST">
<input type="text" name="studentId" autofocus>
</form>
</div>
<h3 id="welcome" class="alert alert-success text-center m-5">
Welcome!
</h3>
<script type="text/javascript"> /* Add */
$('#welcome').delay(1000).hide(200); /* Add */
</script> /* Add */
</body>
- Refresh the page and notice the "Welcome!" splash that appears
- Update the student's grade in
routes.js
app.post('/events/:id', async (req, res) => {
// Capture input from Scan to Attend page
req.api.course.assignment.updateGrade({ /* Add */
courseId: ____, /* Add */
assignmentId: ____, /* Add */
studentId: ____, /* Add */
points: 1, /* Add */
}); /* Add */
});
- Add course id: get this from the launch info that caccl adds automatically
app.post('/events/:id', async (req, res) => {
// Capture input from Scan to Attend page
req.api.course.assignment.updateGrade({
courseId: req.session.launchInfo.courseId, /* Change */
assignmentId: ____,
studentId: ____,
points: 1,
});
});
- Add assignment id: if you remember, events are just 1-point assignments. Thus, the event id is the assignment id. We get the event id from the url. We do this using
req.params.id
app.post('/events/:id', async (req, res) => {
// Capture input from Scan to Attend page
req.api.course.assignment.updateGrade({
courseId: req.session.launchInfo.courseId,
assignmentId: req.params.id, /* Change */
studentId: ____,
points: 1,
});
});
- Add student id: this was typed into the form. We called that
studentId
(show the Scan to Attend page and point to the "name" field). We get the studentId from the form body usingreq.body.studentId
app.post('/events/:id', async (req, res) => {
// Capture input from Scan to Attend page
req.api.course.assignment.updateGrade({
courseId: req.session.launchInfo.courseId,
assignmentId: req.params.id,
studentId: req.body.studentId, /* Change */
points: 1,
});
});
- Redirect the user back to the swipe to attend page so we can take another attendance
app.post('/events/:id', async (req, res) => {
// Capture input from Scan to Attend page
req.api.course.assignment.updateGrade({
courseId: req.session.launchInfo.courseId,
assignmentId: req.params.id,
studentId: req.body.studentId,
points: 1,
});
res.redirect('/events/' + req.params.id); /* Add */
});
- Restart the server (it's been updated): kill the server process using
ctrl + c
, start it again usingnpm run dev:server
- Simulate launch: go to the tab where you ran
npm run dev:canvas
and copy/paste link from canvas simulator into browser to demo the simulated launch process - Test the app:
a. Enter "Lab 2" as the title and press enter
b. Scan many students' barcode cards
c. Visit Canvas gradebook, see that students have their grades
- Open our Heroku app, open "Deployment" tab and link to GitHub, turn on automatic deploys
- Open Settings, scroll to Config Vars, add the names to the variables:
CONSUMER_KEY = consumer_key
CONSUMER_SECRET = consumer_secret
CLIENT_ID = (get this from HUIT)
CLIENT_SECRET = (get this from HUIT)
CANVAS_HOST = canvas.harvard.edu
- Fill in the config var values: unplug the computer, fill in the values, hide the config vars, plug the computer back in
- Push code to github
git add -A
git commit -m "Initial Commit"
git push
- Try it out