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()