Skip to content

Commit

Permalink
Merge pull request #13 from QuantumStack/dev
Browse files Browse the repository at this point in the history
Version 1.3
  • Loading branch information
fishdev authored Feb 3, 2019
2 parents 1d2ef26 + 1121480 commit 8165842
Show file tree
Hide file tree
Showing 110 changed files with 7,372 additions and 5,359 deletions.
116 changes: 116 additions & 0 deletions client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/
23 changes: 16 additions & 7 deletions client/checkin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import pyqrcode
import getpass
import sys
import sys, os
import random

home = os.path.expanduser('~/')

def main():
# Get the student ID of the user from their user directory
student_id = getpass.getuser()
Expand All @@ -11,19 +13,26 @@ def main():
course = sys.argv[1]
url = sys.argv[2]

print("Student ID: " + student_d + " (" + course + ")")
print("Student ID: " + student_id + " (" + course + ")")
print("Check-in ID: " + str(random.randint(100000, 999999)))

# Build URL string for QR
url_str = (url + '/checkin/' + student_id + '/')

# Create QR code
url = pyqrcode.create(url_str)
# Create a Low Error Correcting QR code
url = pyqrcode.create(url_str, error='L')

# Print the low error correcting QR code to terminal
print(url.terminal(quiet_zone=1, module_color='black', background='white'))

# Create a high error correcting QR code
url = pyqrcode.create(url_str, error='H')

# Save high error correcting QR Code as a PNG for bash script to open
url.png(home + "." + course + 'cmulab.png', scale=9,
module_color=[0, 0, 0, 255], background=[0xff, 0xff, 0xff])

# Print QR Code
print(url.terminal(quiet_zone=1))

if __name__ == "__main__":
main()


Binary file added client/checkin_gnu
Binary file not shown.
Binary file added client/checkin_mac
Binary file not shown.
30 changes: 27 additions & 3 deletions client/cmulab
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,35 @@
# Change directory to the directory of the script
cd "$(dirname "$0")"

export CMULAB_COURSE="" # insert course number here
export CMULAB_LOC="" # insert hosting URL here (without trailing /)

# TODO: Change these variables for your course
CMULAB_COURSE="" # insert course number here
CMULAB_LOC="" # insert hosting URL here (without trailing /)


# Generate location for QR Code
CMULAB_QRCODE_LOC="${HOME}/.${CMULAB_COURSE}cmulab.png"


# Create the QR Code
unameOut="$(uname -s)"
case "${unameOut}" in
Darwin*) ./checkin_mac $CMULAB_COURSE $CMULAB_LOC;;
Darwin*) ./checkin_mac $CMULAB_COURSE $CMULAB_LOC
open ${CMULAB_QRCODE_LOC}
exit;;
*) ./checkin_gnu $CMULAB_COURSE $CMULAB_LOC;;
esac

# Display the QR Code if possible
if xset q &>/dev/null; then
echo "Opening QR Code, please wait."
display ${CMULAB_QRCODE_LOC}
elif ! [ -n "$SSH_CLIENT" ] && ! [ -n "$SSH_TTY" ]; then
echo "Opening QR Code, please wait."
display ${CMULAB_QRCODE_LOC}
fi

# Otherwise, they're probably on Windows without an Xserver...
# We just have to exit and deal with a lower error-correcting code...
rm ${CMULAB_QRCODE_LOC}

1 change: 1 addition & 0 deletions client/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pypng==0.0.19
PyQRCode==1.2.1
20 changes: 5 additions & 15 deletions server/.env
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,10 @@ CMULAB_LOC=""
CMULAB_GOOGLE_ID=""
CMULAB_GOOGLE_SECRET=""

# Course ID
CMULAB_COURSE=""
# Email credentials
CMULAB_SMTP_SERVER=""
CMULAB_SMTP_PORT=""
CMULAB_SMTP_USER=""
CMULAB_SMTP_PASS=""

# Email domain of users
CMULAB_EMAILDOMAIN=""

# Min lab score
CMULAB_MINSCORE=""

# Max lab score
CMULAB_MAXSCORE=""

# Use buttons for score input
CMULAB_RADIOINPUT=""

# Allow setting lab manually
CMULAB_MANUALLAB=""
3 changes: 3 additions & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ typings/

# next.js build output
.next

# config file
config.json
34 changes: 12 additions & 22 deletions server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ const logger = require('morgan');
const sassMiddleware = require('node-sass-middleware');
const mongoose = require('mongoose');

require('dotenv').config();

const config = require('./util/config');
const helpers = require('./util/helpers');
const User = require('./models/User');
const indexRouter = require('./routes/index');
const checkinRouter = require('./routes/checkin');
const adminRouter = require('./routes/admin');
const loginRouter = require('./routes/login');

require('dotenv').config();

const app = express();

// view engine setup
Expand All @@ -27,24 +29,7 @@ app.engine('hbs', hbs({
defaultLayout: 'main',
layoutsDir: path.join(__dirname, '/views/layouts/'),
partialsDir: path.join(__dirname, '/views/partials/'),
helpers: {
trim: str => str.toString().slice(0, str.toString().indexOf(' (UTC')),
round: n => Math.round(n * 100) / 100,
freqreduce: (arr) => {
const freqs = {};
arr.forEach((item) => {
if (!freqs[item]) freqs[item] = 0;
freqs[item] += 1;
});
return Object.entries(freqs).sort((a, b) => b[1] - a[1])
.map(([item]) => item).join(', ');
},
range: (n, m) => {
const L = [];
for (let i = n; i <= m; i += 1) L.push(i);
return L;
},
},
helpers,
}));
app.set('view engine', 'hbs');

Expand All @@ -68,11 +53,14 @@ passport.use(new GoogleStrategy({
clientID: process.env.CMULAB_GOOGLE_ID,
clientSecret: process.env.CMULAB_GOOGLE_SECRET,
callbackURL: `${process.env.CMULAB_LOC}/login/callback`,
userProfileURL: 'https://www.googleapis.com/oauth2/v3/userinfo',
}, (token, tokenSecret, profile, done) => {
// check if email ends with approved email domain
const email = profile.emails[0].value;
if (!email.endsWith(`@${process.env.CMULAB_EMAILDOMAIN}`)) {
if (!email.endsWith(`@${config.get('emailDomain')}`)) {
return done('Not a university user');
}
// check if user is authorized in db
const student_id = email.slice(0, email.lastIndexOf('@'));
User.findOne({ _id: student_id }, (err, user) => {
if (!user) return done('Not permitted');
Expand Down Expand Up @@ -100,7 +88,9 @@ app.use('/login', loginRouter);
/* POST go to checkin */
app.post('/go', (req, res) => {
const { student_id } = req.body;
// if student_id is not provided, go to home page
if (!student_id) return res.redirect('/');
// go to checkin page for student
res.redirect(`/checkin/${student_id}`);
});

Expand All @@ -113,7 +103,7 @@ app.use((req, res, next) => {
app.use((err, req, res, next) => { // eslint-disable-line no-unused-vars
res.status(err.status || 500);
res.render('error', {
course: process.env.CMULAB_COURSE,
course: config.get('course'),
loc: process.env.CMULAB_LOC,
err,
});
Expand Down
16 changes: 16 additions & 0 deletions server/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"course": "",
"emailDomain": "",
"offsetHours": 0,
"offsetMinutes": 0,
"minScore": 0,
"maxScore": 3,
"lowercaseStudents": false,
"radioInput": true,
"manualLab": false,
"flagSection": false,
"flagAttempts": "",
"flagAttemptsThreshold": 3600000,
"flagGhosts": true,
"sections": {}
}
13 changes: 10 additions & 3 deletions server/models/Entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ const mongoose = require('mongoose');
const entrySchema = new mongoose.Schema({
student_id: { type: String, required: true },
date: { type: Date, required: true },
lab: { type: Number, min: 0 },
lab: { type: String },
section: { type: String, required: true, uppercase: true },
score: {
type: Number,
required: true,
min: process.env.CMULAB_MINSCORE,
max: process.env.CMULAB_MAXSCORE,
},
ta: { type: String, required: true },
good: { type: Boolean, default: true },
flags: {
ghost: { type: Boolean },
attempt: { type: Boolean },
attemptDiff: { type: Number },
attemptSection: { type: String, uppercase: true },
attemptScore: { type: Number },
section: { type: Boolean },
},
});

module.exports = mongoose.model('Entry', entrySchema);
Loading

0 comments on commit 8165842

Please sign in to comment.