diff options
Diffstat (limited to 'inboxapp-service/app')
| -rw-r--r-- | inboxapp-service/app/factory/__init__.py | 40 | ||||
| -rw-r--r-- | inboxapp-service/app/inboxapp/__init__.py | 18 | ||||
| -rw-r--r-- | inboxapp-service/app/inboxapp/client.py | 115 | ||||
| -rw-r--r-- | inboxapp-service/app/inboxapp/mailconverter.py | 99 | ||||
| -rw-r--r-- | inboxapp-service/app/pixelated_service.py | 136 | ||||
| -rw-r--r-- | inboxapp-service/app/search/__init__.py | 59 |
6 files changed, 0 insertions, 467 deletions
diff --git a/inboxapp-service/app/factory/__init__.py b/inboxapp-service/app/factory/__init__.py deleted file mode 100644 index 84051015..00000000 --- a/inboxapp-service/app/factory/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -# -# Copyright (c) 2014 ThoughtWorks, Inc. -# -# Pixelated is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pixelated is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -import inboxapp - -class ProviderNotFoundException(Exception): - def __init__(self, provider): - self.provider = provider - - def __str__(self): - return "Provider '%s' not found" % self.provider - -class ClientFactory: - - @staticmethod - def create(provider, account): - if provider == 'inboxapp': - return inboxapp.Client(account) - raise ProviderNotFoundException(provider) - -class MailConverterFactory: - - @staticmethod - def create(provider, client): - if provider == 'inboxapp': - return inboxapp.MailConverter(client) - raise ProviderNotFoundException(provider) - diff --git a/inboxapp-service/app/inboxapp/__init__.py b/inboxapp-service/app/inboxapp/__init__.py deleted file mode 100644 index b0eb4d0f..00000000 --- a/inboxapp-service/app/inboxapp/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright (c) 2014 ThoughtWorks, Inc. -# -# Pixelated is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pixelated is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -from client import Client -from mailconverter import MailConverter - diff --git a/inboxapp-service/app/inboxapp/client.py b/inboxapp-service/app/inboxapp/client.py deleted file mode 100644 index 0106a1d4..00000000 --- a/inboxapp-service/app/inboxapp/client.py +++ /dev/null @@ -1,115 +0,0 @@ -# -# Copyright (c) 2014 ThoughtWorks, Inc. -# -# Pixelated is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pixelated is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -import json -import urllib2 -import requests - - -class Client: - - INBOX_APP_ROOT = 'http://localhost:5555/n' - - def _get_user(self, account): - accounts = json.load(urllib2.urlopen(self.INBOX_APP_ROOT)) - return [a for a in accounts if a['email_address'] == account][0] - - def __init__(self, account): - self.user = self._get_user(account) - self.namespace = self.user['namespace'] - - def _get(self, append_url): - url = "%s/%s/%s" % (self.INBOX_APP_ROOT, self.namespace, append_url) - return requests.get(url).json() - - def _post(self, append_url, body): - url = "%s/%s/%s" % (self.INBOX_APP_ROOT, self.namespace, append_url) - return requests.post(url, json.dumps(body)).json() - - def _put(self, append_url, body): - url = "%s/%s/%s" % (self.INBOX_APP_ROOT, self.namespace, append_url) - return requests.put(url, json.dumps(body)).json() - - def mails(self, query): - url = "messages" - if('tags' in query and len(query['tags']) > 0): - url = url + "?tag=%s" % ",".join(query['tags']) - return self._get(url) - - def drafts(self): - return self._get("drafts") - - def mail(self, mail_id): - return self._get("messages/%s" % mail_id) - - def thread(self, thread_id): - return self._get("threads/%s" % thread_id) - - def mark_as_read(self, mail_id): - mail_to_mark = self.mail(mail_id) - self._put("messages/%s" % mail_id, {"unread": False}) - self.remove_tag_from_thread(mail_to_mark["thread"], "unread") - - def tags_for_thread(self, thread): - url = "threads/%s" % thread - tags = self._get(url)['tags'] - return [tag['name'] for tag in tags] - - def add_tag_to_thread(self, thread_id, tag): - url = "threads/%s" % thread_id - response = self._put(url, {'add_tags': [tag]}) - return response - - def remove_tag_from_thread(self, thread_id, tag): - url = "threads/%s" % thread_id - response = self._put(url, {'remove_tags': [tag]}) - return response - - def delete_mail(self, mail_id): - thread_id = self.mail(mail_id)['thread'] - tags = self.tags_for_thread(thread_id) - if('trash' in tags): - self.add_tag_to_thread(thread_id, 'delete') - else: - self.add_tag_to_thread(thread_id, 'trash') - return None - - def save_draft(self, draft): - if 'id' in draft and draft['id']: - url = 'drafts/%s' % draft["id"] - else: - url = "drafts" - result = self._post(url, draft) - self.mark_as_read(result['id']) - return result['id'] - - def send_draft(self, draft): - new_draft_id = self.save_draft(draft) - response = self._post("send", {"draft_id": new_draft_id}) - return response - - def draft_reply_for(self, mail_id): - thread = self.thread(self.mail(mail_id)["thread"]) - if thread['drafts']: - response = self.mail(thread['drafts'][0]) - else: - response = None - return response - - def all_tags(self): - return self._get("tags") - - def all_contacts(self, query): - return self._get("contacts?filter=%s&order_by=rank" % query['general']) diff --git a/inboxapp-service/app/inboxapp/mailconverter.py b/inboxapp-service/app/inboxapp/mailconverter.py deleted file mode 100644 index b056c496..00000000 --- a/inboxapp-service/app/inboxapp/mailconverter.py +++ /dev/null @@ -1,99 +0,0 @@ -# -# Copyright (c) 2014 ThoughtWorks, Inc. -# -# Pixelated is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pixelated is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -from inboxapp import Client -from datetime import datetime -import calendar - - -class MailConverter: - - def __init__(self, client): - self.client = client - - def _from_epoch(self, epoch): - return datetime.fromtimestamp(epoch).isoformat() - - def _to_epoch(self, iso8601): - return calendar.timegm( - datetime.strptime(iso8601, "%Y-%m-%dT%H:%M:%S.%f").timetuple() - ) - - def _to_contacts(self, pixelated_contacts): - return [{"name": "", "email": x} for x in pixelated_contacts] - - def _from_contacts(self, inbox_contacts): - return [contact['email'] for contact in inbox_contacts] - - def from_mail(self, inbox_mail): - tags = sorted(self.client.tags_for_thread(inbox_mail['thread'])) - status = [] if "unread" in tags else ["read"] - return { - 'header': { - 'from': inbox_mail['from'][0]['email'], - 'to': self._from_contacts(inbox_mail['to']), - 'cc': self._from_contacts(inbox_mail['cc']), - 'bcc': self._from_contacts(inbox_mail['bcc']), - 'date': self._from_epoch(inbox_mail['date']), - 'subject': inbox_mail['subject'] - }, - 'ident': inbox_mail['id'], - 'tags': tags, - 'status': status, - 'security_casing': {}, - 'body': inbox_mail['body'], - } - - def to_mail(self, pixelated_mail, account): - mail = { - "to": self._to_contacts(pixelated_mail['header']['to']), - "cc": self._to_contacts(pixelated_mail['header']['cc']), - "bcc": self._to_contacts(pixelated_mail['header']['bcc']), - "from": account, - "body": pixelated_mail["body"], - "subject": pixelated_mail["header"]["subject"], - "date": self._to_epoch(datetime.now().isoformat()), - "id": pixelated_mail["ident"], - "object": "message", - } - if "draft_reply_for" in pixelated_mail: - referred_mail = self.client.mail(pixelated_mail["draft_reply_for"]) - mail["reply_to_thread"] = referred_mail["thread"] - return mail - - def from_tag(self, inbox_tag): - default_tags = ["inbox", "sent", "trash", "drafts"] - return { - 'name': inbox_tag['name'], - 'ident': inbox_tag['id'], - 'default': inbox_tag['name'] in default_tags, - 'counts': { - 'total': 0, - 'read': 0, - 'starred': 0, - 'reply': 0 - } - } - - def from_contact(self, inbox_contact): - return { - 'ident': inbox_contact['id'], - 'name': inbox_contact['name'], - 'addresses': [inbox_contact['email']], - 'mails_received': 0, - 'mails_sent': 0, - 'last_received': None, - 'last_sent': None - } diff --git a/inboxapp-service/app/pixelated_service.py b/inboxapp-service/app/pixelated_service.py deleted file mode 100644 index 175ee11d..00000000 --- a/inboxapp-service/app/pixelated_service.py +++ /dev/null @@ -1,136 +0,0 @@ -# -# Copyright (c) 2014 ThoughtWorks, Inc. -# -# Pixelated is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pixelated is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -from flask import Flask, request, Response, redirect -from factory import MailConverterFactory, ClientFactory -from search import SearchQuery - -import json -import datetime -import requests - -app = Flask(__name__, static_url_path='', static_folder='../../web-ui/app') -client = None -converter = None -account = None - - -def from_iso8061_to_date(iso8061): - return datetime.datetime.strptime(iso8061, "%Y-%m-%dT%H:%M:%S") - - -def respond_json(entity): - response = json.dumps(entity) - return Response(response=response, mimetype="application/json") - - -@app.route('/mails', methods=['POST']) -def save_draft_or_send(): - ident = None - if 'sent' in request.json['tags']: - ident = client.send_draft(converter.to_mail(request.json, account)) - else: - ident = client.save_draft(converter.to_mail(request.json, account)) - return respond_json({'ident': ident}) - - -@app.route('/mails', methods=['PUT']) -def update_draft(): - ident = client.save_draft(converter.to_mail(request.json, account)) - return respond_json({'ident': ident}) - - -@app.route('/mails') -def mails(): - query = SearchQuery.compile(request.args.get("q")) - mails = client.drafts() if "drafts" in query['tags'] else client.mails(query) - mails = [converter.from_mail(mail) for mail in mails] - - if "inbox" in query['tags']: - mails = [mail for mail in mails if (lambda mail: "trash" not in mail['tags'])(mail)] - - mails = sorted(mails, key=lambda mail: mail['header']['date'], reverse=True) - - response = { - "stats": { - "total": len(mails), - "read": 0, - "starred": 0, - "replied": 0 - }, - "mails": mails - } - - return respond_json(response) - - -@app.route('/mail/<mail_id>', methods=['DELETE']) -def delete_mails(mail_id): - client.delete_mail(mail_id) - return respond_json(None) - - -@app.route('/tags') -def tags(): - tags = map(lambda x: converter.from_tag(x), client.all_tags()) - return respond_json(tags) - - -@app.route('/mail/<mail_id>') -def mail(mail_id): - mail = client.mail(mail_id) - return respond_json(converter.from_mail(mail)) - - -@app.route('/mail/<mail_id>/tags') -def mail_tags(mail_id): - mail = converter.from_mail(client.mail(mail_id)) - return respond_json(mail['tags']) - - -@app.route('/mail/<mail_id>/read', methods=['POST']) -def mark_mail_as_read(mail_id): - client.mark_as_read(mail_id) - return "" - - -@app.route('/contacts') -def contacts(): - query = SearchQuery.compile(request.args.get("q")) - desired_contacts = [converter.from_contact(contact) for contact in client.all_contacts(query)] - return respond_json({'contacts': desired_contacts}) - - -@app.route('/draft_reply_for/<mail_id>') -def draft_reply_for(mail_id): - draft = client.draft_reply_for(mail_id) - if draft: - return respond_json(converter.from_mail(draft)) - else: - return respond_json(None) - - -@app.route('/') -def index(): - return app.send_static_file('index.html') - -if __name__ == '__main__': - app.config.from_envvar('PIXELATED_SERVICE_CFG') - provider = app.config['PROVIDER'] - account = app.config['ACCOUNT'] - - client = ClientFactory.create(provider, account) - converter = MailConverterFactory.create(provider, client) - app.run(host=app.config['HOST'], debug=app.config['DEBUG'], port=app.config['PORT']) diff --git a/inboxapp-service/app/search/__init__.py b/inboxapp-service/app/search/__init__.py deleted file mode 100644 index f21e84a1..00000000 --- a/inboxapp-service/app/search/__init__.py +++ /dev/null @@ -1,59 +0,0 @@ -# -# Copyright (c) 2014 ThoughtWorks, Inc. -# -# Pixelated is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pixelated is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -from scanner import StringScanner, StringRegexp - - -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 compiled |
