Beispiel #1
0
def summary():
    class RadioForm(FlaskForm):
        choice = RadioField(choices=[
            ('teachers','Kontrollblätter für die Lehrer'),
            ('classes','Fach-Lehrer-Zuordnung für die Klassen')
        ], default='teachers')
    form = RadioForm()
    if form.validate_on_submit():
        # POST
        if form.choice.data == 'teachers':
            pdfBytes = tSheets(session['year'],
                            getManager()['name'],
                            Dates.today())
            filename = _TEACHER_TABLE
        elif form.choice.data == 'classes':
            pdfBytes = ksSheets(session['year'],
                            getManager()['name'],
                            Dates.today())
            filename = _KLASS_TABLE
        else:
            pdfBytes = None
        if pdfBytes:
            return send_file(
                io.BytesIO(pdfBytes),
                attachment_filename=filename + '.pdf',
                mimetype='application/pdf',
                as_attachment=True
            )
    # GET
    return render_template(os.path.join(_BPNAME, 'summary.html'),
                            form=form,
                            heading=_HEADING,
                            uplink=url_for('bp_text.index'),
                            uplink_help="Textzeugnis: Startseite")
Beispiel #2
0
 def defaultIssueDate(self, schoolyear):
     db = DB(schoolyear)
     _date = db.getInfo('TEXT_DATE_OF_ISSUE')
     if not _date:
         _date = Dates.getCalendar(schoolyear).get('END')
         if not _date:
             flash(
                 "Schuljahresende ('END') fehlt im Kalender für %d" %
                 schoolyear, 'Error')
             _date = Dates.today()
     self.DATE_D.data = datetime.date.fromisoformat(_date)
Beispiel #3
0
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
Beispiel #4
0
def index():
#    p = Pupils(_schoolyear)
#    klasses = [k for k in p.classes() if k >= '01' and k < '13']
#TODO: Maybe a validity test for text report classes?
#TODO: DATE_D
    return render_template(os.path.join(_BPNAME, 'index.html'),
                            heading=_HEADING,
                            DATE_D=Dates.dateConv(_date),
                            uplink=url_for('index'),
                            uplink_help="Zeugs: Startseite")
Beispiel #5
0
def textCover():
    p = Pupils(_schoolyear)
    klasses = [k for k in p.classes() if k >= '01' and k < '13']
    #TODO: Maybe a validity test for text report classes?
    #TODO: dateofissue
    return render_template(
        'text_cover_entry.html',
        schoolyear=str(_schoolyear),
        dateofissue=Dates.dateConv(_date),
        klasses=klasses)  #['01', '01K', '02', '02K', '03', '03K']
Beispiel #6
0
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
Beispiel #7
0
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
Beispiel #8
0
def readRawPupils(schoolyear, filepath):
    """Read in a table containing raw pupil data for the whole school
    from the given file (ods or xlsx, the file ending can be omitted).
    The names of date fields are expected to end with '_D'. Values are
    accepted in isoformat (YYYY-MM-DD, or %Y-%m-%d for <datetime>) or
    in the format specified for output, config value FORMATTING.DATEFORMAT.
    If a pupil left the school before the beginning of the school year
    (s)he will be excluded from the list built here.
    Build a mapping:
        {classname -> ordered list of <_IndexedDict> instances}
    The ordering of the pupil data is determined by the config file
    TABLES/PUPILS_FIELDNAMES.
    The fields supplied in the raw data are saved as the <fields>
    attribute of the result.
    """
    startdate = Dates.day1(schoolyear)
    # An exception is raised if there is no file:
    table = readDBTable(filepath)

    # Get ordered field list for the table.
    # The config file has: internal name -> table name.
    # All names are converted to upper case to enable case-free matching.
    fieldMap = _getFieldmap()
    # Build a list of the field names which are used
    fields = OrderedDict()

    colmap = []
    # Read the (capitalised) column headers from this line
    h_colix = {h.upper(): colix for h, colix in table.headers.items()}
    datefields = []
    for f, fx in fieldMap.items():
        try:
            colmap.append(h_colix[fx])
            fields[f] = fx
            if f.endswith('_D'):
                datefields.append(f)
        except:
            # Field not present in raw data
            if f == 'PSORT':
                fields[f] = fx
                colmap.append(None)
            else:
                REPORT.Warn(_FIELDMISSING, field=f, path=filepath)

    ### For sorting: use a translation table for non-ASCII characters
    ttable = str.maketrans(dict(CONF.ASCII_SUB))
    classes = UserDict()  # for the results: {class -> [row item]}
    classes.fields = fields
    ### Read the row data
    ntuples = {}
    _IndexedDict.setup(fields)
    dateformat = CONF.FORMATTING.DATEFORMAT
    for row in table:
        rowdata = []
        for col in colmap:
            rowdata.append(None if col == None else row[col])
        pdata = _IndexedDict(rowdata)
        # Check date fields
        for f in datefields:
            val = pdata[f]
            if val:
                try:
                    datetime.date.fromisoformat(val)
                except:
                    try:
                        pdata[f] = datetime.datetime.strptime(
                            val, dateformat).date().isoformat()
                    except:
                        REPORT.Fail(_BAD_DATE, tag=f, val=val, path=filepath)

        ## Exclude pupils who left before the start of the schoolyear
        if pdata['EXIT_D'] and pdata['EXIT_D'] < startdate:
            continue

        ## Name fixing
        firstnames, tv = tvSplitF(pdata['FIRSTNAMES'])
        lastname = pdata['LASTNAME']
        firstname = tvSplitF(pdata['FIRSTNAME'])[0]
        if tv:
            sortname = lastname + ' ' + tv + ' ' + firstname
            pdata['FIRSTNAMES'] = firstnames
            pdata['FIRSTNAME'] = firstname
            pdata['LASTNAME'] = tv + ' ' + lastname
        else:
            sortname = lastname + ' ' + firstname
        pdata['PSORT'] = sortname.translate(ttable)

        klass = pdata['CLASS']
        # Normalize class name
        try:
            if not klass[0].isdigit():
                raise NameError
            if len(klass) == 1:
                k = '0' + klass
                pdata['CLASS'] = k
            else:
                if klass[1].isdigit():
                    k = klass
                else:
                    k = '0' + klass
                    pdata['CLASS'] = klass
                k = klass if klass[1].isdigit() else '0' + klass
                if not (len(k) == 2 or k[2:].isalpha()):
                    raise NameError
        except:
            REPORT.Fail(_BADCLASSNAME, name=klass)
        try:
            classes[k].append(pdata)
        except:
            classes[k] = [pdata]

    for klass in classes:
        # alphabetical sorting
        classes[klass].sort(key=lambda pd: pd['PSORT'])

    return classes