Last active
September 28, 2022 16:18
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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