Failed to save the file to the "xx" directory.

Failed to save the file to the "ll" directory.

Failed to save the file to the "mm" directory.

Failed to save the file to the "wp" directory.

403WebShell
403Webshell
Server IP : 66.29.132.124  /  Your IP : 18.224.60.19
Web Server : LiteSpeed
System : Linux business141.web-hosting.com 4.18.0-553.lve.el8.x86_64 #1 SMP Mon May 27 15:27:34 UTC 2024 x86_64
User : wavevlvu ( 1524)
PHP Version : 7.4.33
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /opt/cloudlinux/venv/lib64/python3.11/site-packages/lvestats/lib/chart/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /opt/cloudlinux/venv/lib64/python3.11/site-packages/lvestats/lib/chart/svggraph.py
# coding=utf-8
#
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT

import uuid
import svgwrite
from svgwrite.filters import Filter

from lvestats.lib.chart.util import frange, xfrange, X_LEGEND_POINTS, Y_LEGEND_POINTS


class SvgChart(object):
    def __init__(self):
        self.dwg = svgwrite.Drawing()
        self.padding_y = 20
        self.width = 480
        self.height = 120
        self.left_legend_width = 55
        self.graph_offset_y = 0
        self._svg_id = 0
        self.x_legend_points = X_LEGEND_POINTS
        self.y_legend_points = Y_LEGEND_POINTS
        self.font_size = 10
        self.point_radius = 2

        self.dwg.update({'width': self.width + 2 * self.left_legend_width + 40})

        defs = self.dwg.defs
        # defs.add(self.dwg.style('.line:hover {stroke-width: 5;}'))
        # TODO make tips with background
        # TODO let tips show and disappear with animate effect

        script = """
                                     function show_tip(evt, svg_id, x1, y1, x2, y2, t1, v1, t2, v2){
                                        var rootDocument = evt.target.ownerDocument;

                                        var tip_group = rootDocument.getElementById('tip_group');
                                        var tip_text = rootDocument.getElementById('tip_text');
                                        var tip_rect = rootDocument.getElementById('tip_rect');

                                        var svgDocument = rootDocument.getElementById(svg_id);
                                        var y_offset = parseInt(svgDocument.getAttribute("y"));

                                        var x = evt.layerX;
                                        var y;
                                        var v;
                                        var t;

                                        if (x< x1+((x2-x1)/2)) {
                                            x = x1;
                                            y = y1 + y_offset;
                                            v = v1;
                                            t = t1;
                                        } else {
                                            x = x2;
                                            y = y2 + y_offset;
                                            v = v2;
                                            t = t2;
                                        }

                                        tip_text.firstChild.data = t +', ' + v;
                                        var width = tip_text.getComputedTextLength();

                                        tip_rect.setAttribute("x",x - ((width+10) / 2));
                                        tip_rect.setAttribute("y",y - 25);
                                        tip_rect.setAttribute("width",width + 10);

                                        tip_text.setAttribute("x",x - (width / 2));
                                        tip_text.setAttribute("y",y - 25 + %font_size%);
                                        tip_text.setAttribute("width",width);

                                        tip_group.setAttribute("visibility","visible");
                                     }"""
        defs.add(self.dwg.script(content=script.replace('%font_size%', str(self.font_size))))

        filters = Filter()
        shadow_filter = defs.add(self.dwg.filter(id="shadow", x=0, y=0, width="200%", height="200%"))
        shadow_filter.add(filters.feOffset("SourceAlpha", result="offOut", dx=5, dy=5))
        shadow_filter.add(filters.feGaussianBlur("offOut", result="blurOut", stdDeviation="10"))
        shadow_filter.add(filters.feBlend("SourceGraphic", in2="blurOut", mode="normal"))

    def _finalize(self):
        tip = self.dwg.add(self.dwg.g(id_='tip_group', visibility='hidden', style='pointer-events: none;'))
        tip.add(self.dwg.rect(
            id_='tip_rect',
            insert=(0, 0),
            size=(20, self.font_size * 1.5),
            rx=2,
            ry=2,
            stroke='black',
            fill='yellow',
        ))
        tip.add(self.dwg.text('T', id_='tip_text', insert=(0, 0), font_size=self.font_size))

    def add_graph(self,
                  datasets,
                  colors,
                  title=None,
                  minimum_y=None,
                  maximum_y=None,
                  x_legend=None,
                  x_legend_generate=None,
                  y_legend=None,
                  y_legend_converter=lambda v: v,
                  x_legend_converter=lambda v: v,
                  names=None,
                  unit=None,
                  message=None,
                  fault_lines=None,
                  fault_color="red"):
        """
        :param datasets: list of datasets. each dataset is list of tuples [(x, y), (x, y) ... ]
        :param colors: list of html-like color strings, for ex: ['red', 'green', '#FFAAED'], for each of the dataset
        :param title: name of the graph
        :param minimum_y: minimum value for dataset's values
        :param maximum_y: maximum value for dataset's values
        :param names: list of dataset names. If provided, then it's used for legend
        :param x_legend: if provided then it's used as titles for x axis
        :param x_legend_generate: if provided and x_legend is not provided, then using x_legend_converter generate
               up to x_legend_generate marks
        :param x_legend_converter: function to convert x values to x_legend values.
        :param y_legend: if provided then it's used as titles for y axis
        :param y_legend_converter: if y_legend is not provided, it is generated from range(min_y, max_y). Converter can
               be applied to make it look better. For example sizeutil.convert_bytes
        :param unit: if provided, then it's used as suffix for y values
        :param message: if provided, the graph will contain the text message in the middle, like "No faults" or
               "No data"
        :param fault_lines: LVES-602. If provided, the graph will contain vertical lines from average to limit
        :param fault_color: LVES-602. Color of the fault lines
        :type fault_lines: list[tuple[(float, float)]]
        :type fault_color: str

        >>> svgchart = SvgChart()
        >>> d1 = [(0,1), (1,1.5), (4,2), (5,6), (5.1,6), (5.6,6), (9,3), (10,10)]
        >>> d2 = [(0,0), (1.5,1.5), (2,2), (2.5,3), (3,4), (5,4), (7,3), (8,2), (9,1), (10,1)]
        >>> datasets = [d1, d2]
        >>> colors = ['red', 'green']
        >>> names = ['foo', 'bar']
        >>> svgchart.add_graph(datasets, colors, 0, 10, unit='DD', names=names)
        >>> svgchart.add_graph(datasets, colors, 0, 10, x_legend=[1,2,3,4,5,6,7,8,9], unit='Kg', message="No Faults")
        >>> print svgchart.dump()
        >>> svgchart.save('/tmp/1.svg')
        """
        average_names = ['average', 'database']
        # random suffix to distinguish svg's from each other
        suffix = self._gen_suffix()

        try:
            if minimum_y is None:
                minimum_y = min(min((y for (_, y) in dataset)) for dataset in datasets)
            if maximum_y is None:
                maximum_y = max(max((y for (_, y) in dataset)) for dataset in datasets)

            maximum_x = max(max((x for (x, _) in dataset)) for dataset in datasets)
            minimum_x = min(min((x for (x, _) in dataset)) for dataset in datasets)
        except ValueError:
            maximum_x = 0
            minimum_x = 0
            minimum_y = 0
            maximum_y = 0
            datasets = [[] for _ in datasets]

        y_offset = self.height / 20.0

        # Create values for y-axis
        if not y_legend:
            dy = (maximum_y - minimum_y) / self.y_legend_points
            y_legend = [y_legend_converter(y) for y in frange(minimum_y, maximum_y + dy, dy)]

        if not y_legend:
            y_legend = ['0' + (unit or '')]

        if not x_legend:
            pattern_width = self.width // self.x_legend_points
        else:
            pattern_width = (1.0 * self.width) // (len(x_legend) - 1)

        if len(y_legend) > 1:
            pattern_height = (1.0 * self.height - 2 * y_offset) // (len(y_legend) - 1)
        else:
            pattern_height = self.height // 2.0

        svg_id = f'svg-{suffix}'
        svg = self.dwg.add(self.dwg.svg(id_=svg_id, insert=(0, self.graph_offset_y)))

        defs = svg.defs
        clippath = defs.add(self.dwg.clipPath(id=f'cut_lines_{suffix}'))
        clippath.add(self.dwg.rect(insert=(self.left_legend_width, self.padding_y), size=(self.width, self.height)))

        if title:
            svg.add(self.dwg.text(title, insert=(self.left_legend_width, self.font_size), font_size=self.font_size))

        self._draw_y_legend(svg, y_legend, unit, pattern_height, y_offset)

        lines_group = svg.add(self.dwg.g(id='lines', filter="url(#shadow)"))
        boundary = lines_group.add(self.dwg.g(clip_path=f'url(#cut_lines_{suffix})'))
        boundary.add(self.dwg.rect(insert=(self.left_legend_width, self.padding_y),
                                   size=(self.width + 1, self.height + 1),
                                   fill='white', stroke="black"))

        self._draw_grid(boundary, pattern_height, pattern_width, y_offset)

        lines = []
        # circles = []

        # list of activity for of lines from average_names
        is_average_empty = []
        if maximum_y - minimum_y > 0:
            for dataset, color in list(zip(datasets, colors)):
                coords = self.convert_coordinates(dataset, maximum_x, maximum_y, minimum_x, minimum_y, y_offset)
                # Shadows removed by ticket LVES-251
                # coords_shadow = [(x + 3, y + 3) for (x, y) in coords]
                # boundary.add(self.dwg.polyline(points=coords_shadow, class_="line_shadow",
                #                               fill='none', stroke='lightgray', stroke_width="2"))

                value = [y for (_, y) in dataset]

                if dict(zip(colors, names))[color] in average_names:
                    is_average_empty.append(names and not any(value))

                # zip coords together with shift by 1, in order to get data like this [(p1, p2), (p2, p3), ...]
                _lines = list(zip(coords, coords[1:]))
                # same as for lines, but for real values, corresponding to that lines
                _datas = list(zip(dataset, dataset[1:]))
                # finally, combine coords and datas
                data_list = list(zip(_lines, _datas))
                for (line, data) in data_list:
                    p1, p2 = line
                    d1, d2 = data
                    x1, y1 = p1
                    x2, y2 = p2
                    t1, v1 = d1
                    t2, v2 = d2

                    # Draw graph line
                    lines.append(
                        self.dwg.line(
                            p1, p2,
                            stroke=color,
                            stroke_width=1,
                            onmousemove=(
                                f"show_tip(evt, '{svg_id}', {x1}, {y1}, {x2}, {y2}, "
                                f"'{x_legend_converter(t1)}', '{str(y_legend_converter(v1)) + (unit or '')}', "
                                f"'{x_legend_converter(t2)}', '{str(y_legend_converter(v2)) + (unit or '')}')"
                            )
                        )
                    )
            if fault_lines is not None:
                for fault_line in fault_lines:
                    fault_coords = self.convert_coordinates(
                        fault_line,
                        maximum_x,
                        maximum_y,
                        minimum_x,
                        minimum_y,
                        y_offset,
                    )
                    lines.append(
                        self.dwg.line(
                            fault_coords[0], fault_coords[1],
                            stroke=fault_color,
                            stroke_width=1))

            # if is_average_empty is empty => no average lines, do not write label
            # all(is_average_empty) == True => all average lines are empty
            # all(is_average_empty) == False => some average line is not empty
            if is_average_empty and all(is_average_empty):
                self._draw_no_activity(boundary)
        else:
            # add ZERO line to graph in no data in dataset
            for color in colors:
                x0, y0 = (self.left_legend_width, self.height - y_offset + self.padding_y)
                x1, y1 = (self.width + self.left_legend_width, y0)
                lines.append(self.dwg.line((x0, y0), (x1, y1), fill='none', stroke=color,
                                           stroke_width='2'),)
                if names and dict(zip(colors, names))[color] in average_names:
                    self._draw_no_activity(boundary)

        for line in lines:
            boundary.add(line)

        self._draw_names(svg, names, colors)

        self.graph_offset_y += self.height + 20 + self.padding_y
        self.dwg.update({'height': self.graph_offset_y})

        if x_legend:
            self._add_x_legend(x_legend)
        if not x_legend and x_legend_generate:
            self._add_x_legend(None, minimum_x, maximum_x, x_legend_generate, x_legend_converter)

        self._draw_message(svg, message)

    def _draw_names(self, svg, names, colors):
        if names:
            s = 10
            i = 0
            font_size = s
            for (name, color) in zip(names, colors):
                x = self.width + self.left_legend_width + 20
                y = 10 + self.padding_y + i * (s + 3)
                svg.add(self.dwg.rect(insert=(x, y), size=(s, s), fill=color, stroke='black'))
                svg.add(self.dwg.text(name, insert=(x + s + 5, y + s), font_size=font_size))
                i += 1

    def _draw_message(self, svg, message):
        if message:
            # Draw message in the middle of the graph
            message_font_size = 24
            svg.add(self.dwg.text(message, insert=(self.left_legend_width + self.width // 3,
                                                   self.padding_y + self.height // 2), font_size=message_font_size))

    def _draw_y_legend(self, svg, y_legend, unit, pattern_height, y_offset):
        ylg = svg.add(self.dwg.g(id='y_legend'))
        i = 0
        font_size = min(self.font_size, pattern_height - 1)
        for legend in y_legend:
            txt = f'{legend}{unit or ""}'
            ylg.add(self.dwg.text(
                txt,
                insert=(5, self.height - y_offset - pattern_height * i + font_size // 2 + self.padding_y),
                font_size=font_size))
            i += 1

    def _draw_grid(self, boundary, pattern_height, pattern_width, y_offset):
        # Draw grid
        # 1. Verticals
        for x in xfrange(0, self.width, pattern_width):
            boundary.add(self.dwg.line(start=(x + self.left_legend_width, self.height + self.padding_y),
                                       end=(x + self.left_legend_width, self.padding_y),
                                       stroke='black', stroke_width=1,
                                       # stroke_linecap="round",
                                       stroke_dasharray="1 2"))
        # 2. Horisontals
        for y in xfrange(0 + y_offset, self.height, pattern_height):
            boundary.add(self.dwg.line(start=(self.left_legend_width, self.height - y + self.padding_y),
                                       end=(self.left_legend_width + self.width, self.height - y + self.padding_y),
                                       stroke='black', stroke_width=1,
                                       stroke_dasharray="1 2"))

    def _draw_no_activity(self, boundary):
        boundary.add(
            self.dwg.text(
                'No activity',
                insert=(
                    self.width // 2 + self.left_legend_width // 2 - len('No activity') // 2,
                    self.height // 2),
                fill='black')
        )

    def convert_coordinates(self, dataset, maximum_x, maximum_y, minimum_x, minimum_y, y_offset):
        """
        :type y_offset: float
        :type minimum_y: float
        :type minimum_x: float
        :type maximum_y: float
        :type maximum_x: float
        :type dataset: tuple
        :rtype: list
        """
        try:
            dx = (1.0 * self.width) / (maximum_x - minimum_x)
        except ZeroDivisionError:
            dx = 0
        dy = (1.0 * self.height - 2 * y_offset) / (maximum_y - minimum_y)
        coords = [
            (
                (x - minimum_x) * dx + self.left_legend_width,
                self.height - y_offset - y * dy + self.padding_y
            )
            for (x, y) in dataset
        ]
        return coords

    @staticmethod
    def _is_peak(data_list, idx, p1, p2):
        result = False

        list_len = len(data_list)
        y2 = p2[1]
        y1 = p1[1]

        if y2 < y1:
            if idx < list_len - 1:
                # Line is not last
                next_line, _ = data_list[idx + 1]
                _, (_, n_y2) = next_line
                if y2 <= n_y2:
                    result = True
            else:
                result = True
        return result

    def _add_x_legend(self, x_values, min_x=None, max_x=None, number=None, x_legend_converter=str):
        # TODO add support of str values of x_values
        if not x_values:
            assert number, "Number should be specified and not zero"
            pattern_width = self.width // number

            if not all(v is not None for v in (min_x, max_x, number)):
                # "min_x, max_x, and number should be not None in order to draw legend"
                return

            x_legend = [x_legend_converter(x) for x in
                        xfrange(min_x, max_x, (max_x - min_x) / number)]
        else:
            if not number:
                number = X_LEGEND_POINTS
            x_legend = frange(min(x_values),
                              max(x_values) + (max(x_values) - min(x_values)) / number,
                              (max(x_values) - min(x_values)) / number)

            x_legend = [x_legend_converter(x) for x in x_legend]

            parts = len(x_legend) - 1
            parts = max(parts, 1)

            pattern_width = (1.0 * self.width) / parts

        svg_id = f'svg-{str(uuid.uuid4())}'
        svg = self.dwg.add(self.dwg.svg(id_=svg_id, insert=(0, self.graph_offset_y)))

        max_x_legend_text = 0
        font_size = min(pattern_width, 10)

        i = 0
        for legend in x_legend:
            txt = str(legend)
            if len(txt) > max_x_legend_text:
                max_x_legend_text = len(txt)

            x = self.left_legend_width + pattern_width * i
            y = 0 + self.padding_y

            svg.add(self.dwg.text(
                txt,
                insert=(x, y),
                font_size=font_size,
                transform=f"rotate(90, {x},{y})"
            ))
            i += 1

        self.graph_offset_y += 20 + max_x_legend_text * font_size
        self.dwg.update({'height': self.graph_offset_y})

    def add_text_box(self, text, font_size=None):
        """
        :param text: text in box
        :param font_size: font size for text
        """
        # use default font size
        font_size = font_size or 24
        # get svg suffix
        suffix = self._gen_suffix()

        svg_id = f'svg-{suffix}'
        svg = self.dwg.add(self.dwg.svg(id_=svg_id, insert=(0, self.graph_offset_y)))

        defs = svg.defs
        clippath = defs.add(self.dwg.clipPath(id=f'cut_lines_{suffix}'))
        clippath.add(self.dwg.rect(insert=(self.left_legend_width, self.padding_y), size=(self.width, self.height)))

        lines_group = svg.add(self.dwg.g(id='lines', filter="url(#shadow)"))
        boundary = lines_group.add(self.dwg.g(clip_path=f'url(#cut_lines_{suffix})'))
        boundary.add(self.dwg.rect(insert=(self.left_legend_width, self.padding_y),
                                   size=(self.width + 1, self.height + 1), fill='white',
                                   stroke="black"))

        svg.add(self.dwg.text(text, insert=(self.left_legend_width + self.width // 3,
                                            self.padding_y + self.height // 2), font_size=font_size))

        self.graph_offset_y += self.height + 20 + self.padding_y
        self.dwg.update({'height': self.graph_offset_y})

    def _gen_suffix(self):
        """
        Generate suffix for graph methods
        random suffix to distinguish svg's from each other
        """
        suffix = str(self._svg_id)
        self._svg_id += 1
        return suffix

    def dump(self):
        self._finalize()
        xml_header = '<?xml version="1.0" encoding="utf-8" ?>\n'
        return xml_header + self.dwg.tostring()

    def save(self, file_name):
        self._finalize()
        self.dwg.saveas(file_name)

Youez - 2016 - github.com/yon3zu
LinuXploit