diff options
Diffstat (limited to 'archive_folder.py')
-rwxr-xr-x | archive_folder.py | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/archive_folder.py b/archive_folder.py new file mode 100755 index 0000000..7859bea --- /dev/null +++ b/archive_folder.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3.6 +# note http://docs.python.org/lib/module-doctest.html +# resp. file:///usr/share/doc/python-docs-*/html/lib/module-doctest.html +# FIXME +# read https://www.toptal.com/python/an-introduction-to-mocking-in-python +# & https://blog.fugue.co/2016-02-11-python-mocking-101.html +# for unittest.mock tutorials +# for testing +import argparse +import configparser +import email +import logging +import os +import sys +from datetime import date + +logging.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', + stream=sys.stdout, level=logging.DEBUG) +log = logging.getLogger('imapArch') + +# from java.util import Properties, Date +# from java.lang import System +# from javax.mail import * +# from jarray import array + + +class FolderNotFoundError(IOError): + pass + + +def __getMessageDate(msg): + """ + Return the date of the message + + @param msg analyzed message + @return GregorianCalendar of the messages date + @throws MessagingException + """ + dateMS = msg.getReceivedDate().getTime() // 1000 + dateStruct = date.fromtimestamp(dateMS) + return dateStruct + + +class ArchivableFolder(list): + + def __init__(self, source, year): + """ + Constructor for the folder. + + @param source folder from the messages should be archived + @param year int of the year for which the messages are + archived + """ + self.sourceFolder = source + targetName = self.__getArchiveFolderName(source.getFullName(), year) + self.targetFolder = self.sourceFolder.getFolder(targetName) + + def __getArchiveFolderName(self, srcFldName, year): + """ + @param folder string with the folder name + @param year int + @return + """ + archFolder = "INBOX/Archiv/" + rootLen = len("INBOX/") + if not(srcFldName[:rootLen] == "/INBOX/"): + raise FolderNotFoundError( + "We expect all folders to be under INBOX folder.") + baseName = srcFldName[rootLen:] + archFolder = "/" + archFolder + year + "/" + baseName + return archFolder + + def add(self, msg): + self.append(msg) + + def doArchive(self): + for message in self: + log.debug("Moving %s from %s to %s.", + message, self.sourceFolder.getFullName(), + self.targetFolder.getFullName()) + # self.sourceFolder.copyMessages(array(self, Message), + # self.targetFolder) + # for message in self: + # message.setFlag(Flags.Flag.DELETED, true) + # self.sourceFolder.expunge() + + +class Archives(dict): + """Collects archivable folders indexed by tuple of + folderName and year + """ + + def __init__(self): + pass + + def add(self, msg): + """ + Check whether the ArchivableFolder (@see ArchivableFolder) + for the particular year actually exists, and if not then create + it. In any event add + @param msg + """ + fld = msg.getFolder() + msgDate = __getMessageDate(msg) + year = msgDate.year + + if not(self.search(fld, year)): + archfld = ArchivableFolder(fld, year) + self[self.__createKey(fld, year)] = archfld + + def __createKey(self, name, year): + """ + Create a key for the list + @param fldName String with the full name of the folder + @param year int of the year of the messages to be stored + there + @return tuple consisting from the both parameters of + this function. + """ + return(name, year) + + def search(self, fld, year): + """ + Find out whether the object with the key consisting of + the folder name and year exists in this object. + + @param fld Folder where the message is stored + @param year int year of the message date + @return boolean saying whether the folder for this message + has been already added to this object. + """ + key = self.__createKey(fld.getFullName(), year) + return key in self + + def archive(self): + for key in self: + self.doArchive() + + +class ArchivedStore(object): + + def __init__(self, serverKey=None): + config = configparser.ConfigParser() + config.read(os.path.expanduser('~/.config/imap_archiver.cfg')) + acc_name = serverKey if serverKey is not None \ + else config['general']['account'] + self.cfg = dict(config.items(acc_name)) + # if debug: + # self.session.setDebug(True) + # try: + # self.store = self.session.getStore("imaps") + # except: + # print >> sys.stderr, "Cannot get Store" + # raise + + self._threeMonthAgo = date.today() + newmonth = self._threeMonthAgo.month - 3 + self._threeMonthAgo = self._threeMonthAgo.replace(month=newmonth) + + log.debug("host = %s, user = %s, password = %s", + self.cfg['host'], self.cfg['username'], + self.cfg['password']) + + self.__login(host, user, password) + + def __login(self, server, user, password): + try: + self.store.connect(server, user, password) + except: + log.debug("Cannot connect to %s as %s with password %s", + server, user, password) + raise + + def archive(self, from_folder, archive_base): + # type: (str, str) -> None + inboxfolder = self.store.getDefaultFolder().getFolder("INBOX") + folderList = inboxfolder.list('*') + + for folder in folderList: + if folder.getFullName()[:len("INBOX/Archiv")] != "INBOX/Archiv": + archMsgsCnt = self.__archiveFolder(folder) + # folder.close(False) + print("Processed messages = %d" % archMsgsCnt) + + def __archiveFolder(self, fld): + fld.open(Folder.READ_WRITE) + for msg in fld.getMessages(): + msgDate = __getMessageDate(msg) + print >>sys.stderr, str(msgDate), str(self._threeMonthAgo) + if msgDate < self._threeMonthAgo: + archFolder = self.__getArchiveFolderName(msg) +# print >> sys.stderr, archFolder +# fld.copyMessages(array([msg],type(msg)), archFolder) +# msg.setFlag(Flags.Flag.DELETED, true) + print("%s -> %s : %s" % (fld.getFullName(), + archFolder.getFullName(), msgDate)) + folderLen = len(fld.getMessages()) + fld.close(False) + return(folderLen) + + def __enter__(self): + return self + + def __exit__(self): + pass # FIXME self.something.close() + + +if __name__ == '__main__': + argp = argparse.ArgumentParser() + argp.add_argument('-d', + help='How old messages we should keep') + argp.add_argument('server', + help='Symbolic name of the server to be used') + argp.add_argument('folder', + help='Folder which should be archived') + argp.add_argument('archive', + help='Root folder to store annual archives to') + args = argp.parse_args() + + with ArchivedStore(args.server) as myStore: + myStore.archive(args.folder, args.archive) |