ListMessages()   F
last analyzed

Complexity

Conditions 19

Size

Total Lines 82

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 82
rs 2.1041
cc 19

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like ListMessages() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
from core.helpers import catch_errors, pad_title, try_convert, redirect
2
3
from plugin.core.constants import PLUGIN_PREFIX
4
from plugin.core.environment import translate as _
5
from plugin.core.message import InterfaceMessages
6
from plugin.managers.exception import ExceptionManager, MessageManager, VERSION_BASE
7
from plugin.models import Exception, Message
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in Exception.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
8
9
from ago import human
10
from datetime import datetime, timedelta
11
import logging
12
13
log = logging.getLogger(__name__)
14
15
CONNECTION_TYPES = [
16
    Message.Type.Plex,
17
    Message.Type.Sentry,
18
    Message.Type.Trakt
19
]
20
21
ERROR_TYPES = [
22
    Message.Type.Exception,
23
24
    Message.Type.Warning,
25
    Message.Type.Error,
26
    Message.Type.Critical
27
]
28
29
30
@route(PLUGIN_PREFIX + '/messages/list')
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'route'
Loading history...
31
@catch_errors
32
def ListMessages(days=14, version='latest', viewed=False, *args, **kwargs):
0 ignored issues
show
Unused Code introduced by
The argument kwargs seems to be unused.
Loading history...
Unused Code introduced by
The argument args seems to be unused.
Loading history...
33
    # Cast `viewed` to boolean
34
    if type(viewed) is str:
35
        if viewed == 'None':
36
            viewed = None
37
        else:
38
            viewed = viewed == 'True'
39
40
    # Retrieve messages
41
    messages = list(List(
42
        days=try_convert(days, int),
43
        version=version,
44
        viewed=viewed
45
    ).order_by(
46
        Message.last_logged_at.desc()
47
    ).limit(50))
48
49
    total_messages = List(
50
        days=try_convert(days, int),
51
        version=version,
52
    ).count()
53
54
    # Construct container
55
    oc = ObjectContainer(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ObjectContainer'
Loading history...
56
        title2=_("Messages")
57
    )
58
59
    # Add "Dismiss All" button
60
    if viewed is False and len(messages) > 1:
61
        oc.add(DirectoryObject(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'DirectoryObject'
Loading history...
62
            key=Callback(DismissMessages),
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Callback'
Loading history...
63
            title=pad_title(_("Dismiss all"))
64
        ))
65
66
    # Add interface messages
67
    for record in InterfaceMessages.records:
68
        # Pick object thumb
69
        if record.level >= logging.WARNING:
70
            thumb = R("icon-error.png")
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'R'
Loading history...
71
        else:
72
            thumb = R("icon-notification.png")
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'R'
Loading history...
73
74
        # Add object
75
        oc.add(DirectoryObject(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'DirectoryObject'
Loading history...
76
            key=PLUGIN_PREFIX + '/messages/list',
77
            title=pad_title('[%s] %s' % (logging.getLevelName(record.level).capitalize(), record.message)),
78
            thumb=thumb
79
        ))
80
81
    # Add stored messages
82
    for m in messages:
83
        if m.type is None or\
84
           m.summary is None:
85
            continue
86
87
        # Pick thumb
88
        if m.type == Message.Type.Exception:
89
            thumb = R("icon-exception-viewed.png") if m.viewed else R("icon-exception.png")
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'R'
Loading history...
90
        elif m.type == Message.Type.Info:
91
            thumb = R("icon-notification-viewed.png") if m.viewed else R("icon-notification.png")
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'R'
Loading history...
92
        elif m.type in CONNECTION_TYPES:
93
            thumb = R("icon-connection-viewed.png") if m.viewed else R("icon-connection.png")
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'R'
Loading history...
94
        else:
95
            thumb = R("icon-error-viewed.png") if m.viewed else R("icon-error.png")
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'R'
Loading history...
96
97
        # Add object
98
        oc.add(DirectoryObject(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'DirectoryObject'
Loading history...
99
            key=Callback(ViewMessage, error_id=m.id),
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Callback'
Loading history...
100
            title=pad_title('[%s] %s' % (Message.Type.title(m.type), m.summary)),
101
            thumb=thumb
102
        ))
103
104
    # Append "View All" button
105
    if len(messages) != 50 and len(messages) < total_messages:
106
        oc.add(DirectoryObject(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'DirectoryObject'
Loading history...
107
            key=Callback(ListMessages, days=None, viewed=None),
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Callback'
Loading history...
108
            title=pad_title(_("View All"))
109
        ))
110
111
    return oc
112
113
@route(PLUGIN_PREFIX + '/messages/view')
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'route'
Loading history...
114
@catch_errors
115
def ViewMessage(error_id, *args, **kwargs):
0 ignored issues
show
Unused Code introduced by
The argument kwargs seems to be unused.
Loading history...
Unused Code introduced by
The argument args seems to be unused.
Loading history...
116
    # Retrieve message from database
117
    message = MessageManager.get.by_id(error_id)
118
119
    # Update `last_viewed_at` field
120
    message.last_viewed_at = datetime.utcnow()
121
    message.save()
122
123
    # Parse request headers
124
    web_client = Request.Headers.get('X-Plex-Product', '').lower() == 'plex web'
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Request'
Loading history...
125
126
    # Build objects
127
    oc = ObjectContainer(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ObjectContainer'
Loading history...
128
        title2='[%s] %s' % (Message.Type.title(message.type), Trim(message.summary))
129
    )
130
131
    if message.type == Message.Type.Exception:
132
        # Display exception samples
133
        for e in message.exceptions.order_by(Exception.timestamp.desc()).limit(50):
134
            since = datetime.utcnow() - e.timestamp
135
136
            callback = Callback(ViewMessage, error_id=error_id)
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Callback'
Loading history...
137
138
            if web_client:
139
                # Display exception traceback in Plex/Web
140
                callback = Callback(ViewException, exception_id=e.id)
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Callback'
Loading history...
141
142
            oc.add(DirectoryObject(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'DirectoryObject'
Loading history...
143
                key=callback,
144
                title=pad_title('[%s] %s: %s' % (human(since, precision=1), e.type, e.message)),
145
                thumb=R("icon-exception.png")
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'R'
Loading history...
146
            ))
147
    elif message.type in [Message.Type.Info, Message.Type.Warning, Message.Type.Error, Message.Type.Critical]:
148
        # Display message code
149
        oc.add(DirectoryObject(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'DirectoryObject'
Loading history...
150
            key='',
151
            title=pad_title(_('Code: %s') % hex(message.code))
152
        ))
153
154
        # Display message description
155
        if message.description:
156
            oc.add(DirectoryObject(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'DirectoryObject'
Loading history...
157
                key='',
158
                title=pad_title(_('Description: %s') % message.description)
159
            ))
160
161
    return oc
162
163
@route(PLUGIN_PREFIX + '/exceptions/view')
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'route'
Loading history...
164
@catch_errors
165
def ViewException(exception_id, *args, **kwargs):
0 ignored issues
show
Unused Code introduced by
The argument kwargs seems to be unused.
Loading history...
Unused Code introduced by
The argument args seems to be unused.
Loading history...
166
    # Retrieve exception from database
167
    exception = ExceptionManager.get.by_id(exception_id)
168
169
    # Split traceback into lines
170
    traceback = exception.traceback
171
172
    if traceback:
173
        traceback = traceback.split('\n')
174
175
    # Build exception view
176
    oc = ObjectContainer(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ObjectContainer'
Loading history...
177
        title2='%s: %s' % (exception.type, Trim(exception.message))
178
    )
179
180
    if not traceback:
181
        return oc
182
183
    for line in traceback:
184
        if not line:
185
            continue
186
187
        oc.add(DirectoryObject(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'DirectoryObject'
Loading history...
188
            key=Callback(ViewException, exception_id=exception_id),
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Callback'
Loading history...
189
            title=pad_title(line)
190
        ))
191
192
    return oc
193
194
195
@route(PLUGIN_PREFIX + '/messages/dismissAll')
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'route'
Loading history...
196
@catch_errors
197
def DismissMessages(days=14, version='latest', *args, **kwargs):
0 ignored issues
show
Unused Code introduced by
The argument kwargs seems to be unused.
Loading history...
Unused Code introduced by
The argument args seems to be unused.
Loading history...
198
    # Retrieve messages that match the specified criteria
199
    messages = List(
200
        days=days,
201
        version=version,
202
        viewed=False
203
    )
204
205
    # Mark all messages as viewed
206
    for message in messages:
207
        # Update `last_viewed_at` field
208
        message.last_viewed_at = datetime.utcnow()
209
        message.save()
210
211
    # Redirect back to the messages view
212
    return redirect(
213
        '/messages/list',
214
        days=days,
215
        version=version
216
    )
217
218
219
def Status(viewed=None):
220
    """Get the number and type of messages logged in the last week"""
221
    messages = List(
222
        days=14,
223
        version='latest',
224
        viewed=viewed
225
    )
226
227
    count = 0
228
    type = 'notification'
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
229
230
    # Process stored messages
231
    for message in messages:
232
        if message.type in ERROR_TYPES:
233
            type = 'error'
234
        elif message.type in CONNECTION_TYPES and type != 'error':
235
            type = 'connection'
236
237
        count += 1
238
239
    # Process interface messages
240
    for record in InterfaceMessages.records:
241
        if record.level >= logging.ERROR:
242
            type = 'error'
243
244
        count += 1
245
246
    return count, type
247
248
249
def List(days=None, version=None, viewed=None):
250
    """Get messages logged in the last week"""
251
    where = []
252
253
    # Days
254
    if days is not None:
255
        where.append(
256
            Message.last_logged_at > datetime.utcnow() - timedelta(days=days)
257
        )
258
259
    # Version
260
    if version == 'latest':
261
        where.append(
262
            Message.version_base == VERSION_BASE
263
        )
264
    elif version is not None:
265
        log.warn('Unknown version specified: %r', version)
266
267
    # Viewed state
268
    if viewed is True:
269
        where.append(
270
            ~(Message.last_viewed_at >> None),
271
            Message.last_viewed_at > Message.last_logged_at
272
        )
273
    elif viewed is False:
274
        where.append(
275
            (Message.last_viewed_at >> None) | (Message.last_viewed_at < Message.last_logged_at)
276
        )
277
278
    # Build query
279
    if where:
280
        return MessageManager.get.where(*where)
281
282
    return MessageManager.get.all()
283
284
285
def Trim(value, length=45):
286
    if value and len(value) > length:
287
        return value[:length - 3] + "..."
288
289
    return value
290