flexget.plugins.filter.reject_failed
Covered: 81 lines
Missed: 36 lines
Skipped 31 lines
Percent: 69 %
  1
import logging
  2
from datetime import datetime
  3
from sqlalchemy import Column, Integer, String, Unicode, DateTime
  4
from sqlalchemy.schema import Index, MetaData
  5
from flexget import schema
  6
from flexget.plugin import register_plugin, register_parser_option, priority
  7
from flexget.manager import Session
  8
from flexget.utils.tools import console
  9
from flexget.utils.sqlalchemy_utils import table_add_column
 11
SCHEMA_VER = 1
 13
log = logging.getLogger('failed')
 14
Base = schema.versioned_base('failed', SCHEMA_VER)
 17
@schema.upgrade('failed')
 18
def upgrade(ver, session):
 19
    if ver is None:
 21
        table_add_column('failed', 'count', Integer, session, default=1)
 22
        ver = 0
 23
    if ver == 0:
 25
        log.info('Adding database index ...')
 26
        meta = MetaData(bind=session.connection(), reflect=True)
 27
        failed = meta.tables['failed']
 28
        Index('failed_title_url', failed.c.title, failed.c.url, failed.c.count).create()
 29
        ver = 1
 30
    return ver
 33
class FailedEntry(Base):
 34
    __tablename__ = 'failed'
 36
    id = Column(Integer, primary_key=True)
 37
    title = Column(Unicode)
 38
    url = Column(String)
 39
    tof = Column(DateTime)
 40
    count = Column(Integer, default=1)
 42
    def __init__(self, title, url):
 43
        self.title = title
 44
        self.url = url
 45
        self.tof = datetime.now()
 47
    def __str__(self):
 48
        return '<Failed(title=%s)>' % self.title
 51
columns = Base.metadata.tables['failed'].c
 52
Index('failed_title_url', columns.title, columns.url, columns.count)
 55
class PluginFailed(object):
 56
    """Provides tracking for failures and related commandline utilities."""
 58
    def on_process_start(self, feed, config):
 59
        if feed.manager.options.failed:
 60
            feed.manager.disable_feeds()
 61
            self.print_failed()
 62
            return
 63
        if feed.manager.options.clear_failed:
 64
            feed.manager.disable_feeds()
 65
            self.clear_failed()
 66
            return
 68
    def print_failed(self):
 69
        """Parameter --failed"""
 71
        failed = Session()
 72
        try:
 73
            results = failed.query(FailedEntry).all()
 74
            if not results:
 75
                console('No failed entries recorded')
 76
            for entry in results:
 77
                console('%16s - %s - %s times' % (entry.tof.strftime('%Y-%m-%d %H:%M'), entry.title, entry.count))
 78
        finally:
 79
            failed.close()
 82
    def add_failed(self, entry):
 83
        """Adds entry to internal failed list, displayed with --failed"""
 84
        failed = Session()
 85
        try:
 87
            item = failed.query(FailedEntry).filter(FailedEntry.title == entry['title']).\
 88
                                             filter(FailedEntry.url == entry['url']).first()
 89
            if not item:
 90
                item = FailedEntry(entry['title'], entry['url'])
 91
            else:
 92
                item.count += 1
 93
                item.tof = datetime.now()
 94
            failed.merge(item)
 95
            log.debug('Marking %s in failed list. Has failed %s times.' % (item.title, item.count))
 98
            for row in failed.query(FailedEntry).order_by(FailedEntry.tof.desc())[25:]:
 99
                failed.delete(row)
100
            failed.commit()
101
        finally:
102
            failed.close()
104
    def clear_failed(self):
105
        """Clears list of failed entries"""
106
        session = Session()
107
        try:
108
            results = session.query(FailedEntry).all()
109
            for row in results:
110
                session.delete(row)
111
            console('Cleared %i items.' % len(results))
112
            session.commit()
113
        finally:
114
            session.close()
116
    def on_entry_fail(self, feed, entry, **kwargs):
117
        self.add_failed(entry)
120
class FilterRejectFailed(object):
121
    """Rejects entries that have failed X or more times in the past."""
123
    def validator(self):
124
        from flexget import validator
125
        root = validator.factory()
126
        root.accept('integer')
127
        root.accept('boolean')
128
        return root
130
    @priority(255)
131
    def on_feed_filter(self, feed, config):
132
        if config is False:
133
            return
134
        max_count = 3 if config in [None, True] else config
135
        for entry in feed.entries:
136
            item = feed.session.query(FailedEntry).filter(FailedEntry.title == entry['title']).\
137
                                            filter(FailedEntry.url == entry['url']).\
138
                                            filter(FailedEntry.count >= max_count).first()
139
            if item:
140
                feed.reject(entry, 'Has already failed %s times in the past' % item.count)
142
register_plugin(PluginFailed, '--failed', builtin=True, api_ver=2)
143
register_plugin(FilterRejectFailed, 'reject_failed', builtin=True, api_ver=2)
144
register_parser_option('--failed', action='store_true', dest='failed', default=0,
145
                       help='List recently failed entries.')
146
register_parser_option('--clear', action='store_true', dest='clear_failed', default=0,
147
                       help='Clear recently failed list.')