Test Failed
Push — develop ( 572338...fb7300 )
by Dean
02:29
created

SyncStateTrakt.refresh()   B

Complexity

Conditions 2

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.9322
Metric Value
cc 2
dl 0
loc 24
ccs 5
cts 13
cp 0.3846
crap 2.9322
rs 8.9713

1 Method

Rating   Name   Duplication   Size   Complexity  
A SyncStateTrakt.setup_progress_group() 0 6 1
1 1
from plugin.core.database import Database
2 1
from plugin.sync.core.enums import SyncData
0 ignored issues
show
Unused Code introduced by
Unused SyncData imported from plugin.sync.core.enums
Loading history...
3
4 1
from stash import ApswArchive
5 1
from trakt_sync.cache.backends import StashBackend
0 ignored issues
show
Bug introduced by
The name backends does not seem to exist in module trakt_sync.cache.
Loading history...
Configuration introduced by
Unable to import 'trakt_sync.cache.backends' (invalid syntax (<string>, line 11))

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
6 1
from trakt_sync.cache.main import Cache
7 1
from trakt_sync.differ.core.base import KEY_AGENTS
8 1
import elapsed
9 1
import logging
10 1
import trakt.objects
11
12 1
log = logging.getLogger(__name__)
13
14
15 1
class SyncStateTrakt(object):
16 1
    def __init__(self, state):
17 1
        self.state = state
18 1
        self.task = state.task
19
20 1
        self.cache = None
21
22 1
        self.changes = None
23 1
        self.table = Table(self.task)
24
25 1
    def load(self):
26
        # Construct cache
27
        self.cache = self._build_cache()
28
29
        # Load table handler
30
        self.table.load()
31
32 1
    def _build_cache(self):
33
        def storage(name):
34
            return StashBackend(
35
                ApswArchive(Database.cache('trakt'), name),
36
                'lru:///?capacity=500&compact_threshold=1500',
37
                'pickle:///?protocol=2'
38
            )
39
40
        cache = Cache(self.task.media, self.task.data, storage)
41
42 1
        # Bind to cache events
43
        cache.events.on([
44
            'refresh.sync.progress',
45
            'refresh.list.progress'
46
        ], self.on_refresh_progress)
47
48
        return cache
49
50
    def on_refresh_progress(self, source, current):
0 ignored issues
show
Unused Code introduced by
The argument current seems to be unused.
Loading history...
51
        # Step refresh progress for `source`
52
        self.task.progress.group(SyncStateTrakt, 'refresh:%s' % source).step()
53
54
    def __getitem__(self, key):
55 1
        collection = [
56
            self.task.account.trakt.username,
57
            Cache.Media.get(key[0]),
58
            Cache.Data.get(key[1])
59
        ]
60
61
        if len(key) > 2:
62
            # Include extra parameters (list id)
63
            collection.extend(key[2:])
64 1
65
        return self.cache[collection]
66
67
    def invalidate(self, *key):
68
        """Invalidate collection in trakt cache"""
69
        username = self.task.account.trakt.username
70
71
        # Invalidate collection
72
        self.cache.invalidate([username] + list(key))
73
74
        log.debug('Invalidated trakt cache %r for account: %r', key, username)
75
76
    @elapsed.clock
77
    def refresh(self):
78 1
        # Task checkpoint
79
        self.task.checkpoint()
80
81
        # Construct progress groups
82
        def setup_progress_group(source):
83 1
            # Retrieve steps from cache source
84
            steps = self.cache.source(source).steps()
85
86
            # Setup progress group with total steps
87
            self.task.progress.group(SyncStateTrakt, 'refresh:%s' % source).add(steps)
88
89
        setup_progress_group('list')
90
        setup_progress_group('sync')
91
92
        # Refresh cache for account, store changes
93
        self.changes = self.cache.refresh(self.task.account.trakt.username)
94
95
        # Resolve changes
96
        self.changes = list(self.changes)
97 1
98 1
        # Reset current table
99 1
        self.table.reset()
100
101 1
    @elapsed.clock
102
    def build_table(self):
103 1
        # Build table from cache
104 1
        self.table.build(self.cache)
105 1
106
    @elapsed.clock
107 1
    def flush(self):
108 1
        with elapsed.clock(SyncStateTrakt, 'flush:collections'):
109
            # Flush trakt collections to disk
110 1
            self.cache.collections.flush()
111
112
        with elapsed.clock(SyncStateTrakt, 'flush:stores'):
113
            # Flush trakt stores to disk
114
            for key, store in self.cache.stores.items():
115
                log.debug('[%-38s] Flushing collection...', '/'.join(key))
116
117
                store.flush()
118
119
120
class Table(object):
121
    def __init__(self, task):
122 1
        self.task = task
123
124
        self.table = None
125
126
        self.movies = None
127
        self.shows = None
128
        self.episodes = None
129 1
130
        self._data = None
131
        self._media = None
132
133
    def load(self):
134
        # Parse data/media enums into lists
135
        self._data = [
136
            Cache.Data.get(d)
137
            for d in Cache.Data.parse(self.task.data)
138
        ]
139
140
        self._media = [
141
            Cache.Media.get(m)
142
            for m in Cache.Media.parse(self.task.media)
143
        ]
144
145
    def reset(self):
146
        self.table = None
147
148
        self.movies = None
149
        self.shows = None
150
        self.episodes = None
151
152
    def build(self, cache):
153
        # Map item `keys` into a table
154
        self.table = {}
155
156
        self.movies = set()
157
        self.shows = set()
158
        self.episodes = {}
159
160
        log.debug('Building table...')
161
162
        log.debug(' - Data: %s', ', '.join([
163
            '/'.join(x) if type(x) is tuple else x
164
            for x in self._data
165
        ]))
166
167
        log.debug(' - Media: %s', ', '.join([
168
            '/'.join(x) if type(x) is tuple else x
169
            for x in self._media
170
        ]))
171
172
        # Construct progress group
173
        self.task.progress.group(Table, 'build').add(len(cache.collections))
174
175
        # Map each item in cache collections
176
        for key in cache.collections:
177
            # Increment one step
178
            self.task.progress.group(Table, 'build').step()
179
180
            # Parse `key`
181
            if len(key) == 3:
182
                # Sync
183
                username, media, data = key
184
            elif len(key) == 4:
185
                # Lists
186
                username, media, data = tuple(key[0:3])
187
            else:
188
                log.warn('Unknown key: %r', key)
189 1
                continue
190
191
            if username != self.task.account.trakt.username:
192
                # Collection isn't for the current account
193
                continue
194
195
            if media and media not in self._media:
196
                log.debug('[%-38s] Media %r has not been enabled', '/'.join(key), media)
197
                continue
198 1
199
            if data not in self._data:
200
                log.debug('[%-38s] Data %r has not been enabled', '/'.join(key), data)
201
                continue
202
203
            # Map store items
204
            if data in [
205
                Cache.Data.get(Cache.Data.Liked),
206
                Cache.Data.get(Cache.Data.Personal)
207
            ]:
208
                self.map_items(key, cache[key])
209
            else:
210
                self.map_items(key, cache[key], media)
211
212
        log.debug(
213
            'Built table with %d keys (movies: %d, shows: %d, episodes: %d)',
214
            len(self.table),
215
            len(self.movies),
216
            len(self.shows),
217 1
            len(self.episodes)
218
        )
219
220
    def get_keys(self, key, media):
0 ignored issues
show
Unused Code introduced by
The argument key seems to be unused.
Loading history...
221
        if media == 'movies':
222
            return self.movies
223
224
        if media in ['shows', 'seasons', 'episodes']:
225
            return self.shows
226
227
        return None
228
229
    @staticmethod
230
    def get_media(item):
231
        i_type = type(item)
232
233
        if i_type is trakt.objects.Movie:
234
            return 'movies'
235
236
        if i_type is trakt.objects.Show:
237
            return 'shows'
238
239
        if i_type is trakt.objects.Season:
240
            return 'seasons'
241
242
        if i_type is trakt.objects.Episode:
243
            return 'episodes'
244
245
        log.warn('Unknown item type: %r', i_type)
246
        return None
247
248
    def map_items(self, key, store, media=None):
249
        # Retrieve key map
250
        if media is not None:
251
            keys = self.get_keys(key, media)
252
253
            if keys is None:
254
                log.debug('[%-38s] Collection has been ignored (unknown/unsupported media)', '/'.join(key))
255
                return
256
        else:
257
            keys = None
258
259
        # Map each item in store
260
        log.debug('[%-38s] Building table from collection...', '/'.join(key))
261
262
        for pk, item in store.iteritems():
263
            # Trim `pk` season/episode values
264
            if len(pk) > 2:
265
                pk = tuple(pk[:2])
266
267
            if pk[0] not in ['imdb', 'tvdb']:
268
                log.info('Ignoring item with an unknown primary agent: %r', pk)
269
                continue
270
271
            # Detect media type from `item`
272
            if media is not None:
273
                i_media = media
274
                i_keys = keys
275
            else:
276
                i_media = self.get_media(item)
277
                i_keys = self.get_keys(key, i_media)
278
279
            # Store `pk` in `keys
280
            if i_keys is not None:
281
                i_keys.add(pk)
282
283
            # Map `item.keys` -> `pk`
284
            for key in item.keys:
285
                # Expand `key`
286
                if type(key) is not tuple or len(key) != 2:
287 1
                    continue
288 1
289
                agent, _ = key
290 1
291
                # Check if agent is supported
292
                if agent not in KEY_AGENTS:
293
                    continue
294
295
                # Store key in table
296
                if key in self.table:
297
                    continue
298
299
                self.table[key] = pk
300
301
            # Map episodes in show
302
            if i_media == 'episodes':
303
                if type(item) is trakt.objects.Show:
304
                    if pk not in self.episodes:
305
                        self.episodes[pk] = set()
306
307
                    for identifier, _ in item.episodes():
308
                        self.episodes[pk].add(identifier)
309
                elif type(item) is trakt.objects.Episode:
310
                    # TODO
311
                    pass
312
                else:
313
                    log.debug('Unknown episode item: %r', item)
314
315
        # Task checkpoint
316
        self.task.checkpoint()
317
318
    def get(self, key, default=None):
319
        return self.table.get(key, default)
320
321
    def __getitem__(self, key):
322
        return self.table[key]
323