#!/usr/bin/env python import csv import os import time import shutil # copyfile, make_archive import argparse import sys import utils.matnum as utils def find_unmatched_pdfs(infolder, matnums, nowarn): """Finds matnumbers not present in CSV but in PDF folder Args: infolder (str): path to input folder matnums (list): list of matriculation numbers nowarn (int): flag """ print("\nSearching for matnumbers not present in CSV but in PDF folder:") # Loop over all PDFs: notfoundmatnums = [] for root, dirs, files in os.walk(infolder): for pdffile in files: if pdffile.endswith(".pdf"): # Get matriculation number from file matnum = utils.get_matnum(pdffile) # Search matriculation number in CSV if matnum not in matnums: notfoundmatnums.append(matnum) if not nowarn: print("Warning: {} not in CSV".format(matnum)) # Report back if len(notfoundmatnums) > 0: print('''Could not find following {} matnumbers in CSV: {}'''.format(len(notfoundmatnums), ", ".join(notfoundmatnums))) print("Done.\n") def main(args): """Main routine """ # Parse input arguments parser = argparse.ArgumentParser(description=''' prepares batch upload to Moodle via assignment module. PDFs in folder 'in' are moved to folder 'tmp' with a certain folder structure and finally zipped to 'out'. Attention: zip-archive 'out' will be overwritten in the following! ''') parser.add_argument("-i", "--infolder", default="./pdfs_encrypted", help="Input folder with PDFs. Default: ./pdfs_encrypted") parser.add_argument("-c", "--csv", default="./Bewertungen.csv", help="Moodle grading CSV file, needed to construct the folder names. Default: ./Bewertungen.csv") parser.add_argument("-o", "--outzip", default="./moodle_feedbacks.zip", help="Output zip archive. Default: ./moodle_feedbacks.zip") parser.add_argument("-d", "--dry", action='store_true', help="Flag for dry run, displays only the folder structure inside the archive moodle_feedbacks.zip") parser.add_argument("-t", "--tmp", default="./tmp", help="tmp folder. Default: ./tmp") parser.add_argument("--nowarn", action='store_true', help="Disables warnings") args = parser.parse_args(args) infolder = args.infolder csvfilename = args.csv outzip = args.outzip tmpfolder = os.path.join(args.tmp, "to_be_zipped_for_moodle") dry = args.dry nowarn = args.nowarn starttime = time.time() # Print status with total number of lines numlines = 0 with open(csvfilename, newline='') as csvfile: numlines = sum(1 for line in csvfile) print('''Preparing for moodle upload Processing {} lines '''.format(numlines)) dryout = "" if dry: print("Dry run\n") else: # Remove zip file if os.path.exists(outzip): os.remove(outzip) # Create temporary folder within given temporary directory if not os.path.isdir(tmpfolder): os.mkdir(tmpfolder) # Open CSV file with open(csvfilename, newline='') as csvfile: numfoundpdfs = 0 matnums = [] line_cnt = 0 print("Start iterating...", sep='', end='', flush=True) # Loop over all lines in CSV file reader = csv.reader(csvfile, delimiter=',', quotechar='"') next(reader) # skip header CSV line for row in reader: # Parse required fields from CSV line # Moodle has its own internal ID per participant alongside # matriculation number moodleid = row[0] moodleid = moodleid.replace("Teilnehmer/in", "") # German moodleid = moodleid.replace("Participant ", "") # English name = row[1] # Lastname, Firstname matnum = row[2] # matriculation number (6-digit) matnums.append(matnum) # save matriculation number for later # Copy PDF files # Find all PDFs starting with matriculation number, e.g. # '123456_Lastname_sheet.pdf' and '123456_Lastname_exam.pdf' # If pdf files for current student exists, create a directory and # copy the pdf files to it. The resulting directories can be # uploaded to Moodle longpdffiles = utils.find_file(matnum + "*.pdf", infolder) if len(longpdffiles) > 0: # Found some file(s) numfoundpdfs += 1 # Prepare folder # For upload, Moodle accepts submission files per participant folder = "{}_{}_assignsubmission_file_".format(name, moodleid) longfolder = os.path.join(tmpfolder, folder) # Create folder if not dry: os.mkdir(longfolder) # Copy all files to folder for longpdffile in longpdffiles: pdffile = os.path.basename(longpdffile) if not dry: shutil.copyfile(longpdffile, os.path.join(longfolder, pdffile)) else: dryout += "\n{}".format(os.path.join(folder, pdffile)) else: if not nowarn: print("Warning: PDF corresponding to matnumber {} (moodleid={}, name={}) not available.".format( matnum, moodleid, name )) # Print progress if not (line_cnt % max(1, round(numlines/10))): print(".", sep=' ', end='', flush=True) line_cnt += 1 # Print results print("Found {} PDFs (CSV had {} entries)".format(numfoundpdfs, numlines)) print("done.") # Sanity check: # Check for PDFs not reflected in CSV (student not registered in Moodle) find_unmatched_pdfs(infolder, matnums, nowarn) # Zipping if not dry: # Zip print("Zipping") shutil.make_archive(os.path.splitext(outzip)[0], 'zip', tmpfolder) print('The Zip archive is available at: '+outzip) # Delete temporary folder shutil.rmtree(tmpfolder) else: print("\nDry run results:\n{}".format(dryout)) endtime = time.time() print("""Done. Time taken: {:.2f}""".format(endtime-starttime)) if __name__ == '__main__': main(sys.argv[1:])