From 6592bfad8cb90d7256de949b181ed9d0b684afec Mon Sep 17 00:00:00 2001 From: Patrick Maia and Victor Shyba Date: Tue, 26 Aug 2014 22:06:43 +0000 Subject: extracts tag and status conversion logic --- service/pixelated/adapter/mail_service.py | 9 ++-- service/pixelated/adapter/pixelated_mail.py | 43 +++------------- service/pixelated/adapter/status.py | 43 ++++++++++++++++ service/pixelated/adapter/tag.py | 68 +++++++++++++++++++++++++ service/pixelated/tags.py | 78 ----------------------------- service/pixelated/user_agent.py | 3 +- service/test/adapter/mail_service_test.py | 3 +- service/test/adapter/pixelated_mail_test.py | 12 +---- service/test/adapter/test_status.py | 35 +++++++++++++ service/test/adapter/test_tag.py | 46 +++++++++++++++++ service/test/test_tags.py | 57 --------------------- 11 files changed, 206 insertions(+), 191 deletions(-) create mode 100644 service/pixelated/adapter/status.py create mode 100644 service/pixelated/adapter/tag.py delete mode 100644 service/pixelated/tags.py create mode 100644 service/test/adapter/test_status.py create mode 100644 service/test/adapter/test_tag.py delete mode 100644 service/test/test_tags.py (limited to 'service') diff --git a/service/pixelated/adapter/mail_service.py b/service/pixelated/adapter/mail_service.py index 82925870..f2db8d16 100644 --- a/service/pixelated/adapter/mail_service.py +++ b/service/pixelated/adapter/mail_service.py @@ -22,7 +22,6 @@ from pixelated.bitmask_libraries.provider import LeapProvider from pixelated.bitmask_libraries.session import LeapSessionFactory from pixelated.bitmask_libraries.auth import LeapCredentials from pixelated.adapter.pixelated_mail import PixelatedMail -from pixelated.tags import Tags class MailService: @@ -34,7 +33,6 @@ class MailService: self.server_name = server_name self.mailbox_name = 'INBOX' self.certs_home = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "certificates")) - self.tags = Tags() self._open_leap_session() except: traceback.print_exc(file=sys.stdout) @@ -64,10 +62,11 @@ class MailService: def _update_tag_list(self, tags): for tag in tags: - self.tags.add(tag) + pass + # self.tags.add(tag) def _update_flags(self, new_tags, mail_id): - new_tags_flag_name = ['tag_' + tag.name for tag in new_tags if tag.name not in Tags.SPECIAL_TAGS] + new_tags_flag_name = ['tag_' + tag.name for tag in new_tags if tag.name not in ['inbox', 'drafts', 'sent', 'trash']] self.set_flags(mail_id, new_tags_flag_name) def set_flags(self, mail_id, new_tags_flag_name): @@ -80,7 +79,7 @@ class MailService: return PixelatedMail.from_leap_mail(message) def all_tags(self): - return self.tags + return [] def thread(self, thread_id): raise NotImplementedError() diff --git a/service/pixelated/adapter/pixelated_mail.py b/service/pixelated/adapter/pixelated_mail.py index 4169bb23..3e4e415b 100644 --- a/service/pixelated/adapter/pixelated_mail.py +++ b/service/pixelated/adapter/pixelated_mail.py @@ -13,32 +13,13 @@ # # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see . -from pixelated.tags import Tag -from pixelated.tags import Tags +from pixelated.adapter.tag import Tag +from pixelated.adapter.status import Status import dateutil.parser as dateparser class PixelatedMail: - LEAP_FLAGS = ['\\Seen', - '\\Answered', - '\\Flagged', - '\\Deleted', - '\\Draft', - '\\Recent', - 'List'] - - LEAP_FLAGS_STATUSES = { - '\\Seen': 'read', - '\\Answered': 'replied' - } - - LEAP_FLAGS_TAGS = { - '\\Deleted': 'trash', - '\\Draft': 'drafts', - '\\Recent': 'inbox' - } - def __init__(self): pass @@ -56,8 +37,7 @@ class PixelatedMail: return mail def _extract_status(self): - flags = self.leap_mail.getFlags() - return [converted for flag, converted in self.LEAP_FLAGS_STATUSES.items() if flag in flags] + return Status.from_flags(self.leap_mail.getFlags()) def _extract_headers(self): temporary_headers = {} @@ -69,21 +49,9 @@ class PixelatedMail: def _extract_tags(self): flags = self.leap_mail.getFlags() - tag_names = self._converted_tags(flags) + self._custom_tags(flags) - tags = [] - for tag in tag_names: - tags.append(Tag(tag)) + tags = set(Tag.from_flag(flag) for flag in flags) return tags - def _converted_tags(self, flags): - return [converted for flag, converted in self.LEAP_FLAGS_TAGS.items() if flag in flags] - - def _custom_tags(self, flags): - return [self._remove_prefix(flag) for flag in self.leap_mail.getFlags() if flag.startswith('tag_')] - - def _remove_prefix(self, flag_name): - return flag_name.replace('tag_', '', 1) - def update_tags(self, tags): self.tags = [Tag(tag) for tag in tags] return self.tags @@ -93,11 +61,12 @@ class PixelatedMail: def as_dict(self): tags = [tag.name for tag in self.tags] + statuses = [status.name for status in self.status] return { 'header': self.headers, 'ident': self.ident, 'tags': tags, - 'status': self.status, + 'status': statuses, 'security_casing': self.security_casing, 'body': self.body } diff --git a/service/pixelated/adapter/status.py b/service/pixelated/adapter/status.py new file mode 100644 index 00000000..2002b649 --- /dev/null +++ b/service/pixelated/adapter/status.py @@ -0,0 +1,43 @@ +# +# 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 . + +class Status: + + LEAP_FLAGS_STATUSES = { + '\\Seen': 'read', + '\\Answered': 'replied' + } + + @classmethod + def from_flag(cls, flag): + return Status(cls.LEAP_FLAGS_STATUSES[flag]) + + @classmethod + def from_flags(cls, flags): + return set(cls.from_flag(flag) for flag in flags if flag in cls.LEAP_FLAGS_STATUSES.keys()) + + def __init__(self, name): + self.name = name + self.ident = name.__hash__() + + def __eq__(self, other): + return self.name == other.name + + def __hash__(self): + return self.name.__hash__() + + def __repr__(self): + return self.name diff --git a/service/pixelated/adapter/tag.py b/service/pixelated/adapter/tag.py new file mode 100644 index 00000000..daef30fa --- /dev/null +++ b/service/pixelated/adapter/tag.py @@ -0,0 +1,68 @@ +# +# 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 . +import json + + +class Tag: + + LEAP_FLAGS_TAGS = { + '\\Deleted': 'trash', + '\\Draft': 'drafts', + '\\Recent': 'inbox' + } + + @classmethod + def from_flags(cls, flags): + return set(filter(None, (cls.from_flag(flag) for flag in flags))) + + @classmethod + def from_flag(cls, flag): + if flag in cls.LEAP_FLAGS_TAGS.keys(): + return Tag(cls.LEAP_FLAGS_TAGS[flag]) + if flag.startswith('tag_'): + return Tag(cls._remove_prefix(flag)) + return None + + @classmethod + def _remove_prefix(cls, flag_name): + return flag_name.replace('tag_', '', 1) + + def __init__(self, name, default=False): + self.name = name + self.default = default + self.ident = name.__hash__() + + def __eq__(self, other): + return self.name == other.name + + def __hash__(self): + return self.name.__hash__() + + def as_dict(self): + return { + 'name': self.name, + 'default': self.default, + 'ident': self.ident, + 'counts': { + 'total': 0, + 'read': 0, + 'starred': 0, + 'replied': 0 + } + } + + def __repr__(self): + return self.name diff --git a/service/pixelated/tags.py b/service/pixelated/tags.py deleted file mode 100644 index f5c6b877..00000000 --- a/service/pixelated/tags.py +++ /dev/null @@ -1,78 +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 . -import json - - -class Tag: - - def __init__(self, name, default=False): - self.name = name - self.default = default - self.ident = name.__hash__() - - def __eq__(self, other): - return self.name == other.name - - def __hash__(self): - return self.name.__hash__() - - def as_dict(self): - return { - 'name': self.name, - 'default': self.default, - 'ident': self.ident, - 'counts': { - 'total': 0, - 'read': 0, - 'starred': 0, - 'replied': 0 - } - } - - def __repr__(self): - return self.name - - -class Tags: - - SPECIAL_TAGS = ['inbox', 'sent', 'drafts', 'trash'] - - def __init__(self): - self.tags = {} - self.create_default_tags() - - def create_default_tags(self): - for name in self.SPECIAL_TAGS: - self.tags[name] = self.add(name) - - def add(self, tag_input): - if tag_input.__class__.__name__ == 'Tag': - tag_input = tag_input.name - tag = Tag(tag_input, tag_input in self.SPECIAL_TAGS) - self.tags[tag_input] = tag - return tag - - def find(self, name): - return self.tags[name] - - def __len__(self): - return len(self.tags) - - def __iter__(self): - return self.tags.itervalues() - - def as_dict(self): - return [tag.as_dict() for tag in self.tags.values()] diff --git a/service/pixelated/user_agent.py b/service/pixelated/user_agent.py index 2509ea30..b9ac05bb 100644 --- a/service/pixelated/user_agent.py +++ b/service/pixelated/user_agent.py @@ -26,7 +26,6 @@ import pixelated.reactor_manager as reactor_manager import pixelated.search_query as search_query from pixelated.adapter.mail_service import MailService from pixelated.adapter.pixelated_mail import PixelatedMail -from pixelated.tags import Tags static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "web-ui", "app")) # this is a workaround for packaging @@ -105,7 +104,7 @@ def delete_mails(mail_id): @app.route('/tags') def tags(): tags = mail_service.all_tags() - return respond_json(tags.as_dict()) + return respond_json([tag.as_dict() for tag in tags]) @app.route('/mail/') diff --git a/service/test/adapter/mail_service_test.py b/service/test/adapter/mail_service_test.py index d4a0d5c3..86194036 100644 --- a/service/test/adapter/mail_service_test.py +++ b/service/test/adapter/mail_service_test.py @@ -18,8 +18,7 @@ import unittest from pixelated.adapter.mail_service import MailService from mock import Mock, MagicMock, patch import test_helper -from pixelated.tags import Tag - +from pixelated.adapter.tag import Tag class TestMailService(unittest.TestCase): diff --git a/service/test/adapter/pixelated_mail_test.py b/service/test/adapter/pixelated_mail_test.py index 308424e0..c7fe9112 100644 --- a/service/test/adapter/pixelated_mail_test.py +++ b/service/test/adapter/pixelated_mail_test.py @@ -16,8 +16,8 @@ import unittest from pixelated.adapter.pixelated_mail import PixelatedMail -from pixelated.tags import Tag -from pixelated.tags import Tags +from pixelated.adapter.tag import Tag +from pixelated.adapter.status import Status import test_helper @@ -35,14 +35,6 @@ class TestPixelatedMail(unittest.TestCase): pixelated_mail = PixelatedMail.from_leap_mail(test_helper.leap_mail(leap_flags=['\\Draft'])) self.assertIn(Tag('drafts'), pixelated_mail.tags) - def test_leap_seen_flag_is_translated_to_read_status(self): - pixelated_mail = PixelatedMail.from_leap_mail(test_helper.leap_mail(leap_flags=['\\Seen'])) - self.assertIn('read', pixelated_mail.status) - - def test_leap_answered_flag_is_translated_to_replied_status(self): - pixelated_mail = PixelatedMail.from_leap_mail(test_helper.leap_mail(leap_flags=['\\Answered'])) - self.assertIn('replied', pixelated_mail.status) - def test_leap_flags_that_are_custom_tags_are_handled(self): pixelated_mail = PixelatedMail.from_leap_mail(test_helper.leap_mail(extra_flags=['tag_work'])) self.assertIn(Tag('work'), pixelated_mail.tags) diff --git a/service/test/adapter/test_status.py b/service/test/adapter/test_status.py new file mode 100644 index 00000000..b39d4846 --- /dev/null +++ b/service/test/adapter/test_status.py @@ -0,0 +1,35 @@ +# +# 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 . +import unittest + +from pixelated.adapter.status import Status +import test_helper + + +class TestStatus(unittest.TestCase): + + def test_leap_seen_flag_is_translated_to_read_status(self): + status = Status.from_flag('\\Seen') + self.assertEquals(Status('read'), status) + + def test_leap_answered_flag_is_translated_to_replied_status(self): + status = Status.from_flag('\\Answered') + self.assertEquals(Status('replied'), status) + + def test_bulk_conversion(self): + statuses = Status.from_flags(['\\Answered', '\\Seen', '\\Recent', 'tag_a_custom']) + self.assertEquals(set([Status('read'), Status('replied')]), statuses) + diff --git a/service/test/adapter/test_tag.py b/service/test/adapter/test_tag.py new file mode 100644 index 00000000..30bc1550 --- /dev/null +++ b/service/test/adapter/test_tag.py @@ -0,0 +1,46 @@ +# +# 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 . +import unittest + +from pixelated.adapter.tag import Tag +import test_helper + + +class TestTag(unittest.TestCase): + + def test_leap_recent_flag_is_translated_to_inbox_tag(self): + tag = Tag.from_flag('\\Recent') + self.assertEquals(Tag('inbox'), tag) + + def test_leap_deleted_flag_is_translated_to_trash_tag(self): + tag = Tag.from_flag('\\Deleted') + self.assertEquals(Tag('trash'), tag) + + def test_leap_draft_flag_is_translated_to_draft_tag(self): + tag = Tag.from_flag('\\Draft') + self.assertEquals(Tag('drafts'), tag) + + def test_leap_flags_that_are_custom_tags_are_handled(self): + tag = Tag.from_flag('tag_work') + self.assertEquals(Tag('work'), tag) + + def test_custom_tags_containing_our_prefix_are_handled(self): + tag = Tag.from_flag('tag_tag_work_tag_') + self.assertEquals(Tag('tag_work_tag_'), tag) + + def test_bulk_conversion(self): + tags = Tag.from_flags(['\\Answered', '\\Seen', '\\Recent', 'tag_a_custom', 'List']) + self.assertEquals(set([Tag('inbox'), Tag('a_custom')]), tags) diff --git a/service/test/test_tags.py b/service/test/test_tags.py deleted file mode 100644 index abd59b20..00000000 --- a/service/test/test_tags.py +++ /dev/null @@ -1,57 +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 . -import unittest -from pixelated.tags import Tags, Tag - - -class TagTestCase(unittest.TestCase): - - def test_create_tag(self): - tag = Tag('test', True) - self.assertEqual(tag.name, 'test') - - -class TagsTestCase(unittest.TestCase): - - def test_add_tag_to_collection(self): - tag_collection = Tags() - tag_collection.add('test') - self.assertEqual(len(tag_collection), len(Tags()) + 1) - tag_collection.add('test2') - self.assertEqual(len(tag_collection), len(Tags()) + 2) - - def test_no_tag_duplication(self): - tag_collection = Tags() - tag_collection.add('test') - self.assertEqual(len(tag_collection), len(Tags()) + 1) - tag_collection.add('test') - self.assertEqual(len(tag_collection), len(Tags()) + 1) - - def test_find_tag_on_collection(self): - tag_collection = Tags() - tag_collection.add('test') - tag_collection.add('test2') - self.assertEqual(tag_collection.find('test'), Tag('test', True)) - - def test_special_tags_always_exist(self): - special_tags = ['inbox', 'sent', 'drafts', 'trash'] - tag_collection = Tags() - for tag in special_tags: - self.assertIn(Tag(tag, True), tag_collection) - - def test_special_tags_are_default(self): - tags = Tags() - self.assertTrue(tags.find('inbox').default) -- cgit v1.2.3