def makeAbiReports (schoolyear, klass): """Build abitur grade reports using the grade tables defined by the configuration path FILE_ABITABLE. A template is used to construct the report files. The results – odt files – are placed according to the configuration path FILE_ABIREPORT, first removing any existing files in this folder. odt files are created from a template. Return a tuple: the output folder and a list of odt-file-names (without folder path). """ sheetname = CONF.TABLES.ABITUR_RESULTS.GRADE_TABLE_SHEET infile = Paths.getYearPath (schoolyear, 'FILE_ABITABLE', klass=klass) filepath = Paths.getYearPath (schoolyear, 'FILE_ABIREPORT', klass=klass, make=-1) outdir = os.path.dirname (filepath) if os.path.isdir (outdir): shutil.rmtree (outdir) files = [] for f in sorted (glob (infile)): # Extract pupil info from file-name try: _, index, pid, pname = f.rsplit ('.', 1) [0].rsplit ('-', 3) except: continue data = readTableData (f, table=sheetname) ofile = filepath.replace ('*', '{pnum}-{pid}-{name}'.format ( pnum=index, pid=pid, name=pname)) files.append (os.path.basename (makeAbiReport (ofile, data))) REPORT.Info (_MADENREPORTS, klass=klass, n=len (files), folder=outdir) return outdir, files
def test_01(): filepath = Paths.getYearPath(_year, 'FILE_SP_TEACHERS') REPORT.Test("\n --------------\n%s" % repr(readSPTable(filepath))) filepath = Paths.getYearPath(_year, 'FILE_SP_CLASSES') REPORT.Test("\n --------------\n%s" % repr(readSPTable(filepath))) filepath = Paths.getYearPath(_year, 'FILE_SP_SUBJECTS') REPORT.Test("\n --------------\n%s" % repr(readSPTable(filepath))) filepath = Paths.getYearPath(_year, 'FILE_SP_LESSONS') REPORT.Test("\n --------------\n%s" % repr(readSPTable(filepath)))
def importLatestRaw(schoolyear): allfiles = glob(Paths.getYearPath(schoolyear, 'FILE_PUPILS_RAW')) fpath = sorted(allfiles)[-1] REPORT.Info(_IMPORT_FROM, path=fpath) rpd = readRawPupils(schoolyear, fpath) updateFromRaw(schoolyear, rpd) return rpd
def test_02(): pdfBytes = makeSheets(_year, _date, Klass(_klass)) folder = Paths.getUserPath('DIR_TEXT_REPORT_TEMPLATES') fpath = os.path.join(folder, 'test.pdf') with open(fpath, 'wb') as fh: fh.write(pdfBytes) REPORT.Test(" --> %s" % fpath)
def test_01 (): _term = '1' _date = '2016-01-29' for _klass, _pid in ('13', '200301'), ('12.RS', '200403'): klass = Klass(_klass) REPORT.Test("Reading basic grade data for class %s" % klass) #TODO: This might not be the optimal location for this file. filepath = Paths.getYearPath(_testyear, 'FILE_GRADE_TABLE', term=_term).replace('*', str(klass).replace('.', '-')) pgrades = readGradeTable(filepath) REPORT.Test(" ++ INFO: %s" % repr(pgrades.info)) for pid, grades in pgrades.items(): REPORT.Test("\n -- %s: %s\n" % (pid, repr(grades))) REPORT.Test("\nReading template data for class %s" % klass) # Get the report type from the term and klass/stream _rtype = klass.match_map(CONF.GRADES.REPORT_TEMPLATES['_' + _term]) gradedata = GradeReportData(_testyear, _rtype, klass) REPORT.Test(" Indexes:\n %s" % repr(gradedata.sgroup2indexes)) REPORT.Test(" Grade tags:\n %s" % repr(gradedata.sgroup2sids)) grademap = klass.match_map(CONF.MISC.GRADE_SCALE) REPORT.Test("\nTemplate grade map for pupil %s (using %s)" % (_pid, grademap)) tagmap = gradedata.getTagmap(pgrades[_pid], "Pupil_%s" % _pid, grademap) REPORT.Test(" Grade tags:\n %s" % repr(tagmap))
def makeAbiReport (outpath, pdata): """Build an Abitur grade report for a pupil. The necessary information is supplied in the mapping <pdata>: {key: value}. The keys are user-fields in the document template (odt), the values are the strings to be inserted. The resulting file is placed at <outpath>, creating leading folders if necessary. <outpath> need not end in '.odt', if not present it will be added automatically. """ NOTCHOSEN = CONF.FORMATTING.NOTCHOSEN ## Get template path template = Paths.getUserPath ('FILE_ABITUR_REPORT_TEMPLATE') # fieldnames = OdtUserFields.listUserFields (template) ## Convert the dates. for f in pdata: d = pdata [f] if f.endswith ('_D'): # print ("???", f, d) if d: pdata [f] = Dates.dateConv (d) # Substitute non-date empty cells elif not d: pdata [f] = NOTCHOSEN folder = os.path.dirname (outpath) if not os.path.isdir (folder): os.makedirs (folder) ofile, used, missing = OdtUserFields.fillUserFields ( template, outpath, pdata) REPORT.Info (_ABIREPORTDONE, path=ofile) return ofile
def test_02(): _term = '2' for ks in '10', '11.Gym', '11.RS-HS-_', '12.RS-HS', '12.Gym', '13': klass = Klass(ks) bytefile = makeGradeTable(_testyear, _term, klass, "Noten: 1. Halbjahr") filepath = Paths.getYearPath(_testyear, 'FILE_GRADE_FULL', make=-1, term=_term).replace('*', str(klass).replace('.', '-')) + '.xlsx' with open(filepath, 'wb') as fh: fh.write(bytefile) REPORT.Test(" --> %s" % filepath) bytefile = stripTable(_testyear, _term, klass, "Noten: 1. Halbjahr") filepath = Paths.getYearPath(_testyear, 'FILE_GRADE_INPUT', make=-1, term=_term).replace('*', str(klass).replace('.', '-')) + '.xlsx' with open(filepath, 'wb') as fh: fh.write(bytefile) REPORT.Test(" --> %s" % filepath)
def test_05(): _klass_stream = Klass('11') pdfBytes = makeReports(_year, _term, _klass_stream, _date) folder = Paths.getUserPath('DIR_GRADE_REPORT_TEMPLATES') fpath = os.path.join(folder, 'test_%s_%s.pdf' % (_klass_stream, _date)) with open(fpath, 'wb') as fh: fh.write(pdfBytes)
def test_02(): from glob import glob files = glob(Paths.getYearPath(_testyear, 'FILE_GRADE_TABLE', term='*')) for filepath in files: pgrades = readGradeTable(filepath) grades2db(_testyear, pgrades)
def test_01(): pdfBytes = tSheets(_year, _manager, _date) fpath = Paths.getYearPath(_year, 'FILE_TEACHER_REPORT_LISTS') with open(fpath, 'wb') as fh: fh.write(pdfBytes) REPORT.Info("Class and subject lists for each teacher:\n {path}", path=fpath)
def logger(messages, suppressok): l = session.get('logger') if not l: user = session.get('user_id', '##') now = datetime.datetime.now() # Make new log-file l = now.isoformat(timespec='seconds') + '-' + user session['logger'] = l mimax = -10 toflash = [] for mi, mt, msg in messages: if mi > 9: msg = "Unerwarter Programmfehler: siehe Log-Datei %s" % l elif mi > mimax: mimax = mi try: etype = ERROR_TYPES[mi] except: etype = "ERROR %d" % mi mimax = 10 toflash.append((etype + '::: ' + msg, mt)) if len(toflash) > 10: flash("Abgekürzt: für alle Meldungen, siehe Log-Datei %s. ..." % l) toflash = toflash[-9:] for msg in toflash: flash(*msg) # Add a headline (the template will render this to the top, visible, line) if mimax >= 6: flash("!!! Aktion mit Fehler(n) abgeschlossen ...", "Error") elif mimax >= 4: flash("*** Aktion mit Warnung(en) abgeschlossen ...", "Warning") elif not suppressok: flash("+++ Aktion erfolgreich abgeschlossen ...", "Info") return Paths.logfile(l)
def login(): form = LoginForm() if form.validate_on_submit(): session.clear() tid = form.USER.data permission = Users().permission(tid) session['user_id'] = tid session['permission'] = permission # The logging handler will be set when needed, see the start module # (<flask_app.__init__>, method <logger>): session['logger'] = None # Set the school-year to the latest one: session['year'] = Paths.getYears()[0] session.permanent = True # Delete old session files sdir = current_app.config['SESSION_FILE_DIR'] now = time.time() for f in os.listdir(sdir): ff = os.path.join(sdir, f) delta = (now - os.path.getmtime(ff)) / 86400 if delta > 2: os.remove(ff) if session.get('user_id'): return redirect(url_for('index')) return render_template(os.path.join(_BPNAME, 'login.html'), form=form)
def test_04(): """Export pupil data to a spreadsheet table. """ REPORT.Test("Exporting pupil data for school-year %d" % _testyear) fpath = os.path.join(Paths.getYearPath(_testyear, 'DIR_SCHOOLDATA'), '_test', 'export_pupils_0') classes = exportPupils(_testyear, fpath) REPORT.Test("Exported to %s" % fpath)
def test_01(): klass = Klass('13') bytefile = choiceTable(_testyear, klass) filepath = Paths.getYearPath(_testyear, 'FILE_SUBJECT_CHOICE_TABLE', make=-1).replace('*', str(klass).replace('.', '-')) + '.xlsx' with open(filepath, 'wb') as fh: fh.write(bytefile) REPORT.Test(" --> %s" % filepath)
def __init__(self, schoolyear): l = importLessons(schoolyear) self.newdata = l[1] self.tid2name = l[2] self.badsubjects = l[3] self.filepath = Paths.getYearPath(_year, 'FILE_SUBJECTS') self._wb = load_workbook(self.filepath + '.xlsx') self._ws = self._wb.active self.makeStyle()
def __init__ (self, schoolyear, klass): self._class = klass self._year = schoolyear self._month = Month (self._year) # Get the template self._tpath = Paths.getUserPath ('TEMPLATE_ATTENDANCE_TABLE') self._table = Table (self._tpath) self._wsInfo = self._table.getSheet (0) self._wsMonth = self._table.getSheet (1)
def test_03(): """Import pupil data – an old version (to later test updates). The data is in a spreadsheet table. """ fpath = os.path.join(Paths.getYearPath(_testyear, 'DIR_SCHOOLDATA'), '_test', 'import_pupils_0') REPORT.Test("Importing pupil data for school-year %d from %s" % (_testyear, fpath)) classes = importPupils(_testyear, fpath) REPORT.Test("CLASSES/PUPILS: %s" % repr(classes))
def test_07(): # Reports for second term _term = '2' _date = '2016-06-22' for _ks in '11', '12.RS-HS-_', '12.Gym': _klass_stream = Klass(_ks) pdfBytes = makeReports(_year, _term, _klass_stream, _date) folder = Paths.getUserPath('DIR_GRADE_REPORT_TEMPLATES') fpath = os.path.join(folder, 'test_%s_%s.pdf' % (_klass_stream, _date)) with open(fpath, 'wb') as fh: fh.write(pdfBytes)
def tSheets(schoolyear, manager, date): courses = CourseTables(schoolyear) tidmap = {} for k in courses.classes(): klass = Klass(k) sid2tids = courses.filterText(klass) for sid, tids in sid2tids.items(): if tids.TEXT: if not tids: tids = [_nn] for tid in tids: try: tmap = tidmap[tid] except: tidmap[tid] = {klass.klass: {sid}} continue try: tmap[klass.klass].add(sid) except: tmap[klass.klass] = {sid} noreports = [] teachers = [] for tid in courses.teacherData: lines = [] tname = courses.teacherData.getTeacherName(tid) try: tmap = tidmap[tid] except: noreports.append(tname) continue for k in sorted(tmap): for sid in tmap[k]: sname = courses.subjectName(sid) lines.append((k, sname)) teachers.append((tname, lines)) tpdir = Paths.getUserPath('DIR_TEXT_REPORT_TEMPLATES') templateLoader = jinja2.FileSystemLoader(searchpath=tpdir) templateEnv = jinja2.Environment(loader=templateLoader, autoescape=True) tpfile = 'summary-teachers.html' try: template = templateEnv.get_template(tpfile) except: REPORT.Fail(_NOTEMPLATE, path=os.path.join(tpdir, tpfile)) source = template.render(year=printSchoolYear(schoolyear), manager=manager, date=Dates.dateConv(date), teachers=teachers, noreports=noreports) html = HTML(string=source) pdfBytes = html.write_pdf() return pdfBytes
def test_06(): """Compare new raw data with saved complete version: Import complete version, then perform update from raw data. """ REPORT.Test("\n --1-----------------\n RESET PUPILS TABLE") fpath = os.path.join(Paths.getYearPath(_testyear, 'DIR_SCHOOLDATA'), '_test', 'Schuelerdaten_1') REPORT.Test("Importing pupil data for school-year %d from %s" % (_testyear, fpath)) REPORT.Test("\n --2-----------------\n COMPARE UPDATES:") importLatestRaw(_testyear)
def test_03(): _k = '12K' _k = '12' _klass = Klass(_k) pupils = Pupils(_year) plist = pupils.classPupils(_klass) pdata = plist[0] pdfBytes = makeOneSheet(_year, _date, _klass, pdata) folder = Paths.getUserPath('DIR_TEXT_REPORT_TEMPLATES') fpath = os.path.join(folder, 'test1.pdf') with open(fpath, 'wb') as fh: fh.write(pdfBytes) REPORT.Test(" --> %s" % fpath)
def test_01(): from wz_compat.template import openTemplate, getTemplateTags, pupilFields from glob import glob fbase = Paths.getUserPath('DIR_TEMPLATES') fx = 'Notenzeugnis/*.html' fmask = os.path.join(fbase, *fx.split('/')) for f in glob(fmask): fname = os.path.basename(f) fpath = fx.rsplit('/', 1)[0] + '/' + os.path.basename(f) REPORT.Test("TEMPLATE: %s" % fpath) t = openTemplate(fpath) tags = getTemplateTags(t) REPORT.Test("Pupil fields: %s" % repr(pupilFields(tags)))
def test_06(): #TODO: Perhaps if _GS is set, the type should be overriden? _klass = Klass('12') _pid = '200407' pupils = Pupils(_year) pall = pupils.classPupils(_klass) # list of data for all pupils pdata = pall.pidmap[_pid] pdfBytes = makeOneSheet(_year, '2016-02-03', pdata, _term, 'Abgang') folder = Paths.getUserPath('DIR_GRADE_REPORT_TEMPLATES') ptag = pdata['PSORT'].replace(' ', '_') fpath = os.path.join(folder, 'test_%s_Abgang.pdf' % ptag) with open(fpath, 'wb') as fh: fh.write(pdfBytes) REPORT.Test(" --> %s" % fpath)
def __init__(self, schoolyear, filetag, klass=None, **kargs): """<filetag> is an entry in the PATHS config file, which may contain '*' (which will be replaced by the class). If there is already a file with this path, this will be read in using <readDBTable> (which adds a file extension if necessary). """ self.schoolyear = schoolyear self.klass = klass # Configuration info for the options table self.fieldnames = CONF.TABLES.COURSE_PUPIL_FIELDNAMES self.filepath = Paths.getYearPath(schoolyear, filetag, make=-1, **kargs).replace('*', klass)
def choiceTable(schoolyear, klass): """Build a subject choice table for the given school-class. <klass> is a <Klass> instance. """ template = Paths.getUserPath('FILE_SUBJECT_CHOICE_TEMPLATE') table = KlassMatrix(template) # Title already set in template: #table.setTitle("Kurswahl") # "Translation" of info items: kmap = CONF.TABLES.COURSE_PUPIL_FIELDNAMES info = ( (kmap['SCHOOLYEAR'], str(schoolyear)), (kmap['CLASS'], klass.klass), ) table.setInfo(info) ### Manage subjects courses = CourseTables(schoolyear) sid2tlist = courses.classSubjects(klass) # <table.headers> is a list of cell values in the header row. rsid = table.rowindex - 1 # row tag for sid rsname = table.rowindex # row tag for subject name # Go through the template columns and check if they are needed: for sid in sid2tlist: if sid[0] != '_': sname = courses.subjectName(sid) # Add subject col = table.nextcol() table.write(rsid, col, sid) table.write(rsname, col, sname) # Delete excess columns table.delEndCols(col + 1) ### Add pupils pupils = Pupils(schoolyear) for pdata in pupils.classPupils(klass): row = table.nextrow() table.write(row, 0, pdata['PID']) table.write(row, 1, pdata.name()) table.write(row, 2, pdata['STREAM']) # Delete excess rows table.delEndRows(row + 1) ### Save file table.protectSheet() return table.save()
def openTemplate(tpath): """Return a jinja2 <Template> instance. <tpath> is the path (folder separator '/') to the template file, relative to the main templates folder. The filepath is available as attribute <filename>. """ tpsplit = tpath.split('/') fname = tpsplit.pop() tpdir = Paths.getUserPath('DIR_TEMPLATES') if tpsplit: tpdir = os.path.join(tpdir, *tpsplit) templateLoader = jinja2.FileSystemLoader(searchpath=tpdir) templateEnv = jinja2.Environment(loader=templateLoader, autoescape=True) try: return templateEnv.get_template(fname) except: REPORT.Fail(_NO_TEMPLATE, fname=os.path.join(tpdir, fname))
def ksSheets(schoolyear, manager, date): courses = CourseTables(schoolyear) tidmap = { tid: courses.teacherData.getTeacherName(tid) for tid in courses.teacherData } klasses = [] for k in courses.classes(): klass = Klass(k) sidmap = {} sid2tids = courses.filterText(klass) for sid, tids in sid2tids.items(): if tids.TEXT: if not tids: tids = [_nn] for tid in tids: try: sidmap[sid].add(tid) except: sidmap[sid] = {tid} lines = [] for sid, tids in sidmap.items(): sname = courses.subjectName(sid) for tid in tids: lines.append((sname, tidmap[tid])) klasses.append((klass.klass, lines)) tpdir = Paths.getUserPath('DIR_TEXT_REPORT_TEMPLATES') templateLoader = jinja2.FileSystemLoader(searchpath=tpdir) templateEnv = jinja2.Environment(loader=templateLoader, autoescape=True) tpfile = 'summary-classes.html' try: template = templateEnv.get_template(tpfile) except: REPORT.Fail(_NOTEMPLATE, path=os.path.join(tpdir, tpfile)) source = template.render(year=printSchoolYear(schoolyear), manager=manager, date=Dates.dateConv(date), klasses=klasses) html = HTML(string=source) pdfBytes = html.write_pdf() return pdfBytes
def test_02(): """ Initialise PUPILS table from "old" raw data (creation from scratch, no pre-existing table). """ db = DB(_testyear, 'RECREATE') fpath = os.path.join(Paths.getYearPath(_testyear, 'DIR_SCHOOLDATA'), '_test', 'pupil_data_0_raw') REPORT.Test( "Initialise with raw pupil data for school-year %d from:\n %s" % (_testyear, fpath)) rpd = readRawPupils(_testyear, fpath) for klass in sorted(rpd): REPORT.Test("\n +++ Class %s" % klass) for row in rpd[klass]: REPORT.Test(" --- %s" % repr(row)) updateFromRaw(_testyear, rpd) db.renameTable('PUPILS', 'PUPILS0') db.deleteIndexes('PUPILS0') REPORT.Test("Saved in table PUPILS0")
def index(): class YearForm(FlaskForm): YEAR = SelectField("Schuljahr", coerce=int) schoolyear = session['year'] form = YearForm() form.YEAR.choices = [(y, y) for y in Paths.getYears()] if form.validate_on_submit(): # POST y = form.YEAR.data if y != schoolyear: schoolyear = y session['year'] = y flash("Schuljahr auf %d gesetzt." % y) # GET form.YEAR.default = schoolyear return render_template(os.path.join(_BPNAME, 'index.html'), form=form, heading=_HEADING)
def readHols (schoolyear): """Return a <set> of <datetime.date> instances for all valid dates in the holidays file (configuration item "HOLIDAYS"). The dates are in isoformat (YYYY-MM-DD), but also MM-DD is acceptable, in which case the year will be added automatically (from the current school year). """ deltaday = datetime.timedelta (days=1) kalinfo = ConfigFile (Paths.getYearPath (schoolyear, 'FILE_HOLIDAYS')) hols = set () for d0 in kalinfo.SINGLE_DAYS.split ('|'): d = getDate (schoolyear, d0, dateformat=False) if d: hols.add (d) else: raise RuntimeError ("Date Error") for r0 in kalinfo.RANGES.split ('|'): try: d01, d02 = kalinfo [r0].split ('|') except: REPORT.Fail ("Ungültige Ferienzeit: %s" % r0) d1 = getDate (schoolyear, d01, dateformat=False) d2 = getDate (schoolyear, d02, dateformat=False) if (not d1) or (not d2): raise RuntimeError ("Date Error") if d1 >= d2: REPORT.Fail (("Ungültige Ferienangabe: %s" "Startdatum ist nach dem Enddatum") % item) continue while True: hols.add (d1) d1 += deltaday if d1 > d2: break return hols