Passed
Push — develop ( 456c9f...9a88f4 )
by Dean
03:13
created

Sessions._get_session_key()   A

Complexity

Conditions 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 11
ccs 0
cts 8
cp 0
rs 9.4285
cc 3
crap 12
1
from plugin.core.helpers.thread import synchronized
0 ignored issues
show
Bug introduced by
The name thread does not seem to exist in module plugin.core.helpers.
Loading history...
Configuration introduced by
Unable to import 'plugin.core.helpers.thread' (invalid syntax (<string>, line 98))

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...
2
from plugin.modules.core.base import Module
3
from plugin.preferences import Preferences
4
5
from datetime import datetime, timedelta
6
from threading import RLock
7
import logging
8
9
log = logging.getLogger(__name__)
10
11
12
class Sessions(Module):
13
    __key__ = 'sessions'
14
15
    def __init__(self):
16
        self._lock = RLock()
17
18
        self._by_account = {}
19
        self._by_session_key = {}
20
21
        self._idle_since = datetime.min
22
23
    def is_account_streaming(self, account, rating_key=None):
24
        if type(account) is not int:
25
            account = account.id
26
27
        return self._by_account.get(account, {}).get(rating_key, False)
28
29
    def is_idle(self):
30
        return self._idle_since and datetime.utcnow() - self._idle_since > timedelta(seconds=Preferences.get('sync.idle_delay'))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (128/120).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
31
32
    def is_streaming(self):
33
        if self._by_session_key:
34
            return True
35
36
        return False
37
38
    #
39
    # Session methods
40
    #
41
42
    @synchronized(lambda self: self._lock)
43
    def create(self, session):
44
        if session.state not in ['create', 'start', 'pause']:
45
            return
46
47
        # Retrieve session parameters
48
        account_id = self._get_session_account_id(session)
49
        rating_key = session.rating_key
50
51
        # Build session key
52
        session_key = self._get_session_key(session)
53
54
        if session_key is None or session_key.endswith(':None'):
0 ignored issues
show
Bug introduced by
The Instance of int does not seem to have a member named endswith.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
55
            return
56
57
        # Construct session
58
        state = SessionState(session_key, account_id, rating_key)
59
60
        # Ensure account exists in `active`
61
        if account_id not in self._by_account:
62
            self._by_account[account_id] = {}
63
64
        # Store item in `active`
65
        self._by_account[account_id][rating_key] = state
66
67
        # Store session in `active_by_id` map
68
        self._by_session_key[session_key] = state
69
70
        # Clear idle status
71
        self._idle_since = None
72
73
    @synchronized(lambda self: self._lock)
74
    def delete(self, state):
75
        # Remove item from `active` dictionary
76
        if state.account_id in self._by_account and state.rating_key in self._by_account[state.account_id]:
77
            try:
78
                del self._by_account[state.account_id][state.rating_key]
79
            except KeyError:
0 ignored issues
show
Unused Code introduced by
This except handler seems to be unused and could be removed.

Except handlers which only contain pass and do not have an else clause can usually simply be removed:

try:
    raises_exception()
except:  # Could be removed
    pass
Loading history...
80
                pass
81
        else:
82
            log.debug('Unable to find item %r for account %r', state.rating_key, state.account_id)
83
84
        # Remove item from `active_by_id` dictionary
85
        if state.session_key in self._by_session_key:
86
            try:
87
                del self._by_session_key[state.session_key]
88
            except KeyError:
0 ignored issues
show
Unused Code introduced by
This except handler seems to be unused and could be removed.

Except handlers which only contain pass and do not have an else clause can usually simply be removed:

try:
    raises_exception()
except:  # Could be removed
    pass
Loading history...
89
                pass
90
        else:
91
            log.debug('Unable to find session %r', state.session_key)
92
93
        # Update idle status
94
        if not self._by_session_key:
95
            self._idle_since = datetime.utcnow()
96
97
        return True
98
99
    @synchronized(lambda self: self._lock)
100
    def replace(self, state, session):
101
        # Remove current state
102
        self.delete(state)
103
104
        # Create new session
105
        self.create(session)
106
107
    @synchronized(lambda self: self._lock)
108
    def update(self, session):
109
        # Retrieve session parameters
110
        s_account_id = self._get_session_account_id(session)
111
112
        if s_account_id is None:
113
            return
114
115
        # Build session key
116
        session_key = self._get_session_key(session)
117
118
        if session_key is None:
119
            return
120
121
        # Check if session exists
122
        if session_key not in self._by_session_key:
123
            self.create(session)
124
            return
125
126
        # Check if an update is required
127
        state = self._by_session_key[session_key]
128
129
        if state.account_id != s_account_id or state.rating_key != session.rating_key:
130
            # Replace session details
131
            self.replace(state, session)
132
        elif session.state == 'stop':
133
            # Delete current session
134
            self.delete(state)
135
        else:
136
            # Update session
137
            state.update(session)
138
139
    #
140
    # Event handlers
141
    #
142
143
    @synchronized(lambda self: self._lock)
144
    def on_created(self, session):
145
        # Store session
146
        self.create(session)
147
148
        # Display active session message
149
        self._log()
150
151
    @synchronized(lambda self: self._lock)
152
    def on_updated(self, session):
153
        # Update session
154
        self.update(session)
155
156
        # Display active session message
157
        self._log()
158
159
    #
160
    # Helpers
161
    #
162
163
    def _log(self):
164
        if not self._by_session_key:
165
            return
166
167
        log.debug('%d active session(s): %r', len(self._by_session_key), self._by_session_key)
168
169
    @classmethod
170
    def _get_session_account_id(cls, session):
171
        try:
172
            return session.account_id
173
        except KeyError:
174
            return None
175
176
    @classmethod
177
    def _get_session_key(cls, session):
178
        if session.session_key is None:
179
            return None
180
181
        try:
182
            return int(session.session_key)
183
        except ValueError:
0 ignored issues
show
Unused Code introduced by
This except handler seems to be unused and could be removed.

Except handlers which only contain pass and do not have an else clause can usually simply be removed:

try:
    raises_exception()
except:  # Could be removed
    pass
Loading history...
184
            pass
185
186
        return session.session_key
187
188
189
class SessionState(object):
190
    def __init__(self, session_key, account_id, rating_key):
191
        self.session_key = session_key
192
193
        self.account_id = account_id
194
        self.rating_key = rating_key
195
196
        self.seen_at = datetime.utcnow()
197
198
    def update(self, session):
0 ignored issues
show
Unused Code introduced by
The argument session seems to be unused.
Loading history...
199
        self.seen_at = datetime.utcnow()
200
201
    def __repr__(self):
202
        return '<Session %s>' % (
203
            ', '.join([
204
                ('%s: %r' % (key, getattr(self, key))) for key in [
205
                    'session_key',
206
207
                    'account_id',
208
                    'rating_key',
209
210
                    'seen_at'
211
                ]
212
            ])
213
        )
214