Completed
Push — master ( 1806d1...053f07 )
by Nicolas
01:42
created

GlancesCursesBrowser.display()   F

Complexity

Conditions 23

Size

Total Lines 117

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 23
c 1
b 0
f 0
dl 0
loc 117
rs 2

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 GlancesCursesBrowser.display() 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
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2016 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
"""Curses browser interface class ."""
21
22
import sys
23
24
from glances.outputs.glances_curses import _GlancesCurses
25
26
from glances.logger import logger
27
from glances.timer import Timer
28
29
30
class GlancesCursesBrowser(_GlancesCurses):
31
32
    """Class for the Glances curse client browser."""
33
34
    def __init__(self, args=None):
35
        # Init the father class
36
        super(GlancesCursesBrowser, self).__init__(args=args)
37
38
        _colors_list = {
39
            'UNKNOWN': self.no_color,
40
            'SNMP': self.default_color2,
41
            'ONLINE': self.default_color2,
42
            'OFFLINE': self.ifCRITICAL_color2,
43
            'PROTECTED': self.ifWARNING_color2,
44
        }
45
        self.colors_list.update(_colors_list)
46
47
        # First time scan tag
48
        # Used to display a specific message when the browser is started
49
        self.first_scan = True
50
51
        # Init refresh time
52
        self.__refresh_time = args.time
53
54
        # Init the cursor position for the client browser
55
        self.cursor_position = 0
56
57
        # Active Glances server number
58
        self._active_server = None
59
60
    @property
61
    def active_server(self):
62
        """Return the active server or None if it's the browser list."""
63
        return self._active_server
64
65
    @active_server.setter
66
    def active_server(self, index):
67
        """Set the active server or None if no server selected."""
68
        self._active_server = index
69
70
    @property
71
    def cursor(self):
72
        """Get the cursor position."""
73
        return self.cursor_position
74
75
    @cursor.setter
76
    def cursor(self, position):
77
        """Set the cursor position."""
78
        self.cursor_position = position
79
80
    def cursor_up(self, servers_list):
81
        """Set the cursor to position N-1 in the list."""
82
        if self.cursor_position > 0:
83
            self.cursor_position -= 1
84
        else:
85
            self.cursor_position = len(servers_list) - 1
86
87
    def cursor_down(self, servers_list):
88
        """Set the cursor to position N-1 in the list."""
89
        if self.cursor_position < len(servers_list) - 1:
90
            self.cursor_position += 1
91
        else:
92
            self.cursor_position = 0
93
94
    def __catch_key(self, servers_list):
95
        # Catch the browser pressed key
96
        self.pressedkey = self.get_key(self.term_window)
97
98
        if self.pressedkey != -1:
99
            logger.debug("Key pressed. Code=%s" % self.pressedkey)
100
101
        # Actions...
102
        if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'):
103
            # 'ESC'|'q' > Quit
104
            self.end()
105
            logger.info("Stop Glances client browser")
106
            sys.exit(0)
107
        elif self.pressedkey == 10:
108
            # 'ENTER' > Run Glances on the selected server
109
            logger.debug("Server number {0} selected".format(self.cursor + 1))
110
            self.active_server = self.cursor
111
        elif self.pressedkey == 65:
112
            # 'UP' > Up in the server list
113
            self.cursor_up(servers_list)
114
        elif self.pressedkey == 66:
115
            # 'DOWN' > Down in the server list
116
            self.cursor_down(servers_list)
117
118
        # Return the key code
119
        return self.pressedkey
120
121
    def update(self, servers_list):
122
        """Update the servers' list screen.
123
124
        Wait for __refresh_time sec / catch key every 100 ms.
125
126
        servers_list: Dict of dict with servers stats
127
        """
128
        # Flush display
129
        logger.debug('Servers list: {0}'.format(servers_list))
130
        self.flush(servers_list)
131
132
        # Wait
133
        exitkey = False
134
        countdown = Timer(self.__refresh_time)
135
        while not countdown.finished() and not exitkey:
136
            # Getkey
137
            pressedkey = self.__catch_key(servers_list)
138
            # Is it an exit or select server key ?
139
            exitkey = (
140
                pressedkey == ord('\x1b') or pressedkey == ord('q') or pressedkey == 10)
141
            if not exitkey and pressedkey > -1:
142
                # Redraw display
143
                self.flush(servers_list)
144
            # Wait 100ms...
145
            self.wait()
146
147
        return self.active_server
148
149
    def flush(self, servers_list):
150
        """Update the servers' list screen.
151
152
        servers_list: List of dict with servers stats
153
        """
154
        self.erase()
155
        self.display(servers_list)
156
157
    def display(self, servers_list):
158
        """Display the servers list.
159
160
        Return:
161
            True if the stats have been displayed
162
            False if the stats have not been displayed (no server available)
163
        """
164
        # Init the internal line/column for Glances Curses
165
        self.init_line_column()
166
167
        # Get the current screen size
168
        screen_x = self.screen.getmaxyx()[1]
169
        screen_y = self.screen.getmaxyx()[0]
170
171
        # Init position
172
        x = 0
173
        y = 0
174
175
        # Display top header
176
        if len(servers_list) == 0:
177
            if self.first_scan and not self.args.disable_autodiscover:
178
                msg = 'Glances is scanning your network. Please wait...'
179
                self.first_scan = False
180
            else:
181
                msg = 'No Glances server available'
182
        elif len(servers_list) == 1:
183
            msg = 'One Glances server available'
184
        else:
185
            msg = '{0} Glances servers available'.format(len(servers_list))
186
        if self.args.disable_autodiscover:
187
            msg += ' ' + '(auto discover is disabled)'
188
        self.term_window.addnstr(y, x,
189
                                 msg,
190
                                 screen_x - x,
191
                                 self.colors_list['TITLE'])
192
193
        if len(servers_list) == 0:
194
            return False
195
196
        # Display the Glances server list
197
        # ================================
198
199
        # Table of table
200
        # Item description: [stats_id, column name, column size]
201
        column_def = [
202
            ['name', 'Name', 16],
203
            ['alias', None, None],
204
            ['load_min5', 'LOAD', 6],
205
            ['cpu_percent', 'CPU%', 5],
206
            ['mem_percent', 'MEM%', 5],
207
            ['status', 'STATUS', 9],
208
            ['ip', 'IP', 15],
209
            # ['port', 'PORT', 5],
210
            ['hr_name', 'OS', 16],
211
        ]
212
        y = 2
213
214
        # Display table header
215
        xc = x + 2
216
        for cpt, c in enumerate(column_def):
217
            if xc < screen_x and y < screen_y and c[1] is not None:
218
                self.term_window.addnstr(y, xc,
219
                                         c[1],
220
                                         screen_x - x,
221
                                         self.colors_list['BOLD'])
222
                xc += c[2] + self.space_between_column
223
        y += 1
224
225
        # If a servers has been deleted from the list...
226
        # ... and if the cursor is in the latest position
227
        if self.cursor > len(servers_list) - 1:
228
            # Set the cursor position to the latest item
229
            self.cursor = len(servers_list) - 1
230
231
        # Display table
232
        line = 0
233
        for v in servers_list:
234
            # Get server stats
235
            server_stat = {}
236
            for c in column_def:
237
                try:
238
                    server_stat[c[0]] = v[c[0]]
239
                except KeyError as e:
240
                    logger.debug(
241
                        "Cannot grab stats {0} from server (KeyError: {1})".format(c[0], e))
242
                    server_stat[c[0]] = '?'
243
                # Display alias instead of name
244
                try:
245
                    if c[0] == 'alias' and v[c[0]] is not None:
246
                        server_stat['name'] = v[c[0]]
247
                except KeyError:
248
                    pass
249
250
            # Display line for server stats
251
            cpt = 0
252
            xc = x
253
254
            # Is the line selected ?
255
            if line == self.cursor:
256
                # Display cursor
257
                self.term_window.addnstr(
258
                    y, xc, ">", screen_x - xc, self.colors_list['BOLD'])
259
260
            # Display the line
261
            xc += 2
262
            for c in column_def:
263
                if xc < screen_x and y < screen_y and c[1] is not None:
264
                    # Display server stats
265
                    self.term_window.addnstr(
266
                        y, xc, format(server_stat[c[0]]), c[2], self.colors_list[v['status']])
267
                    xc += c[2] + self.space_between_column
268
                cpt += 1
269
            # Next line, next server...
270
            y += 1
271
            line += 1
272
273
        return True
274