<commit_before>from sqlalchemy import Column, Integer, Text, ForeignKey, Boolean
from sqlalchemy.orm import relationship
from sqlalchemy.sql.expression import false

from inbox.sqlalchemy_ext.util import JSON
from inbox.models.base import MailSyncBase
from inbox.models.namespace import Namespace

ADD_TAG_ACTIONS = {
    'inbox': 'unarchive',
    'archive': 'archive',
    'starred': 'star',
    'unread': 'mark_unread',
    'spam': 'mark_spam',
    'trash': 'mark_trash'
}

REMOVE_TAG_ACTIONS = {
    'inbox': 'archive',
    'archive': 'unarchive',
    'starred': 'unstar',
    'unread': 'mark_read',
    'spam': 'unmark_spam',
    'trash': 'unmark_trash'
}


class ActionError(Exception):
    def __init__(self, error, namespace_id):
        self.error = error
        self.namespace_id = namespace_id

    def __str__(self):
        return 'Error {0} for namespace_id {1}'.format(
            self.error, self.namespace_id)


def schedule_action_for_tag(tag_public_id, thread, db_session, tag_added):
    if tag_added:
        action = ADD_TAG_ACTIONS.get(tag_public_id)
    else:
        action = REMOVE_TAG_ACTIONS.get(tag_public_id)
    if action is not None:
        schedule_action(action, thread, thread.namespace_id, db_session)


def schedule_action(func_name, record, namespace_id, db_session, **kwargs):
    # Ensure that the record's id is non-null
    db_session.flush()

    # Ensure account is valid
    account = db_session.query(Namespace).get(namespace_id).account

    if account.sync_state == 'invalid':
        raise ActionError(error=403, namespace_id=namespace_id)

    if account.sync_state == 'connerror':
        raise ActionError(error=503, namespace_id=namespace_id)

    log_entry = ActionLog(
        action=func_name,
        table_name=record.__tablename__,
        record_id=record.id,
        namespace_id=namespace_id,
        extra_args=kwargs)
    db_session.add(log_entry)


class ActionLog(MailSyncBase):
    # STOPSHIP(emfree) should we set ondelete='CASCADE' here?
    namespace_id = Column(ForeignKey(Namespace.id), nullable=False, index=True)
    namespace = relationship(
        'Namespace',
        primaryjoin='and_(ActionLog.namespace_id==Namespace.id, '
                    'Namespace.deleted_at.is_(None))')

    action = Column(Text(40), nullable=False)
    record_id = Column(Integer, nullable=False)
    table_name = Column(Text(40), nullable=False)
    executed = Column(Boolean, server_default=false(), nullable=False)

    extra_args = Column(JSON, nullable=True)
<commit_msg>Don't refuse to schedule syncback actions for accounts in 'connerror' state.<commit_after>from sqlalchemy import Column, Integer, Text, ForeignKey, Boolean
from sqlalchemy.orm import relationship
from sqlalchemy.sql.expression import false

from inbox.sqlalchemy_ext.util import JSON
from inbox.models.base import MailSyncBase
from inbox.models.namespace import Namespace

ADD_TAG_ACTIONS = {
    'inbox': 'unarchive',
    'archive': 'archive',
    'starred': 'star',
    'unread': 'mark_unread',
    'spam': 'mark_spam',
    'trash': 'mark_trash'
}

REMOVE_TAG_ACTIONS = {
    'inbox': 'archive',
    'archive': 'unarchive',
    'starred': 'unstar',
    'unread': 'mark_read',
    'spam': 'unmark_spam',
    'trash': 'unmark_trash'
}


class ActionError(Exception):
    def __init__(self, error, namespace_id):
        self.error = error
        self.namespace_id = namespace_id

    def __str__(self):
        return 'Error {0} for namespace_id {1}'.format(
            self.error, self.namespace_id)


def schedule_action_for_tag(tag_public_id, thread, db_session, tag_added):
    if tag_added:
        action = ADD_TAG_ACTIONS.get(tag_public_id)
    else:
        action = REMOVE_TAG_ACTIONS.get(tag_public_id)
    if action is not None:
        schedule_action(action, thread, thread.namespace_id, db_session)


def schedule_action(func_name, record, namespace_id, db_session, **kwargs):
    # Ensure that the record's id is non-null
    db_session.flush()

    # Ensure account is valid
    account = db_session.query(Namespace).get(namespace_id).account

    if account.sync_state == 'invalid':
        raise ActionError(error=403, namespace_id=namespace_id)

    log_entry = ActionLog(
        action=func_name,
        table_name=record.__tablename__,
        record_id=record.id,
        namespace_id=namespace_id,
        extra_args=kwargs)
    db_session.add(log_entry)


class ActionLog(MailSyncBase):
    # STOPSHIP(emfree) should we set ondelete='CASCADE' here?
    namespace_id = Column(ForeignKey(Namespace.id), nullable=False, index=True)
    namespace = relationship(
        'Namespace',
        primaryjoin='and_(ActionLog.namespace_id==Namespace.id, '
                    'Namespace.deleted_at.is_(None))')

    action = Column(Text(40), nullable=False)
    record_id = Column(Integer, nullable=False)
    table_name = Column(Text(40), nullable=False)
    executed = Column(Boolean, server_default=false(), nullable=False)

    extra_args = Column(JSON, nullable=True)

{'fraction_non_alphanumeric': 0.11002785515320335, 'fraction_numerical': 0.004178272980501393, 'mean_word_length': 3.8105263157894735, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3595876', 'n_tokens_mistral': 1633, 'n_tokens_neox': 1588, 'n_words': 340}