Test Failed
Push — master ( aac275...383c01 )
by
unknown
01:22 queued 19s
created

DispDisplay._separator()   A

Complexity

Conditions 3

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
nop 6
dl 0
loc 20
rs 9.9
c 0
b 0
f 0
1
# Copyright 2014 Diamond Light Source Ltd.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#     http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14
15
"""
16
.. module:: display_formatter
17
   :platform: Unix
18
   :synopsis: Classes for formatting plugin list output in the configurator.
19
20
.. moduleauthor:: Nicola Wadeson <[email protected]>
21
22
"""
23
import os
24
import textwrap
25
26
from colorama import Fore, Back, Style
27
28
from savu.plugins import utils as pu
29
import savu.data.framework_citations as fc
30
from savu.data.plugin_list import CitationInformation
31
32
33
WIDTH = 95
34
35
36
def get_terminal_width():
37
    """Return the width of the terminal"""
38
    try:
39
        terminal_width = os.get_terminal_size().columns
40
        return terminal_width if terminal_width < WIDTH else WIDTH
41
    except (AttributeError, OSError) as ae:
42
        return WIDTH
43
44
45
class DisplayFormatter(object):
46
    def __init__(self, plugin_list):
47
        self.plugin_list_inst = plugin_list
48
        self.plugin_list = plugin_list.plugin_list
49
50
    def _get_string(self, **kwargs):
51
        out_string = []
52
        width = get_terminal_width()
53
54
        verbosity = kwargs.get("verbose", False)
55
        level = kwargs.get("current_level", "basic")
56
        datasets = kwargs.get("datasets", False)
57
        expand_dim = kwargs.get("expand_dim", None)
58
59
        start = kwargs.get("start", 0)
60
        stop = kwargs.get("stop", len(self.plugin_list))
61
        subelem = kwargs.get("subelem", False)
62
63
        if stop == -1:
64
            stop = len(self.plugin_list)
65
66
        count = start
67
        plugin_list = self.plugin_list[start:stop]
68
        line_break = "%s" % ("-" * width)
69
        out_string.append(line_break)
70
71
        display_args = {
72
            "subelem": subelem,
73
            "datasets": datasets,
74
            "expand_dim": expand_dim,
75
        }
76
        for p_dict in plugin_list:
77
            count += 1
78
            description = self._get_description(
79
                width, level, p_dict, count, verbosity, display_args
80
            )
81
            out_string.append(description)
82
            out_string.append(line_break)
83
        return "\n".join(out_string)
84
85
    def _get_description(
86
        self, width, level, p_dict, count, verbose, display_args
87
    ):
88
        if verbose == "-q":
89
            return self._get_quiet(p_dict, count, width)
90
        if not verbose:
91
            return self._get_default(
92
                level, p_dict, count, width, display_args
93
            )
94
        if verbose == "-v":
95
            return self._get_verbose(
96
                level, p_dict, count, width, display_args
97
            )
98
        if verbose == "-vv":
99
            return self._get_verbose_verbose(
100
                level, p_dict, count, width, display_args
101
            )
102
103
    def _get_plugin_title(self, p_dict, width, fore_colour, back_colour,
104
                          active="", quiet=False, pos=None):
105
        pos = f"{pos})" if pos else ""
106
        active = f"{active} " if active else ""
107
        title = f"{active}{pos} {p_dict['name']}"
108
        title = title if quiet else f"{title} ({p_dict['id']})"
109
        title_str = self._get_equal_lines(
110
            title, width, back_colour+fore_colour, Style.RESET_ALL, " ")
111
        return title_str
112
113
    def _get_quiet(self, p_dict, count, width, quiet=True):
114
        active = (
115
            "***OFF***" if "active" in p_dict and not p_dict["active"] else ""
116
        )
117
        p_dict["data"] = self._remove_quotes(p_dict["data"])
118
        pos = p_dict["pos"].strip() if "pos" in list(p_dict.keys()) else count
119
        fore = Fore.LIGHTWHITE_EX
120
        back = Back.RED if active else Back.LIGHTBLACK_EX
121
        return self._get_plugin_title(
122
            p_dict, width, fore, back, active=active, quiet=quiet, pos=pos
123
        )
124
125
    def _get_synopsis(self, p_dict, width, colour_on, colour_off):
126
        doc_str = p_dict["doc"]
127
        synopsis = self._get_equal_lines(
128
            doc_str.get("synopsis"), width, colour_on, colour_off, " " * 2
129
        )
130
        return "\n" + synopsis
131
132
    def _get_extra_info(self, p_dict, width, colour_off, info_colour,
133
                        warn_colour):
134
        doc_str = p_dict["doc"]
135
        info = self._get_equal_lines(doc_str.get("info"), width, info_colour,
136
                                     colour_off, " " * 2)
137
        info = "\n"+info if info else ''
138
139
        doc_link = doc_str.get("documentation_link")
140
        if doc_link:
141
            documentation_link = f"  {doc_link}"
142
            info +="\n"+documentation_link
143
144
        warn = self._get_equal_lines(doc_str.get('warn'), width, warn_colour,
145
                                     colour_off, " "*2)
146
        warn = "\n"+warn if warn else ""
147
        return info, warn
148
149
    def _get_equal_lines(self, string, width, colour_on, colour_off, offset,
150
                         option_colour=False):
151
        """ Format the input string so that it is the width specified.
152
        Surround the string with provided colour.
153
        """
154
        if not string or not colour_on:
155
            return ""
156
        # Call method directly for split to be used with string and unicode
157
        string = string.splitlines()
158
        str_list = []
159
        for s in string:
160
            str_list += textwrap.wrap(s, width=width - len(offset))
161
        new_str_list = []
162
        if option_colour:
163
            # Alternate colour choice and doesn't colour the full width
164
            new_str_list = self._get_option_format(str_list, width,
165
                            colour_on, colour_off, offset, option_colour,
166
                            new_str_list)
167
        else:
168
            # Fill the whole line with the colour
169
            for line in str_list:
170
                lwidth = width - len(line) - len(offset)
171
                new_str_list.append(
172
                    colour_on + offset + line + " " * lwidth + colour_off)
173
        return "\n".join(new_str_list)
174
175
    def _remove_quotes(self, data_dict):
176
        """Remove quotes around variables for display"""
177
        for key, val in data_dict.items():
178
            val = str(val).replace("'", "")
179
            data_dict[key] = val
180
        return data_dict
181
182
class ParameterFormatter(DisplayFormatter):
183
    def __init__(self, plugin_list):
184
        super(ParameterFormatter, self).__init__(plugin_list)
185
186
    def _get_param_details(self, level, p_dict, width,
187
                           desc=False, breakdown=False):
188
        """ Return a list of parameters organised by visibility level
189
190
        :param level: The visibility level controls which parameters the
191
           user can see
192
        :param p_dict: Parameter dictionary for one plugin
193
        :param width: The terminal display width for output string
194
        :param desc: The description for use later on within the display
195
          formatter
196
        :param breakdown: Boolean True if the verbose verbose information
197
          should be shown
198
        :return: List of parameters in order
199
        """
200
        params = ""
201
        keycount = 0
202
        # Return the list of parameters according to the visibility level
203
        keys = pu.set_order_by_visibility(p_dict["param"], level=level)
204
        longest_key = max(keys, key=len)
205
        try:
206
            for key in keys:
207
                keycount += 1
208
                params = self._create_display_string(
209
                    desc, key, p_dict, params, keycount, longest_key,
210
                    width, breakdown
211
                )
212
            return params
213
        except Exception as e:
214
            print("ERROR: " + str(e))
215
            raise
216
217
    def _create_display_string(self, desc, key, p_dict, params, keycount,
218
                               longest_key, width, breakdown, expand_dim=None):
219
        """Create a string to describe the current parameter (key)
220
        to display to the terminal
221
222
        :param desc: the verbose description to display
223
        :param key: current parameter name
224
        :param p_dict: parameter dictionary for one plugin
225
        :param params: parameter info string to display
226
        :param keycount: current parameter number
227
        :param longest_key: longest parameter name for this plugin
228
        :param width: display text width
229
        :param breakdown: bool True if the verbose verbose information
230
          should be shown
231
        :param expand_dim: number of dimensions
232
        :return: string to describe parameter (key)
233
        """
234
        margin = 6
235
        str_margin = " " * margin
236
        sp = (len(longest_key) - len(key)) * " "
237
        # Align the parameter numbers
238
        temp = f"{keycount}) {sp}{key} : "
239
        temp = "\n   %2i) %20s : %s"
240
        val = p_dict["data"][key]
241
        if key == "preview" and expand_dim is not None:
242
            expand_dict = p_dict["tools"].get_expand_dict(val, expand_dim)
243
            val = self._dict_to_str(expand_dict, 15*" ")
244
        params += temp % (keycount, key, val)
245
        if desc:
246
            params = self._append_description(desc, key, p_dict, str_margin,
247
                                              width, params, breakdown)
248
        return params
249
250
251
    def _dict_to_str(self, _dict, indent):
252
        """Change the dictionary to a formatted string
253
254
        :param _dict:
255
        :param indent: number of space to indent by
256
        :return: String containing the formatted dict
257
        """
258
        dict_str = ""
259
        indent += (3 * " ")
260
        for k,v in _dict.items():
261
            if isinstance(v, dict):
262
                v = self._dict_to_str(v, indent+"  ")
263
            dict_str += f"\n{indent}{k:>11} : {v}"
264
        return dict_str
265
266
267
class DispDisplay(ParameterFormatter):
268
    def __init__(self, plugin_list):
269
        super(DispDisplay, self).__init__(plugin_list)
270
271
    def _get_default(self, level, p_dict, count, width, display_args=False):
272
        title = self._get_quiet(p_dict, count, width)
273
        params = self._get_param_details(level, p_dict, width, display_args)
274
        return title + params
275
276
    def _get_param_details(self, level, p_dict, width, display_args=False,
277
                           desc=False, breakdown=False):
278
        """
279
        Return a list of parameters organised by visibility level
280
281
        :param level: The visibility level controls which parameters the
282
           user can see
283
        :param p_dict: Parameter dictionary for one plugin
284
        :param width: The terminal display width for output strings
285
        :param display_args: A dictionary with subelem and datasets.
286
          This filters the visible parameters again. If subelem is chosen,
287
          only that one parameter is shown (this is useful when ONE parameter
288
          is modified in the terminal). If datasets is chosen, ONLY the
289
          in/out dataset parameters are shown
290
        :param desc: The description for use later on within the display
291
          formatter
292
        :param breakdown: Boolean True if the verbose verbose information
293
          should be shown
294
        :return: List of parameters in order
295
        """
296
        # Check if the parameters need to be filtered
297
        subelem = display_args.get("subelem")
298
        datasets = display_args.get("datasets")
299
        expand_dim = display_args.get("expand_dim")
300
301
        level = level if not (subelem or datasets) else False
302
        # Return the list of parameters according to the visibility level
303
        # unless a subelement or dataset choice is specified
304
        keys = pu.set_order_by_visibility(p_dict["param"], level=level)
305
306
        filter = subelem if subelem else datasets
307
        # If datasets parameter specified, only show these
308
        filter_items = (
309
            [pu.param_to_str(subelem, keys)]
310
            if subelem
311
            else ["in_datasets", "out_datasets"]
312
        )
313
        # Get the longest parameter name, to align values in console
314
        if filter:
315
            longest_key = max(filter_items, key=len)
316
        else:
317
            longest_key = max(keys, key=len)
318
        params = ""
319
        keycount = 0
320
        prev_visibility = ""
321
        try:
322
            for key in keys:
323
                keycount += 1
324
                if filter:
325
                    if key in filter_items:
326
                        params = \
327
                            self._separator(key, p_dict, prev_visibility,
328
                                            params, width)
329
                        params = self._create_display_string(desc, key,
330
                                       p_dict, params, keycount, longest_key,
331
                                       width, breakdown, expand_dim)
332
                else:
333
                    params = \
334
                        self._separator(key, p_dict, prev_visibility,
335
                                        params, width)
336
                    params = self._create_display_string(desc, key, p_dict,
337
                                        params, keycount, longest_key, width,
338
                                        breakdown, expand_dim)
339
                prev_visibility = p_dict["param"][key]["visibility"]
340
341
            return params
342
        except Exception as e:
343
            print("ERROR: " + str(e))
344
            raise
345
346
    def _separator(self, key, p_dict, prev_visibility, params, width):
347
        """Add a line separator to the parameter string 'params'
348
349
        :param key: parameter name
350
        :param p_dict: dictionary of parameter definitions
351
        :param prev_visibility: visibility level for the previous parameter
352
        :param params: parameter string
353
        :param width: width of the console display
354
        :return: parameter string
355
        """
356
        offset = 2
357
        align = "left"
358
        cur_visibility = p_dict["param"][key]["visibility"]
359
        split = "-" * ((width - len(cur_visibility)) - offset)
360
        if cur_visibility != prev_visibility:
361
            if align == "left":
362
                params += "\n" + "-" * offset + cur_visibility + split
363
            else:
364
                params += "\n" + split + cur_visibility + "-" * offset
365
        return params
366
367
    def _get_verbose(
368
        self, level, p_dict, count, width, display_args, breakdown=False
369
    ):
370
        title = self._get_quiet(p_dict, count, width, quiet=False)
371
        colour_on = Back.LIGHTBLACK_EX + Fore.LIGHTWHITE_EX
372
        colour_off = Back.RESET + Fore.RESET
373
        synopsis = self._get_synopsis(p_dict, width, colour_on, colour_off)
374
        param_desc = {k: v["description"] for k, v in p_dict["param"].items()}
375
        params = self._get_param_details(
376
            level, p_dict, width, display_args, desc=param_desc
377
        )
378
        if breakdown:
379
            params = self._get_param_details(
380
                level,
381
                p_dict,
382
                width,
383
                display_args,
384
                desc=param_desc,
385
                breakdown=breakdown,
386
            )
387
            return title, synopsis, params
388
        return title + synopsis + params
389
390
    def _get_verbose_verbose(self, level, p_dict, count, width, display_args):
391
        title, synopsis, param_details = self._get_verbose(
392
            level, p_dict, count, width, display_args, breakdown=True
393
        )
394
        info_c = Back.CYAN + Fore.LIGHTWHITE_EX
395
        warn_c = Style.RESET_ALL + Fore.RED
396
        c_off = Back.RESET + Fore.RESET
397
        info, warn = self._get_extra_info(
398
            p_dict, width, c_off, info_c, warn_c
399
        )
400
        # Synopsis and get_extra info both call plugin instance and populate
401
        # parameters which means yaml_load will be called twice
402
        return title + synopsis + info + warn + param_details
403
404
    def _notices(self):
405
        width = 86
406
        warnings = self.get_warnings(width)
407
        if warnings:
408
            notice = (
409
                Back.RED
410
                + Fore.WHITE
411
                + "IMPORTANT PLUGIN NOTICES"
412
                + Back.RESET
413
                + Fore.RESET
414
                + "\n"
415
            )
416
            border = "*" * width + "\n"
417
            print((border + notice + warnings + "\n" + border))
418
419
    def get_warnings(self, width):
420
        # remove display styling outside of this class
421
        colour = Back.RESET + Fore.RESET
422
        warnings = []
423
        names = []
424
        for plugin in self.plugin_list:
425
            if plugin["name"] not in names:
426
                names.append(plugin["name"])
427
                doc_str = plugin["doc"]
428
                warn = doc_str.get("warn")
429
                if warn:
430
                    for w in warn.split("\n"):
431
                        string = plugin["name"] + ": " + w
432
                        warnings.append(
433
                            self._get_equal_lines(
434
                                string, width - 1, colour, colour, " " * 2
435
                            )
436
                        )
437
        return "\n".join(
438
            ["*" + "\n ".join(w.split("\n")) for w in warnings if w]
439
        )
440
441
    def _append_description(self, desc, key, p_dict, str_margin, width,
442
                            params, breakdown):
443
        c_off = Back.RESET + Fore.RESET
444
        description_verbose = False
445
        description_keys = ""
446
        if isinstance(desc[key], str):
447
            pdesc = " ".join(desc[key].split())
448
            pdesc = self._get_equal_lines(
449
                pdesc, width, Fore.CYAN, Fore.RESET, str_margin
450
            )
451
            params += "\n" + pdesc
452
        elif isinstance(desc[key], dict):
453
            description_keys = desc[key].keys()
454
            for param_key in description_keys:
455
                if param_key == "summary":
456
                    pdesc = desc[key][param_key]
457
                    pdesc = self._get_equal_lines(
458
                        pdesc, width, Fore.CYAN, Fore.RESET, str_margin
459
                    )
460
                    params += "\n" + pdesc
461
462
                if breakdown:
463
                    params = self._get_verbose_param_details(
464
                        p_dict, param_key, desc, key, params, width
465
                    )
466
                    description_verbose = True
467
468
        options = p_dict["param"][key].get("options")
469
        if options:
470
            option_text = "Options:"
471
            option_text = self._get_equal_lines(
472
                option_text, width, Fore.BLUE, Fore.RESET, str_margin
473
            )
474
            params += "\n" + option_text
475
            for opt in options:
476
                current_opt = p_dict["data"][key]
477
                option_verbose = \
478
                    self._get_verbose_option_string(opt, current_opt,
479
                            description_keys, description_verbose,
480
                            desc, key, c_off, width, str_margin)
481
482
                temp = "\n" + "%s" + c_off + Style.RESET_ALL
483
                params += temp % option_verbose
484
485
        return params
486
487
    def _get_verbose_option_string(self, opt, current_opt, description_keys,
488
                                   description_verbose, desc, key, c_off,
489
                                   width, str_margin):
490
        """ Get the option description string and correctly format it """
491
        colour, v_colour = self._get_verbose_option_colours(opt, current_opt)
492
        unicode_bullet_point = "\u2022"
493
        opt_margin = str_margin + (2 * " ")
494
        if (description_verbose is True) and ("options" in description_keys):
495
            # If there are option descriptions present
496
            options_desc = {k: v
497
                            for k, v in desc[key]["options"].items()
498
                            if v}
499
            if opt in options_desc.keys():
500
                # Append the description
501
                opt_d = unicode_bullet_point + str(opt) + ": " \
502
                        + options_desc[opt]
503
            else:
504
                # No description if the field is blank
505
                opt_d = unicode_bullet_point + str(opt) + ": "
506
            option_verbose = "\n" + opt_d
507
            option_verbose = \
508
                self._get_equal_lines(option_verbose,
509
                                       width, v_colour, c_off,
510
                                       opt_margin, option_colour=colour)
511
        else:
512
            option_verbose = unicode_bullet_point + str(opt)
513
            option_verbose = \
514
                self._get_equal_lines(option_verbose,
515
                                       width, colour, c_off, opt_margin,
516
                                       option_colour=colour)
517
        return option_verbose
518
519
    def _get_verbose_option_colours(self, opt, current_opt):
520
        """Set the colour of the option text
521
522
        :param opt: Option string
523
        :param current_opt: The curently selected option value
524
        :return: colour, verbose_color
525
        """
526
        if current_opt == opt:
527
            # Highlight the currently selected option by setting a
528
            # background colour and white text
529
            colour = Back.BLUE + Fore.LIGHTWHITE_EX
530
            verbose_color = Back.BLACK + Fore.LIGHTWHITE_EX
531
        else:
532
            # Make the option bold using Style.BRIGHT
533
            colour = Fore.BLUE + Style.BRIGHT
534
            # Remove bold style for the description
535
            verbose_color = Style.RESET_ALL + Fore.BLACK
536
        return colour, verbose_color
537
538
    def _get_verbose_param_details(
539
        self, p_dict, param_key, desc, key, params, width
540
    ):
541
        margin = 6
542
        str_margin = " " * margin
543
        if param_key == "verbose":
544
            verbose = desc[key][param_key]
545
            # Account for margin space
546
            style_on = Fore.CYAN + "\033[3m"
547
            style_off = Fore.RESET + "\033[0m"
548
            verbose = self._get_equal_lines(
549
                verbose, width, style_on, style_off, str_margin
550
            )
551
            params += "\n" + verbose
552
553
        if param_key == "range":
554
            p_range = desc[key][param_key]
555
            if p_range:
556
                try:
557
                    r_color = Fore.MAGENTA
558
                    r_off = Fore.RESET
559
                    p_range = self._get_equal_lines(
560
                        p_range, width, r_color, r_off, str_margin
561
                    )
562
                    params += "\n" + p_range
563
                except TypeError:
564
                    print(f"You have not filled in the {param_key} field "
565
                          f"within the yaml information.")
566
        return params
567
568
    def _get_option_format(self, str_list, width, colour_on, colour_off,
569
                           offset, option_colour, new_str_list):
570
        """ Special format for the options list """
571
        count = 0
572
        for line in str_list:
573
            lwidth = width - len(line) - len(offset)
574
            count += 1
575
            if count == 1:
576
                """At the first line, split the key so that it's colour is
577
                different. This is done here so that I keep the key and
578
                value on the same line.
579
                I have not passed in the unicode colour before this
580
                point as the textwrap does not take unicode into
581
                account when calculating the final string width.
582
                """
583
                if ":" in line:
584
                    option_text = line.split(":")[0]
585
                    opt_descr_text = line.split(":")[1]
586
                    line = (
587
                        option_colour
588
                        + option_text
589
                        + ":"
590
                        + colour_on
591
                        + opt_descr_text
592
                    )
593
                    new_str_list.append(
594
                        offset + line + colour_off + " " * lwidth
595
                    )
596
                else:
597
                    # Assumes that the option string is one line, the length
598
                    # of the width
599
                    new_str_list.append(
600
                        offset
601
                        + option_colour
602
                        + line
603
                        + colour_off
604
                        + " " * lwidth
605
                    )
606
            else:
607
                new_str_list.append(
608
                    offset + colour_on + line + colour_off + " " * lwidth
609
                )
610
        return new_str_list
611
612
613
class ListDisplay(ParameterFormatter):
614
    def __init__(self, plugin_list):
615
        super(ListDisplay, self).__init__(plugin_list)
616
617
    def _get_quiet(self, p_dict, count, width):
618
        return self._get_plugin_title(
619
            p_dict, width, Fore.RESET, Back.RESET, quiet=True
620
        )
621
622
    def _get_default(self, level, p_dict, count, width, display_args=False):
623
        title = self._get_quiet(p_dict, count, width)
624
        synopsis = self._get_synopsis(p_dict, width, Fore.CYAN, Fore.RESET)
625
        return title + synopsis
626
627
    def _get_verbose(
628
        self, level, p_dict, count, width, display_args, breakdown=False
629
    ):
630
        default_str = self._get_default(level, p_dict, count, width)
631
        info_c = Fore.CYAN
632
        c_off = Back.RESET + Fore.RESET
633
        info, warn = self._get_extra_info(
634
            p_dict, width, c_off, info_c, info_c
635
        )
636
        return default_str + info
637
638
    def _get_verbose_verbose(self, level, p_dict, count, width, display_args):
639
        all_params = self._get_param_details("all", p_dict, width)
640
        default_str = self._get_default(level, p_dict, count, width)
641
        info_c = Fore.CYAN
642
        warn_c = Fore.RED
643
        c_off = Back.RESET + Fore.RESET
644
        info, warn = self._get_extra_info(
645
            p_dict, width, c_off, info_c, warn_c
646
        )
647
        return default_str + info + warn + all_params
648
649
650
class CiteDisplay(DisplayFormatter):
651
    def __init__(self, plugin_list):
652
        super(CiteDisplay, self).__init__(plugin_list)
653
654
    def _get_default(self, level, p_dict, count, width, display_args=False):
655
        """Find the citations and print them. Only display citations
656
        if they are required. For example, if certain methods are being
657
        used.
658
        """
659
        title = self._get_quiet(p_dict, count, width)
660
        citation = self._get_citation_str(
661
            p_dict["tools"].get_citations(), width, parameters=p_dict["data"]
662
        )
663
        if count == 1:
664
            # Display framework citations before the first citation
665
            framework_citations = self._get_framework_citations(width)
666
            return framework_citations + title + citation
667
668
        return title + citation
669
670
    def _get_citation_str(self, citation_dict, width, parameters=""):
671
        """Get the plugin citation information
672
673
        :param: citation_dict: Dictionay containing citation information
674
        :param parameters: Dictionary containing parameter information
675
        :param width: The terminal display width for output strings
676
        :return: cite, A string containing plugin citations
677
        """
678
        margin = 6
679
        str_margin = " " * margin
680
        line_break = "\n" + str_margin + "-" * (width - margin) + "\n"
681
        cite = ""
682
        if citation_dict:
683
            for citation in citation_dict.values():
684
                if citation.dependency and parameters:
685
                    # If the citation is dependent upon a certain parameter
686
                    # value being chosen
687
                    str_dep = self._get_citation_dependency_str(
688
                        citation, parameters, width, str_margin
689
                    )
690
                    if str_dep:
691
                        cite += line_break + str_dep + "\n" \
692
                                + self._get_citation_lines(citation,
693
                                                           width,
694
                                                           str_margin)
695
                else:
696
                    cite += line_break \
697
                            + self._get_citation_lines(citation,
698
                                                       width, str_margin)
699
        else:
700
            cite = f"\n\n{' '}No citations"
701
        return cite
702
703
    def _get_citation_dependency_str(
704
        self, citation, parameters, width, str_margin
705
    ):
706
        """Create a message for citations dependent on a
707
        certain parameter
708
709
        :param citation: Single citation dictionary
710
        :param parameters: List of current parameter values
711
        :param width: The terminal display width for output strings
712
        :param str_margin: The terminal display margin
713
        :return: str_dep, A string to identify citations dependent on
714
        certain parameter values
715
        """
716
        str_dep = ""
717
        for (citation_dependent_parameter, citation_dependent_value) \
718
            in citation.dependency.items():
719
            current_value = parameters[citation_dependent_parameter]
720
            if current_value == citation_dependent_value:
721
                str_dep = (
722
                    f"This citation is for the {citation_dependent_value}"
723
                    f" {citation_dependent_parameter}"
724
                )
725
                str_dep = self._get_equal_lines(
726
                    str_dep, width, Style.BRIGHT, Style.RESET_ALL, str_margin
727
                )
728
        return str_dep
729
730
    def _get_framework_title(self, width, fore_colour, back_colour):
731
        title = "Framework Citations "
732
        width -= len(title)
733
        title_str = (
734
            back_colour + fore_colour + title + " " * width + Style.RESET_ALL
735
        )
736
        return title_str
737
738
    def _get_framework_citations(self, width):
739
        """Create a string containing framework citations
740
741
        :param width: Width of formatted text
742
        :return: String with framework citations
743
        """
744
        citation_dict = {}
745
        framework_cites = fc.get_framework_citations()
746
        for name, cite in framework_cites.items():
747
            citation_dict.update({name: cite})
748
        title = \
749
            self._get_framework_title(width, Fore.LIGHTWHITE_EX,
750
                                             Back.LIGHTBLACK_EX)
751
        cite = self._get_citation_str(citation_dict, width)
752
        return title + cite + "\n"
753
754
    def _get_citation_lines(self, citation, width, str_margin):
755
        """Print certain information about the citation in order.
756
757
        :param citation: Single citation dictionary
758
        :param width: The terminal display width for output strings
759
        :param str_margin: The terminal display margin
760
        :return: A string containing citation details
761
        """
762
        cite_keys = ["name", "description", "doi", "bibtex", "endnote"]
763
        cite_dict = citation.__dict__
764
        cite_str = ""
765
766
        style_on = Style.BRIGHT
767
        style_off = Style.RESET_ALL
768
769
        for key in cite_keys:
770
            if cite_dict[key]:
771
                # Set the key name to be bold
772
                cite_key = self._get_equal_lines(
773
                    key.title(), width, style_on, style_off, str_margin
774
                )
775
                # No style for the citation content
776
                cite_value = self._get_equal_lines(
777
                    cite_dict[key], width, style_off, style_off, str_margin
778
                )
779
                # New line for each item
780
                cite_str += "\n" + cite_key + "\n" + cite_value + "\n"
781
782
        return cite_str
783