Skip to content

Instantly share code, notes, and snippets.

@mjdargen
Last active September 28, 2022 16:18
Show Gist options
  • Save mjdargen/57d907fbdfdecbe76ba38ae7ad09228e to your computer and use it in GitHub Desktop.
Save mjdargen/57d907fbdfdecbe76ba38ae7ad09228e to your computer and use it in GitHub Desktop.
Unzips all zip Canvas submissions for webdev, reorganizes file structure, and validates all HTML & CSS files.
# 1. Download the submissions.zip file from Canvas.
# 2. Place it in an empty folder for the specific assignment. Make sure it is called submissions.zip.
# 3. Copy the webdev_zip_grader.py script into the folder.
# 4. Make sure the folder is empty except for webdev_zip_grader.py and submissions.zip because it will delete everything else.
# 5. Double-click the script to run.
# 6. Hit the enter key in the script window to advance it to the next page.
import os
import shutil
import requests
import subprocess
import webbrowser
from zipfile import ZipFile
dir = os.path.dirname(os.path.abspath(__file__))
filename = 'submissions.zip'
path = os.path.join(dir, filename)
# remove everything
for root, subdirs, _files in os.walk(dir):
for d in subdirs:
shutil.rmtree(os.path.join(dir, d))
# unzip
with ZipFile(path, 'r') as zip:
zip.extractall(dir)
# rename zip files
submissions = [f for f in os.listdir(
dir) if f.endswith(".zip") and f != filename]
for sub in submissions:
sub = os.path.join(dir + '/' + sub)
new = os.path.join(dir + '/' + sub.split('/')[-1].split('_')[0] + '.zip')
os.rename(sub, new)
# extract and delete zips
submissions = [f for f in os.listdir(
dir) if f.endswith(".zip") and f != filename]
for sub in submissions:
path = os.path.join(dir, sub)
with ZipFile(path, 'r') as zip:
zip.extractall(path[:-4])
os.remove(path)
# parse student folders and move all files
students = next(os.walk(dir))[1]
for sub in students:
path = os.path.join(dir, sub)
for root, subdirs, files in os.walk(path):
if "__MACOSX" in subdirs:
shutil.rmtree(os.path.join(path, "__MACOSX"))
# find the files
def find_folder(path):
for root, subdirs, files in os.walk(path):
print(path, root)
if path == root:
print(files)
if [f for f in files if f.split('.')[-1].lower() in ['html', 'css']]:
return root
else:
if len(subdirs) == 1:
return find_folder(os.path.join(path, subdirs[0]))
else:
print("Error: too many subfolders?")
location = find_folder(path)
# move and copy files/folders
for _r, _s, _f in os.walk(location):
_f = [f for f in _f if '.ds_store' not in f.lower()]
for s in _s:
shutil.move(os.path.join(_r, s),
os.path.join(path, s))
for _r, _s, _f in os.walk(location):
_f = [f for f in _f if '.ds_store' not in f.lower()]
for f in _f:
if f.split('.')[-1].lower() in ['html', 'css', 'js']:
shutil.move(os.path.join(_r, f),
os.path.join(path, f))
elif os.path.join(_r, f) != os.path.join(path, f): # images
shutil.copy2(os.path.join(_r, f),
os.path.join(path, f))
if location != path:
shutil.rmtree(location)
# check to see if it validates
def validate_file(filename):
with open(filename, 'r', encoding='utf-8', errors='ignore') as f:
raw = f.read()
if filename.lower().endswith('.html'):
r = requests.post('https://validator.nu/',
data=raw.encode('utf-8'),
params={'out': 'json'},
headers={'Content-Type': 'text/html; charset=UTF-8'})
if r.json()['messages']:
messages = [msg for msg in r.json()['messages']]
try:
[msg.pop('hiliteStart') for msg in messages]
[msg.pop('hiliteLength') for msg in messages]
except:
pass
messages = [str(msg) for msg in messages]
messages = '\n\t\t'.join(messages)
return f"{len(r.json()['messages'])} errors" "\n\t\t" + messages
else:
return False
elif filename.lower().endswith('.css'):
r = requests.get('https://jigsaw.w3.org/css-validator/validator',
params={"text": raw.encode('utf-8'),
"output": "json"},
headers={'Content-Type': 'text/plain; charset=UTF-8'})
data = r.json()['cssvalidation']
if data['result']['errorcount']:
messages = [msg for msg in data['errors']]
[msg.pop('source') for msg in messages]
messages = [str(msg) for msg in messages]
messages = '\n\t\t'.join(messages)
count = f"{data['result']['errorcount']} errors & {data['result']['warningcount']} warnings"
return count + "\n\t\t" + messages
else:
return False
# main processing loop for each student
f = open(f"{dir}/{filename.split('.')[0]}.txt", 'w', encoding='utf-8')
for s in students:
f.write(f'\n{s}\n')
print(f'\n{s}')
files = os.listdir(os.path.join(dir, s))
html = [f for f in files if f.lower().endswith('.html')]
files = [f for f in files if f.lower().endswith('.css')]
files.extend(html)
for p in files:
f.write(f'\t{p}\n')
print(f'\t{p}')
p = os.path.join(dir, s, p)
invalidated = validate_file(p)
if invalidated:
f.write('\t\tINVALID: ' + invalidated + '\n')
print('\t\tINVALID: ' + invalidated)
if not invalidated:
f.write('\t\tVALID!\n')
print('\t\tVALID!')
# open
subprocess.call(
[r"C:\\Program Files\\Notepad++\\notepad++.exe", p, "-n1500"])
if p.lower().endswith('.html'):
webbrowser.open(p, new=2)
input()
f.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment