This project is a rewrite of the Emacs techela project (https://github.com/jkitchin/techela-emacs) using Python and a webbrowser instead. The code is currently specific to Carnegie Mellon University and relies on the following components:
- Github - the course content (syllabus, notes, assignments, solutions) are hosted in a publicly accessible GitHUB repository.
- Box.com - assignments are currently turned in and returned via email through a Box.com folder. The email is authenticated using Carnegie Mellon University mail servers. You should setup Box Sync to synchronize assignments and solutions. This folder contains “private” things, such as the roster, graded assignments, etc. that noone but the instructors and Tas should see.
- Python + Flask - techela is written as a Flask app that runs in a browser, and runs Python commands to open jupyter notebooks, submit assignments, etc.
You need to setup a json file containing some course information. This data should be put into a file named <course-label>.json and added to the registered-courses directory. Here is an example.
import json
d = {'title': 'Mathematical Modeling of Chemical Engineering Processes',
'label': 'f18-06623',
'year': 2018,
'semester': 'Fall',
'submit-email': '[email protected]',
'course-url': 'https://github.com/jkitchin/f18-06623/',
'course-raw-url': 'https://raw.githubusercontent.com/jkitchin/f18-06623/master/',
'instructor': 'John Kitchin',
'instructor-email': '[email protected]',
'admin-names': ["John Kitchin", "Mingjie Liu", "Noriyuki Yoshio"],
'admin-andrewids': ['jkitchin', 'mingjie1', 'nyoshio'],
'categories': [['homework', 'quiz', 'exam-1', 'exam-2', 'exam-3'],
[0.2, 0.2, 0.12, 0.23, 0.25]],
'rubrics': {'default': [['technical', 'presentation'],
[0.8, 0.2]],
'just-technical': [['technical'],
[1.0]]},
'local-box-path': '~/Box Sync/f18-06623'}
with open('registered-courses/f18-06623.json', 'w') as f:
f.write(json.dumps(d, indent=4))
This file must be added to https://github.com/jkitchin/techela/tree/master/registered-courses.
The <course-label> will be used to construct various paths, so it probably should not have spaces or other problematic characters.
The general idea is you will use git to push content that can be visible to all your students to your course. That allows you to keep the course under version control, and to determine when it becomes available. Anything pushed becomes available.
You should create a new git repo at the location defined in the ‘course-url’ data from Registering your course in techela.
The following files are expected to exist:
- syllabus.org
- course-schedule.org
- announcements.org
- lectures directory
- It is assumed that all committed ipynb files in this directory are lecture notes
- assignments directory
- It is assumed that all committed ipynb files in this directory are assignments
- solutions directory
- It is assumed that all committed ipynb files in this directory are solutions
You should use a pre-commit hook to automatically create a course-data.json file that is an integral part of the Flask app. It will contain all the relevant data required to build the web-pages.
For better or worse, techela uses a json data file (course-data.json) to tell the flask app what to do. The json file is automatically created in a pre-commit hook. The hook is a Python program shown below.
#!/usr/bin/env python
import glob
import json
import os
import subprocess
import sys
import time
# This script updates the json file for this course prior to committing.
def get_output(cmd):
p = subprocess.Popen(cmd.split(' '),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = p.communicate()
files = [f for f in out.decode("utf-8").strip().split('\n') if f.endswith('.ipynb')]
return files
lectures = get_output('git ls-files lectures')
assignments = get_output('git ls-files assignments')
solutions = get_output('git ls-files solutions')
# the assignments are like assignments/label.ipynb
assignment_data = {}
for assignment in assignments:
with open(assignment) as f:
j = json.loads(f.read())
duedate = None
md = j.get('metadata', None)
if md:
org = md.get('org', None)
if org:
duedate = org.get('DUEDATE', None)
grader = org.get('GRADER', None)
points = org.get('POINTS', '?')
category = org.get('CATEGORY', '?')
label = os.path.splitext(os.path.split(assignment)[-1])[0]
assignment_data[assignment] = {'label': label,
'duedate': duedate,
'points': points,
'category': category,
'grader': grader}
lecture_keywords = []
for lf in lectures:
with open(lf) as f:
print(lf)
jd = json.loads(f.read())
md = jd['metadata']
org = md.get('org', {})
if org:
lecture_keywords += [org.get('KEYWORDS', '')]
else:
lecture_keywords += ['']
if os.path.exists('announcements.html'):
with open('announcements.html') as f:
announcements = f.read()
else:
announcements = ''
data = {'lectures': lectures,
'lecture_keywords': lecture_keywords,
'assignments': assignment_data,
'solutions': solutions,
'announcements': announcements}
with open('course-files.json', 'w') as f:
f.write(json.dumps(data, indent=4))
os.system('git add course-files.json')
sys.exit(0)
This folder contains information that should not be distributed to the students. They will email their assignments to a special email that uploads attachments to the submissions folder.
The box folder should have:
local-box-admin-folder
- roster.csv
- submissions (this is the folder you want the email for)
- solutions
You should set this folder to sync automatically to your local machine, and make sure that it syncs to the location specified in ‘local-box-path’ in Registering your course in techela.
The flask app has an admin page for collecting, grading and returning assignments
Students will run one command:
python -m techela.app <course-label>
This will launch their browser. They will be prompted to register their andrewid and email address, and then will see the home page for the course. They will typically just click on links to open lecture notes, assignments, etc. as well as to turn in assignments. The assignments will be turned in and returned by email.
You can put announcements into the announcements.org file, and then generate an HTML version of that file. Alternatively, just make an announcements.html file with the contents you want to show.
I prepare the lecture notes in org-mode, and then export them to ipython notebooks. When you are ready to make them available, you simply commit the notebook to the git repo and push it to github. This will automatically update the course-data.json file and the notes should become available the next time students open the course.
You can specify keywords for the lecture to help students navigate the list of files.
#+OX-IPYNB-KEYWORD-METADATA: keywords
I create assignments in org-mode, and export them to ipython notebooks. Basically, one heading is one problem, and you should assign several properties to the heading:
- DUEDATE in the form DD-MM-YYYY HH:mm:ss
- POINTS - how many points this problem is worth
- RUBRIC - which rubric to use
- TYPE - this is the category, e.g. homework, exam-1, quiz
- LABEL - a name for the assignment
- GRADER - Name of the person responsible for grading
The point of these is that this information is saved in machine-readable form in the notebooks, and integrated into the browser, and gradebook.
Instructors should also run
python -m techela.app <course-label>
They will also register their andrewid and names. When their andrewid is listed in the course information, they will see an admin link that will use information in the Box admin folder.
From the admin page, you can click on a label to collect the assignment. This will copy the assignments from the submissions folder into the assignments and assignments-archive folders. The assignments folder contains copies of the assignments that will be graded, and the assignments-archive folder is just to keep a copy of the files that are unaltered.
After the assignments are collected, you will see a page showing links to each assignment file. You can click on the link to open the file for grading.
techela provides some extensions to the jupyter notebook to facilitate grading. You can press C to enter a comment. After you are ready to grade, you press G to enter the grades. You will be prompted for a technical grade, and for a presentation grade. You should enter letter grades for these. The total grade will be automatically computed and stored in the notebook.
After you are done grading all the assignments, you can post the solution to the public github site, and then click on the Return all assignments link on the assignment page.
You just download a new roster from S3 and rename it as roster.csv in the admin folder.