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

import csv
4
import os,time
5
6
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
import shutil  # copyfile, make_archive
import argparse, sys


def find_file(pattern, path):
  if os.name == "posix":
    import subprocess

    result = [line[2:] for line in subprocess.check_output(
        "find " + path + " -type f -name " + pattern, 
        shell=True).splitlines()]
    result = [tmp.decode("utf-8") for tmp in result]

  else: 
    import fnmatch

    result = []
    for root, _, files in os.walk(path):
      for name in files:
        if fnmatch.fnmatch(name, pattern):
          result.append(os.path.join(root, name))

  return result

if __name__ == '__main__':

  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")
49
50
  parser.add_argument("-b","--batch", default="0", 
    help="Check whether it runs through batch script or not. Default: 0")
51
52
53
54
55
56
57
58

  args = parser.parse_args()
  infolder = args.infolder
  csvfilename = args.csv
  outzip = args.outzip
  tmpfolder = args.tmp
  dry = args.dry
  nowarn = args.nowarn
59
  batch_process = int(args.batch)
60
61

  numlines = 0
62
  starttime = time.time()
63
64
65
66
67
68
69
70
71
72
73
  with open(csvfilename, newline='') as csvfile:
    numlines = sum(1 for line in csvfile)

  print('''Preparing for moodle upload
  Processing {} lines
  '''.format(numlines))

  if dry:
    print("Dry run\n")
    dryoutput=""
  else:
74
75
76
77
78
79
80
    if batch_process == 0:
        for root, dirs, files in os.walk(tmpfolder):
          for f in files:
              os.unlink(os.path.join(root, f))
          for d in dirs:
              shutil.rmtree(os.path.join(root, d))
        
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

    if os.path.exists(outzip): os.remove(outzip)


  with open(csvfilename, newline='') as csvfile:
    
    # Loop over all lines in CSV file
    numfoundpdfs = 0
    cnt = 0
    print("Start iterating...", sep='', end='', flush=True)

    reader = csv.reader(csvfile, delimiter=',', quotechar='"')
    next(reader)  # skip first row in CSV file since this should be the header
    for row in reader:
      # parse the required fields from the csv file
      id = row[0]
      id = id.replace("Teilnehmer/in", "")
da-admin's avatar
da-admin committed
98
      id = id.replace("Participant ", "")
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
      name = row[1]
      matnum = row[2]

      # if a pdf file for current student exists, create a directory and copy
      # the pdf file to it. The resulting directories can be uploaded to moodle
      longpdffile = ''
      paths = find_file(matnum + "*.pdf", infolder)

      if len(paths) > 0:
        longpdffile = paths[0]
      if len(paths) > 1:  # TODO: implement second loop for enabling distribution of multiple files
        raise Exception("More than one PDFs starting with matnum {} found!".format(matnum)) 
      
      
      if os.path.isfile(longpdffile):
        numfoundpdfs += 1
        pdffile = os.path.basename(longpdffile)
116
        folder = "{}_{}_assignsubmission_file_".format(name, id)
117
118
119
120
121
122
123
124
125
126
127
128
129
130
        longfolder = os.path.join(tmpfolder, folder)

        if not dry:
          os.mkdir(longfolder)
          shutil.copyfile(longpdffile, os.path.join(longfolder, pdffile))
        else:
          dryoutput += "\n{}".format(os.path.join(folder, pdffile))
      else:
          if not nowarn:
            print("Warning: PDF corresponding to matriculation number {} (id={}, name={}) not available.".format(
              matnum, id, name
            ))
      
      # Progress
131
      if not (cnt % max(1,round(numlines/10))): 
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
        print(".", sep=' ', end='', flush=True)
      cnt += 1
      
  print("done.\n")

  print("Found {} PDFs (CSV had {} entries)\n".format(numfoundpdfs, numlines))

  print("Searching for matriculation numbers not present in CSV but in PDF input folder:")

  # Check for PDFs which are not reflected in CSV (student not registered in Moodle)
  numnotfoundmatnums = 0
  notfoundmatnums = ""

  for root, dirs, files in os.walk(infolder):
      for pdffile in files:
          if pdffile.endswith(".pdf"):
            # Get matriculation number from file
            matnum = pdffile[0:6]
            
            # Search in CSV
            with open(csvfilename, 'r') as csvfile:
              notfound = True
              for line in csvfile:
                  if matnum in line:
                    notfound = False

            if notfound:
              numnotfoundmatnums += 1
              notfoundmatnums += matnum + ", "
              if not nowarn:
                print("Warning: Could not find {} in CSV".format(matnum))

  if numnotfoundmatnums > 0:
    print('''I could not find the following {} matriculation numbers in CSV:
      {}'''.format(numnotfoundmatnums, notfoundmatnums))

  print("Done.")
169
  
170
171
172
173
174
175
176
177
178
179
180

  # Zipping
  if not dry:
    print("Zipping")

    shutil.make_archive(os.path.splitext(outzip)[0], 'zip', tmpfolder)

  else:
    print("\nResults from dry ryn:\n{}".format(dryoutput))


181
182
183
184
185
  print("\nDone.\n")

  endtime = time.time()
  print('\n The Zip archive is available at: '+outzip)
  print(f'\nTime taken: {endtime-starttime:.2f}s\n')