Created
July 10, 2015 00:29
-
-
Save sandromello/de7e2487043c7cc6059f to your computer and use it in GitHub Desktop.
Secret Gist
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from redis import StrictRedis | |
from redis.client import BasePipeline | |
from itertools import izip | |
from ast import literal_eval | |
import marshal, uuid | |
MARSHAL_TYPES = [list, dict, bool] | |
MAILDATA_CUSTOM_CALLBACK = { | |
'customerid' : str, | |
'domainid' : str, | |
'accountid' : str, | |
'messageid' : str, | |
'headers' : dict, | |
'hasattachment' : bool, | |
'mailfrom' : str, | |
'mailfromname' : str, | |
'maifromdomain' : str, | |
'rcpts' : list, | |
'subject' : str, | |
'fromip' : str, | |
'isblankmailfrom' : bool, | |
'ismailerdaemon' : bool, | |
'body' : str, | |
'contentbody' : str, | |
'discard' : bool, | |
'discardattachment' : bool, | |
'attachments' : list | |
} | |
ATTACHMENT_CUSTOM_CALLBACK = { | |
'name' : str, | |
'contenttype' : str, | |
'content' : str, | |
'size' : float, | |
} | |
class MarshalRedis(StrictRedis): | |
def hmset(self, name, mapping, mapping_types=None): | |
if mapping_types: | |
for key, type_info in mapping_types.items(): | |
if key in mapping and type_info in MARSHAL_TYPES: | |
mapping[key] = marshal.dumps(mapping[key]) | |
#result_pipeline = None | |
#with super(MarshalRedis, self).pipeline as pipe: | |
#pipe.set('body:%s' % mapping['body']['id'], mapping['body']['content']) | |
#pipe.set('attachment:%s' % mapping['attachment']['id'], mapping['attachment']['content']) | |
#pipe.hmset(name, mapping) | |
#result_pipeline = pipe.execute() | |
#return result_pipeline | |
return super(MarshalRedis, self).hmset(name, mapping) | |
def hgetall(self, name, callbacks=None): | |
response = super(MarshalRedis, self).hgetall(name) | |
if not callbacks: | |
callbacks = dict(MAILDATA_CUSTOM_CALLBACK.items()) | |
return self.pairs_to_dict_typed(response, callbacks) or {} | |
def pipeline(self, transaction=True, shard_hint=None): | |
return MarshalStrictPipeline( | |
self.connection_pool, | |
self.response_callbacks, | |
transaction, | |
shard_hint | |
) | |
def pairs_to_dict_typed(self, response, type_info): | |
result = {} | |
for key, value in response.items(): | |
if key in type_info: | |
try: | |
if type_info[key] in MARSHAL_TYPES: | |
value = marshal.loads(value) | |
else: | |
value = type_info[key](value) | |
except Exception: | |
# if for some reason the value can't be coerced, just use | |
# the string value | |
pass | |
result[key] = value | |
return result | |
# Pipeline must overrided methods | |
class MarshalStrictPipeline(BasePipeline, MarshalRedis): pass | |
# TODO: Need to validate keys and type before writing to redis! | |
# TODO: Need marshal Class Type, must use pickle instead | |
class BaseMail(object): | |
def validate(self, custom_callback): | |
for entry in self.__dict__: | |
if entry not in custom_callback: | |
raise NameError('Wrong key found: %s' % entry) | |
def is_valid(self): | |
# Check for valid attachment on self.attachments | |
pass | |
class MailAttachment(BaseMail): | |
def __init__(self): | |
#super(MailAttachment, self) | |
for key, type_value in ATTACHMENT_CUSTOM_CALLBACK.items(): | |
self.__dict__[key] = type_value() | |
class MailMetadata(BaseMail): | |
def __init__(self): | |
#super(MailMetadata, self) | |
for key, type_value in MAILDATA_CUSTOM_CALLBACK.items(): | |
self.__dict__[key] = type_value() | |
class Session(object): | |
def __init__(self, redis): | |
self.redis = redis | |
def load(self, key, load_body=True, load_attachments=True): | |
result = self.redis.hgetall(key, MAILDATA_CUSTOM_CALLBACK) | |
if load_body: | |
result['body'] = self.redis.get(result['body']) | |
if load_attachments: | |
for mattach in result['attachments']: | |
mattach.content = self.redis.get('attachment:content:%s' % mattach.content) | |
mdata = MailMetadata() | |
for key, value in result.items(): | |
mdata.__dict__[key] = value | |
return mdata | |
def save(self, maildata): | |
if not type(maildata) == MailMetadata: | |
raise TyperError('Accept only maildata object') | |
mainid, bodyid = str(uuid.uuid4()), str(uuid.uuid4()) | |
with self.redis.pipeline() as pipe: | |
pipe.set(bodyid, maildata.body) | |
for mattach in maildata.attachments: | |
mattach.is_valid() | |
attachmentid = str(uuid.uuid4()) | |
pipe.set('attachment:content:%s' % attachmentid, mattach.content) | |
mattach.content = attachmentid | |
pipe.hmset('attachment:%s' % attachmentid, mattach.__dict__, dict(ATTACHMENT_CUSTOM_CALLBACK.items())) | |
maildata.body = bodyid | |
pipe.hmset('mail:metadata:%s' % mainid, maildata.__dict__, dict(MAILDATA_CUSTOM_CALLBACK.items())) | |
pipe.execute() | |
return mainid | |
if __name__ == '__main__': | |
attachment = MailAttachment() | |
attachment.name = 'sandro.pdf' | |
attachment.contenttype = 'pdf' | |
attachment.content = 'mypdffile data' | |
attachment.size = 1024121213389 | |
mail = MailMetadata() | |
mail.messageid = '[email protected]' | |
mail.headers = {'X-MS-Has-Attach' : 'yes', 'x-ms-exchange-transport-fromentityheader' : 'hosted'} | |
mail.hasattachment = True | |
mail.mailfrom = '[email protected]' | |
mail.mailfromname = 'Zhu, Ray', | |
mail.rcpts = ['[email protected]'] | |
mail.subject = 'Kinesis' | |
mail.isblankmailfrom = False | |
mail.ismailerdaemon = False | |
mail.body = 'Your insights are very important to us and I really appreciate your feedbac=' | |
mail.attachments = [attachment] | |
session = Session(MarshalRedis('192.168.6.74')) | |
mainid = session.save(mail) | |
session.load('mail:metadata:%s' % mainid) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment