lolamontes69

Ch12 App1: Bayesian Classifier. Programming Collective Intel

Sep 25th, 2013
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 29.60 KB | None | 0 0
  1. """ Chapter 12 Programming Collective Intelligence: Bayesian Classifier.
  2.  
  3.    As the chapter has no exercises in it I have decided to create a practical
  4.    application for each of the Algorithms and Methods described.
  5.    First up the Bayesian Classifier.
  6.    For this I have created a simple gmail email client with a Trainable Bayesian Classifier.
  7.    There is lots of space for adding features, but I am sticking to the subject matter
  8.    of the book :)
  9.        --lolamontes69
  10.    """
  11. import imaplib
  12. import htmlentitydefs
  13. import poplib, email, string
  14. import getpass as getpass
  15. import re as re
  16. import math as math
  17. from pysqlite2 import dbapi2 as sqlite
  18. import os as os
  19.  
  20. def htmlentitydecode(s):
  21.     def entity2char(m):
  22.         entity = m.group(1)
  23.         if entity in htmlentitydefs.name2codepoint:
  24.             return unichr(htmlentitydefs.name2codepoint[entity])
  25.         return u" "  # Unknown entity: We replace with a space.
  26.     t = re.sub(u'&(%s);' % u'|'.join(htmlentitydefs.name2codepoint), entity2char, s)
  27.     t = re.sub(u'&#(\d+);', lambda x: unichr(int(x.group(1))), t)
  28.     return re.sub(u'&#x(\w+);', lambda x: unichr(int(x.group(1),16)), t)
  29.  
  30. def stripTags(s):
  31.     intag = [False]
  32.     def chk(c):
  33.         if intag[0]:
  34.             intag[0] = (c != '>')
  35.             return False
  36.         elif c == '<':
  37.             intag[0] = True
  38.             return False
  39.         return True
  40.     return ''.join(c for c in s if chk(c))
  41.  
  42. def getwords(doc):
  43.     splitter=re.compile('\\W*')
  44.     # Split the words by non-alpha characters
  45.     words=[s.lower() for s in splitter.split(doc) if len(s)>2 and len(s)<20]
  46.     # Return the unique set of words only
  47.     return dict([(w,1) for w in words])
  48.  
  49. class classifier:
  50.     def __init__(self,getfeatures,filename=None):
  51.         self.getfeatures=getfeatures
  52.  
  53.     def setdb(self,dbfile):
  54.         self.con=sqlite.connect(dbfile)
  55.         self.con.execute('create table if not exists fc(feature,category,count,ap)')
  56.         self.con.execute('create table if not exists cc(category,count)')
  57.        
  58.     # Increase the count of a feature/category pair
  59.     def incf(self,f,cat):
  60.         count=self.fcount(f,cat)
  61.         try:
  62.             if count==0:
  63.                 self.con.execute("insert into fc values ('%s','%s',1,0.5)" % (f,cat)) # default ap value is 0.5
  64.             else:
  65.                 self.con.execute("update fc set count=%d where feature='%s' and category='%s'" % (count+1,f,cat))
  66.         except: print "error adding",f,"in category",cat
  67.  
  68.     def set_ap(self,f,cat,ap):
  69.         changeit = "update fc set ap="+str(ap)+" where feature='"+f+"' and category='"+cat+"'"
  70.         self.con.execute(changeit)
  71.  
  72.     def get_ap(self,f,cat):
  73.         res=self.con.execute('select ap from fc where feature="%s" and category="%s"' % (f,cat)).fetchone()
  74.         if res==None: return 0   # default ap value
  75.         else: return float(res[0])
  76.  
  77.     # Increase the count of a category
  78.     def incc(self,cat):
  79.         count=self.catcount(cat)
  80.         try:
  81.             if count==0:
  82.                 self.con.execute("insert into cc values ('%s',1)" % (cat))
  83.             else:
  84.                 self.con.execute("update cc set count=%d where category='%s'" % (count+1,cat))
  85.         except: print "error adding",f,"in category",cat
  86.  
  87.     # The number of times a feature has appeared in a category
  88.     def fcount(self,f,cat):
  89.         res=self.con.execute('select count from fc where feature="%s" and category="%s"' % (f,cat)).fetchone()
  90.         if res==None: return 0
  91.         else: return float(res[0])
  92.  
  93.     # The number of items in a category
  94.     def catcount(self,cat):
  95.         res=self.con.execute('select count from cc where category="%s"' % (cat)).fetchone()
  96.         if res==None: return 0
  97.         else: return float(res[0])
  98.  
  99.     # The total number of items
  100.     def totalcount(self):
  101.         res=self.con.execute('select sum(count) from cc').fetchone()
  102.         if res==None: return 0
  103.         return res[0]
  104.  
  105.     # The list of all the categories
  106.     def categories(self):
  107.         cur=self.con.execute('select category from cc')
  108.         return [d[0] for d in cur]
  109.  
  110.     def train(self,item,cat):
  111.         features=self.getfeatures(item)
  112.         # Increment the count for every feature with this category
  113.         for f in features:
  114.             self.incf(f,cat)
  115.  
  116.         # Increment the count for this category
  117.         self.incc(cat)
  118.         self.con.commit()
  119.  
  120.     def fprob(self,f,cat):
  121.         if self.catcount(cat)==0: return 0
  122.  
  123.         # The total number of times this feature appeared in this category
  124.         # divided by the total number of items in this category
  125.         return self.fcount(f,cat)/self.catcount(cat)
  126.  
  127.  
  128.     def weightedprob(self,f,cat,prf,weight=1.0):
  129.         # Calculate current probability
  130.         basicprob=prf(f,cat)
  131.         # Count the number of times this feature has appeared in all categories
  132.         totals=sum([self.fcount(f,c) for c in self.categories()])
  133.  
  134.         # Get the assumed probability for the feature
  135.         ap = self.get_ap(f,cat)
  136.         if ap==0: ap=0.5                    # If feature not in db give it the default value
  137.         # Calculate the weighted average
  138.         bp=((weight*ap)+(totals*basicprob))/(weight+totals)
  139.         return bp
  140.  
  141.     # See what features are in a certain category
  142.     def examine_category(self):
  143.         catnam = str(raw_input('Type category name to examine features for>'))
  144.         cur = self.con.execute('select * from fc where category="%s"' % catnam)
  145.         count = 0
  146.         for a in cur:        
  147.             count+=1
  148.             if count == 30:
  149.                 pause=raw_input('press enter to continue')
  150.                 count=0
  151.             print a
  152.         cont=(raw_input('Press <ENTER> to continue'))
  153.  
  154.     # look at the category names in cc
  155.     def get_catnames(self):
  156.         cur = self.con.execute('select * from cc')
  157.         print "-"*44
  158.         for a in cur: print a[0]
  159.         cont=(raw_input('Press <ENTER> to continue'))
  160.  
  161.     # rename/correct name of a category in cc  
  162.     def rename_category_in_fc(self):
  163.         catnam = str(raw_input('Enter category to rename >'))
  164.         newcatname = str(raw_input('Enter new category name >'))
  165.         self.con.execute("update fc set category='%s' where category='%s'" % (newcatname,catnam))
  166.         print catnam,"renamed as",newcatname
  167.         cont=(raw_input('Press <ENTER> to continue'))
  168.         self.con.commit()
  169.  
  170.     # rename/correct name of a category in cc  
  171.     def rename_category_in_fc(self):
  172.         catnam = str(raw_input('Enter category to rename >'))
  173.         newcatname = str(raw_input('Enter new category name >'))
  174.         self.con.execute("update fc set category='%s' where category='%s'" % (newcatname,catnam))
  175.         print catnam,"renamed as",newcatname
  176.         cont=(raw_input('Press <ENTER> to continue'))
  177.         self.con.commit()
  178.  
  179.     # rename/correct name of a category in cc
  180.     def rename_category_in_cc(self):
  181.         catnam = str(raw_input('Enter category name to rename >'))
  182.         newcatname = str(raw_input('Enter new category name >'))
  183.         self.con.execute("update cc set category='%s' where category='%s'" % (newcatname,catnam))
  184.         print catnam,"renamed as",newcatname
  185.         cont=(raw_input('Press <ENTER> to continue'))
  186.         self.con.commit()
  187.  
  188.     # change the weight of a feature in fc
  189.     def change_featweight(self):
  190.         featnam = str(raw_input('Enter feature name >'))
  191.         catnam = str(raw_input('Enter category name >'))
  192.         newweight= float(raw_input('Enter new weight for feature >'))
  193.         self.set_ap(featnam,catnam,newweight)
  194.         print "New feaure weight is",newweight
  195.         cont=(raw_input('Press <ENTER> to continue'))
  196.         self.con.commit()
  197.  
  198.     # show all examples of a feature in fc (from all categories)
  199.     def examine_feature(self):
  200.         featnam = str(raw_input('Enter feature name >'))
  201.         cur = self.con.execute('select * from fc where feature="%s"' % featnam)
  202.         print "-"*44
  203.         for a in cur: print a
  204.         cont=(raw_input('Press <ENTER> to continue'))
  205.        
  206.     # rename/correct name of a category in cc  
  207.     def recategorize_in_fc(self):
  208.         featnam = str(raw_input('Enter feature to recategorize >'))
  209.         newcatname = str(raw_input('Enter new category name >'))
  210.         self.con.execute("update fc set category='%s' where feature='%s'" % (newcatname,featnam))
  211.         print featnam,"recategorized as",newcatnam
  212.         cont=(raw_input('Press <ENTER> to continue'))
  213.         self.con.commit()
  214.  
  215.     def delete_feat(self):
  216.         featnam = str(raw_input('Enter feature to delete >'))
  217.         cur = self.con.execute('select * from fc where feature="%s"' % featnam)
  218.         print "-"*44
  219.         for a in cur: print a
  220.         delme = str(raw_input("Is this the feature you wish to delete?(y/n) >"))
  221.         if delme=="y" or delme=="Y":
  222.             self.con.execute("delete from fc where feature='%s'" % featnam)
  223.             print "Feature deleted."
  224.         else: print "Aborting delete..."
  225.         cont=(raw_input('Press <ENTER> to continue'))
  226.         self.con.commit()
  227.  
  228.     def delete_cat(self):
  229.         catnam = str(raw_input('Enter category to delete >'))
  230.         delme = str(raw_input("Are you sure you wish to delete this category and its associated features?(y/n) >"))
  231.         if delme=="y" or delme=="Y":
  232.             self.con.execute("delete from fc where category='%s'" % catnam)
  233.             print "Category and its associated features deleted."
  234.         else: print "Aborting delete..."
  235.         cont=(raw_input('Press <ENTER> to continue'))
  236.         self.con.commit()
  237.  
  238.     def delete_cat1(self):
  239.         catnam = str(raw_input('Enter category to delete >'))
  240.         delme = str(raw_input("Are you sure you wish to delete this category?(y/n) >"))
  241.         if delme=="y" or delme=="Y":
  242.             self.con.execute("delete from cc where category='%s'" % catnam)
  243.             print "Category deleted, make sure you delete it from fc too."
  244.         else: print "Aborting delete..."
  245.         cont=(raw_input('Press <ENTER> to continue'))
  246.         self.con.commit()
  247.  
  248.     def database_menu(self):
  249.         ch = """
  250. ##################################################
  251. #   email classifier database operations menu    #
  252. ##################################################
  253. #                                                #
  254. #  1 Train database                              #
  255. #  2 Get Category Names                          #
  256. #  3 Look at features in a category              #
  257. #  4 Look at all instances of a feature          #
  258. #  5 Change feature weight                       #
  259. #  6 Rename a category in cc                     #
  260. #  7 Rename all instances of a category in cc    #
  261. #  8 Recategorize a feature                      #
  262. #  9 Delete a feature from fc                    #
  263. # 10 Delete a category from cc                   #
  264. # 11 Delete a category and its features from fc  #
  265. #  0 QUIT                                        #
  266. ##################################################
  267. """
  268.         while True:
  269.             os.system('clear')
  270.             print ch
  271.             choosea1=raw_input('Enter choice >')
  272.             try: choosea=int(choosea1)
  273.             except: break
  274.             if choosea==1:
  275.                 get_mails(self)
  276.                 cont=(raw_input('Press <ENTER> to continue'))
  277.             elif choosea==2:
  278.                 self.get_catnames()
  279.             elif choosea==3:
  280.                 self.examine_category()
  281.             elif choosea==4:
  282.                 self.examine_feature()
  283.             elif choosea==5:
  284.                 self.change_featweight()
  285.             elif choosea==6:
  286.                 self.rename_category_in_cc()
  287.             elif choosea==7:
  288.                 self.rename_category_in_fc()
  289.             elif choosea==8:
  290.                 self.recategorize_in_f()
  291.             elif choosea==9:
  292.                 self.delete_feat()
  293.             elif choosea==10:
  294.                 self.delete_cat1()
  295.             elif choosea==11:
  296.                 self.delete_cat()
  297.             elif choosea==0: break
  298.  
  299.  
  300. class naivebayes(classifier):
  301.  
  302.     def __init__(self,getfeatures):
  303.         classifier.__init__(self,getfeatures)
  304.         self.thresholds={}
  305.  
  306.     def docprob(self,item,cat):
  307.         features=self.getfeatures(item)
  308.  
  309.         # Multiply the probabilities of all the features together
  310.         p=1
  311.         for f in features: p *= self.weightedprob(f,cat,self.fprob)
  312.         return p
  313.  
  314.     def prob(self,item,cat):
  315.         catprob=self.catcount(cat)/self.totalcount()
  316.         docprob=self.docprob(item,cat)
  317.         return docprob*catprob
  318.  
  319.     def setthreshold(self,cat,t):
  320.         self.thresholds[cat]=t
  321.  
  322.     def getthreshold(self,cat):
  323.         if cat not in self.thresholds: return 1.0
  324.         return self.thresholds[cat]
  325.  
  326.     def classify(self,item,default=None):
  327.         probs={}
  328.         # Find the category with the highest probability
  329.         best=default
  330.         max=0.0
  331.         for cat in self.categories():
  332.             probs[cat]=self.prob(item,cat)
  333.             if probs[cat]>max:
  334.                 max=probs[cat]
  335.                 best=cat
  336.  
  337.         # Make sure the probability exceeds threshold*next best
  338.         for cat in probs:
  339.             if cat==best: continue
  340.             if probs[cat]*self.getthreshold(best)>probs[best]: return default
  341.         return best
  342.  
  343. class fisherclassifier(classifier):
  344.     def __init__(self,getfeatures):
  345.         classifier.__init__(self,getfeatures)
  346.         self.minimums={}
  347.  
  348.     def setminimum(self,cat,min):
  349.         self.minimums[cat]=min
  350.  
  351.     def getminimum(self,cat):
  352.         if cat not in self.minimums: return 0
  353.         return self.minimums[cat]
  354.        
  355.     def cprob(self,f,cat):
  356.         # The frequency of this feature in this category
  357.         clf=self.fprob(f,cat)
  358.         if clf==0: return 0
  359.  
  360.         # The frequency of this feature in all the categories
  361.         freqsum=sum([self.fprob(f,c) for c in self.categories()])
  362.  
  363.         # The probability is the frequency in this category divided by the overall frequency
  364.         p=clf/(freqsum)
  365.  
  366.         return p
  367.  
  368.     def fisherprob(self,item,cat):
  369.         # Multiply all the probabilities together
  370.         p=1
  371.         features=self.getfeatures(item)
  372.         for f in features:
  373.             p *= (self.weightedprob(f,cat,self.cprob))
  374.  
  375.         # Take the natural log and multiply by -2
  376.         try: fscore=-2*math.log(p)   # if db returns none for ap because feature not in fc
  377.         except: return 0
  378.         # Use the inverse chi2 function to get a probability
  379.         return self.invchi2(fscore,len(features)*2)
  380.  
  381.     def invchi2(self,chi,df):
  382.         m=chi/2.0
  383.         sum = term = math.exp(-m)
  384.         for i in range(1, df//2):
  385.             term *= m/i
  386.             sum += term
  387.         return min(sum, 1.0)
  388.  
  389.     def classify(self,item,default=None):
  390.         # Loop through looking for the best result
  391.         best=default
  392.         max=0.0
  393.         for c in self.categories():
  394.             p=self.fisherprob(item,c)
  395.             # Make sure it exceeds the minimum
  396.             if p>self.getminimum(c) and p>max:
  397.                 best=c
  398.                 max=p
  399.         return best
  400.  
  401. def getwords1(doc):
  402.     ignore_list = ['a', 'about', 'above', 'after', 'again', 'all', 'also', 'am', 'an', 'and', 'any', 'are', "aren't", 'as', 'at', 'be', 'because', 'been', 'before', 'being', 'both', 'but', 'by', "can", "can't", 'cannot', 'could', "couldn't", "couldn", 'did', "didn", "didn't", 'do', 'does', 'doesn', "doesn't", 'doing', "don't", 'down', 'during', 'each', "even", 'every', 'few', 'for', 'from', 'further', 'had', "hadn't", 'has', "hasn't", 'have', "haven", "haven't", 'having', 'he', "he'd", "he'll", "he's", 'her', 'here', "here's", 'hers', 'him', 'his', 'how', "how's", 'i', "i'd", "i'll", "i'm", "i've", 'if', 'in', 'into', 'is', "isn't", 'it', "it's", 'its', 'itself', 'laquo', 'lsaquo', 'ldquo', "let's", 'mdash', 'me', 'more', 'most', "mustn", "mustn't", 'my', "nbsp", 'no', 'nor', 'not', 'of', 'off', 'on', 'once', 'only', 'or', 'other', 'ought', 'our', 'ours', 'out', 'over', 'own', 'quot', 'raquo', 'rsaquo', 'rsquo', 'said', 'same', 'say', 'says', "shan't", 'she', "she'd", "she'll", "she's", 'should', "shouldn", "shouldn't", 'so', 'some', 'still', 'such', 'than', 'that', "that's", 'the', 'their', 'theirs', 'them', 'then', 'there', "there's", 'these', 'they', "they'd", "they'll", "they're", "they've", 'this', 'those', 'through', 'to', 'too', 'under', 'until', 'up', 'very', 'was', 'wasn', "wasn't", 'we', "we'd", "we'll", "we're", "we've", 'were', "weren't", 'what', "what's", 'when', "when's", 'where', "where's", 'which', 'while', 'who', "who's", 'whom', 'why', "why's", "will", "with", "won't", "wouldn't", 'wouldn', 'you', "you'd", "you'll", "you're", "you've", 'your', 'yours']
  403.  
  404.     wordnum=2
  405.     splitter=re.compile('\\W*')
  406.  
  407.     variousnumbers = re.findall(" [/(0-9/) -0-9]{11,25}", doc) + re.findall(r"[^[]*\[([^]]*)\]", doc)
  408.  
  409.     emails = re.findall("[\w.]+@[\w.]+", doc)
  410.     for a in range(len(emails)):  
  411.         if emails[a][-1] == '>':emails[a]= emails[a][:-1]
  412.     urls = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', doc)
  413.     for a in range(len(urls)):  
  414.         if urls[a][-1] == '>':urls[a]= urls[a][:-1]
  415.  
  416.     # So these are only added the one time.
  417.     for a in urls: doc = doc.replace(a," ")
  418.     for a in emails: doc = doc.replace(a," ")
  419.     for a in variousnumbers: doc = doc.replace(a," ")
  420.  
  421.     doc = ''.join(i for i in doc if not i.isdigit())
  422.     words=[s.lower() for s in splitter.split(doc) if len(s)>2 and len(s)<20]
  423.  
  424.     # Change wordnum for word combo length
  425.     for a in range(len(words)-wordnum+1):
  426.         phrases = ' '.join(words[a:a+wordnum])
  427.         words.append(phrases)
  428.  
  429.     # Put all the lists together and return a unique wordset
  430.     words = words + urls + emails + variousnumbers
  431.     frog = dict([(w,1) for w in words])
  432.     for a in ignore_list:
  433.         if a in frog: del frog[a]
  434.     return frog
  435.  
  436. def read(message,fulltext,classifier):
  437.     print message
  438.  
  439.     # Print the best guess at the current category
  440.     guess = str(classifier.classify(fulltext))
  441.     print '\nCategory guess: '+ guess
  442.  
  443.     # Ask the user to specify the correct category and train on that
  444.     cl=raw_input('Enter category (q to QUIT): ')
  445.     if cl == "": cl = guess          # If the guess is correct just press enter as a timesaver
  446.     elif cl == "q":
  447.         classifier.con.commit()
  448.         return cl
  449.     classifier.train(fulltext,cl)
  450.     return cl
  451.  
  452. def get_mails(classifier):
  453.     username = str(raw_input('Enter username or press <enter> for lolamontes69 >'))
  454.     if username == "": username = "lolamontes69"            # 'recent:lolamontes69' limits it to last months mail
  455.     password = getpass.getpass(prompt='Enter password >')
  456.  
  457.     mailserver = poplib.POP3_SSL('pop.gmail.com')
  458.     mailserver.user(username)                  
  459.     mailserver.pass_(password)
  460.     (numMsgs, totalSize) = mailserver.stat()               # Print some stats...
  461.     print "numMsgs =",numMsgs,"\ntotalSize =",totalSize    # just so you know what's coming :)
  462.     # paused = raw_input('Press <ENTER> to continue')        # Dramatic pause
  463.     numMessages1 = len(mailserver.list()[1])
  464.     numMessages=int(raw_input('Enter number of messages to fetch >'))
  465.     for i in reversed(range(numMessages)):
  466.         fulltext = ""
  467.         message = "_________________________________\ni =" + str(i)
  468.         message += "\n_________________________________\n" # reversed runs the range in reverse
  469.         msg = mailserver.retr(i+1)
  470.         str1 = string.join(msg[1], "\n")
  471.         mail = email.message_from_string(str1)
  472.         try:
  473.             message += "From: " + mail["From"] + "\n" + "Subject: " + mail["Subject"] + "\n" + "Date: " + mail["Date"] + "\n"
  474.             fulltext += mail["From"] + " " + mail["Subject"] + " " + mail["Received"] + " "
  475.         except: pass
  476.         for part in mail.walk():
  477.             if part.get_content_type() == 'text/plain':
  478.                 message += "Body: " + part.get_payload()   # adds the raw text to message
  479.                 fulltext += part.get_payload()
  480.         try:
  481.             a=read(message,fulltext,classifier)                  # train the database
  482.             if a=="q": break
  483.         except: print "-"*44,"\nUnable to process this e-mail\n","-"*44
  484.  
  485. def get_first_text_block(self, email_message_instance):
  486.     maintype = email_message_instance.get_content_maintype()
  487.     if maintype == 'multipart':
  488.         for part in email_message_instance.get_payload():
  489.             if part.get_content_maintype() == 'text':
  490.                 return part.get_payload()
  491.     elif maintype == 'text':
  492.         return email_message_instance.get_payload()
  493.  
  494. def getXNewMails(imap_server,cl):
  495.     X = int(raw_input("Enter number of new mails to fetch >"))
  496.     X=X-1
  497.     result, data = imap_server.search(None, "ALL") # fetch numbers of emails
  498.     ids = data[0]
  499.     id_list = ids.split()
  500.     latest_email_id = id_list[-1]
  501.     mailnums=range(int(latest_email_id)-X,int(latest_email_id)+1)
  502.     mailnums.reverse()
  503.  
  504.     for i in mailnums:
  505.         result, data = imap_server.fetch(i, "(RFC822)") # fetch the email body (RFC822) for the given ID
  506.         raw_email = data[0][1] # raw text of the whole email including headers and alternate payloads
  507.         email_message = email.message_from_string(raw_email)
  508.         print "-"*44
  509.         fulltext=""
  510.         print "To:",email_message['To']
  511.         fulltext+=email_message['To']+' '
  512.         print "From:",email_message['From']
  513.         fulltext+=email_message['From']+' '
  514.         print "Date:",email_message['Date']
  515.         print "Subject:",email_message['Subject']
  516.         fulltext+=email_message['Subject']+' '
  517.         print ""
  518.         a = get_first_text_block(imap_server, email_message)
  519.         if a==None: c="No message"
  520.         else:
  521.             b=stripTags(a)
  522.             c=htmlentitydecode(b)
  523.         print c
  524.         fulltext+=c
  525.         print '\nCategory guess: ',str(cl.classify(fulltext))
  526.         cont=(raw_input('Press <ENTER> to continue'))
  527.  
  528. def get_newest_email(imap_server,cl):
  529.     result, data = imap_server.search(None, "ALL") # fetch numbers of emails
  530.     ids = data[0]
  531.     id_list = ids.split()
  532.     latest_email_id = id_list[-1]
  533.     result, data = imap_server.fetch(latest_email_id, "(RFC822)") # fetch the email body (RFC822) for the given ID
  534.     raw_email = data[0][1] # raw text of the whole email including headers and alternate payloads
  535.     email_message = email.message_from_string(raw_email)
  536.     print "-"*44
  537.     fulltext=""
  538.     print "To:",email_message['To']
  539.     fulltext+=email_message['To']+' '
  540.     print "From:",email_message['From']
  541.     fulltext+=email_message['From']+' '
  542.     print "Date:",email_message['Date']
  543.     print "Subject:",email_message['Subject']
  544.     fulltext+=email_message['Subject']+' '
  545.     print ""
  546.     a = get_first_text_block(imap_server, email_message)
  547.     if a==None: c="No message"
  548.     else:
  549.         b=stripTags(a)
  550.         c=htmlentitydecode(b)
  551.     print c
  552.     fulltext+=c
  553.     print '\nCategory guess: ',str(cl.classify(fulltext))
  554.  
  555. def send_a_mail(username,password):
  556.     import smtplib
  557.     fromaddr = str(raw_input("Enter sender's email address >"))
  558.     toaddrs  = str(raw_input("Enter recipients's email address >"))
  559.     subj = str(raw_input("Enter subject of message >"))
  560.     fromline = "From: %s" % fromaddr
  561.     toline = "To: %s" % toaddrs
  562.     subjline = "Subject: %s" % subj
  563.     messageline = ""
  564.     print "Enter message (enter q on a line on its own to send message.) >"
  565.     while True:
  566.         mess1 = str(raw_input())
  567.         if mess1=='q': break
  568.         messageline+=mess1+'\n'
  569.     msg = "\r\n".join([fromline, toline, subjline, "",messageline])
  570.     server = smtplib.SMTP('smtp.gmail.com:587')
  571.     server.ehlo()
  572.     server.starttls()
  573.     server.login(username,password)
  574.     server.sendmail(fromaddr, toaddrs, msg)
  575.     server.quit()
  576.     print "Mail sent."
  577.  
  578. def get_emails(imap_server,cl):
  579.     em1=int(raw_input('Enter email number >'))
  580.     result, data = imap_server.fetch(em1, "(RFC822)") # fetch the email body (RFC822) for the given ID
  581.     raw_email = data[0][1] # raw text of the whole email including headers and alternate payloads
  582.     email_message = email.message_from_string(raw_email)
  583.     print "-"*44
  584.     fulltext=""
  585.     try:
  586.         print "To:",email_message['To']
  587.         fulltext+=email_message['To']+' '
  588.     except: pass
  589.     try:
  590.         print "From:",email_message['From']
  591.         fulltext+=email_message['From']+' '
  592.     except: pass
  593.     try: print "Date:",email_message['Date']
  594.     except: pass
  595.     try:
  596.         print "Subject:",email_message['Subject']
  597.         fulltext+=email_message['Subject']+' '
  598.     except: pass
  599.     print ""
  600.     a = get_first_text_block(imap_server, email_message)
  601.     if a==None: a="No message"
  602.     else:
  603.         b=stripTags(a)
  604.         c=htmlentitydecode(b)
  605.     print c
  606.     fulltext+=c
  607.     print '\nCategory guess: ',str(cl.classify(fulltext))
  608.  
  609. def get_subjects(imap_server):
  610.     email_ids=[]
  611.     while True:
  612.         em1=int(raw_input('Enter email id >'))
  613.         email_ids.append(em1)
  614.         again=str(raw_input('Enter another?(y/n) >'))
  615.         if again=='n' or again=='N': break
  616.     try:
  617.         subjects = []
  618.         for e_id in email_ids:
  619.             _, response = imap_server.fetch(e_id, '(body[header.fields (subject)])')
  620.             subjects.append( response[0][1][9:] )
  621.         print "-"*44
  622.         for a in subjects:
  623.             b=stripTags(a)
  624.             print htmlentitydecode(b)
  625.         print "-"*44
  626.         return subjects
  627.     except: print "Unable to retrieve mail"
  628.  
  629. def emails_from(imap_server):
  630.     name=str(raw_input("Enter person's name >"))
  631.     # Search for all mail from name
  632.     try:
  633.         status, response = imap_server.search(None, '(FROM "%s")' % name)
  634.         email_ids = [e_id for e_id in response[0].split()]
  635.         print 'Number of emails from %s: %i. IDs: %s' % (name, len(email_ids), email_ids)
  636.         return email_ids
  637.     except: print "Unable to retrieve mail"
  638.  
  639. def mailmenu(imap_server,username,password,cl):
  640.     ch="""
  641. #############################################
  642. #              mail menu                    #
  643. #############################################"""
  644.     ch1="""
  645. #############################################
  646. #                                           #
  647. # 1 Fetch newest mail                       #
  648. # 2 Fetch X new mails                       #
  649. # 3 Fetch a specific email by number        #
  650. # 4 Fetch emails from a specific person     #
  651. # 5 Fetch only the subject line/s           #
  652. # 6 Send mail                               #
  653. # 0 QUIT                                    #
  654. #                                           #
  655. #############################################"""
  656.     # Count the unread emails
  657.     status, response = imap_server.status('INBOX', "(UNSEEN)")
  658.     unreadcount = int(response[0].split()[2].strip(').,]'))
  659.  
  660.     # Search for all new mail
  661.     status, email_ids = imap_server.search(None, '(UNSEEN)')
  662.     print "The numbers for these emails are",email_ids
  663.  
  664.     while True:
  665.         os.system('clear')
  666.         print ch
  667.         print "You have",unreadcount,"unread mails"
  668.         print "The ids for these emails are;"
  669.         for a in email_ids: print a
  670.         print ch1
  671.         choosea1=raw_input('Enter choice >')
  672.         try: choosea=int(choosea1)
  673.         except:
  674.             imap_server.logout()
  675.             break
  676.  
  677.         if choosea==1:
  678.             get_newest_email(imap_server,cl)
  679.             cont=(raw_input('Press <ENTER> to continue'))
  680.         elif choosea==2:
  681.             getXNewMails(imap_server,cl)
  682.         elif choosea==3:
  683.             get_emails(imap_server,cl)
  684.             cont=(raw_input('Press <ENTER> to continue'))
  685.         elif choosea==4:
  686.             emails_from(imap_server)
  687.             cont=(raw_input('Press <ENTER> to continue'))
  688.         elif choosea==5:
  689.             get_subjects(imap_server)
  690.             cont=(raw_input('Press <ENTER> to continue'))
  691.         elif choosea==6:
  692.             send_a_mail(username,password)
  693.             cont=(raw_input('Press <ENTER> to continue'))
  694.         elif choosea==0:
  695.             imap_server.logout()
  696.             break
  697.  
  698. def email_startup(cl):
  699.     os.system('clear')
  700.     username = str(raw_input('Enter username or press <enter> for lolamontes69 >'))
  701.     if username == "": username = "lolamontes69"  
  702.     password = getpass.getpass(prompt='Enter password >')
  703.     imap_server = imaplib.IMAP4_SSL("imap.gmail.com",993)
  704.     imap_server.login(username, password)
  705.     imap_server.select('INBOX')
  706.     mailmenu(imap_server,username,password,cl)
  707.  
  708. def main():
  709.     ch2="""
  710. ###############################################
  711. #       lolamontes69 email client             #
  712. ###############################################
  713. #                                             #
  714. # 1: Email                                    #
  715. # 2: Bayesian Classifier                      #
  716. # 0: QUIT                                     #
  717. #                                             #
  718. ###############################################"""
  719.     os.system('clear')
  720.     dbnam=str(raw_input('Enter full database name (eg emails1.db) >'))
  721.     wot=raw_input('(N)aivebayes or (F)isherclassifier? >')
  722.     if wot=='N' or wot=='n':
  723.         cl=naivebayes(getwords1)
  724.         cl.setdb(dbnam)
  725.     else:
  726.         cl=fisherclassifier(getwords1)
  727.         cl.setdb(dbnam)
  728.     while True:
  729.         os.system('clear')
  730.         print ch2
  731.         choosea1=raw_input('Enter choice >')
  732.         try: choosea=int(choosea1)
  733.         except: break
  734.         if choosea==1:
  735.             email_startup(cl)
  736.         elif choosea==2:
  737.             cl.database_menu()
  738.         elif choosea==0:
  739.             cl.con.commit()
  740.             break
  741.  
  742. if __name__ == "__main__":
  743.     main()
Advertisement
Add Comment
Please, Sign In to add comment