<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#!/usr/bin/env python3

import sys, time, socket
from ldif import LDIFParser, LDIFWriter
import LaTexAccents as TeX
import csv
from alumnizer_config import *

TeXConverter = TeX.AccentConverter()

# ------------------------------------------------------------------------
# CSV Readers
# ------------------------------------------------------------------------

def importTeXCodes(teXCodeFn):
    teXCodes = {}
    with open(teXCodeFn, encoding="UTF8") as __csvfile:
        __reader = csv.reader(__csvfile, delimiter=",", quotechar='"')
        __cnt = 0
        for __row in __reader:
            if not __row:
                continue
            __cnt += 1
            if __cnt==1:
                # Skip header line
                continue
            if __row[0] not in teXCodes.keys():
                teXCodes[__row[0]] = ""
            if len(__row)==2:
                key = __row[0].strip()
                replacement = __row[1].strip()
                teXCodes[key] = replacement
            else:
                print("Found invalid replacement in %r, line %d"%(teXCodeFn, __cnt), file=sys.stderr)

    return teXCodes

def importSortingSpecials(sortingSpecialsFn):
    sortingSpecials = {}
    with open(sortingSpecialsFn, encoding="UTF8") as __csvfile:
        __reader = csv.reader(__csvfile, delimiter=",", quotechar='"')
        __cnt = 0
        for __row in __reader:
            if not __row:
                continue
            __cnt += 1
            if __cnt==1:
                # Skip header line
                continue
            if __row[0] not in sortingSpecials.keys():
                sortingSpecials[__row[0]] = ""
            if len(__row)==2:
                key = __row[0].strip()
                replacement = __row[1].strip()
                sortingSpecials[key] = replacement
            else:
                print("Found invalid replacement in %r, line %d"%(sortingSpecialsFn, __cnt), file=sys.stderr)

    return sortingSpecials

def importdcFGCodes(dcFGCodeFn):
    dcFGCodes = {}
    with open(dcFGCodeFn, encoding="UTF8") as __csvfile:
        __reader = csv.reader(__csvfile, delimiter=",", quotechar='"')
        __cnt = 0
        for __row in __reader:
            if not __row:
                continue
            __cnt += 1
            if __cnt==1:
                # Skip header line
                continue
            if __row[0] not in dcFGCodes.keys():
                dcFGCodes[__row[0]] = ""
            if len(__row)==6:
                sortcode_en = __row[0].strip()
                sortcode_de = __row[1].strip()
                key = __row[2].strip()
                replacement_en = __row[3].strip()
                replacement_de = __row[4].strip()
                url = __row[5].strip()
                dcFGCodes[key] = {"code": key,
                                  "sortcode_en": sortcode_en,
                                  "sortcode_de": sortcode_de,
                                  "en": replacement_en,
                                  "de": replacement_de,
                                  "url": url}
            else:
                print("Found invalid replacement in %r, line %d"%(dcFGCodeFn, __cnt), file=sys.stderr)
                print("read value is: %r"%(__row,), file=sys.stderr)
    #print("dcFGCodes keys:", dcFGCodes.keys(), file=sys.stderr)
    return dcFGCodes

# ------------------------------------------------------------------------
# Handling ldif
# ------------------------------------------------------------------------

class alumniReader(LDIFParser):

    def __init__(self, input, output):
        LDIFParser.__init__(self, input)
        #self.writer = LDIFWriter(output)
        self.output = output
        #self.output = sys.stderr
        if self.output:
            print("LDIFParser.__init__", file=self.output)
        self.keylist = []
        self.descriptionNames = []
        self.content = {}

    def handle(self, dn, entry):
        entrydict = {}
        sn = ""
        fn = ""
        if self.output:
            print("-"*80, file=self.output)
            print("DN: %r"%dn, file=self.output)
            print("-"*80, file=self.output)
        kl = sorted(entry.keys())
        longest = 0
        for k in kl:
            longest = max(longest, len(k))
        for k in kl:
            if k not in self.keylist:
                self.keylist.append(k)
            dk = k.ljust(longest)
            #print("%s: %r"%(dk, entry[k]), file=self.output)
            first = True
            elements = []
            longestname = 0
            if type(entry[k]==type([])):
                entrydict[k] = ""
                for e in entry[k]:
                    entrydict[k] += e.decode()+", "
                entrydict[k] = TeXConverter.decode_Tex_Accents(entrydict[k][:-2],
                                                               utf8_or_ascii=1)
            else:
                entrydict[k] = TeXConverter.decode_Tex_Accents(entry[k].encode("utf-8"),
                                                               utf8_or_ascii=1)
            for tc in teXCodes.keys():
                entrydict[k] = entrydict[k].replace(tc, teXCodes[tc])
            if k=="sn":
                sn = entrydict[k].upper()
            elif k=="givenName":
                fn = entrydict[k].upper()
            elif k=="dcFG":
                pass
            for element in entry[k]:
                if b"=" in element and k!="dcRelation":
                    name,value = element.split(b"=")
                else:
                    name = b""
                    value = element
                longestname = max(longestname, len(name))
                elements.append((name, value))
                if name and name.decode() not in self.descriptionNames:
                    self.descriptionNames.append(name.decode())
                if name:
                    entrydict["description."+str(name.decode())] = value.decode()
            for (name, value) in sorted(elements):
                if first:
                    if name:
                        if self.output:
                            print("%s: %s = %s"%(dk, name.ljust(longestname).decode(), value.decode()), file=self.output)
                    else:
                        if self.output:
                            print("%s: %s"%(dk, value.decode()), file=self.output)
                    first = False
                else:
                    if self.output:
                        if name:
                            print(" "*longest+"  %s = %s"%(name.ljust(longestname).decode(), value.decode()), file=self.output)
                        else:
                            print(" "*longest+"  %s"%(value.decode()), file=self.output)
        if entrydict:
            contentkey = "%s.%s"%(sn, fn)
            for k in sortingSpecials:
                contentkey = contentkey.replace(k, sortingSpecials[k])
            if "description.consentinfo" in entrydict:
                if entrydict["description.consentinfo"]=="y":
                    entrydict["contentkey"] = contentkey
                    self.content[contentkey] = entrydict
        if self.output:
            print("-"*80+"\n", file=self.output)

def __populatePdict(keylist, entry):
    pdict = {}
    for k in keylist:
        if k in entry:
            pdict[k] = entry[k]
        else:
            pdict[k] = ""
    return pdict

positions = []
def renderEntry(entry):
    classes = []
    position = entry.get("description.position")
    if position:
        classes.append("alumni_dcfg_%s"%position)
        #if position not in positions:
        #    print(position, file=sys.stderr)
        #    positions.append(position)
    firstletter = entry.get("contentkey")[0]
    classes.append("alumni_letter_%s"%firstletter)
    for fDE in filterableDescriptionEntries:
        if entry.get("description."+fDE):
            classes.append("alumni_dcfg_"+fDE)
    dcFGres = []
    if "dcFG" in entry:
        elemlist = entry["dcFG"].split(",")
        cnt = 0
        for el in elemlist:
            cnt += 1
            if cnt&gt;1:
                dcFGres.append(", ")
            els = el.split(":")
            if len(els)==1:
                code = el
            else:
                code = els[1]
            if code in dcFGCodes:
                if dcFGCodes[code]:
                    if dcFGCodes[code]["url"]:
                        dcFGres.append("""&lt;a href="#" onclick="gotoUrl('%(url)s')"&gt;
                                            &lt;span class="dcfgtext%(code)s"&gt;
                                              %(en)s
                                            &lt;/span&gt;&lt;/a&gt;"""%dcFGCodes[code])
                    else:
                        dcFGres.append("""&lt;span class="dcfgtext%(code)s"&gt;
                                            %(en)s
                                          &lt;/span&gt;"""%dcFGCodes[code])
                    classes.append("alumni_dcfg_"+code)
            else:
                dcFGres.append("&lt;span style='color: red'&gt;???%s???&lt;/span&gt;"%code)


    res = ["""&lt;div class='col-sm-8 mabox %s'&gt;
                &lt;p class='underline' style='width: 100%%'&gt;"""%(" ".join(classes))]

    pdict = __populatePdict(("title", "sn", "givenName"), entry)
    if "dcRelation" in entry:
        pdict["url"] = entry["dcRelation"]
        res.append("""&lt;span style="font-weight: bold"&gt;
                        &lt;a href="%(url)s" target="_blank"&gt;
                          %(title)s %(sn)s, %(givenName)s
                        &lt;/a&gt;
                      &lt;/span&gt;&lt;br&gt;"""%pdict)
    else:
        res.append("""&lt;span style="font-weight: bold"&gt;
                        %(title)s %(sn)s, %(givenName)s
                        &lt;/span&gt;&lt;br&gt;"""%pdict)
    if "description.consentduration" in entry:
        if entry["description.consentduration"] == "y":
            pdict = __populatePdict(("description.atwiasfrom", "description.atwiasto"), entry)
            res.append("@WIAS %(description.atwiasfrom)s-%(description.atwiasto)s"%pdict)
    res.append("&lt;br&gt;")
    res += dcFGres
    res.append("&lt;br&gt;")
    if "dcCinst" in entry:
        res.append(entry["dcCinst"])

    res.append("&lt;br&gt;")
    res.append("&lt;/p&gt;&lt;/div&gt;")

    # Just for debugging. remove in production!
    if debugging:
        res.append("&lt;!-- ")
        for k in entry:
            res.append("%s: %s\n"%(k, entry[k]))
        res.append("--&gt;")

    res = "\n".join(res)
    res = res.replace("\n,", ", ")
    return res


teXCodes = importTeXCodes(teXCodeFn)
sortingSpecials = importSortingSpecials(sortingSpecialsFn)
dcFGCodes = importdcFGCodes(dcFGCodesFn)

def usage():
    print("Usage: %s [-cf] filename.ldif"%(sys.argv[0]), file=sys.stderr)
    print("       -cf    use connected filters", file=sys.stderr)
    sys.exit()

def main():
    flag = ""
    if len(sys.argv)&lt;2:
        usage()
    if len(sys.argv)&gt;2:
        flag = sys.argv[1]
        if flag!="-cf":
            flag = ""
        fn = sys.argv[2]
        if flag!="-cf":
            usage()
    else:
        fn = sys.argv[1]
    connectedFilters = False
    if flag=="-cf":
        connectedFilters = True
    try:
        infile = open(fn, "rb")
    except Exception as e:
        print("Error opening %r"%sys.argv[1], file=sys.stderr)
        print(repr(e), file=sys.stderr)
        sys.exit(1)
    print("input files read, init parser...", file=sys.stderr)
    parser = alumniReader(infile, sys.stderr)
    print("parsing...", file=sys.stderr)
    parser.parse()
    print("Keys found: "+", ".join(sorted(parser.keylist)), file=sys.stderr)
    print("Names found in descriptions: "+", ".join(sorted(parser.descriptionNames)), file=sys.stderr)
    kl = sorted(parser.content.keys())
    #print("Content keys: %r"%kl)
    with open(includeJSFn, "r") as f:
        jscode = f.read()
    with open(includeCSSFn, "r") as f:
        csscode = f.read()
    with open(templateFn, "r") as f:
        template = f.read()
        template = template.replace("%", "%%").replace("%%(maincontent)s", "%(maincontent)s")
        #result = ["&lt;link rel='stylesheet' href='equal-height-columns.css' /&gt;"]
        result = []
        t = time.localtime(time.time())
        result.append("""
&lt;!-- Running on:     %s (%s)
     Commandline:    %s
     Execution time: %s
--&gt;
                      """%(socket.gethostname(), socket.getfqdn(),
                           " ".join(sys.argv),
                           "%04d-%02d-%02d, %02d:%02d:%02d"%(t.tm_year,
                                                             t.tm_mon,
                                                             t.tm_mday,
                                                             t.tm_hour,
                                                             t.tm_min,
                                                             t.tm_sec)))
        if csscode:
            result.append("&lt;!-- include from %s --&gt;"%includeCSSFn)
            result.append("&lt;style&gt;\n%s\n&lt;/style&gt;\n"%csscode)
            result.append("&lt;!-- end of include from %s --&gt;"%includeCSSFn)
        if jscode:
            result.append("&lt;!-- include from %s --&gt;"%includeJSFn)
            result.append("&lt;script type='text/javascript'&gt;")
            if connectedFilters:
                result.append("\nvar connectedFilters = true;\n")
            else:
                result.append("\nvar connectedFilters = false;\n")
            result.append(jscode)
            result.append("&lt;/script&gt;\n")
            result.append("&lt;!-- end of include from %s --&gt;"%includeJSFn)
        oldsn = ""
        dcFGlist_de = []
        dcFGlist_en = []
        dcFGdone = []
        for k in kl:
            entry = parser.content[k]
            newsn = entry["sn"][0]
            for k in sortingSpecials:
                newsn = newsn.replace(k, sortingSpecials[k])
            newsn = newsn.upper()
            if newsn!=oldsn:
                if oldsn:
                    result.append("&lt;/div&gt;")
                result.append("&lt;span class='jumptarget' id='index-%s'&gt;&lt;/span&gt;"%newsn)
                result.append("""&lt;h3 class='underline alumni_big_letter
                                            alumni_big_letter_%s alumni_letter_%s'
                                     style="clear: both;"&gt;
                                   &lt;span class='big-letter'&gt;%s&lt;/span&gt;
                                 &lt;/h3&gt;"""%(newsn, newsn, newsn))
                result.append("&lt;div class='row' style='display: flex; flex-wrap: wrap'&gt;")
                oldsn = newsn
            result.append(renderEntry(entry))
            fg = entry.get("dcFG", "")
            if fg:
                if "," in fg:
                    fgl = fg.split(",")
                else:
                    fgl = [fg]
                for fge in fgl:
                    if ":" in fge:
                        (pos, code) = fge.split(":")
                    else:
                        pos = 0
                        code = fge
                    if code not in dcFGdone:
                        dcFGdone.append(code)
                        if code not in dcFGCodes:
                            print("Unknown code %r in ldap file"%code, file=sys.stderr)
                        else:
                            dcFGlist_de.append((dcFGCodes[code]["sortcode_de"], code))
                            dcFGlist_en.append((dcFGCodes[code]["sortcode_en"], code))

        if kl:
            result.append("&lt;/div&gt;")
        for key in dcFGCodes:
            entry = dcFGCodes[key]
            if not entry:
                continue
            if key not in dcFGdone:
                if entry["sortcode_de"]:
                    if entry["code"].startswith("header"):
                        dcFGlist_de.append((entry["sortcode_de"], key))
                        dcFGlist_en.append((entry["sortcode_en"], key))
                        dcFGdone.append(key)
                    elif entry["code"] in filterableDescriptionEntries:
                        dcFGlist_de.append((entry["sortcode_de"], key))
                        dcFGlist_en.append((entry["sortcode_en"], key))
                        dcFGdone.append(key)
        dcFGlist_de = sorted(dcFGlist_de)
        dcFGlist_en = sorted(dcFGlist_en)
        #print(sorted(dcFGlist_de), file=sys.stderr)
        result.append("&lt;!-- filter setup --&gt;")
        result.append("&lt;script type='text/javascript'&gt;\n")
        result.append("dcFGFilters_en = new Array;\n")
        result.append("dcFGFilters_de = new Array;\n")
        for (sortcode, code) in dcFGlist_de:
            if not sortcode or sortcode=="xxx":
                continue
            #print(code, file=sys.stderr)
            result.append('dcFGFilters_de.push(["'+code+'", "'+dcFGCodes[code]["de"]+'"]);\n')
        for (sortcode, code) in dcFGlist_en:
            if not sortcode or sortcode=="xxx":
                continue
            #print(code, file=sys.stderr)
            result.append('dcFGFilters_en.push(["'+code+'", "'+dcFGCodes[code]["en"]+'"]);\n')
        result.append("&lt;/script&gt;")
        html = template%{"maincontent": "\n".join(result)}
        print(html, file=sys.stdout)

if __name__ == "__main__":
    main()</pre></body></html>