preparemoodle.py 6.52 KB
Newer Older
1
2
3
#!/usr/bin/env python

import csv
4
5
import os
import time
6
import shutil  # copyfile, make_archive
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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))
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
    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))
186
187
188


if __name__ == '__main__':
189
    main(sys.argv[1:])