From e7db94a986f411e1a86b9a6cc2af21a7e74e1fcf Mon Sep 17 00:00:00 2001 From: Duda Dornelles Date: Tue, 12 Aug 2014 13:30:24 -0300 Subject: adding mails query, tags and contacts to py-fake-service --- .gitignore | 1 + py-fake-service/app/adapter/__init__.py | 2 +- py-fake-service/app/adapter/contacts.py | 19 +++++++++ py-fake-service/app/adapter/mail.py | 12 +++--- py-fake-service/app/adapter/mail_service.py | 33 +++++++++++---- py-fake-service/app/adapter/mailset.py | 8 +++- py-fake-service/app/adapter/tag.py | 17 ++++++++ py-fake-service/app/adapter/tagsset.py | 16 +++++++ py-fake-service/app/pixelated_user_agent.py | 19 ++++++--- py-fake-service/app/search/__init__.py | 1 + py-fake-service/app/search/search_query.py | 65 +++++++++++++++++++++++++++++ 11 files changed, 173 insertions(+), 20 deletions(-) create mode 100644 py-fake-service/app/adapter/contacts.py create mode 100644 py-fake-service/app/adapter/tag.py create mode 100644 py-fake-service/app/adapter/tagsset.py create mode 100644 py-fake-service/app/search/__init__.py create mode 100644 py-fake-service/app/search/search_query.py diff --git a/.gitignore b/.gitignore index 207be4cc..6a9b42dd 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ env/ fake-service/data/mail-sets/ .vagrant/ __pycache__/ +.virtualenv diff --git a/py-fake-service/app/adapter/__init__.py b/py-fake-service/app/adapter/__init__.py index 8b137891..47addd32 100644 --- a/py-fake-service/app/adapter/__init__.py +++ b/py-fake-service/app/adapter/__init__.py @@ -1 +1 @@ - +from mail_service import MailService diff --git a/py-fake-service/app/adapter/contacts.py b/py-fake-service/app/adapter/contacts.py new file mode 100644 index 00000000..45aa2177 --- /dev/null +++ b/py-fake-service/app/adapter/contacts.py @@ -0,0 +1,19 @@ +import re + +class Contacts: + def __init__(self): + self.contacts = [] + + def add(self, mbox_mail): + contact = mbox_mail.get_from() + self.contacts.append(Contact(contact)) + + def search(self, query): + contacts_query = re.compile(query) + return [contact.__dict__ for contact in self.contacts if contacts_query.match(contact.addresses[0])] + +class Contact: + def __init__(self, contact): + self.addresses = [contact] + self.name = '' + diff --git a/py-fake-service/app/adapter/mail.py b/py-fake-service/app/adapter/mail.py index 1140f062..859eb50c 100644 --- a/py-fake-service/app/adapter/mail.py +++ b/py-fake-service/app/adapter/mail.py @@ -1,4 +1,5 @@ from datetime import datetime +import random class Mail: @@ -13,14 +14,15 @@ class Mail: def _get_headers(self, mbox_mail): headers = {} headers['from'] = mbox_mail.get_from() - headers['to'] = [mbox_mail.items()['To']] - headers['subject'] = mbox_mail.items()['Subject'] + headers['to'] = [mbox_mail.get('To')] + headers['subject'] = mbox_mail.get('Subject') headers['date'] = datetime.fromtimestamp(random.randrange(1222222222, 1444444444)).isoformat() return headers def _get_tags(self, mbox_mail): - return mbox_mail.items()['X-TW-Pixelated-Tags'].split(', ') - - + return mbox_mail.get('X-TW-Pixelated-Tags').split(', ') + @property + def subject(self): + return self.headers['subject'] diff --git a/py-fake-service/app/adapter/mail_service.py b/py-fake-service/app/adapter/mail_service.py index b657e59a..649ea96e 100644 --- a/py-fake-service/app/adapter/mail_service.py +++ b/py-fake-service/app/adapter/mail_service.py @@ -1,19 +1,38 @@ import os import mailbox +from tagsset import TagsSet +from mailset import MailSet +from contacts import Contacts class MailService: - MAILSET_PATH = os.join(os.environ('HOME'), 'mailset', 'mediumtagged')) + MAILSET_PATH = os.path.join(os.environ['HOME'], 'mailsets', 'mediumtagged') def __init__(self): - self.mails = MailSet() + self.mailset = MailSet() + self.tagsset = TagsSet() + self.contacts = Contacts() def load_mailset(self): - mbox_filenames = [filename for filename in os.listdir(MAILSET_PATH) if mbox.startswith('mbox')] - boxes = (mailbox.mbox(os.path.join(MAILSET_PATH, mbox)) for mbox in mbox_filenames) + mbox_filenames = [filename for filename in os.listdir(self.MAILSET_PATH) if filename.startswith('mbox')] + boxes = (mailbox.mbox(os.path.join(self.MAILSET_PATH, mbox)) for mbox in mbox_filenames) for box in boxes: - message = box.popitem() - self.mails.add(message[1]) + message = box.popitem()[1] + if message.is_multipart(): + continue + self.mailset.add(message) + self.tagsset.add(message) + self.contacts.add(message) + + def mails(self, query, page, window_size): + mails = self.mailset.values() + mails = [mail for mail in mails if query.test(mail)] + return mails + + def mail(self, mail_id): + return self.mailset.get(mail_id) + + def search_contacts(self, query): + return self.contacts.search(query) - diff --git a/py-fake-service/app/adapter/mailset.py b/py-fake-service/app/adapter/mailset.py index d81600e0..2b1627ae 100644 --- a/py-fake-service/app/adapter/mailset.py +++ b/py-fake-service/app/adapter/mailset.py @@ -8,7 +8,13 @@ class MailSet: self.mails = {} def add(self, mbox_mail): - self.mails[self.ident] = Mail(mbox_mail) + self.mails[self.ident] = Mail(mbox_mail, self.ident) self.ident += 1 + def values(self): + return self.mails.values() + + def get(self, mail_id): + return self.mails.get(int(mail_id)) + diff --git a/py-fake-service/app/adapter/tag.py b/py-fake-service/app/adapter/tag.py new file mode 100644 index 00000000..bc98669d --- /dev/null +++ b/py-fake-service/app/adapter/tag.py @@ -0,0 +1,17 @@ +class Tag: + DEFAULT_TAGS = ["inbox", "sent", "trash", "drafts"] + + def __init__(self, name, ident): + self.counts = { + 'total': 1, + 'read': 0, + 'starred': 0, + 'reply': 0 + } + + self.ident = ident + self.name = name + self.default = name in self.DEFAULT_TAGS + + def increment_count(self): + self.counts['total'] += 1 diff --git a/py-fake-service/app/adapter/tagsset.py b/py-fake-service/app/adapter/tagsset.py new file mode 100644 index 00000000..23941735 --- /dev/null +++ b/py-fake-service/app/adapter/tagsset.py @@ -0,0 +1,16 @@ +from tag import Tag + +class TagsSet: + def __init__(self): + self.tags = {} + self.ident = 0 + + def add(self, mbox_mail): + tags = mbox_mail.get('X-TW-Pixelated-Tags').split(', ') + for tag in tags: + tag = self.tags.setdefault(tag, Tag(tag, self.ident)) + tag.increment_count() + self.ident += 1 + + def all_tags(self): + return self.tags.values() diff --git a/py-fake-service/app/pixelated_user_agent.py b/py-fake-service/app/pixelated_user_agent.py index 62ccd206..20ccadcc 100644 --- a/py-fake-service/app/pixelated_user_agent.py +++ b/py-fake-service/app/pixelated_user_agent.py @@ -3,6 +3,8 @@ from flask import Flask, request, Response, redirect import json import datetime import requests +from adapter import MailService +from search import SearchQuery app = Flask(__name__, static_url_path='', static_folder='../../web-ui/app') client = None @@ -32,7 +34,12 @@ def update_draft(): @app.route('/mails') def mails(): - mails = [] + query = SearchQuery.compile(request.args.get('q', '')) + page = request.args.get('p', '') + window_size = request.args.get('w', '') + fetched_mails = mail_service.mails(query, page, window_size) + + mails = [mail.__dict__ for mail in fetched_mails] response = { "stats": { "total": len(mails), @@ -53,13 +60,13 @@ def delete_mails(mail_id): @app.route('/tags') def tags(): - tags = [] - return respond_json(tags) + tags = mail_service.tagsset.all_tags() + return respond_json([tag.__dict__ for tag in tags]) @app.route('/mail/') def mail(mail_id): - return respond_json({}) + return respond_json(mail_service.mail(mail_id).__dict__) @app.route('/mail//tags') @@ -71,10 +78,10 @@ def mail_tags(mail_id): def mark_mail_as_read(mail_id): return "" - @app.route('/contacts') def contacts(): - return respond_json({'contacts': []}) + contacts_query = request.args.get('q') + return respond_json({'contacts': mail_service.search_contacts(contacts_query)}) @app.route('/draft_reply_for/') diff --git a/py-fake-service/app/search/__init__.py b/py-fake-service/app/search/__init__.py new file mode 100644 index 00000000..da90bc7f --- /dev/null +++ b/py-fake-service/app/search/__init__.py @@ -0,0 +1 @@ +from search_query import SearchQuery diff --git a/py-fake-service/app/search/search_query.py b/py-fake-service/app/search/search_query.py new file mode 100644 index 00000000..cb8d5f57 --- /dev/null +++ b/py-fake-service/app/search/search_query.py @@ -0,0 +1,65 @@ +from scanner import StringScanner, StringRegexp +import re + + +def _next_token(): + return StringRegexp('[^\s]+') + + +def _separators(): + return StringRegexp('[\s&]+') + + +def _compile_tag(compiled, token): + tag = token.split(":").pop() + if token[0] == "-": + compiled["not_tags"].append(tag) + else: + compiled["tags"].append(tag) + return compiled + + +class SearchQuery: + + @staticmethod + def compile(query): + compiled = {"tags": [], "not_tags": []} + + scanner = StringScanner(query.encode('utf8').replace("\"", "")) + first_token = True + while not scanner.is_eos: + token = scanner.scan(_next_token()) + + if not token: + scanner.skip(_separators()) + continue + + if ":" in token: + compiled = _compile_tag(compiled, token) + elif first_token: + compiled["general"] = token + + if not first_token: + first_token = True + + return SearchQuery(compiled) + + def __init__(self, compiled): + self.compiled = compiled + + def test(self, mail): + if set(self.compiled.get('tags')).intersection(mail.tags) or 'all' in self.compiled.get('tags'): + return True + + if self.compiled.get('general'): + search_terms = re.compile(self.compiled['general']) + if search_terms.match(mail.body) or search_terms.match(mail.subject): + return True + + if not self.compiled.get('tags') and not self.compiled.get('not_tags'): + return True + + return False + + + -- cgit v1.2.3