Esempio n. 1
0
def klassview(klass):
    _klass = Klass(klass)
    form = DateForm()
    # Set date
    schoolyear = session['year']
    if form.validate_on_submit():
        # POST
        _d = form.getDate()
        pids = request.form.getlist('Pupil')
        if pids:
            pdfBytes = makeSheets(schoolyear, _d, _klass, pids=pids)
            return send_file(io.BytesIO(pdfBytes),
                             attachment_filename='Mantel_%s.pdf' % _klass,
                             mimetype='application/pdf',
                             as_attachment=True)
        flash("Keine Schüler gewählt", "Warning")

    # GET
    form.defaultIssueDate(schoolyear)
    p = Pupils(schoolyear)
    pdlist = p.classPupils(_klass)
    return render_template(os.path.join(_BPNAME, 'text_cover_klass.html'),
                           form=form,
                           heading=_HEADING,
                           klass=str(_klass),
                           pupils=[(pd['PID'], pd.name()) for pd in pdlist])
Esempio n. 2
0
def pupilview(klass, pid):
    _klass = Klass(klass)
    schoolyear = session['year']
    template = getTextTemplate('Mantelbogen', _klass)
    tags = getTemplateTags(template)
    _fields = dict(pupilFields(tags))
    fields = [(f0, f1) for f0, f1 in CONF.TABLES.PUPILS_FIELDNAMES.items()
              if f0 in _fields]
    form = DateForm()
    if form.validate_on_submit():
        # POST
        _d = form.getDate()
        pupil = SimpleNamespace(**{f: request.form[f] for f, _ in fields})
        pdfBytes = makeOneSheet(schoolyear, _d, _klass, pupil)
        return send_file(io.BytesIO(pdfBytes),
                         attachment_filename='Mantel_%s.pdf' %
                         sortingName(pupil.FIRSTNAMES, pupil.LASTNAME),
                         mimetype='application/pdf',
                         as_attachment=True)
    # GET
    form.defaultIssueDate(schoolyear)
    p = Pupils(schoolyear)
    try:
        pdlist = p.classPupils(_klass)
        pdata = pdlist.pidmap[pid]
        pupil = {f: (fname, pdata[f]) for f, fname in fields}
    except:
        abort(404)
    return render_template(os.path.join(_BPNAME, 'text_cover_pupil.html'),
                           form=form,
                           heading=_HEADING,
                           klass=str(_klass),
                           pupil=pupil)
Esempio n. 3
0
def klassview(klass):
    form = DateForm()
    if form.validate_on_submit():
        # POST
        _d = form.dateofissue.data.isoformat()
        pdfBytes = makeSheets(
            _schoolyear,
            _d,
            klass,
            #TODO check list not empty ...
            pids=request.form.getlist('Pupil'))
        return send_file(io.BytesIO(pdfBytes),
                         attachment_filename='Mantel_%s.pdf' % klass,
                         mimetype='application/pdf',
                         as_attachment=True)
    # GET
    p = Pupils(_schoolyear)
    pdlist = p.classPupils(klass)
    klasses = [k for k in p.classes() if k >= '01' and k < '13']
    return render_template('text_cover_klass.html',
                           form=form,
                           schoolyear=str(_schoolyear),
                           klass=klass,
                           klasses=klasses,
                           pupils=[(pd['PID'], pd.name()) for pd in pdlist])
Esempio n. 4
0
def pupilview(klass, pid):
    fields = pupilFields(klass)
    form = DateForm()
    if form.validate_on_submit():
        # POST
        _d = form.dateofissue.data.isoformat()
        pupil = SimpleNamespace(**{f: request.form[f] for f, _ in fields})
        pdfBytes = makeOneSheet(_schoolyear, _d, klass, pupil)
        return send_file(io.BytesIO(pdfBytes),
                         attachment_filename='Mantel_%s.pdf' %
                         sortingName(pupil.FIRSTNAMES, pupil.LASTNAME),
                         mimetype='application/pdf',
                         as_attachment=True)
    # GET
    p = Pupils(_schoolyear)
    pdlist = p.classPupils(klass)
    pupils = []
    for pdata in pdlist:
        _pid = pdata['PID']
        pupils.append((_pid, pdata.name()))
        if _pid == pid:
            pupil = {f: (fname, pdata[f]) for f, fname in fields}
    return render_template('text_cover_pupil.html',
                           form=form,
                           schoolyear=str(_schoolyear),
                           klass=klass,
                           pupil=pupil,
                           pupils=pupils)
Esempio n. 5
0
def migratePupils (schoolyear):
    """Read the pupil data from the previous year and build a preliminary
    database table for the current (new) year, migrating the class
    names according to <CONF.MISC.MIGRATE_CLASS>
    """
    # Get pupil data from previous year
    pdb = Pupils (schoolyear-1)
    # Maximum year number for various streams:
    maxyear = {}
    try:
        for x in CONF.MISC.STREAM_MAX_YEAR:
            k, v = x.split (':')
            maxyear [k] = v
    except:
        REPORT.Fail (_BAD_STREAM_MAX_YEAR, val=x)
    rows = []
    for c_old in pdb.classes ():
        # Increment the year part of the class name
        try:
            cnum = int (c_old [:2]) + 1
            ctag = c_old [2:]
        except:
            REPORT.Fail (_BADCLASSNAME, klass=c_old)
        c_new = '%02d%s' % (cnum, ctag)
        for prow in pdb.classPupils (c_old):
            left = False
            if prow ['EXIT_D']:
                # If there is an exit date, assume the pupil has left.
                left = True

            else:
                try:
                    mxy = maxyear [prow ['STREAM']]
                except:
                    mxy = maxyear ['']
                if cnum > int (mxy):
                    left = True

            if left:
                REPORT.Info (_PUPIL_LEFT, klass=c_old, name=prow.name ())
                continue

            prow ['CLASS'] = c_new
            rows.append (prow)

    # Create the database table PUPILS from the loaded pupil data.
    db = DB (schoolyear, flag='CANCREATE')
    # Use (CLASS, PSORT) as primary key, with additional index on PID.
    # This makes quite a small db (without rowid).
    db.makeTable2 ('PUPILS', PupilData.fields (), data=rows,
            force=True,
            pk=('CLASS', 'PSORT'), index=('PID',))
Esempio n. 6
0
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)
Esempio n. 7
0
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)
Esempio n. 8
0
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()
Esempio n. 9
0
def pupils(klass):
    """View: select pupil from given school-class (single report generation).
    """
    try:
        dfile = session.pop('download')
    except:
        dfile = None
    schoolyear = session['year']
    # Collect list of pupils for this school-class.
    pupils = Pupils(schoolyear)
    # List the classes with the highest first, as these are more
    # likely to need grades.
    _klass = Klass(klass)
    plist = pupils.classPupils(_klass)
    return render_template(os.path.join(_BPNAME, 'pupils.html'),
                           heading=_HEADING,
                           klass=_klass.klass,
                           pupils=plist,
                           dfile=dfile)
Esempio n. 10
0
def db2grades(schoolyear, term, klass, checkonly=False):
    """Fetch the grades for the given school-class/group, term, schoolyear.
    Return a list [(pid, pname, {subject -> grade}), ...]
    <klass> is a <Klass> instance, which can include a list of streams
    (including '_' for pupils without a stream). If there are streams,
    only grades for pupils in one of these streams will be included.
    """
    slist = klass.streams
    plist = []
    # Get the pupils from the pupils db and search for grades for these.
    pupils = Pupils(schoolyear)
    db = DB(schoolyear)
    for pdata in pupils.classPupils(klass):
        # Check pupil's stream if there is a stream filter
        pstream = pdata['STREAM']
        if slist and (pstream not in slist):
            continue
        pid = pdata['PID']
        gdata = db.select1('GRADES', PID=pid, TERM=term)
        if gdata:
            gstring = gdata['GRADES'] or None
            if gstring:
                if gdata['KLASS'] != klass.klass or gdata['STREAM'] != pstream:
                    # Pupil has switched klass and/or stream.
                    # This can only be handled via individual view.
                    gstring = None
        else:
            gstring = None
        if gstring and not checkonly:
            try:
                gmap = grades2map(gstring)
            except ValueError:
                REPORT.Fail(_BAD_GRADE_DATA, pid=pid, term=term)
            plist.append((pid, pdata.name(), gmap))
        else:
            plist.append((pid, pdata.name(), gstring))
    return plist
Esempio n. 11
0
def makeSheets(schoolyear, date, klass, pids=None):
    """
    <schoolyear>: year in which school-year ends (int)
    <data>: date of issue ('YYYY-MM-DD')
    <klass>: a <Klass> instance for the school-class
    <pids>: a list of pids (must all be in the given klass), only
        generate reports for pupils in this list.
        If not supplied, generate reports for the whole klass.
    """
    pupils = Pupils(schoolyear)
    plist = pupils.classPupils(klass)
    if pids:
        pall = plist
        pset = set(pids)
        plist = []
        for pdata in pall:
            try:
                pset.remove(pdata['PID'])
            except KeyError:
                continue
            plist.append(pdata)
        if pset:
            REPORT.Bug(_PUPILSNOTINCLASS, pids=', '.join(pset), klass=klass)

    template = getTextTemplate('Mantelbogen', klass)
    source = template.render(SCHOOLYEAR=printSchoolYear(schoolyear),
                             DATE_D=date,
                             todate=Dates.dateConv,
                             pupils=plist,
                             klass=klass)

    if plist:
        html = HTML(string=source, base_url=os.path.dirname(template.filename))
        pdfBytes = html.write_pdf(font_config=FontConfiguration())
        return pdfBytes
    else:
        REPORT.Fail(_NOPUPILS)
Esempio n. 12
0
def makeReports(schoolyear, term, klass, date, pids=None):
    """Build a single file containing reports for the given pupils.
    This only works for groups with the same report type and template.
    <term> is the term.
    <date> is the date of issue ('YYYY-MM-DD').
    <pids>: a list of pids (must all be in the given klass), only
        generate reports for pupils in this list.
        If not supplied, generate reports for the whole klass/group.
    <klass> is a <Klass> instance: it can be a just a school-class,
    but it can also have a stream, or list of streams.
    """
    # <db2grades> returns a list: [(pid, pname, grade map), ...]
    # <grades>: {pid -> (pname, grade map)}
    grades = {
        pid: (pname, gmap)
        for pid, pname, gmap in db2grades(schoolyear, term, klass)
    }
    pupils = Pupils(schoolyear)
    pall = pupils.classPupils(klass)  # list of data for all pupils
    # If a pupil list is supplied, select the required pupil data.
    # Otherwise use the full list.
    if pids:
        pset = set(pids)
        plist = []
        for pdata in pall:
            try:
                pset.remove(pdata['PID'])
            except KeyError:
                continue
            plist.append(pdata)
        if pset:
            REPORT.Bug(_PUPILS_NOT_IN_CLASS_STREAM,
                       pids=', '.join(pset),
                       ks=klass)
    else:
        plist = pall

    ### Get a tag mapping for the grade data of each pupil
    # Get the name of the relevant configuration file in folder GRADES:
    grademap = klass.match_map(CONF.MISC.GRADE_SCALE)
    # <GradeReportData> manages the report template, etc.:
    # Get the report type from the term and klass/stream
    rtype = klass.match_map(CONF.GRADES.REPORT_TEMPLATES['_' + term])
    reportData = GradeReportData(schoolyear, rtype, klass)
    pmaplist = []
    for pdata in plist:
        pid = pdata['PID']
        pname, gmap = grades[pid]  # get pupil name and grade map
        # Build a grade mapping for the tags of the template:
        pdata.grades = reportData.getTagmap(gmap, pname, grademap)
        pmaplist.append(pdata)
        # Update grade database
        updateGradeReport(schoolyear, pid, term, date=date, rtype=rtype)

    ### Generate html for the reports


# Testing:
#    n = 0  # with change below, just generate nth of list
#    print("§§§", pmaplist[n])
    source = reportData.template.render(
        report_type=rtype,
        SCHOOLYEAR=printSchoolYear(schoolyear),
        DATE_D=date,
        todate=Dates.dateConv,
        STREAM=printStream,
        pupils=pmaplist,
        #            pupils = [pmaplist[n]],
        klass=klass)
    # Convert to pdf
    if not plist:
        REPORT.Fail(_NOPUPILS)
    html = HTML(string=source,
                base_url=os.path.dirname(reportData.template.filename))
    pdfBytes = html.write_pdf(font_config=FontConfiguration())
    REPORT.Info(_MADEKREPORTS, ks=klass)
    return pdfBytes
Esempio n. 13
0
def makeGradeTable(schoolyear, term, klass, title):
    """Make a grade table for the given school-class/group.
    <klass> is a <Klass> instance.
    <term> is a string.
    """
    # Info concerning grade tables:
    gtinfo = CONF.GRADES.GRADE_TABLE_INFO
    # Determine table template
    t = klass.match_map(gtinfo.GRADE_TABLE_TEMPLATE)
    if not t:
        REPORT.Fail(_NO_TEMPLATE, ks=klass)
    template = Paths.getUserPath('FILE_GRADE_TABLE_TEMPLATE').replace('*', t)
    table = KlassMatrix(template)

    ### Insert general info
    table.setTitle(title)
    # "Translation" of info items:
    kmap = CONF.TABLES.COURSE_PUPIL_FIELDNAMES
    info = (
        (kmap['SCHOOLYEAR'], str(schoolyear)),
        (kmap['CLASS'], klass.klass),
        (kmap['TERM'], term)
    )
    table.setInfo(info)

    ### Manage subjects
    courses = CourseTables(schoolyear)
    sid2tlist = courses.classSubjects(klass)
#    print ("???1", list(sid2tlist))
    # Go through the template columns and check if they are needed:
    colmap = {}
    col = _FIRSTSIDCOL
    for k in table.headers[_FIRSTSIDCOL:]:
        if k:
            if k in sid2tlist:
                colmap[k] = col
            elif k == _UNUSED:
                table.hideCol(col, True)
            else:
                # Handle extra _tags
                klassmap = gtinfo.get(k)
                if klassmap:
                    m = klass.match_map(klassmap)
                    if m and term in m.split():
                        colmap[k] = col
                    else:
                        table.hideCol(col, True)
                else:
                    table.hideCol(col, True)
        col += 1
#    print("???COLMAP:", colmap)

    ### Add pupils
    pupils = Pupils(schoolyear)
    for pdata in pupils.classPupils(klass):
        row = table.nextrow()
        pid = pdata['PID']
        table.write(row, 0, pid)
        table.write(row, 1, pdata.name())
        table.write(row, 2, pdata['STREAM'])
        # Add existing grades
        gd = getGradeData(schoolyear, pid, term)
#        print("\n???", pid, gd)
        if gd:
            grades = gd['GRADES']
            if grades:
                for k, v in grades.items():
                    try:
                        col = colmap[k]
                    except KeyError:
#                        print("!!! excess subject:", k)
                        continue
                    if k:
                        if k.startswith('__'):
                            # Calculated entry
                            continue
                        table.write(row, col, v)
    # Delete excess rows
    table.delEndRows(row + 1)

    ### Save file
    table.protectSheet()
    return table.save()
Esempio n. 14
0
def stripTable(schoolyear, term, klass, title):
    """Build a basic pupil/subject table for entering grades.
    <klass> is a <Klass> instance.
    <term> is a string.
     """
    # Info concerning grade tables:
    gtinfo = CONF.GRADES.GRADE_TABLE_INFO

    ### Determine table template (output)
    t = klass.match_map(gtinfo.GRADE_INPUT_TEMPLATE)
    if not t:
        REPORT.Fail(_NO_ITEMPLATE, ks=klass)
    template = Paths.getUserPath('FILE_GRADE_TABLE_TEMPLATE').replace('*', t)
    table = KlassMatrix(template)
    table.setTitle(title)
    table.setInfo([])

    ### Read input table template (for determining subjects and order)
    # Determine table template (input)
    t = klass.match_map(gtinfo.GRADE_TABLE_TEMPLATE)
    if not t:
        REPORT.Fail(_NO_TEMPLATE, ks=klass)
    template0 = Paths.getUserPath('FILE_GRADE_TABLE_TEMPLATE').replace('*', t)
    table0 = KlassMatrix(template0)
    i, x = 0, 0
    for row0 in table0.rows:
        i += 1
        if row0[0] and row0[0] != '#':
            # The subject key line
            break
    # <row0> is the title row.

    ### Manage subjects
    courses = CourseTables(schoolyear)
    sid2tlist = courses.classSubjects(klass)
#    print ("???1", list(sid2tlist))
    # Set klass cell
    rowix = table.rowindex - 1
    table.write(rowix, 0, table.headers[0].replace('*', klass.klass))
    # Go through the template columns and check if they are needed:
    col = 0
    for sid in row0:
        if sid and sid[0] != '_' and sid in sid2tlist:
            sname = courses.subjectName(sid)
            # Add subject
            col = table.nextcol()
            table.write(rowix, 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.name())
        table.write(row, 1, pdata['STREAM'])
    # Delete excess rows
    table.delEndRows(row + 1)

    ### Save file
    table.protectSheet()
    return table.save()
Esempio n. 15
0
def grades2db(schoolyear, gtable, term=None):
    """Enter the grades from the given table into the database.
    <schoolyear> is checked against the value in the info part of the
    table (gtable.info['SCHOOLYEAR']).
    <term>, if given, is only used as a check against the value in the
    info part of the table (gtable.info['TERM']).
    """
    # Check school-year
    try:
        y = gtable.info.get('SCHOOLYEAR', '–––')
        yn = int(y)
    except ValueError:
        REPORT.Fail(_INVALID_YEAR, val=y)
    if yn != schoolyear:
        REPORT.Fail(_WRONG_YEAR, year=y)
    # Check term
    rtag = gtable.info.get('TERM', '–––')
    if term:
        if term != rtag:
            REPORT.Fail(_WRONG_TERM, term=term, termf=rtag)
    # Check klass
    klass = Klass(gtable.info.get('CLASS', '–––'))
    # Check validity
    pupils = Pupils(schoolyear)
    try:
        plist = pupils.classPupils(klass)
        if not plist:
            raise ValueError
    except:
        REPORT.Fail(_INVALID_KLASS, klass=klass)
    # Filter the relevant pids
    p2grades = {}
    p2stream = {}
    for pdata in plist:
        pid = pdata['PID']
        try:
            p2grades[pid] = gtable.pop(pid)
        except KeyError:
            # The table may include just a subset of the pupils
            continue
        p2stream[pid] = pdata['STREAM']
    # Anything left unhandled in <gtable>?
    for pid in gtable:
        REPORT.Error(_UNKNOWN_PUPIL, pid=pid)

#TODO: Sanitize input ... only valid grades?

    # Now enter to database
    if p2grades:
        db = DB(schoolyear)
        for pid, grades in p2grades.items():
            gstring = map2grades(grades)
            db.updateOrAdd('GRADES',
                    {   'KLASS': klass.klass, 'STREAM': p2stream[pid],
                        'PID': pid, 'TERM': rtag, 'REPORT_TYPE': None,
                        'DATE_D': None, 'GRADES': gstring
                    },
                    TERM=rtag,
                    PID=pid
            )
        REPORT.Info(_NEWGRADES, n=len(p2grades),
                klass=klass, year=schoolyear, term=rtag)
    else:
        REPORT.Warn(_NOPUPILS)