Programmieren macht das Leben leichter

In der Schweizer Sonntagszeitung ist ein netter Artikel über einen Informatik-Professor, der fordert, dass Informatik Pflichtfach an den Schulen werden muss.

Ja, es ist wie beim Übertritt von der Agrar- zur Industriegesellschaft, als Begriffe wie Energie und Materie wichtig wurden: Da führte man Physik, Chemie und Biologie an den Gymnasien ein. Heute sind es Begriffe wie Information und Algorithmus, die bestimmend sind, und darum muss die Informatik ins Gymnasium.

Die Argumentation ist bestechend - wir bringen unseren Kindern ja auch in Biologie bei, wie der menschliche Körper funktioniert, ohne dass sie gleich alle Ärzte werden. Ebenso müssen am Ende nicht lauter Programmierer herrauskommen, abe Menschen, die verstehen, wie die Maschinen und Software funktionieren, die immer mehr unser Leben bestimmen. Ich habe hier ja schon vor zweieinhalb Jahren geschrieben: Lernt programmieren!

Neben einem gewissen Verständnis kann Programmieren übrigens auch helfen, kleine Problemchen zu lösen (und es macht Spaß).

Ich habe mich zum Beispiel immer über die Mailflut geärgert und mich gefragt, ob nicht vielleicht noch jemand auf eine Antwort von mir wartet oder ob ich jemandem geschrieben habe, der sich dann gar nicht gemeldet hat. Wäre doch nett, wenn Software mir da ein bisschen auf die Sprünge helfen könnte. Zum Beispiel in dem mein Adressbuch mit meinem Mail-Ein- und -Ausgang verglichen wird. Und wenn ich mich eine bestimmte Zeit nicht mehr bei jemandem gemeldet habe, eine freundliche Erinnerung vom MailButler bekomme.

Adressbuchabfrage etc. ist jetzt ein bisschen komplex, aber einfach mal den eigenen Mail-Ein- und -Ausgang durchschauen und gucken, wie es da aussieht, ist eigentlich eine Kleinigkeit. Das wird kein Programmierkurs, sondern es soll nur zeigen, wie wenig Aufwand das wirklich ist. (Und ich bin mir sicher, dass ich weder besonders effizient noch schön programmiere, aber es funktioniert eben - ich bin Heimwerker-Programmierer.)

Als Programmiersprache habe ich für das Beispiel Python gewählt. Python ist einfach und auf jedem Linux-Rechner eigentlich vorinstaliert, aber auch unter Windows und Mac OS verfügbar.

Im Ergebnis bekomme ich von dem kleinen Programm dann einen Text (bei dem ich die Mailadressen unkenntlich gemacht habe):

XXX xxx.xxx@xxx.edu wartet seit 21 Tagen auf Antwort von Dir zum Thema YYY. Insgesamt Kontakt: 3.

XXX xxx@ifttt.com wartet seit 18 Tagen auf Antwort von Dir zum Thema YYY. Insgesamt Kontakt: 5.

xxx@abc.de wartet seit 11 Tagen auf Antwort von Dir zum Thema ZZZ. Insgesamt Kontakt: 7.

###

XXX xxx@xxx.co.nz hat seit 38 Tagen nicht geantwortet zum Thema YYY. Insgesamt Kontakt: 2.

xxx@xx-xxx.de> hat seit 28 Tagen nicht geantwortet zum Thema ZZZ. Insgesamt Kontakt: 3.

XXX xxx.xxx@xxx.de hat seit 21 Tagen nicht geantwortet zum Thema XXX. Insgesamt Kontakt: 17.

Und wie sieht es aus? Nicht mehr als das hier:

#!/usr/bin/python
# -*- coding: utf-8 -*-

'''
Created on 07.03.2013

@author: andi
'''
import imaplib
import dateutil.parser
import datetime
from  pytz import timezone
from email.parser import HeaderParser
import email.utils
import pickle
import os

wer = []
name = []
recvsubj = []
sentsubj = []
recv = []
sent = []
recvcnt = []
sentcnt = []

user="bla@bla.de" # anpassen
pwd="geheim" # anpassen
inbox="[Gmail]/Alle Nachrichten"
outbox="[Gmail]/Gesendet"

maxCheckDays=365                       # so viele Tage maximal in die Vergangenheit schauen
minInteract = 2                          # nur Mailverkehr zeigen, bei dem mindestens x mal Mails hin und her gingen
minWaitDays = 5                          # erst nach x Tagen ohne Antwort / Aktivität erinnern
showOnlyOpenConversations = True        # nur Konversationen zeigen, bei denen mehr Recv als Sent oder umgekehrt vorhanden sind


def readmails():
    import time
    # wir benutzen ein paar globale Variablen
    # nicht unbedingt schön, aber einfach
    global wer
    global name    
    global recvsubj
    global sentsubj
    global recv 
    global sent
    global recvcnt
    global sentcnt    

    # Verbindung mit dem Mailkonto herstellen
    conn = imaplib.IMAP4_SSL('imap.gmail.com')
    conn.login(user,pwd)
    # und die Eingangsmailbox auswählen - von hier wollen wir die Mails lesen
    conn.select(inbox)

    # Falls das Programm schon mal gelaufen ist, dann laden wir die bestehenden Daten
    # damit wir nicht immer alle Mails von Anfang an bearbeiten müssen
    if os.path.exists("./list_wer.p"):  
        wer=pickle.load(open("./list_wer.p", "rb" ))
        name=pickle.load(open("./list_name.p", "rb" ))
        recvsubj=pickle.load(open("./list_recvsubj.p", "rb" ))
        sentsubj=pickle.load(open("./list_sentsubj.p", "rb" ))
        recv=pickle.load(open("./list_recv.p", "rb" ))
        sent=pickle.load(open("./list_sent.p", "rb" ))
        recvcnt=pickle.load(open("./list_recvcnt.p", "rb" ))
        sentcnt=pickle.load(open("./list_sentcnt.p", "rb" ))
        mydate=pickle.load(open("./mydate.p", "rb" ))

        # Vor wie vielen Tagen wurden das letzte mal Mails eingelesen?
        # So viele Tage sollten wir jetzt aktuell einlesen
        checkdays=(datetime.date.today()-mydate).days
        # Falls heute schon einmal eingelesen wurde, nimm den kompletten Tag nochmal
        # sicher ist sicher
        if checkdays<1:
            checkdays=1
    else:
    # Wenn noch keine Daten vorhanden sind, ließ von Anfang an ein -
    # wobei Anfang die maximale Anzahl von Tagen bedeutet, die oben festgelegt wurde
        checkdays=maxCheckDays

    # Wir beginnen mit dem Einlesen

    # Das Datum für unsere Suchabfrage in der Mailbox
    date = (datetime.date.today() - datetime.timedelta(checkdays)).strftime("%d-%b-%Y")

    # So viele Mails müssen wir jetzt bearbeiten
    print "Anzahl Mails: "+str(len(conn.search(None, 'ALL','(SENTSINCE {date})'.format(date=date))[1][0].split()))

    msg_cnt=0

    # In der Schleife werden nach und nach die Mails eingelesen und bearbeitet
    for msg_num in conn.search(None, 'ALL','(SENTSINCE {date})'.format(date=date))[1][0].split(): 

        # wir zählen mal mit
        msg_cnt=msg_cnt+1
        print msg_cnt

        # Hol Dir eine Mail
        data = conn.fetch(str(msg_num), '(BODY[HEADER])')

        # Hol Dir die richtigen Daten
        header_data = data[1][0][1]

        # und lade sie in msg
        parser = HeaderParser()
        msg = parser.parsestr(header_data)

        # uns interessiert hier der Absender
        # sein Name                
        myfrom=email.utils.parseaddr(msg['From'])[1].lower()
        # seine Email-Adresse
        myname=email.utils.parseaddr(msg['From'])[0]
        # von wann stammt die Nachricht?
        try:
            c=datetime.datetime.now(timezone('UTC'))-dateutil.parser.parse(msg['Date'],fuzzy=True)
        except:
            c=datetime.datetime.now()-dateutil.parser.parse(msg['Date'],fuzzy=True)

        # Wenn wir den Absender noch nicht in unserer Liste wer haben, füge ihn und die Daten hinzu 
        if not myfrom in wer:
            # Email-Adresse
            wer.append(myfrom)
            # Name
            name.append(myname)
            # Betreffzeile der empfangenen Nachricht
            recvsubj.append(msg['Subject'])
            # Es gibt keine Betreffzeile für eine gesendete Nachricht
            sentsubj.append(None)
            # Datum des Empfang
            recv.append(c)
            # Es gibt kein Datum für Senden
            sent.append(None)
            # da wir ihn neu aufnehmen, ist es die erste empfangene Nachricht
            recvcnt.append(1)
            # Zähler für gesendete Nachrichten ist 0
            sentcnt.append(0)
        else:
            # Der Absender ist also schon in unserer Liste
            # wo genau?
            i=wer.index(myfrom)
            # wenn noch kein Empfangsdatum, dann setze es
            if recv[i]==None:
                recv[i]=c
                recvsubj[i]=msg['Subject']
            # Wenn diese Nachricht jünger ist, ändere das Empfangsdatum für den Absender
            if recv[i]>c:
                recv[i]=c
                # und ändere die letzte empfangene Betreffzeile
                recvsubj[i]=msg['Subject']
            # Und erhöhe die Anzahl der Interaktionen mit empfangenen Mails um 1
            recvcnt[i]=recvcnt[i]+1

    # Jetzt wiederholen wir das alles für unsere gesendeten Nachrichten

    # Wechsle in die Imap-Mailbox der gesendeten Nachrichten
    conn.select(outbox)

    # Das Datum für unsere Suchabfrage in der Mailbox
    date = (datetime.date.today() - datetime.timedelta(checkdays)).strftime("%d-%b-%Y")

    # So viele Mails müssen wir jetzt bearbeiten
    print "Anzahl Mails: "+str(len(conn.search(None, 'ALL','(SENTSINCE {date})'.format(date=date))[1][0].split()))

    msg_cnt=0

    # In der Schleife werden nach und nach die Mails eingelesen und bearbeitet  
    # analog oben  
    for msg_num in conn.search(None, 'ALL','(SENTSINCE {date})'.format(date=date))[1][0].split(): # returns a nice list of messag let's say I pick #1 from this
        # wir zählen mal mit
        msg_cnt=msg_cnt+1
        print msg_cnt

        data = conn.fetch(str(msg_num), '(BODY[HEADER])')

        header_data = data[1][0][1]

        parser = HeaderParser()
        msg = parser.parsestr(header_data)

        # anders als bei den empfangenen Mails, die immer nur einen Absender haben
        # können die gesendeten Mails an mehrere Leute gehen

        # uns interessieren hier die Email-Felder
        # an wen (to), Kopie an (cc) und Blindkopie an (bcc)
        tos = msg.get_all('to', [])
        ccs = msg.get_all('cc', [])
        bccs = msg.get_all('bcc', [])
        emails = email.utils.getaddresses(tos + ccs + bccs)
        print emails
        print len(emails)

        # Wir haben eine Liste aller Empfänger der Mails, die wir jetzt bearbeiten
        for b in range(0,len(emails)):
            # seine Email-Adresse
            myto=emails[b][1].lower()
            # sein Name
            myname=emails[b][0]
            print myto
            print msg['Date']
            # von wann stammt die Nachricht?
            try:
                c=datetime.datetime.now(timezone('UTC'))-dateutil.parser.parse(msg['Date'],fuzzy=True)
            except:
                c=datetime.datetime.now()-dateutil.parser.parse(msg['Date'],fuzzy=True)

            # wie oben
            # ist die Email-Adresse noch nicht in unserer Liste, dann nehmen wir sie auf
            if not myto in wer:
                wer.append(myto)
                name.append(myname)
                sentsubj.append(msg['Subject'])
                recvsubj.append(None)
                sent.append(c)
                recv.append(None)
                recvcnt.append(0)
                sentcnt.append(1)
            else:
            # andernfalls ändern wir ggf. letzten Betreff und letztes Datum
                i=wer.index(myto)
                if sent[i]==None:
                    sent[i]=c
                    sentsubj[i]=msg['Subject']            
                if sent[i]>c:
                    sent[i]=c
                    sentsubj[i]=msg['Subject']            
                # und erhöhen den Zähler für die Interaktionen
                sentcnt[i]=sentcnt[i]+1

    # Puh, und die ganze Arbeit machen wir uns nicht jedes mal neu
    # deshalb speichern wir die so erzeugten Listen jetzt schnell mal ab
    pickle.dump(wer,open("./list_wer.p", "wb" ))
    pickle.dump(name,open("./list_name.p", "wb" ))
    pickle.dump(recvsubj,open("./list_recvsubj.p", "wb" ))
    pickle.dump(sentsubj,open("./list_sentsubj.p", "wb" ))
    pickle.dump(recvcnt,open("./list_recvcnt.p", "wb" ))
    pickle.dump(sentcnt,open("./list_sentcnt.p", "wb" ))
    pickle.dump(recv,open("./list_recv.p", "wb" ))
    pickle.dump(sent,open("./list_sent.p", "wb" ))
    pickle.dump(datetime.date.today(),open("./mydate.p", "wb" ))

    return

def missingconversation():
    # wir wollen schauen, mit wem wir schon eine Weile nicht mehr gemailt haben

    # Hier kommt das Ergebnis als lesbarer Text rein
    report=""

    # die Liste enthält die Leute, die auf eine Reaktion von uns warten (könnten)
    recvlist = []
    # die Liste enthält die Leute, die auf eine Mail von uns nicht mehr geantwortet haben
    sentlist = []

    # Gehen wir mal unsere gesamten eingelesen Mails durch
    for a in range(0,len(wer)):   
        # Wir betrachten nur Mails, auf die zutrifft dass:
        # wir mindestens minInteract Nachrichten bekommen und
        # wir mindestens minInteract Nachrichten geschrieben haben
        # Damit fallen z.B. Newsletter, auf die man nicht antwortet, raus
        # Falls showOnlyOpenConversations gesetzt ist, werden zudem nur Mails betrachtet,
        # bei denen Anzahl der gesendeten und empfangenen Mails ungleich ist, also noch eine Antwort aussteht 
        if (recvcnt[a]>=minInteract) and (sentcnt[a]>=minInteract) and ((sentcnt[a]!=recvcnt[a]) or not showOnlyOpenConversations):
            # In die Listen sollen zudem nur Mailadressen, die länger als minWaitDays
            # auf eine Interaktion warten
            if (recv[a]<sent[a]) and (recv[a].days>=minWaitDays):
                recvlist.append([name[a],wer[a],recv[a].days,recvsubj[a],recvcnt[a]])
            if (sent[a]<recv[a]) and (sent[a].days>=minWaitDays):
                sentlist.append([name[a],wer[a],sent[a].days,sentsubj[a],sentcnt[a]])

    # wir sortieren die beiden neu generieten Listen nach der Anzahl der Tage,
    # die Mails bereits offen sind
    recvlist.sort(key=lambda xlist: xlist[2],reverse=True)
    sentlist.sort(key=lambda xlist: xlist[2],reverse=True)

    # Dann basteln wir unseren Report aus diesen sortierten Listen
    for a in range(0,len(recvlist)):
        report=report+"\r\n"+recvlist[a][0]+" <"+recvlist[a][1]+"> wartet seit "+str(recvlist[a][2])+" Tagen auf Antwort von Dir zum Thema "+recvlist[a][3]+". Insgesamt Kontakt: "+str(recvlist[a][4])+"."    

    report=report+"\r\n###\r\n"

    # Und zwar für beide
    for a in range(0,len(sentlist)):
        report=report+"\r\n"+sentlist[a][0]+" <"+sentlist[a][1]+"> hat seit "+str(sentlist[a][2])+" Tagen nicht geantwortet zum Thema "+sentlist[a][3]+". Insgesamt Kontakt: "+str(sentlist[a][4])+"."    

    # und geben ihn zurück
    return report

# MAIN #

readmails()
print missingconversation()
Nächster Beitrag
Vorheriger Beitrag