def add_fake_message(db_session, namespace_id, thread=None, from_addr=None, to_addr=None, cc_addr=None, bcc_addr=None, received_date=None, subject='', body='', snippet='', add_sent_category=False): from inbox.models import Message, Category from inbox.contacts.process_mail import update_contacts_from_message m = Message() m.namespace_id = namespace_id m.from_addr = from_addr or [] m.to_addr = to_addr or [] m.cc_addr = cc_addr or [] m.bcc_addr = bcc_addr or [] m.received_date = received_date or datetime.utcnow() m.size = 0 m.is_read = False m.is_starred = False m.body = body m.snippet = snippet m.subject = subject if thread: thread.messages.append(m) update_contacts_from_message(db_session, m, thread.namespace) db_session.add(m) db_session.commit() if add_sent_category: category = Category.find_or_create( db_session, namespace_id, 'sent', 'sent', type_='folder') if category not in m.categories: m.categories.add(category) db_session.commit() return m
def handle_raw_folder_change(self, db_session, account, raw_folder): folder = db_session.query(Folder).filter( Folder.account_id == account.id, Folder.canonical_name == raw_folder.role).first() if folder: if folder.name != raw_folder.display_name: log.info('Folder name changed on remote', account_id=self.account_id, role=raw_folder.role, new_name=raw_folder.display_name, name=folder.name) folder.name = raw_folder.display_name if folder.category: if folder.category.display_name != \ raw_folder.display_name: folder.category.display_name = raw_folder.display_name # noqa else: log.info('Creating category for folder', account_id=self.account_id, folder_name=folder.name) folder.category = Category.find_or_create( db_session, namespace_id=account.namespace.id, name=raw_folder.role, display_name=raw_folder.display_name, type_='folder') else: Folder.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role)
def add_fake_category(db_session, namespace_id, display_name, name=None): from inbox.models import Category category = Category(namespace_id=namespace_id, display_name=display_name, name=name) db_session.add(category) db_session.commit() return category
def create_categories_for_folders(account, db_session): from inbox.models import Folder, Category for folder in db_session.query(Folder).filter( Folder.account_id == account.id): cat = Category.find_or_create( db_session, namespace_id=account.namespace.id, name=folder.canonical_name, display_name=folder.name, type_='folder') folder.category = cat db_session.commit()
def create_categories_for_folders(account, db_session): from inbox.models import Folder, Category for folder in db_session.query(Folder).filter( Folder.account_id == account.id): cat = Category.find_or_create(db_session, namespace_id=account.namespace.id, name=folder.canonical_name, display_name=folder.name, type_='folder') folder.category = cat db_session.commit()
def add_fake_message( db_session, namespace_id, thread=None, from_addr=None, to_addr=None, cc_addr=None, bcc_addr=None, received_date=None, subject="", body="", snippet="", g_msgid=None, add_sent_category=False, ): from inbox.contacts.processing import update_contacts_from_message from inbox.models import Category, Message m = Message() m.namespace_id = namespace_id m.from_addr = from_addr or [] m.to_addr = to_addr or [] m.cc_addr = cc_addr or [] m.bcc_addr = bcc_addr or [] m.received_date = received_date or datetime.utcnow() m.size = 0 m.is_read = False m.is_starred = False m.body = body m.snippet = snippet m.subject = subject m.g_msgid = g_msgid if thread: thread.messages.append(m) update_contacts_from_message(db_session, m, thread.namespace.id) db_session.add(m) db_session.commit() if add_sent_category: category = Category.find_or_create(db_session, namespace_id, "sent", "sent", type_="folder") if category not in m.categories: m.categories.add(category) db_session.commit() return m
def test_query_target(db, api_client, thread, default_namespace): cat = Category(namespace_id=default_namespace.id, name='inbox', display_name='Inbox', type_='label') for _ in range(3): message = add_fake_message(db.session, default_namespace.id, thread, to_addr=[('Bob', '*****@*****.**')], from_addr=[('Alice', '*****@*****.**')], subject='some subject') message.categories.add(cat) db.session.commit() results = api_client.get_data('/messages?in=inbox') assert len(results) == 3 count = api_client.get_data('/messages?in=inbox&view=count') assert count['count'] == 3
def add_fake_message(db_session, namespace_id, thread=None, from_addr=None, to_addr=None, cc_addr=None, bcc_addr=None, received_date=None, subject='', body='', snippet='', add_sent_category=False): from inbox.models import Message, Category from inbox.contacts.process_mail import update_contacts_from_message m = Message() m.namespace_id = namespace_id m.from_addr = from_addr or [] m.to_addr = to_addr or [] m.cc_addr = cc_addr or [] m.bcc_addr = bcc_addr or [] m.received_date = received_date or datetime.utcnow() m.size = 0 m.is_read = False m.is_starred = False m.body = body m.snippet = snippet m.subject = subject if thread: thread.messages.append(m) update_contacts_from_message(db_session, m, thread.namespace) db_session.add(m) db_session.commit() if add_sent_category: category = Category.find_or_create(db_session, namespace_id, 'sent', 'sent', type_='folder') if category not in m.categories: m.categories.add(category) db_session.commit() return m
def folders_labels_create_api(): category_type = g.namespace.account.category_type data = request.get_json(force=True) display_name = data.get('display_name') valid_display_name(g.namespace.id, category_type, display_name, g.db_session) category = Category.find_or_create(g.db_session, g.namespace.id, name=None, display_name=display_name, type_=category_type) g.db_session.flush() if category_type == 'folder': schedule_action('create_folder', category, g.namespace.id, g.db_session) else: schedule_action('create_label', category, g.namespace.id, g.db_session) return g.encoder.jsonify(category)
def handle_raw_folder_change(self, db_session, account, raw_folder): folder = ( db_session.query(Folder) .filter( Folder.account_id == account.id, Folder.canonical_name == raw_folder.role, ) .first() ) if folder: if folder.name != raw_folder.display_name: log.info( "Folder name changed on remote", account_id=self.account_id, role=raw_folder.role, new_name=raw_folder.display_name, name=folder.name, ) folder.name = raw_folder.display_name if folder.category: if folder.category.display_name != raw_folder.display_name: folder.category.display_name = raw_folder.display_name else: log.info( "Creating category for folder", account_id=self.account_id, folder_id=folder.id, ) folder.category = Category.find_or_create( db_session, namespace_id=account.namespace.id, name=raw_folder.role, display_name=raw_folder.display_name, type_="folder", ) else: Folder.find_or_create( db_session, account, raw_folder.display_name, raw_folder.role )
def test_query_target(db, api_client, thread, default_namespace): cat = Category( namespace_id=default_namespace.id, name="inbox", display_name="Inbox", type_="label", ) for _ in range(3): message = add_fake_message( db.session, default_namespace.id, thread, to_addr=[("Bob", "*****@*****.**")], from_addr=[("Alice", "*****@*****.**")], subject="some subject", ) message.categories.add(cat) db.session.commit() results = api_client.get_data("/messages?in=inbox") assert len(results) == 3 count = api_client.get_data("/messages?in=inbox&view=count") assert count["count"] == 3
def test_filtering(db, api_client, default_namespace): thread = add_fake_thread(db.session, default_namespace.id) message = add_fake_message(db.session, default_namespace.id, thread, to_addr=[('Bob', '*****@*****.**')], from_addr=[('Alice', '*****@*****.**')], subject='some subject') message.categories.add( Category(namespace_id=message.namespace_id, name='inbox', display_name='Inbox', type_='label')) thread.subject = message.subject db.session.commit() t_start = dt_to_timestamp(thread.subjectdate) t_lastmsg = dt_to_timestamp(thread.recentdate) subject = message.subject to_addr = message.to_addr[0][1] from_addr = message.from_addr[0][1] received_date = message.received_date unread = not message.is_read starred = message.is_starred results = api_client.get_data('/threads?thread_id={}'.format( thread.public_id)) assert len(results) == 1 results = api_client.get_data('/messages?thread_id={}'.format( thread.public_id)) assert len(results) == 1 results = api_client.get_data('/threads?cc={}'.format(message.cc_addr)) assert len(results) == 0 results = api_client.get_data('/messages?cc={}'.format(message.cc_addr)) assert len(results) == 0 results = api_client.get_data('/threads?bcc={}'.format(message.bcc_addr)) assert len(results) == 0 results = api_client.get_data('/messages?bcc={}'.format(message.bcc_addr)) assert len(results) == 0 results = api_client.get_data('/threads?filename=test') assert len(results) == 0 results = api_client.get_data('/messages?filename=test') assert len(results) == 0 results = api_client.get_data('/threads?started_after={}'.format(t_start - 1)) assert len(results) == 1 results = api_client.get_data('/messages?started_after={}'.format(t_start - 1)) assert len(results) == 1 results = api_client.get_data( '/messages?last_message_before={}&limit=1'.format(t_lastmsg + 1)) assert len(results) == 1 results = api_client.get_data( '/threads?last_message_before={}&limit=1'.format(t_lastmsg + 1)) assert len(results) == 1 results = api_client.get_data('/threads?in=inbox&limit=1') assert len(results) == 1 results = api_client.get_data('/messages?in=inbox&limit=1') assert len(results) == 1 results = api_client.get_data('/messages?in=banana%20rama') assert len(results) == 0 results = api_client.get_data('/threads?subject={}'.format(subject)) assert len(results) == 1 results = api_client.get_data('/messages?subject={}'.format(subject)) assert len(results) == 1 results = api_client.get_data('/threads?unread={}'.format(unread)) assert len(results) == 1 results = api_client.get_data('/messages?unread={}'.format((not unread))) assert len(results) == 0 results = api_client.get_data('/threads?starred={}'.format((not starred))) assert len(results) == 0 results = api_client.get_data('/messages?starred={}'.format(starred)) assert len(results) == 1 for _ in range(3): add_fake_message(db.session, default_namespace.id, to_addr=[('', '*****@*****.**')], thread=add_fake_thread(db.session, default_namespace.id)) results = api_client.get_data( '/messages?any_email={}'.format('*****@*****.**')) assert len(results) > 1 # Test multiple any_email params multiple_results = api_client.get_data( '/messages?any_email={},{},{}'.format('*****@*****.**', '*****@*****.**', '*****@*****.**')) assert len(multiple_results) > len(results) # Check that we canonicalize when searching. alternate_results = api_client.get_data( '/threads?any_email={}'.format('*****@*****.**')) assert len(alternate_results) == len(results) results = api_client.get_data('/messages?from={}'.format(from_addr)) assert len(results) == 1 results = api_client.get_data('/threads?from={}'.format(from_addr)) assert len(results) == 1 early_time = received_date - datetime.timedelta(seconds=1) late_time = received_date + datetime.timedelta(seconds=1) early_ts = calendar.timegm(early_time.utctimetuple()) late_ts = calendar.timegm(late_time.utctimetuple()) results = api_client.get_data( '/messages?subject={}&started_before={}'.format(subject, early_ts)) assert len(results) == 0 results = api_client.get_data( '/threads?subject={}&started_before={}'.format(subject, early_ts)) assert len(results) == 0 results = api_client.get_data( '/messages?subject={}&started_before={}'.format(subject, late_ts)) assert len(results) == 1 results = api_client.get_data( '/threads?subject={}&started_before={}'.format(subject, late_ts)) assert len(results) == 1 results = api_client.get_data( '/messages?subject={}&last_message_after={}'.format(subject, early_ts)) assert len(results) == 1 results = api_client.get_data( '/threads?subject={}&last_message_after={}'.format(subject, early_ts)) assert len(results) == 1 results = api_client.get_data( '/messages?subject={}&last_message_after={}'.format(subject, late_ts)) assert len(results) == 0 results = api_client.get_data( '/threads?subject={}&last_message_after={}'.format(subject, late_ts)) assert len(results) == 0 results = api_client.get_data( '/messages?subject={}&started_before={}'.format(subject, early_ts)) assert len(results) == 0 results = api_client.get_data( '/threads?subject={}&started_before={}'.format(subject, early_ts)) assert len(results) == 0 results = api_client.get_data( '/messages?subject={}&started_before={}'.format(subject, late_ts)) assert len(results) == 1 results = api_client.get_data( '/threads?subject={}&started_before={}'.format(subject, late_ts)) assert len(results) == 1 results = api_client.get_data('/messages?from={}&to={}'.format( from_addr, to_addr)) assert len(results) == 1 results = api_client.get_data('/threads?from={}&to={}'.format( from_addr, to_addr)) assert len(results) == 1 results = api_client.get_data('/messages?to={}&limit={}&offset={}'.format( '*****@*****.**', 2, 1)) assert len(results) == 2 results = api_client.get_data('/threads?to={}&limit={}'.format( '*****@*****.**', 3)) assert len(results) == 3 results = api_client.get_data('/threads?view=count') assert results['count'] == 4 results = api_client.get_data('/threads?view=ids&to={}&limit=3'.format( '*****@*****.**', 3)) assert len(results) == 3 assert all(isinstance(r, basestring) for r in results), "Returns a list of string"
def save_folder_names(self, db_session, raw_folders): """ Save the folders, labels present on the remote backend for an account. * Create Folder/ Label objects. * Delete Folders/ Labels that no longer exist on the remote. Notes ----- Gmail uses IMAP folders and labels. Canonical folders ('all', 'trash', 'spam') are therefore mapped to both Folder and Label objects, everything else is created as a Label only. We don't canonicalize names to lowercase when saving because different backends may be case-sensitive or otherwise - code that references saved names should canonicalize if needed when doing comparisons. """ account = db_session.query(Account).get(self.account_id) old_labels = {label for label in db_session.query(Label).filter( Label.account_id == self.account_id, Label.deleted_at == None)} new_labels = set() # Create new labels, folders for raw_folder in raw_folders: if raw_folder.role == 'starred': # The starred state of messages is tracked separately # (we set Message.is_starred from the '\\Flagged' flag) continue label = Label.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) new_labels.add(label) if label.deleted_at is not None: # This is a label which was previously marked as deleted # but which mysteriously reappeared. Unmark it. log.info('Deleted label recreated on remote', name=raw_folder.display_name) label.deleted_at = None label.category.deleted_at = None if raw_folder.role in ('all', 'spam', 'trash'): folder = db_session.query(Folder).filter( Folder.account_id == account.id, Folder.canonical_name == raw_folder.role).first() if folder: if folder.name != raw_folder.display_name: log.info('Folder name changed on remote', account_id=self.account_id, role=raw_folder.role, new_name=raw_folder.display_name, name=folder.name) folder.name = raw_folder.display_name if folder.category: if folder.category.display_name != \ raw_folder.display_name: folder.category.display_name = raw_folder.display_name # noqa else: log.info('Creating category for folder', account_id=self.account_id, folder_name=folder.name) folder.category = Category.find_or_create( db_session, namespace_id=account.namespace.id, name=raw_folder.role, display_name=raw_folder.display_name, type_='folder') else: Folder.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) # Ensure sync_should_run is True for the folders we want to sync (for # Gmail, that's just all folders, since we created them above if # they didn't exist.) for folder in account.folders: if folder.imapsyncstatus: folder.imapsyncstatus.sync_should_run = True # Go through the labels which have been "deleted" (i.e: they don't # show up when running LIST) and mark them as such. # We can't delete labels directly because Gmail allows users to hide # folders --- we need to check that there's no messages still # associated with the label. deleted_labels = old_labels - new_labels for deleted_label in deleted_labels: deleted_label.deleted_at = datetime.now() cat = deleted_label.category cat.deleted_at = datetime.now() db_session.commit()
def save_folder_names(self, db_session, raw_folders): """ Save the folders, labels present on the remote backend for an account. * Create Folder/ Label objects. * Delete Folders/ Labels that no longer exist on the remote. Notes ----- Gmail uses IMAP folders and labels. Canonical folders ('all', 'trash', 'spam') are therefore mapped to both Folder and Label objects, everything else is created as a Label only. We don't canonicalize names to lowercase when saving because different backends may be case-sensitive or otherwise - code that references saved names should canonicalize if needed when doing comparisons. """ account = db_session.query(Account).get(self.account_id) old_labels = { label for label in db_session.query(Label).filter( Label.account_id == self.account_id, Label.deleted_at == None) } new_labels = set() # Create new labels, folders for raw_folder in raw_folders: if raw_folder.role == 'starred': # The starred state of messages is tracked separately # (we set Message.is_starred from the '\\Flagged' flag) continue label = Label.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) new_labels.add(label) if label.deleted_at is not None: # This is a label which was previously marked as deleted # but which mysteriously reappeared. Unmark it. log.info('Deleted label recreated on remote', name=raw_folder.display_name) label.deleted_at = None label.category.deleted_at = None if raw_folder.role in ('all', 'spam', 'trash'): folder = db_session.query(Folder).filter( Folder.account_id == account.id, Folder.canonical_name == raw_folder.role).first() if folder: if folder.name != raw_folder.display_name: log.info('Folder name changed on remote', account_id=self.account_id, role=raw_folder.role, new_name=raw_folder.display_name, name=folder.name) folder.name = raw_folder.display_name if folder.category: if folder.category.display_name != \ raw_folder.display_name: folder.category.display_name = raw_folder.display_name # noqa else: log.info('Creating category for folder', account_id=self.account_id, folder_name=folder.name) folder.category = Category.find_or_create( db_session, namespace_id=account.namespace.id, name=raw_folder.role, display_name=raw_folder.display_name, type_='folder') else: Folder.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) # Ensure sync_should_run is True for the folders we want to sync (for # Gmail, that's just all folders, since we created them above if # they didn't exist.) for folder in account.folders: if folder.imapsyncstatus: folder.imapsyncstatus.sync_should_run = True # Go through the labels which have been "deleted" (i.e: they don't # show up when running LIST) and mark them as such. # We can't delete labels directly because Gmail allows users to hide # folders --- we need to check that there's no messages still # associated with the label. deleted_labels = old_labels - new_labels for deleted_label in deleted_labels: deleted_label.deleted_at = datetime.now() cat = deleted_label.category cat.deleted_at = datetime.now() db_session.commit()
def save_folder_names(self, db_session, raw_folders): """ Save the folders, labels present on the remote backend for an account. * Create Folder/ Label objects. * Delete Folders/ Labels that no longer exist on the remote. Notes ----- Gmail uses IMAP folders and labels. Canonical folders ('all', 'trash', 'spam') are therefore mapped to both Folder and Label objects, everything else is created as a Label only. We don't canonicalize names to lowercase when saving because different backends may be case-sensitive or otherwise - code that references saved names should canonicalize if needed when doing comparisons. """ account = db_session.query(Account).get(self.account_id) # Create new labels, folders for raw_folder in raw_folders: if raw_folder.role == 'starred': # The starred state of messages is tracked separately # (we set Message.is_starred from the '\\Flagged' flag) continue Label.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) if raw_folder.role in ('all', 'spam', 'trash'): folder = db_session.query(Folder). \ filter(Folder.account_id == account.id, Folder.canonical_name == raw_folder.role). \ first() if folder: if folder.name != raw_folder.display_name: log.info('Folder name changed on remote', account_id=self.account_id, role=raw_folder.role, new_name=raw_folder.display_name, name=folder.name) folder.name = raw_folder.display_name if folder.category: if folder.category.display_name != \ raw_folder.display_name: folder.category.display_name = raw_folder.display_name else: log.info('Creating category for folder', account_id=self.account_id, folder_name=folder.name) folder.category = Category.find_or_create( db_session, namespace_id=account.namespace.id, name=raw_folder.role, display_name=raw_folder.display_name, type_='folder') else: Folder.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) # Ensure sync_should_run is True for the folders we want to sync (for # Gmail, that's just all folders, since we created them above if # they didn't exist.) for folder in account.folders: if folder.imapsyncstatus: folder.imapsyncstatus.sync_should_run = True db_session.commit()
def test_received_before_after(db, api_client, default_namespace): thread = add_fake_thread(db.session, default_namespace.id) message = add_fake_message(db.session, default_namespace.id, thread, to_addr=[('Bob', '*****@*****.**')], from_addr=[('Alice', '*****@*****.**')], received_date=datetime.datetime(year=1999, day=20, month=03), subject='some subject') thread2 = add_fake_thread(db.session, default_namespace.id) message2 = add_fake_message(db.session, default_namespace.id, thread, to_addr=[('Bob', '*****@*****.**')], from_addr=[('Alice', '*****@*****.**')], received_date=datetime.datetime(year=2000, day=20, month=03), subject='another subject') inbox = Category(namespace_id=message.namespace_id, name='inbox', display_name='Inbox', type_='label') message.categories.add(inbox) thread.subject = message.subject message2.categories.add(inbox) thread2.subject = message2.subject db.session.commit() received_date = message.received_date t_epoch = dt_to_timestamp(datetime.datetime(year=1998, month=2, day=3)) t_firstmsg = dt_to_timestamp(received_date) results = api_client.get_data( '/messages?received_before={}'.format(t_epoch)) assert len(results) == 0 # received_before should be inclusive (i.e: match <=, not just <). results = api_client.get_data( '/messages?received_before={}'.format(t_firstmsg)) assert len(results) == 1 t1 = dt_to_timestamp(received_date + datetime.timedelta(days=1)) results = api_client.get_data('/messages?received_after={}'.format(t1)) assert len(results) == 1 results = api_client.get_data( '/messages?received_before={}&received_after={}'.format( t1, t_firstmsg)) assert len(results) == 0 # bogus values results = api_client.get_data( '/messages?received_before={}&received_after={}'.format(t_epoch, t1)) assert len(results) == 0