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) remote_label_names = {l.display_name.rstrip()[:MAX_LABEL_NAME_LENGTH] for l in raw_folders} assert "all" in {f.role for f in raw_folders}, "Account {} has no detected All Mail folder".format( account.email_address ) local_labels = {l.name: l for l in db_session.query(Label).filter(Label.account_id == self.account_id).all()} # Delete labels no longer present on the remote. # Note that the label with canonical_name='all' cannot be deleted; # remote_label_names will always contain an entry corresponding to it. discard = set(local_labels) - set(remote_label_names) for name in discard: log.info("Label deleted from remote", account_id=self.account_id, name=name) db_session.delete(local_labels[name]) # Create new labels, folders for raw_folder in raw_folders: Label.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) if raw_folder.role in ("all", "spam", "trash"): folder = Folder.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) 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 # 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 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 = Folder.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) 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 # 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 populate_labels(uid, account, db_session): from inbox.models import Label existing_labels = {(l.name, l.canonical_name): l for l in account.labels} uid.is_draft = "\\Draft" in uid.g_labels uid.is_starred = "\\Starred" in uid.g_labels category_map = { "\\Inbox": "inbox", "\\Important": "important", "\\Sent": "sent" } remote_labels = set() for label_string in uid.g_labels: if label_string in ("\\Draft", "\\Starred"): continue elif label_string in category_map: remote_labels.add( (category_map[label_string], category_map[label_string])) else: remote_labels.add((label_string, None)) for key in remote_labels: if key not in existing_labels: label = Label.find_or_create(db_session, account, key[0], key[1]) uid.labels.add(label) account.labels.append(label) else: uid.labels.add(existing_labels[key])
def populate_labels(uid, account, db_session): from inbox.models import Label existing_labels = {(l.name, l.canonical_name): l for l in account.labels} uid.is_draft = "\\Draft" in uid.g_labels uid.is_starred = "\\Starred" in uid.g_labels category_map = {"\\Inbox": "inbox", "\\Important": "important", "\\Sent": "sent"} remote_labels = set() for label_string in uid.g_labels: if label_string in ("\\Draft", "\\Starred"): continue elif label_string in category_map: remote_labels.add((category_map[label_string], category_map[label_string])) else: remote_labels.add((label_string, None)) for key in remote_labels: if key not in existing_labels: label = Label.find_or_create(db_session, account, key[0], key[1]) uid.labels.add(label) account.labels.append(label) else: uid.labels.add(existing_labels[key])
def test_not_deleting_canonical_folders(empty_db, default_account): # Create a label w/ no messages attached. label = Label.find_or_create(empty_db.session, default_account, '[Gmail]/Tous les messages') label.canonical_name = 'all' empty_db.session.commit() monitor = GmailSyncMonitor(default_account) folder_names_and_roles = { ('[Gmail]/Corbeille', 'trash'), ('[Gmail]/Spam', 'spam'), ('Recettes', None), } raw_folders = [RawFolder(*args) for args in folder_names_and_roles] monitor.save_folder_names(empty_db.session, raw_folders) label = empty_db.session.query(Label).get(label.id) assert label.deleted_at is None
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 label(db, default_account): from inbox.models import Label return Label.find_or_create(db.session, default_account, 'Inbox', 'inbox')
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) remote_label_names = { l.display_name.rstrip()[:MAX_LABEL_NAME_LENGTH] for l in raw_folders } assert 'all' in {f.role for f in raw_folders},\ 'Account {} has no detected All Mail folder'.\ format(account.email_address) local_labels = { l.name: l for l in db_session.query(Label).filter( Label.account_id == self.account_id).all() } # Delete labels no longer present on the remote. # Note that the label with canonical_name='all' cannot be deleted; # remote_label_names will always contain an entry corresponding to it. discard = set(local_labels) - set(remote_label_names) for name in discard: log.info('Label deleted from remote', account_id=self.account_id, name=name) db_session.delete(local_labels[name]) # Create new labels, folders for raw_folder in raw_folders: Label.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) if raw_folder.role in ('all', 'spam', 'trash'): folder = Folder.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) 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 # 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 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) current_labels = set() old_labels = {label for label in db_session.query(Label).filter( Label.account_id == self.account_id, Label.deleted_at == None)} # noqa # Is it the first time we've been syncing folders? # It's important to know this because we don't want to # be refreshing the labels for every message at the very # beginning of the initial sync. first_time_syncing_folders = len(old_labels) == 0 # 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 if raw_folder.role in ('all', 'spam', 'trash'): self.handle_raw_folder_change(db_session, account, raw_folder) label = Label.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) 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 current_labels.add(label) new_labels = current_labels - old_labels db_session.commit() if not first_time_syncing_folders: # Try to see if a label has been renamed. for label in new_labels: db_session.refresh(label) db_session.expunge(label) rename_handler = LabelRenameHandler( account_id=self.account_id, namespace_id=self.namespace_id, label_name=label.name, semaphore=self.label_rename_semaphore) rename_handler.start() self.set_sync_should_run_bit(account) deleted_labels = old_labels - current_labels self.mark_deleted_labels(db_session, deleted_labels) 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) current_labels = set() old_labels = {label for label in db_session.query(Label).filter( Label.account_id == self.account_id, Label.deleted_at == None)} # noqa # Is it the first time we've been syncing folders? # It's important to know this because we don't want to # be refreshing the labels for every message at the very # beginning of the initial sync. first_time_syncing_folders = len(old_labels) == 0 # 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 if raw_folder.role in ('all', 'spam', 'trash'): self.handle_raw_folder_change(db_session, account, raw_folder) label = Label.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) 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 = EPOCH current_labels.add(label) new_labels = current_labels - old_labels db_session.commit() if not first_time_syncing_folders: # Try to see if a label has been renamed. for label in new_labels: db_session.refresh(label) db_session.expunge(label) rename_handler = LabelRenameHandler( account_id=self.account_id, namespace_id=self.namespace_id, label_name=label.name, semaphore=self.label_rename_semaphore) rename_handler.start() self.set_sync_should_run_bit(account) deleted_labels = old_labels - current_labels self.mark_deleted_labels(db_session, deleted_labels) 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()