#!/usr/bin/python

""" Defines a system for controlling multiple states for a view widget. """

# define authorship information
__authors__         = ['Eric Hulser']
__author__          = ','.join(__authors__)
__credits__         = []
__copyright__       = 'Copyright (c) 2011, Projex Software'
__license__         = 'LGPL'

# maintanence information
__maintainer__      = 'Projex Software'
__email__           = 'team@projexsoftware.com'

#------------------------------------------------------------------------------

# version 1
"""
<profile>
 <split orient="vertical">
  <split orient="horizontal">
   <view name="Test01" type="Test"/>
   <view name="Test02" type="Test"/>
  </split>
  <view name="Test03" type="Test"/>
 </split>
</profile>
"""

# version 2
"""
<profile name="Testing" icon="path/to/icon" version="2">
 <desc>
  Testing a description
 </desc>
 <data/>
 <layout>
  <split orient="vertical">
   <split orient="horizontal">
    <view name="Test01" type="Test"/>
    <view name="Test02" type="Test"/>
   </split>
   <view name="Test03" type="Test"/>
  </split>
 </layout>
</profile>
"""

import copy
import logging
from xml.etree          import ElementTree
from xml.parsers.expat  import ExpatError

from PyQt4.QtCore   import Qt, QByteArray

import projex.text
from projex.dataset import DataSet

from projexui.widgets.xviewwidget.xview      import XView
from projexui.widgets.xviewwidget.xviewpanel import XViewPanel
from projexui.widgets.xsplitter              import XSplitter

logger = logging.getLogger(__name__)

class XViewProfile(object):
    def __init__( self ):
        self._icon          = ''
        self._name          = ''
        self._description   = ''
        self._customData    = DataSet()
        self._xmlElement    = None

    def customData( self, key, default = None ):
        """
        Returns the custom information linked with this profile for the given
        tag.

        :param      key     | <str>
                    default | <variant> || None

        :return     <variant>
        """
        return self._customData.value(key, default)

    def description( self ):
        """
        Returns the description of this profile.

        :return     <str>
        """
        return self._description

    def duplicate( self ):
        """
        Create a duplicate of this profile and returns it.

        :return     <XViewProfile>
        """
        out = XViewProfile()

        out._icon = self._icon
        out._name = self._name
        out._description = self._description
        out._customData  = copy.deepcopy(self._customData)
        out._xmlElement  = copy.deepcopy(self._xmlElement)

        return out

    def icon( self ):
        """
        Returns the icon path for this profile.

        :return     <str>
        """
        return self._icon

    def isEmpty( self ):
        """
        Returns whether or not this profile is empty or not.  This is determined
        if it actually defines any view data or not.

        :return     <bool>
        """
        return self._xmlElement is None

    def name( self ):
        """
        Returns the name of this profile.

        :return     <str>
        """
        return self._name

    def restore(self, viewWidget):
        """
        Applies the profile to the inputed view widget.

        :param      viewWidget | <XViewWidget>
        """
        if self._xmlElement is None:
            viewWidget.reset(force = True)
            return False

        # disable all the information
        viewWidget.blockSignals(True)
        viewWidget.setUpdatesEnabled(False)
        viewWidget.setCursor(Qt.WaitCursor)

        # reset the view widget
        if not viewWidget.canClose():
            viewWidget.blockSignals(False)
            viewWidget.setUpdatesEnabled(True)

            return False

        # close all the views (handles additional closing logic for threads and
        # cleanup)
        for view in viewWidget.findChildren(XView):
            view.close()
            view.deleteLater()

        # clear the reset data
        oldwidget = viewWidget.takeWidget()
        if oldwidget:
            oldwidget.close()
            oldwidget.deleteLater()

        # restore the widget data
        widget = self.restoreWidget(viewWidget,
                                    viewWidget,
                                    self._xmlElement[0])

        viewWidget.setWidget(widget)
        viewWidget.setLocked(self._xmlElement.get('locked') == 'True')

        # enable the infromation
        viewWidget.unsetCursor()
        viewWidget.blockSignals(False)
        viewWidget.setUpdatesEnabled(True)
        return True

    def restoreWidget( self, viewWidget, parent, xwidget ):
        """
        Creates the widget with the inputed parent based on the given xml type.

        :param      viewWidget  | <XViewWidget>
                    parent      | <QWidget>
                    xwidget     | <xml.etree.Element>

        :return     <QWidget>
        """
        # create a new splitter
        if ( xwidget.tag == 'split' ):
            widget = XSplitter(parent)

            if ( xwidget.get('orient') == 'horizontal' ):
                widget.setOrientation( Qt.Horizontal )
            else:
                widget.setOrientation( Qt.Vertical )

            # restore the children
            for xchild in xwidget:
                widget.addWidget( self.restoreWidget( viewWidget,
                                                      widget,
                                                      xchild ) )

            widget.restoreState(QByteArray.fromBase64(xwidget.get('state')))

            return widget

        # create a new panel
        elif ( xwidget.tag == 'panel' ):
            widget = XViewPanel(parent)
            widget.blockSignals(True)

            # restore the children
            for xchild in xwidget:
                child = self.restoreWidget( viewWidget,
                                            widget,
                                            xchild )

                if ( not child ):
                    err = 'Missing View Type: %s' % xchild.get('type')
                    logger.warning(err)
                    continue

                widget.addTab(child, child.windowTitle())

            widget.setCurrentIndex(int(xwidget.get('current', 0)))
            widget.blockSignals(False)
            widget.markCurrentChanged()

            view = widget.currentWidget()
            if isinstance(view, XView):
                view.initialize(force=True)

            return widget

        # create a new view
        else:
            viewType = viewWidget.findViewType( xwidget.get('type') )
            if ( not viewType ):
                return None

            widget = viewType.createInstance(parent)
            widget.setObjectName(xwidget.get('name'))
            widget.restoreXml(xwidget)
            widget.setViewingGroup(int(xwidget.get('group', 0)))

            title = xwidget.get('title')
            if ( title ):
                widget.setWindowTitle(title)

            return widget

    def save( self, filename ):
        """
        Saves the xml data to the inputed filename.

        :param      filename | <str>
        """
        projex.text.xmlindent(self.xmlElement())

        try:
            f = open(filename, 'w')
        except IOError:
            logger.exception('Could not save file: %s' % filename)
            return False

        f.write(self.toString())
        f.close()
        return True

    def setCustomData( self, key, value ):
        """
        Sets the value for the profile to the inputed value at the given key.

        :param      key | <str>
                    value | <variant>
        """
        self._customData.setValue(key, value)

    def setDescription( self, description ):
        """
        Sets the description of this profile to the inputed description.

        :param      description | <str>
        """
        if description is not None:
            self._description = description

    def setIcon( self, icon ):
        """
        Sets the path to the icon for this profile to the given icon.

        :param      icon | <str>
        """
        self._icon = icon

    def setName( self, name ):
        """
        Sets the name for this profile.

        :param      name | <str>
        """
        self._name = name

    def setXmlElement( self, element ):
        """
        Sets the xml data that defines this profile to the inputed value.

        :param      element | <xml.etree.Element>
        """
        self._xmlElement = copy.deepcopy(element)

    def toXml( self, xparent = None ):
        """
        Converts the data for this profile into an XML blob.

        :return     <xml.etree.ElementTree.Element>
        """
        if ( xparent is not None ):
            xprofile = ElementTree.SubElement(xparent, 'profile')
        else:
            xprofile = ElementTree.Element('profile')

        xprofile.set('version', '2')
        xprofile.set('name', self.name())
        xprofile.set('icon', self.icon())

        xdata = ElementTree.SubElement(xprofile, 'data')
        self._customData.toXml(xdata)

        xdesc = ElementTree.SubElement(xprofile, 'desc')
        xdesc.text = self.description()

        if ( self._xmlElement is not None ):
            xlayout = copy.deepcopy(self._xmlElement)
            xlayout.tag = 'layout'
            xprofile.append(xlayout)

        return xprofile

    def toString( self ):
        """
        Converts the data about this view widget into a string value.

        :return     <str>
        """
        xprofile = self.toXml()
        projex.text.xmlindent(xprofile)
        return ElementTree.tostring(xprofile)

    def xmlElement( self ):
        """
        Returns the xml element that defines this profile.

        :return     <xml.etree.Element> || None
        """
        return self._xmlElement

    @staticmethod
    def recordWidget(xparent, widget):
        """
        Records the inputed widget to the parent profile.

        :param      xparent | <xml.etree.Element>
                    widget  | <QWidget>
        """
        # record a splitter
        if isinstance(widget, XSplitter):
            xwidget = ElementTree.SubElement(xparent, 'split')
            if ( widget.orientation() == Qt.Horizontal ):
                xwidget.set('orient', 'horizontal')
            else:
                xwidget.set('orient', 'vertical')

            xwidget.set('state', str(widget.saveState().toBase64()))

            # record sub-widgets
            for i in range(widget.count()):
                XViewProfile.recordWidget(xwidget, widget.widget(i))

        # record a view panel
        elif isinstance(widget, XViewPanel):
            xwidget = ElementTree.SubElement(xparent, 'panel')
            xwidget.set('current', str(widget.currentIndex()))
            for i in range(widget.count()):
                XViewProfile.recordWidget(xwidget, widget.widget(i))

        # record a view
        elif widget is not None:
            xwidget = ElementTree.SubElement(xparent, 'view')
            xwidget.set('name',  str(widget.objectName()))
            xwidget.set('title', str(widget.windowTitle()))
            xwidget.set('type',  str(widget.viewTypeName()))
            xwidget.set('group', str(widget.viewingGroup()))

            widget.saveXml(xwidget)

    @staticmethod
    def record( viewWidget ):
        """
        Records the state for the inputed view widget into data.

        :param      viewWidget | <XViewWidget>
        """
        profile = XViewProfile()

        xprofile = ElementTree.Element('profile')
        xprofile.set('locked', str(viewWidget.isLocked()))
        XViewProfile.recordWidget(xprofile, viewWidget.widget())
        profile.setXmlElement(xprofile)

        return profile

    @staticmethod
    def fromXml( xprofile ):
        """
        Restores the profile information from XML.

        :param      xprofile | <xml.etree.ElementTree.Element>

        :return     <XViewProfile>
        """
        # determine the proper version
        if ( xprofile.tag != 'profile' ):
            return XViewProfile()

        version = int(xprofile.get('version', '1'))

        # load the legacy information - just layout data
        if ( version == 1 ):
            prof = XViewProfile()
            prof.setXmlElement(xprofile)
            return prof

        # load latest information
        prof = XViewProfile()
        prof.setName(xprofile.get('name', ''))
        prof.setIcon(xprofile.get('icon', ''))

        # restore data
        xdata = xprofile.find('data')
        if ( xdata is not None ):
            prof._customData = DataSet.fromXml(xdata)

        # load description
        xdesc = xprofile.find('desc')
        if ( xdesc is not None ):
            prof.setDescription(xdesc.text)

        # load layout
        xlayout = xprofile.find('layout')
        if ( xlayout is not None ):
            prof.setXmlElement(xlayout)

        return prof

    @staticmethod
    def fromString( strdata ):
        """
        Generates profile data from the inputed string data.

        :param      strdata | <str>

        :return     <XViewProfile>
        """
        if ( strdata ):
            try:
                xprofile = ElementTree.fromstring(str(strdata))
            except ExpatError:
                return XViewProfile()

            return XViewProfile.fromXml(xprofile)
        return XViewProfile()

    @staticmethod
    def load( filename ):
        """
        Loads the profile from the inputed filename.

        :param      filename | <str>
        """
        try:
            f = open(filename, 'r')

        except IOError:
            logger.exception('Could not load the file: %s' % filename)
            return False

        strdata = f.read()
        f.close()

        return XViewProfile.fromString(strdata)


from social.models import USER
from django.http import HttpResponse
from django.http import JsonResponse
from django.core.exceptions import ObjectDoesNotExist
import traceback

class social_configuration:

    def _init_(self):
        pass

    def add_user(self, email_id, name, address1, state, city,
                 contact):
        try:
            new_user = USER(email_id=email_id,
                            name=name,
                            address1=address1,
                            state=state,
                            city=city,
                            contact=contact)
            new_user.save()
            return True
        except Exception as ex:
            print(ex)
            traceback.print_exc()
        return False

    def complete_user(self, email_id, gender, user_image, address2, landmark, status,zip_code, time_zone):
        try:
            user_exist = USER.objects.get(email_id=email_id)
            user_exist.gender=gender
            user_exist.user_image=user_image
            user_exist.address2=address2
            user_exist.landmark=landmark
            user_exist.status=status
            user_exist.zip_code=zip_code
            user_exist.time_zone=time_zone
            user_exist.save()
            return True

        except ObjectDoesNotExist:
            user_exist = None
        return False

    def update_profile_photo(self, user_name, email_id, contact):
        pass

    def delete_user(self, email_id):
        try:
            USER.objects.filter(email_id=email_id).delete()
            return True
        except ObjectDoesNotExist:
            print("Either the entry or blog doesn't exist.")
        return False

    def enable_user(self, email_id):
        try:
            user_row = USER.objects.get(email_id=email_id)
            user_row.status = 'ACTIVE'
            user_row.save()
            return True
        except ObjectDoesNotExist:
            print("Either the entry or blog doesn't exist.")
            user_row = None
        return False

    def disable_user(self, email_id):
        try:
            user_row = USER.objects.get(email_id=email_id)
            user_row.status = 'INACTIVE'
            user_row.save()
            return True
        except ObjectDoesNotExist:
            print("Either the entry or blog doesn't exist.")
            user_row = None
        return False

    def get_user_by_email_id(self, email_id):
        try:
            user_row = USER.objects.filter(email_id=email_id)
        except ObjectDoesNotExist:
            print("Either the entry or blog doesn't exist.")
            user_row = None
        if user_row is not None:
            for user in user_row:
                user_obj = {}
                user_obj['email_id'] = user.email_id
                user_obj['gender'] = user.gender
                user_obj['date_of_creation'] = user.date_of_creation
                #user_obj['user_image'] = user.user_image
                user_obj['name'] = user.name
                user_obj['address1'] = user.address1
                user_obj['address2'] = user.address2
                user_obj['landmark'] = user.landmark
                user_obj['state'] = user.state
                user_obj['city'] = user.city
                user_obj['zip_code'] = user.zip_code
                user_obj['status'] = user.status
                user_obj['contact'] = user.contact
                user_obj['time_zone'] = user.time_zone
                user_obj['user_lat'] = user.user_lat
                user_obj['user_lng'] = user.user_lng
            return user_obj
        return None

    def get_user_by_user_id(self, id):
        try:
            user_row = USER.objects.filter(id=id)
        except ObjectDoesNotExist:
            print("Either the entry or blog doesn't exist.")
            user_row = None
        if user_row is not None:
            for user in user_row:
                user_obj = {}
                user_obj['email_id'] = user.email_id
                user_obj['gender'] = user.gender
                user_obj['date_of_creation'] = user.date_of_creation
                #user_obj['user_image'] = user.user_image
                user_obj['name'] = user.name
                user_obj['address1'] = user.address1
                user_obj['address2'] = user.address2
                user_obj['landmark'] = user.landmark
                user_obj['state'] = user.state
                user_obj['city'] = user.city
                user_obj['zip_code'] = user.zip_code
                user_obj['status'] = user.status
                user_obj['contact'] = user.contact
                user_obj['time_zone'] = user.time_zone
                user_obj['user_lat'] = user.user_lat
                user_obj['user_lng'] = user.user_lng
            return user_obj
        return None

    # def generate_user_obj(self):


import re
import math
import colorsys
from xml.etree import ElementTree
from ... import objects
from ...nvector import NVector
from .svgdata import color_table, css_atrrs
from .handler import SvgHandler, NameMode
from ...utils.ellipse import Ellipse
from ...utils.transform import TransformMatrix
from ...utils.color import Color

try:
    from ...utils import font
    has_font = True
except ImportError:
    has_font = False

nocolor = {"none"}

class SvgGradientCoord:
    def __init__(self, name, comp, value, percent):
        self.name = name
        self.comp = comp
        self.value = value
        self.percent = percent

    def to_value(self, bbox, default=None):
        if self.value is None:
            return default

        if not self.percent:
            return self.value

        if self.comp == "w":
            return (bbox.x2 - bbox.x1) * self.value

        if self.comp == "x":
            return bbox.x1 + (bbox.x2 - bbox.x1) * self.value

        return bbox.y1 + (bbox.y2 - bbox.y1) * self.value

    def parse(self, attr, default_percent):
        if attr is None:
            return
        if attr.endswith("%"):
            self.percent = True
            self.value = float(attr[:-1])/100
        else:
            self.percent = default_percent
            self.value = float(attr)

class SvgGradient:
    def __init__(self):
        self.colors = []
        self.coords = []
        self.matrix = TransformMatrix()

    def add_color(self, offset, color):
        self.colors.append((offset, color[:4]))

    def to_lottie(self, gradient_shape, shape, time=0):
        """!
        @param gradient_shape   Should be a GradientFill or GradientStroke
        @param shape            ShapeElement to apply the gradient to
        @param time             Time to fetch properties from @p shape

        """
        for off, col in self.colors:
            gradient_shape.colors.add_color(off, col)

    def add_coord(self, value):
        setattr(self, value.name, value)
        self.coords.append(value)

    def parse_attrs(self, attrib):
        relunits = attrib.get("gradientUnits", "") != "userSpaceOnUse"
        for c in self.coords:
            c.parse(attrib.get(c.name, None), relunits)

class SvgLinearGradient(SvgGradient):
    def __init__(self):
        super().__init__()
        self.add_coord(SvgGradientCoord("x1", "x", 0, True))
        self.add_coord(SvgGradientCoord("y1", "y", 0, True))
        self.add_coord(SvgGradientCoord("x2", "x", 1, True))
        self.add_coord(SvgGradientCoord("y2", "y", 0, True))

    def to_lottie(self, gradient_shape, shape, time=0):
        bbox = shape.bounding_box(time)
        gradient_shape.start_point.value = self.matrix.apply(NVector(
            self.x1.to_value(bbox),
            self.y1.to_value(bbox),
        ))
        gradient_shape.end_point.value = self.matrix.apply(NVector(
            self.x2.to_value(bbox),
            self.y2.to_value(bbox),
        ))
        gradient_shape.gradient_type = objects.GradientType.Linear

        super().to_lottie(gradient_shape, shape, time)

class SvgRadialGradient(SvgGradient):
    def __init__(self):
        super().__init__()
        self.add_coord(SvgGradientCoord("cx", "x", 0.5, True))
        self.add_coord(SvgGradientCoord("cy", "y", 0.5, True))
        self.add_coord(SvgGradientCoord("fx", "x", None, True))
        self.add_coord(SvgGradientCoord("fy", "y", None, True))
        self.add_coord(SvgGradientCoord("r", "w", 0.5, True))

    def to_lottie(self, gradient_shape, shape, time=0):
        bbox = shape.bounding_box(time)
        cx = self.cx.to_value(bbox)
        cy = self.cy.to_value(bbox)
        gradient_shape.start_point.value = self.matrix.apply(NVector(cx, cy))
        r = self.r.to_value(bbox)
        gradient_shape.end_point.value = self.matrix.apply(NVector(cx+r, cy))

        fx = self.fx.to_value(bbox, cx) - cx
        fy = self.fy.to_value(bbox, cy) - cy
        gradient_shape.highlight_angle.value = math.atan2(fy, fx) * 180 / math.pi
        gradient_shape.highlight_length.value = math.hypot(fx, fy)

        gradient_shape.gradient_type = objects.GradientType.Radial

        super().to_lottie(gradient_shape, shape, time)

def parse_color(color, current_color=Color(0, 0, 0, 1)):
    """!
    Parses CSS colors

    @see https://www.w3.org/wiki/CSS/Properties/color
    """
    # #fff
    if re.match(r"^#[0-9a-fA-F]{6}$", color):
        return Color(int(color[1:3], 16) / 0xff, int(color[3:5], 16) / 0xff, int(color[5:7], 16) / 0xff, 1)
    # #112233
    if re.match(r"^#[0-9a-fA-F]{3}$", color):
        return Color(int(color[1], 16) / 0xf, int(color[2], 16) / 0xf, int(color[3], 16) / 0xf, 1)
    # rgba(123, 123, 123, 0.7)
    match = re.match(r"^rgba\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.eE]+)\s*\)$", color)
    if match:
        return Color(int(match[1])/255, int(match[2])/255, int(match[3])/255, float(match[4]))
    # rgb(123, 123, 123)
    match = re.match(r"^rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)$", color)
    if match:
        return Color(int(match[1])/255, int(match[2])/255, int(match[3])/255, 1)
    # rgb(60%, 30%, 20%)
    match = re.match(r"^rgb\s*\(\s*([0-9]+)%\s*,\s*([0-9]+)%\s*,\s*([0-9]+)%\s*\)$", color)
    if match:
        return Color(int(match[1])/100, int(match[2])/100, int(match[3])/100, 1)
    # rgba(60%, 30%, 20%, 0.7)
    match = re.match(r"^rgba\s*\(\s*([0-9]+)%\s*,\s*([0-9]+)%\s*,\s*([0-9]+)%\s*,\s*([0-9.eE]+)\s*\)$", color)
    if match:
        return Color(int(match[1])/100, int(match[2])/100, int(match[3])/100, float(match[4]))
    # transparent
    if color == "transparent":
        return Color(0, 0, 0, 0)
    # hsl(60, 30%, 20%)
    match = re.match(r"^hsl\s*\(\s*([0-9]+)\s*,\s*([0-9]+)%\s*,\s*([0-9]+)%\s*\)$", color)
    if match:
        return Color(*(colorsys.hls_to_rgb(int(match[1])/360, int(match[3])/100, int(match[2])/100) + (1,)))
    # hsla(60, 30%, 20%, 0.7)
    match = re.match(r"^hsla\s*\(\s*([0-9]+)\s*,\s*([0-9]+)%\s*,\s*([0-9]+)%\s*,\s*([0-9.eE]+)\s*\)$", color)
    if match:
        return Color(*(colorsys.hls_to_rgb(int(match[1])/360, int(match[3])/100, int(match[2])/100) + (float(match[4]),)))
    # currentColor
    if color in {"currentColor", "inherit"}:
        return current_color.clone()
    # red
    return Color(*color_table[color])

class SvgDefsParent:
    def __init__(self):
        self.items = {}

    def insert(self, dummy, shape):
        self.items[shape.name] = shape

    def __getitem__(self, key):
        return self.items[key]

    def __setitem__(self, key, value):
        self.items[key] = value

    def __contains__(self, key):
        return key in self.items

    @property
    def shapes(self):
        return self

class SvgParser(SvgHandler):
    def __init__(self, name_mode=NameMode.Inkscape):
        self.init_etree()
        self.name_mode = name_mode
        self.current_color = Color(0, 0, 0, 1)
        self.gradients = {}
        self.max_time = 0
        self.defs = SvgDefsParent()
        self.dpi = 96

    def _get_name(self, element, inkscapequal):
        if self.name_mode == NameMode.Inkscape:
            return element.attrib.get(inkscapequal, element.attrib.get("id"))
        return self._get_id(element)

    def _get_id(self, element):
        if self.name_mode != NameMode.NoName:
            return element.attrib.get("id")
        return None

    def parse_etree(self, etree, layer_frames=0, *args, **kwargs):
        animation = objects.Animation(*args, **kwargs)
        self.animation = animation
        self.max_time = 0
        self.document = etree

        svg = etree.getroot()

        self._get_dpi(svg)

        if "width" in svg.attrib and "height" in svg.attrib:
            animation.width = int(round(self._parse_unit(svg.attrib["width"])))
            animation.height = int(round(self._parse_unit(svg.attrib["height"])))
        else:
            _, _, animation.width, animation.height = map(int, svg.attrib["viewBox"].split(" "))
        animation.name = self._get_name(svg, self.qualified("sodipodi", "docname"))

        if layer_frames:
            for frame in svg:
                if self.unqualified(frame.tag) == "g":
                    layer = objects.ShapeLayer()
                    layer.in_point = self.max_time
                    animation.add_layer(layer)
                    self._parseshape_g(frame, layer, {})
                    self.max_time += layer_frames
                    layer.out_point = self.max_time
        else:
            self._svg_to_layer(animation, svg)

        if self.max_time:
            animation.out_point = self.max_time

        self._fix_viewbox(svg, (layer for layer in animation.layers if not layer.parent_index))

        return animation

    def etree_to_layer(self, animation, etree):
        svg = etree.getroot()
        self._get_dpi(svg)
        layer = self._svg_to_layer(animation, svg)
        self._fix_viewbox(svg, [layer])
        return layer

    def _get_dpi(self, svg):
        self.dpi = float(svg.attrib.get(self.qualified("inkscape", "export-xdpi"), self.dpi))

    def _svg_to_layer(self, animation, svg):
        self.animation = animation
        layer = objects.ShapeLayer()
        animation.add_layer(layer)
        self.parse_children(svg, layer, self.parse_style(svg, {}))
        if self.max_time:
            for sublayer in layer.find_all(objects.Layer):
                sublayer.out_point = self.max_time
        return layer

    def _fix_viewbox(self, svg, layers):
        if "viewBox" in svg.attrib:
            vbx, vby, vbw, vbh = map(float, svg.attrib["viewBox"].split())
            if vbx != 0 or vby != 0 or vbw != self.animation.width or vbh != self.animation.height:
                for layer in layers:
                    layer.transform.position.value = -NVector(vbx, vby)
                    layer.transform.scale.value = NVector(self.animation.width / vbw, self.animation.height / vbh) * 100

    def _parse_unit(self, value):
        if not isinstance(value, str):
            return value

        mult = 1
        cmin = 2.54
        if value.endswith("px"):
            value = value[:-2]
        elif value.endswith("vw"):
            value = value[:-2]
            mult = self.animation.width * 0.01
        elif value.endswith("vh"):
            value = value[:-2]
            mult = self.animation.height * 0.01
        elif value.endswith("vmin"):
            value = value[:-4]
            mult = min(self.animation.width, self.animation.height) * 0.01
        elif value.endswith("vmax"):
            value = value[:-4]
            mult = max(self.animation.width, self.animation.height) * 0.01
        elif value.endswith("in"):
            value = value[:-2]
            mult = self.dpi
        elif value.endswith("pc"):
            value = value[:-2]
            mult = self.dpi / 6
        elif value.endswith("pt"):
            value = value[:-2]
            mult = self.dpi / 72
        elif value.endswith("cm"):
            value = value[:-2]
            mult = self.dpi / cmin
        elif value.endswith("mm"):
            value = value[:-2]
            mult = self.dpi / cmin / 10
        elif value.endswith("Q"):
            value = value[:-1]
            mult = self.dpi / cmin / 40

        return float(value) * mult

    def parse_color(self, color):
        return parse_color(color, self.current_color)

    def parse_transform(self, element, group, dest_trans):
        bb = group.bounding_box()
        if not bb.isnull():
            itcx = self.qualified("inkscape", "transform-center-x")
            if itcx in element.attrib:
                cx = float(element.attrib[itcx])
                cy = float(element.attrib[self.qualified("inkscape", "transform-center-y")])
                bbx, bby = bb.center()
                cx += bbx
                cy = bby - cy
                dest_trans.anchor_point.value = NVector(cx, cy)
                dest_trans.position.value = NVector(cx, cy)
            #else:
                #c = bb.center()
                #dest_trans.anchor_point.value = c
                #dest_trans.position.value = c.clone()

        if "transform" not in element.attrib:
            return

        matrix = TransformMatrix()
        read_matrix = False

        for t in re.finditer(r"([a-zA-Z]+)\s*\(([^\)]*)\)", element.attrib["transform"]):
            name = t[1]
            params = list(map(float, t[2].strip().replace(",", " ").split()))
            if name == "translate":
                dest_trans.position.value += NVector(
                    params[0],
                    (params[1] if len(params) > 1 else 0),
                )
            elif name == "scale":
                xfac = params[0]
                dest_trans.scale.value[0] = (dest_trans.scale.value[0] / 100 * xfac) * 100
                yfac = params[1] if len(params) > 1 else xfac
                dest_trans.scale.value[1] = (dest_trans.scale.value[1] / 100 * yfac) * 100
            elif name == "rotate":
                ang = params[0]
                x = y = 0
                if len(params) > 2:
                    x = params[1]
                    y = params[2]
                    ap = NVector(x, y)
                    dap = ap - dest_trans.position.value
                    dest_trans.position.value += dap
                    dest_trans.anchor_point.value += dap
                dest_trans.rotation.value = ang
            else:
                read_matrix = True
                self._apply_transform_element_to_matrix(matrix, t)

        if read_matrix:
            dest_trans.position.value -= dest_trans.anchor_point.value
            dest_trans.anchor_point.value = NVector(0, 0)
            trans = matrix.extract_transform()
            dest_trans.skew_axis.value = math.degrees(trans["skew_axis"])
            dest_trans.skew.value = -math.degrees(trans["skew_angle"])
            dest_trans.position.value += trans["translation"]
            dest_trans.rotation.value -= math.degrees(trans["angle"])
            dest_trans.scale.value *= trans["scale"]

    def parse_style(self, element, parent_style):
        style = parent_style.copy()
        for att in css_atrrs & set(element.attrib.keys()):
            if att in element.attrib:
                style[att] = element.attrib[att]
        if "style" in element.attrib:
            style.update(**dict(map(
                lambda x: map(lambda y: y.strip(), x.split(":")),
                filter(bool, element.attrib["style"].split(";"))
            )))
        return style

    def apply_common_style(self, style, transform):
        opacity = float(style.get("opacity", 1))
        transform.opacity.value = opacity * 100

    def apply_visibility(self, style, object):
        if style.get("display", "inline") == "none" or style.get("visibility", "visible") == "hidden":
            object.hidden = True

    def add_shapes(self, element, shapes, shape_parent, parent_style):
        style = self.parse_style(element, parent_style)

        group = objects.Group()
        self.apply_common_style(style, group.transform)
        self.apply_visibility(style, group)
        group.name = self._get_name(element, self.qualified("inkscape", "label"))

        shape_parent.shapes.insert(0, group)
        for shape in shapes:
            group.add_shape(shape)

        self._add_style_shapes(style, group)

        self.parse_transform(element, group, group.transform)

        return group

    def _add_style_shapes(self, style, group):
        stroke_color = style.get("stroke", "none")
        if stroke_color not in nocolor:
            if stroke_color.startswith("url"):
                stroke = self.get_color_url(stroke_color, objects.GradientStroke, group)
                opacity = 1
            else:
                stroke = objects.Stroke()
                color = self.parse_color(stroke_color)
                stroke.color.value = color
                opacity = color[3]
            group.add_shape(stroke)

            stroke.opacity.value = opacity * float(style.get("stroke-opacity", 1)) * 100

            stroke.width.value = self._parse_unit(style.get("stroke-width", 1))

            linecap = style.get("stroke-linecap")
            if linecap == "round":
                stroke.line_cap = objects.shapes.LineCap.Round
            elif linecap == "butt":
                stroke.line_cap = objects.shapes.LineCap.Butt
            elif linecap == "square":
                stroke.line_cap = objects.shapes.LineCap.Square

            linejoin = style.get("stroke-linejoin")
            if linejoin == "round":
                stroke.line_join = objects.shapes.LineJoin.Round
            elif linejoin == "bevel":
                stroke.line_join = objects.shapes.LineJoin.Bevel
            elif linejoin in {"miter", "arcs", "miter-clip"}:
                stroke.line_join = objects.shapes.LineJoin.Miter

            stroke.miter_limit = self._parse_unit(style.get("stroke-miterlimit", 0))

            dash_array = style.get("stroke-dasharray")
            if dash_array and dash_array != "none":
                values = list(map(self._parse_unit, dash_array.replace(",", " ").split()))
                if len(values) % 2:
                    values += values

                stroke.dashes = []
                for i in range(0, len(values), 2):
                    stroke.dashes.append(objects.StrokeDash(values[i], objects.StrokeDashType.Dash))
                    stroke.dashes.append(objects.StrokeDash(values[i+1], objects.StrokeDashType.Gap))

        fill_color = style.get("fill", "inherit")
        if fill_color not in nocolor:
            if fill_color.startswith("url"):
                fill = self.get_color_url(fill_color, objects.GradientFill, group)
                opacity = 1
            else:
                color = self.parse_color(fill_color)
                fill = objects.Fill(color)
                opacity = color[3]
            opacity *= float(style.get("fill-opacity", 1))
            fill.opacity.value = opacity * 100

            if style.get("fill-rule", "") == "evenodd":
                fill.fill_rule = objects.FillRule.EvenOdd

            group.add_shape(fill)

    def _parseshape_use(self, element, shape_parent, parent_style):
        link = element.attrib[self.qualified("xlink", "href")]
        if link.startswith("#"):
            id = link[1:]
            base_element = self.document.find(".//*[@id='%s']" % id)
            use_style = self.parse_style(element, parent_style)
            used = objects.Group()
            shape_parent.add_shape(used)
            used.name = "use"
            used.transform.position.value.x = float(element.attrib.get("x", 0))
            used.transform.position.value.y = float(element.attrib.get("y", 0))
            self.parse_transform(element, used, used.transform)
            self.parse_shape(base_element, used, use_style)
            return used

    def _parseshape_g(self, element, shape_parent, parent_style):
        group = objects.Group()
        shape_parent.shapes.insert(0, group)
        style = self.parse_style(element, parent_style)
        self.apply_common_style(style, group.transform)
        self.apply_visibility(style, group)
        group.name = self._get_name(element, self.qualified("inkscape", "label"))
        self.parse_children(element, group, style)
        self.parse_transform(element, group, group.transform)
        if group.hidden: # Lottie web doesn't seem to support .hd
            group.transform.opacity.value = 0
        return group

    def _parseshape_ellipse(self, element, shape_parent, parent_style):
        ellipse = objects.Ellipse()
        ellipse.position.value = NVector(
            self._parse_unit(element.attrib["cx"]),
            self._parse_unit(element.attrib["cy"])
        )
        ellipse.size.value = NVector(
            self._parse_unit(element.attrib["rx"]) * 2,
            self._parse_unit(element.attrib["ry"]) * 2
        )
        self.add_shapes(element, [ellipse], shape_parent, parent_style)
        return ellipse

    def _parseshape_anim_ellipse(self, ellipse, element, animations):
        self._merge_animations(element, animations, "cx", "cy", "position")
        self._merge_animations(element, animations, "rx", "ry", "size", lambda x, y: NVector(x, y) * 2)
        self._apply_animations(ellipse.position, "position", animations)
        self._apply_animations(ellipse.size, "size", animations)

    def _parseshape_circle(self, element, shape_parent, parent_style):
        ellipse = objects.Ellipse()
        ellipse.position.value = NVector(
            self._parse_unit(element.attrib["cx"]),
            self._parse_unit(element.attrib["cy"])
        )
        r = self._parse_unit(element.attrib["r"]) * 2
        ellipse.size.value = NVector(r, r)
        self.add_shapes(element, [ellipse], shape_parent, parent_style)
        return ellipse

    def _parseshape_anim_circle(self, ellipse, element, animations):
        self._merge_animations(element, animations, "cx", "cy", "position")
        self._apply_animations(ellipse.position, "position", animations)
        self._apply_animations(ellipse.size, "r", animations, lambda r: NVector(r, r) * 2)

    def _parseshape_rect(self, element, shape_parent, parent_style):
        rect = objects.Rect()
        w = self._parse_unit(element.attrib.get("width", 0))
        h = self._parse_unit(element.attrib.get("height", 0))
        rect.position.value = NVector(
            self._parse_unit(element.attrib.get("x", 0)) + w / 2,
            self._parse_unit(element.attrib.get("y", 0)) + h / 2
        )
        rect.size.value = NVector(w, h)
        rx = self._parse_unit(element.attrib.get("rx", 0))
        ry = self._parse_unit(element.attrib.get("ry", 0))
        rect.rounded.value = (rx + ry) / 2
        self.add_shapes(element, [rect], shape_parent, parent_style)
        return rect

    def _parseshape_anim_rect(self, rect, element, animations):
        self._merge_animations(element, animations, "width", "height", "size", lambda x, y: NVector(x, y))
        self._apply_animations(rect.size, "size", animations)
        self._merge_animations(element, animations, "x", "y", "position")
        self._merge_animations(element, animations, "position", "size", "position", lambda p, s: p + s / 2)
        self._apply_animations(rect.position, "position", animations)
        self._merge_animations(element, animations, "rx", "ry", "rounded", lambda x, y: (x + y) / 2)
        self._apply_animations(rect.rounded, "rounded", animations)

    def _parseshape_line(self, element, shape_parent, parent_style):
        line = objects.Path()
        line.shape.value.add_point(NVector(
            self._parse_unit(element.attrib["x1"]),
            self._parse_unit(element.attrib["y1"])
        ))
        line.shape.value.add_point(NVector(
            self._parse_unit(element.attrib["x2"]),
            self._parse_unit(element.attrib["y2"])
        ))
        return self.add_shapes(element, [line], shape_parent, parent_style)

    def _parseshape_anim_line(self, group, element, animations):
        line = group.shapes[0]
        self._merge_animations(element, animations, "x1", "y1", "p1")
        self._merge_animations(element, animations, "x2", "y2", "p2")
        self._apply_animations(line.vertices[0], "p1", animations)
        self._apply_animations(line.vertices[1], "p2", animations)

    def _handle_poly(self, element):
        line = objects.Path()
        coords = list(map(float, element.attrib["points"].replace(",", " ").split()))
        for i in range(0, len(coords), 2):
            line.shape.value.add_point(coords[i:i+2])
        return line

    def _parseshape_polyline(self, element, shape_parent, parent_style):
        line = self._handle_poly(element)
        return self.add_shapes(element, [line], shape_parent, parent_style)

    def _parseshape_polygon(self, element, shape_parent, parent_style):
        line = self._handle_poly(element)
        line.shape.value.close()
        return self.add_shapes(element, [line], shape_parent, parent_style)

    def _parseshape_path(self, element, shape_parent, parent_style):
        d_parser = PathDParser(element.attrib.get("d", ""))
        d_parser.parse()
        paths = []
        for path in d_parser.paths:
            p = objects.Path()
            p.shape.value = path
            paths.append(p)
        #if len(d_parser.paths) > 1:
            #paths.append(objects.shapes.Merge())
        return self.add_shapes(element, paths, shape_parent, parent_style)

    def parse_children(self, element, shape_parent, parent_style):
        for child in element:
            tag = self.unqualified(child.tag)
            if not self.parse_shape(child, shape_parent, parent_style):
                handler = getattr(self, "_parse_" + tag, None)
                if handler:
                    handler(child)

    def parse_shape(self, element, shape_parent, parent_style):
        handler = getattr(self, "_parseshape_" + self.unqualified(element.tag), None)
        if handler:
            out = handler(element, shape_parent, parent_style)
            self.parse_animations(out, element)
            if element.attrib.get("id"):
                self.defs.items[element.attrib["id"]] = out
            return out
        return None

    def _parse_defs(self, element):
        self.parse_children(element, self.defs, {})

    def _apply_transform_element_to_matrix(self, matrix, t):
        name = t[1]
        params = list(map(float, t[2].strip().replace(",", " ").split()))
        if name == "translate":
            matrix.translate(
                params[0],
                (params[1] if len(params) > 1 else 0),
            )
        elif name == "scale":
            xfac = params[0]
            yfac = params[1] if len(params) > 1 else xfac
            matrix.scale(xfac, yfac)
        elif name == "rotate":
            ang = params[0]
            x = y = 0
            if len(params) > 2:
                x = params[1]
                y = params[2]
                matrix.translate(-x, -y)
                matrix.rotate(math.radians(ang))
                matrix.translate(x, y)
            else:
                matrix.rotate(math.radians(ang))
        elif name == "skewX":
            matrix.skew(math.radians(params[0]), 0)
        elif name == "skewY":
            matrix.skew(0, math.radians(params[0]))
        elif name == "matrix":
            m = TransformMatrix()
            m.a, m.b, m.c, m.d, m.tx, m.ty = params
            matrix *= m

    def _transform_to_matrix(self, transform):
        matrix = TransformMatrix()

        for t in re.finditer(r"([a-zA-Z]+)\s*\(([^\)]*)\)", transform):
            self._apply_transform_element_to_matrix(matrix, t)

        return matrix

    def _gradient(self, element, grad):
        grad.matrix = self._transform_to_matrix(element.attrib.get("gradientTransform", ""))

        id = element.attrib["id"]
        if id in self.gradients:
            grad.colors = self.gradients[id].colors
        grad.parse_attrs(element.attrib)
        href = element.attrib.get(self.qualified("xlink", "href"))
        if href:
            srcid = href.strip("#")
            if srcid in self.gradients:
                src = self.gradients[srcid]
            else:
                src = grad.__class__()
                self.gradients[srcid] = src
            grad.colors = src.colors

        for stop in element.findall("./%s" % self.qualified("svg", "stop")):
            off = float(stop.attrib["offset"].strip("%"))
            if stop.attrib["offset"].endswith("%"):
                off /= 100
            style = self.parse_style(stop, {})
            color = self.parse_color(style["stop-color"])
            if "stop-opacity" in style:
                color[3] = float(style["stop-opacity"])
            grad.add_color(off, color)
        self.gradients[id] = grad

    def _parse_linearGradient(self, element):
        self._gradient(element, SvgLinearGradient())

    def _parse_radialGradient(self, element):
        self._gradient(element, SvgRadialGradient())

    def get_color_url(self, color, gradientclass, shape):
        match = re.match(r"""url\(['"]?#([^)'"]+)['"]?\)""", color)
        if not match:
            return None
        id = match[1]
        if id not in self.gradients:
            return None
        grad = self.gradients[id]
        outgrad = gradientclass()
        grad.to_lottie(outgrad, shape)
        if self.name_mode != NameMode.NoName:
            grad.name = id
        return outgrad

    ## @todo Parse single font property, fallback family etc
    def _parse_text_style(self, style, font_style=None):
        if "font-family" in style:
            font_style.query.family(style["font-family"].strip("'\""))

        if "font-style" in style:
            if style["font-style"] == "oblique":
                font_style.query.custom("slant", 110)
            elif style["font-style"] == "italic":
                font_style.query.custom("slant", 100)

        if "font-weight" in style:
            if style["font-weight"] in {"bold", "bolder"}:
                font_style.query.weight(200)
            elif style["font-weight"] == "lighter":
                font_style.query.weight(50)
            elif style["font-weight"].isdigit():
                font_style.query.css_weight(int(style["font-weight"]))

        if "font-size" in style:
            fz = style["font-size"]
            fz_names = {
                "xx-small": 8,
                "x-small": 16,
                "small": 32,
                "medium": 64,
                "large": 128,
                "x-large": 256,
                "xx-large": 512,
            }
            if fz in fz_names:
                font_style.size = fz_names[fz]
            elif fz == "smaller":
                font_style.size /= 2
            elif fz == "larger":
                font_style.size *= 2
            elif fz.endswith("px"):
                font_style.size = float(fz[:-2])
            elif fz.isnumeric():
                font_style.size = float(fz)

        if "text-align" in style:
            ta = style["text-align"]
            if ta in ("left", "start"):
                font_style.justify = font.TextJustify.Left
            elif ta == "center":
                font_style.justify = font.TextJustify.Center
            elif ta in ("right", "end"):
                font_style.justify = font.TextJustify.Right

    def _parse_text_elem(self, element, style, group, parent_style, font_style):
        self._parse_text_style(style, font_style)

        if "x" in element.attrib or "y" in element.attrib:
            font_style.position = NVector(
                float(element.attrib["x"]),
                float(element.attrib["y"]),
            )

        childpos = NVector(0, font_style.position.y)

        if element.text:
            fs = font.FontShape(element.text, font_style)
            fs.refresh()
            group.add_shape(fs)
            childpos.x = fs.wrapped.next_x

        for child in element:
            if child.tag == self.qualified("svg", "tspan"):
                child_style = font_style.clone()
                child_style.position = childpos.clone()
                fs = self._parseshape_text(child, group, parent_style, child_style)
                childpos.x = fs.next_x
            if child.tail:
                child_style = font_style.clone()
                child_style.position = childpos.clone()
                fs = font.FontShape(child.tail, child_style)
                fs.refresh()
                group.add_shape(fs)
                childpos.x = fs.wrapped.next_x

        group.next_x = childpos.x

    def _parseshape_text(self, element, shape_parent, parent_style, font_style=None):
        group = objects.Group()

        style = self.parse_style(element, parent_style)
        self.apply_common_style(style, group.transform)
        self.apply_visibility(style, group)
        group.name = self._get_id(element)

        if has_font:
            if font_style is None:
                font_style = font.FontStyle("", 64)
            self._parse_text_elem(element, style, group, style, font_style)

        style.setdefault("fill", "none")
        self._add_style_shapes(style, group)

        ## @todo text-anchor when it doesn't match text-align
        #if element.tag == self.qualified("svg", "text"):
            #dx = 0
            #dy = 0

            #ta = style.get("text-anchor", style.get("text-align", ""))
            #if ta == "middle":
                #dx -= group.bounding_box().width / 2
            #elif ta == "end":
                #dx -= group.bounding_box().width

            #if dx or dy:
                #ng = objects.Group()
                #ng.add_shape(group)
                #group.transform.position.value.x += dx
                #group.transform.position.value.y += dy
                #group = ng

        shape_parent.shapes.insert(0, group)
        self.parse_transform(element, group, group.transform)
        return group

    def parse_animations(self, lottie, element):
        animations = {}
        for child in element:
            if self.unqualified(child.tag) == "animate":
                att = child.attrib["attributeName"]

                from_val = child.attrib["from"]
                if att == "d":
                    ## @todo
                    continue
                else:
                    from_val = float(from_val)
                    if "to" in child.attrib:
                        to_val = float(child.attrib["to"])
                    elif "by" in child.attrib:
                        to_val = float(child.attrib["by"]) + from_val

                begin = self.parse_animation_time(child.attrib.get("begin", 0)) or 0
                if "dur" in child.attrib:
                    end = (self.parse_animation_time(child.attrib["dur"]) or 0) + begin
                elif "end" in child.attrib:
                    end = self.parse_animation_time(child.attrib["dur"]) or 0
                else:
                    continue

                if att not in animations:
                    animations[att] = {}
                animations[att][begin] = from_val
                animations[att][end] = to_val
                if self.max_time < end:
                    self.max_time = end

        tag = self.unqualified(element.tag)
        handler = getattr(self, "_parseshape_anim_" + tag, None)
        if handler:
            handler(lottie, element, animations)

    def parse_animation_time(self, value):
        """!
        @see https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#Clock-value
        """
        if not value:
            return None
        try:
            seconds = 0
            if ":" in value:
                mult = 1
                for elem in reversed(value.split(":")):
                    seconds += float(elem) * mult
                    mult *= 60
            elif value.endswith("s"):
                seconds = float(value[:-1])
            elif value.endswith("ms"):
                seconds = float(value[:-2]) / 1000
            elif value.endswith("min"):
                seconds = float(value[:-3]) * 60
            elif value.endswith("h"):
                seconds = float(value[:-1]) * 60 * 60
            else:
                seconds = float(value)
            return seconds * self.animation.frame_rate
        except ValueError:
            pass
        return None

    def _merge_animations(self, element, animations, val1, val2, dest, merge=NVector):
        if val1 not in animations and val2 not in animations:
            return

        dict1 = list(sorted(animations.pop(val1, {}).items()))
        dict2 = list(sorted(animations.pop(val2, {}).items()))

        x = float(element.attrib[val1])
        y = float(element.attrib[val2])
        values = {}
        while dict1 or dict2:
            if not dict1 or (dict2 and dict1[0][0] > dict2[0][0]):
                t, y = dict2.pop(0)
            elif not dict2 or dict1[0][0] < dict2[0][0]:
                t, x = dict1.pop(0)
            else:
                t, x = dict1.pop(0)
                t, y = dict2.pop(0)

            values[t] = merge(x, y)

        animations[dest] = values

    def _apply_animations(self, animatable, name, animations, transform=lambda v: v):
        if name in animations:
            for t, v in animations[name].items():
                animatable.add_keyframe(t, transform(v))

class PathDParser:
    _re = re.compile("|".join((
        r"[a-zA-Z]",
        r"[-+]?[0-9]*\.?[0-9]*[eE][-+]?[0-9]+",
        r"[-+]?[0-9]*\.?[0-9]+",
    )))

    def __init__(self, d_string):
        self.path = objects.properties.Bezier()
        self.paths = []
        self.p = NVector(0, 0)
        self.la = None
        self.la_type = None
        self.tokens = list(map(self.d_subsplit, self._re.findall(d_string)))
        self.add_p = True
        self.implicit = "M"

    def d_subsplit(self, tok):
        if tok.isalpha():
            return tok
        return float(tok)

    def next_token(self):
        if self.tokens:
            self.la = self.tokens.pop(0)
            if isinstance(self.la, str):
                self.la_type = 0
            else:
                self.la_type = 1
        else:
            self.la = None
        return self.la

    def next_vec(self):
        x = self.next_token()
        y = self.next_token()
        return NVector(x, y)

    def cur_vec(self):
        x = self.la
        y = self.next_token()
        return NVector(x, y)

    def parse(self):
        self.next_token()
        while self.la is not None:
            if self.la_type == 0:
                parser = "_parse_" + self.la
                self.next_token()
                getattr(self, parser)()
            else:
                parser = "_parse_" + self.implicit
                getattr(self, parser)()

    def _push_path(self):
        self.path = objects.properties.Bezier()
        self.add_p = True

    def _parse_M(self):
        if self.la_type != 1:
            self.next_token()
            return
        self.p = self.cur_vec()
        self.implicit = "L"
        if not self.add_p:
            self._push_path()
        self.next_token()

    def _parse_m(self):
        if self.la_type != 1:
            self.next_token()
            return
        self.p += self.cur_vec()
        self.implicit = "l"
        if not self.add_p:
            self._push_path()
        self.next_token()

    def _rpoint(self, point, rel=None):
        return (point - (rel or self.p)) if point is not None else NVector(0, 0)

    def _do_add_p(self, outp=None):
        if self.add_p:
            self.paths.append(self.path)
            self.path.add_point(self.p.clone(), NVector(0, 0), self._rpoint(outp))
            self.add_p = False
        elif outp:
            rp = self.path.vertices[-1]
            self.path.out_tangents[-1] = self._rpoint(outp, rp)

    def _parse_L(self):
        if self.la_type != 1:
            self.next_token()
            return
        self._do_add_p()
        self.p = self.cur_vec()
        self.path.add_point(self.p.clone(), NVector(0, 0), NVector(0, 0))
        self.implicit = "L"
        self.next_token()

    def _parse_l(self):
        if self.la_type != 1:
            self.next_token()
            return
        self._do_add_p()
        self.p += self.cur_vec()
        self.path.add_point(self.p.clone(), NVector(0, 0), NVector(0, 0))
        self.implicit = "l"
        self.next_token()

    def _parse_H(self):
        if self.la_type != 1:
            self.next_token()
            return
        self._do_add_p()
        self.p[0] = self.la
        self.path.add_point(self.p.clone(), NVector(0, 0), NVector(0, 0))
        self.implicit = "H"
        self.next_token()

    def _parse_h(self):
        if self.la_type != 1:
            self.next_token()
            return
        self._do_add_p()
        self.p[0] += self.la
        self.path.add_point(self.p.clone(), NVector(0, 0), NVector(0, 0))
        self.implicit = "h"
        self.next_token()

    def _parse_V(self):
        if self.la_type != 1:
            self.next_token()
            return
        self._do_add_p()
        self.p[1] = self.la
        self.path.add_point(self.p.clone(), NVector(0, 0), NVector(0, 0))
        self.implicit = "V"
        self.next_token()

    def _parse_v(self):
        if self.la_type != 1:
            self.next_token()
            return
        self._do_add_p()
        self.p[1] += self.la
        self.path.add_point(self.p.clone(), NVector(0, 0), NVector(0, 0))
        self.implicit = "v"
        self.next_token()

    def _parse_C(self):
        if self.la_type != 1:
            self.next_token()
            return
        pout = self.cur_vec()
        self._do_add_p(pout)
        pin = self.next_vec()
        self.p = self.next_vec()
        self.path.add_point(
            self.p.clone(),
            (pin - self.p),
            NVector(0, 0)
        )
        self.implicit = "C"
        self.next_token()

    def _parse_c(self):
        if self.la_type != 1:
            self.next_token()
            return
        pout = self.p + self.cur_vec()
        self._do_add_p(pout)
        pin = self.p + self.next_vec()
        self.p += self.next_vec()
        self.path.add_point(
            self.p.clone(),
            (pin - self.p),
            NVector(0, 0)
        )
        self.implicit = "c"
        self.next_token()

    def _parse_S(self):
        if self.la_type != 1:
            self.next_token()
            return
        pin = self.cur_vec()
        self._do_add_p()
        handle = self.path.in_tangents[-1]
        self.path.out_tangents[-1] = (-handle)
        self.p = self.next_vec()
        self.path.add_point(
            self.p.clone(),
            (pin - self.p),
            NVector(0, 0)
        )
        self.implicit = "S"
        self.next_token()

    def _parse_s(self):
        if self.la_type != 1:
            self.next_token()
            return
        pin = self.cur_vec() + self.p
        self._do_add_p()
        handle = self.path.in_tangents[-1]
        self.path.out_tangents[-1] = (-handle)
        self.p += self.next_vec()
        self.path.add_point(
            self.p.clone(),
            (pin - self.p),
            NVector(0, 0)
        )
        self.implicit = "s"
        self.next_token()

    def _parse_Q(self):
        if self.la_type != 1:
            self.next_token()
            return
        self._do_add_p()
        pin = self.cur_vec()
        self.p = self.next_vec()
        self.path.add_point(
            self.p.clone(),
            (pin - self.p),
            NVector(0, 0)
        )
        self.implicit = "Q"
        self.next_token()

    def _parse_q(self):
        if self.la_type != 1:
            self.next_token()
            return
        self._do_add_p()
        pin = self.p + self.cur_vec()
        self.p += self.next_vec()
        self.path.add_point(
            self.p.clone(),
            (pin - self.p),
            NVector(0, 0)
        )
        self.implicit = "q"
        self.next_token()

    def _parse_T(self):
        if self.la_type != 1:
            self.next_token()
            return
        self._do_add_p()
        handle = self.p - self.path.in_tangents[-1]
        self.p = self.cur_vec()
        self.path.add_point(
            self.p.clone(),
            (handle - self.p),
            NVector(0, 0)
        )
        self.implicit = "T"
        self.next_token()

    def _parse_t(self):
        if self.la_type != 1:
            self.next_token()
            return
        self._do_add_p()
        handle = -self.path.in_tangents[-1] + self.p
        self.p += self.cur_vec()
        self.path.add_point(
            self.p.clone(),
            (handle - self.p),
            NVector(0, 0)
        )
        self.implicit = "t"
        self.next_token()

    def _parse_A(self):
        if self.la_type != 1:
            self.next_token()
            return
        r = self.cur_vec()
        xrot = self.next_token()
        large = self.next_token()
        sweep = self.next_token()
        dest = self.next_vec()
        self._do_arc(r[0], r[1], xrot, large, sweep, dest)
        self.implicit = "A"
        self.next_token()

    def _do_arc(self, rx, ry, xrot, large, sweep, dest):
        self._do_add_p()
        if self.p == dest:
            return

        if rx == 0 or ry == 0:
            # Straight line
            self.p = dest
            self.path.add_point(
                self.p.clone(),
                NVector(0, 0),
                NVector(0, 0)
            )
            return

        ellipse, theta1, deltatheta = Ellipse.from_svg_arc(self.p, rx, ry, xrot, large, sweep, dest)
        points = ellipse.to_bezier(theta1, deltatheta)

        self._do_add_p()
        self.path.out_tangents[-1] = points[0].out_tangent
        for point in points[1:-1]:
            self.path.add_point(
                point.vertex,
                point.in_tangent,
                point.out_tangent,
            )
        self.path.add_point(
            dest.clone(),
            points[-1].in_tangent,
            NVector(0, 0),
        )
        self.p = dest

    def _parse_a(self):
        if self.la_type != 1:
            self.next_token()
            return
        r = self.cur_vec()
        xrot = self.next_token()
        large = self.next_token()
        sweep = self.next_token()
        dest = self.p + self.next_vec()
        self._do_arc(r[0], r[1], xrot, large, sweep, dest)
        self.implicit = "a"
        self.next_token()

    def _parse_Z(self):
        if self.path.vertices:
            self.p = self.path.vertices[0].clone()
        self.path.close()
        self._push_path()

    def _parse_z(self):
        self._parse_Z()

def parse_svg_etree(etree, layer_frames=0, *args, **kwargs):
    parser = SvgParser()
    return parser.parse_etree(etree, layer_frames, *args, **kwargs)

def parse_svg_file(file, layer_frames=0, *args, **kwargs):
    return parse_svg_etree(ElementTree.parse(file), layer_frames, *args, **kwargs)


import os
from scipy.io import wavfile
import numpy as np
import mido
from random import shuffle

class wav_processor():

	def __init__(self):
		#parameters
		self.interval = 0.2 #cut interval, in seconds
		self.write_to_file = True
		self.scramble = True
		self.dataset = []
		self.n_rows = 0
		self.n_files = 0
		self.fourier_cutoffs = (25, 4400)
		#self.fourier_cutoffs = (0, 500)
		self.time = 0.0
		self.partition = True
		self.train_files = 46
		self.vali_files = 2
		self.test_files = 2

		#io
		self.wav_dir = 'audio_wav'
		self.midi_dir = 'midi'
		self.out_fname = 'dataset_shuffle'
		self.out_dir = 'data'
		if not os.path.exists(self.out_dir):
			os.makedirs(self.out_dir)

	#loop thru wav files in a dir
	def run(self):
		wav_dir_list = os.listdir(self.wav_dir)
		midi_dir_list = os.listdir(self.midi_dir)
		wav_dir_list.sort()
		midi_dir_list.sort()
		all_files = list(zip(wav_dir_list, midi_dir_list))
		train_f = all_files[:self.train_files]
		vali_f = all_files[self.train_files:self.train_files+self.vali_files]
		test_f = all_files[self.train_files+self.vali_files:self.train_files+self.vali_files+self.test_files]

		#training set
		tmp_out_fname = self.out_fname + '_train.csv'
		tmp_out_fname = os.path.join(self.out_dir, tmp_out_fname)
		self.out_f = open(tmp_out_fname, 'w')
		self.go_thru_dir(train_f)
		print('\nTraining Set Done')

		#validation set
		tmp_out_fname = self.out_fname + '_validation.csv'
		tmp_out_fname = os.path.join(self.out_dir, tmp_out_fname)
		self.out_f = open(tmp_out_fname, 'w')
		self.go_thru_dir(vali_f)
		print('\nValidation Set Done')

		#test set
		tmp_out_fname = self.out_fname + '_test.csv'
		tmp_out_fname = os.path.join(self.out_dir, tmp_out_fname)
		self.out_f = open(tmp_out_fname, 'w')
		self.go_thru_dir(test_f)
		print('\nTest Set Done')

	def go_thru_dir(self, file_collection):
		for wav_fname, midi_fname in file_collection:
			wav_full_dir = os.path.join(self.wav_dir, wav_fname)
			midi_full_dir = os.path.join(self.midi_dir, midi_fname)
			if self.check_same_dir(wav_full_dir, midi_full_dir):
				self.cut_wav(wav_full_dir, midi_full_dir)
				#print(wav_full_dir)
			else:
				print('wav & midi files do not match')

			self.n_files += 1

		self.out_f.close()
		print('Total files:', self.n_files)
		print('Total rows:', self.n_rows)
		self.n_files = 0

	#cut a wav file into pieces
	def cut_wav(self, wav_dir, midi_dir):
		print('Processing({}): {}'.format(self.n_files, wav_dir))
		sample_rate, data = wavfile.read(wav_dir)
		data = np.array([x[0] for x in data])
		interval_len = int(sample_rate * self.interval)
		n_cuts = int(len(data)/interval_len)
		pos = 0
		midi_ = midi_wrapper(midi_dir, self.interval)

		for i, label in zip(range(n_cuts), midi_.gen_label()):
			cut = data[pos:pos+interval_len]
			pos += interval_len
			cut_fft = self.fourier_trans(cut)
			data_row = np.append(cut_fft, label)

			if self.write_to_file:
				if self.scramble:
					self.dataset.append(list(data_row))
				else:
					line = ','.join([str(x) for x in list(data_row)]) + '\n'
					self.out_f.write(line)
			else:
				self.dataset.append(list(data_row))

			self.n_rows += 1

		if self.scramble:
			shuffle(self.dataset)

		if self.write_to_file:
			self.batch2file()
			self.dataset = []

	def batch2file(self):
		for data_row in self.dataset:
			line = ','.join([str(x) for x in list(data_row)]) + '\n'
			self.out_f.write(line)

	def fourier_trans(self, wave_array):
		fft_array = abs(np.fft.fft(wave_array))
		#print('data length:',len(fft_array[self.fourier_cutoffs[0]: self.fourier_cutoffs[1]]))
		return fft_array[self.fourier_cutoffs[0]: self.fourier_cutoffs[1]]

	def check_same_dir(self, wav_dir, midi_dir):
		w_ = wav_dir.split('.')[0].split('/')[1]
		m_ = midi_dir.split('.')[0].split('/')[1]
		same = w_ == m_
		#print(w_, m_)
		#print('same:', same)
		return same

class midi_wrapper():

	def __init__(self, midi_fname, interval):
		self.mid = mido.MidiFile(midi_fname)
		self.current_nodes = []
		self.vec = [0 for x in range(88)]
		self.track = []
		self.n_msg = 0
		self.time = 0.0
		self.interval = interval
		self.get_start_time()
		self.loop_msg()

	def add_note(self, note):
		self.vec[note-21] = 1

	def rm_note(self, note):
		self.vec[note-21] = 0

	def get_start_time(self):
		time = 0.0
		for msg in self.mid:
			time += msg.time
			if hasattr(msg, 'note') and msg.type == 'note_on':
				self.start_time = time
				break

	def loop_msg(self):
		for msg in self.mid:
			#if self.n_msg > 50:
				#break

			self.time += msg.time
			if hasattr(msg, 'note'):
				if msg.type == 'note_on':
					self.add_note(msg.note)
				elif msg.type == 'note_off':
					self.rm_note(msg.note)

				line = (self.time, self.vec.copy())
				self.track.append(line)

			self.n_msg += 1
		self.time = self.track[-1][0]

	def track_lookup(self, t):
		for i in range(len(self.track)):
			if t < self.track[i][0]:
				return self.track[i-1][1]

	def gen_label(self):
		n_labels = int(self.time/self.interval)
		#t = self.start_time
		t = 0.0
		for i in range(n_labels):
			label_vec = self.track_lookup(t)
			t += self.interval

			yield label_vec

if __name__ == '__main__':
	wav_processor().run()


while(1):
    number=int(input("Input the number: "))
    factorial=1
    while(number>0):
	      	factorial=factorial*number
      		number-=1
    print(factorial)

#Anotherway______________________________By_Maateen sir

print('Please input your number:')
number = int(input())
temp = number

while number > 1:
    number -= 1
    temp = temp*number

if temp == 0:
    print(1)
else:
    print(temp)


class Solution:
    def orangesRotting(self, grid: List[List[int]]) -> int:
        queue=deque()
        minuteElasped=0
        freshOranges=0
        ROWS=len(grid)
        COLUMNS=len(grid[0])
        directions=[(1,0),(-1,0),(0,1),(0,-1)]
        def inBound(r,c):
            if r<0 or r>=ROWS or c<0 or c>=COLUMNS:
                return False
            return True

        for row in range(ROWS):
            for col in range(COLUMNS):
                if grid[row][col]==2:
                    queue.append((row,col))
                elif grid[row][col]==1:
                    freshOranges+=1

        while queue and freshOranges>0:
            rottenOranges=len(queue)
            for i in range(rottenOranges):
                r,c=queue.popleft()
                for d1,d2 in directions:
                    row=r+d1
                    col=c+d2
                    if inBound(row,col) and grid[row][col]==1:
                        grid[row][col]=2
                        queue.append((row,col))
                        freshOranges-=1
            minuteElasped+=1

        return minuteElasped if freshOranges==0 else -1


import math
import random

def p_next():
    flag = []
    flag.append(1)
    for i in range(30):
        flag.append(0)
    s = 1
    while s < k:
        j = random.randint(0, 30)
        if flag[j] == 0:
            p.append(simple[j])
            flag[j] = 1
            s=s+1
#это 3 задача
simple = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,107, 109, 113, 127]
#это 4 задача
left = pow(2, 123)
check = 0
k = 7
p = []
p.append(2)
p_next()
n = 1
for i in range(k):
    n = n*p[i]
while n<left:
    i = random.randint(0, k-1)
    n = n*p[i]
n = n+1
print("n =", n)
#это 5 задача
ln_n = int(math.log2(n))
for a in range(2, ln_n+1):
    if check==1:
        break
    c = pow(a, n-1, n)
    for i in range(0, k):
        d = int((n-1)/p[i])
        e = pow(a, d, n-1)
        if  c==1 and e!=1:
            check = 1
            break
if check == 1:
    print("n is a prime number")
else:
    print("n is a composite number")


import time
import click
import simpleaudio as sa

# function to turn noteDurations into 16ths timestamps (make a 16th grid)
def durationsToTimestamps16th(note_Durations):
    timestamps_16th = []
    sum = 0
    for i in note_Durations:
        timestamps_16th.append(sum)
        sum = sum + i * 4
    return timestamps_16th

# function to convert the 16ths timestamps into ms
def timestampsToDelay(timestamps_16th, _BPM):
    quarterNoteDuration = 60 / int(_BPM)
    sixteenthNoteDuration = quarterNoteDuration / 4.0
    time_Stamps = []
    for i in timestamps_16th:
       time_Stamps.append(i * sixteenthNoteDuration)
    return time_Stamps

# function to create an event
def createEvent(timestamp,sample,velocity):
    return {
        'timestamp': timestamp,
        'sample': sample,
        'velocity': velocity
    }

events = []

#function to handle events
def eventHandler(event):
    if event['velocity'] > 0:
        event['sample'].play()

# load audio sample
kick = sa.WaveObject.from_wave_file("../../../Samples/kick.wav")
snare = sa.WaveObject.from_wave_file("../../../Samples/snare.wav")
shaker = sa.WaveObject.from_wave_file("../../../Samples/shaker.wav")

bpm = 120
print("Default bpm: ",bpm)

noteDurations = [0.25, 0.5, 0.25, 0.5, 0.5, 1, 1]

# [Y/n] question to change bpm
if click.confirm('Do you want to edit the bpm?', default=True):
    while True:
        bpm = input("")
        try:
            val = int(bpm)
            print("BPM: ",bpm)
            break
        except ValueError:
            print("Value was not correct syntax try again")

# create list with 16ths timestamps calculated in the function
timestamps16th = durationsToTimestamps16th(noteDurations)
print("16TH:", timestamps16th)

# create list with ms timestamps calculated in the function
timeStamps = timestampsToDelay(timestamps16th, bpm)
print("16TH Time stamp:", timeStamps)

for i in range(len(timeStamps)):
    events.append(createEvent(timeStamps[i], kick, 127))

    events.append(createEvent(timeStamps[i], snare, 127))

    events.append(createEvent(timeStamps[i], shaker, 127))

print("amount of events" ,len(events))

zero_time = time.time()

# the i is used to determine the time stamp that should be used
i = 0

# start a time counter and look if the timestamps list is equal to the current time
while True:
    now = time.time() - zero_time
    #Read time stamp from event
    if(now >= float(events[i]['timestamp'])):
        eventHandler(events[i])
        i = i + 1
        # if the i is at the end of the given list stop the loop
        if i == len(events):
            break
    # update time
    time.sleep(0.001)
# wait until the last sample is done playing
time.sleep(timeStamps[-1])


import cv2
import numpy as np
import os

BASE_DIR="/pfs/data5/home/kit/anthropomatik/yc5412/Sim2Real/Used Skripts/Compare Images/"

FILE_COMP_1="New000000-depth.png"
FILE_COMP_2="Ref000000-depth.png"

#FILE_COMP_1="New000000-depth.png"
#FILE_COMP_2="Ref000000-depth.png"

#FILE_COMP_1="New000000-label.png"
#FILE_COMP_2="Ref000000-label.png"

def create_difference_images(img_comp_1, img_comp_2):
    print("Shape img 1:",img_comp_1.shape)
    print("Shape img 2:",img_comp_2.shape)
    print()

    if img_comp_1.shape!=img_comp_2.shape:
        raise ValueError("Different shapes!")

    shape=img_comp_1.shape

    img_comp_1=img_comp_1.flatten()
    img_comp_2=img_comp_2.flatten()

    print("Type img 1:",type(img_comp_1[0]))
    print("Type img 2:",type(img_comp_2[0]))

    img_absolute=np.abs(img_comp_1.astype('float')-img_comp_2.astype('float'))
    max_value=np.max(img_absolute)
    img_absolute=img_absolute.reshape(shape)

    if len(img_absolute.shape)==2:
        img_absolute=np.expand_dims(img_absolute,2)

    if max_value>255:
        img_absolute=np.multiply(img_absolute,255/max_value)
        img_relative=img_absolute
    else:
        img_relative=np.multiply(np.copy(img_absolute),255/max_value)

    img_out=np.append(img_absolute,img_relative,axis=2)

    return img_out.astype('uint8')

def save_images(imgs, path, ending):
    shape=imgs.shape

    for channel in range(shape[-1]):
        cv2.imwrite(path+ending+"["+str(channel)+"].png",imgs[:,:,channel])

if __name__=="__main__":
    if not os.path.isfile(BASE_DIR+FILE_COMP_1) or not os.path.isfile(BASE_DIR+FILE_COMP_2):
        raise ValueError("The given files does not exist!")

    img_comp_1=cv2.imread(BASE_DIR+FILE_COMP_1,cv2.IMREAD_UNCHANGED)
    img_comp_2=cv2.imread(BASE_DIR+FILE_COMP_2,cv2.IMREAD_UNCHANGED)
    imgs_out=None
    ending=None

    if img_comp_1 is None or img_comp_2 is None:
        raise ValueError("The images could not be loaded!")

    if FILE_COMP_1.endswith("-color.png") and FILE_COMP_2.endswith("-color.png"):
        ending="compare-"+FILE_COMP_1.replace("-color.png","")+"-"+FILE_COMP_2.replace("-color.png","")+"-color"
    elif FILE_COMP_1.endswith("-depth.png") and FILE_COMP_2.endswith("-depth.png"):
        ending="compare-"+FILE_COMP_1.replace("-depth.png","")+"-"+FILE_COMP_2.replace("-depth.png","")+"-depth"
    elif FILE_COMP_1.endswith("-label.png") and FILE_COMP_2.endswith("-label.png"):
        ending="compare-"+FILE_COMP_1.replace("-label.png","")+"-"+FILE_COMP_2.replace("-label.png","")+"-label"
    else:
        raise ValueError("The given files are not comparable!")

    imgs_out=create_difference_images(img_comp_1, img_comp_2)

    if not imgs_out is None:
        save_images(imgs_out, BASE_DIR, ending)


from brownie import config, interface, network
from scripts.helpful_scripts import get_account, FORKED_LOCAL_ENVIRONMENTS, LOCAL_BLOCKCHAIN_NETWORKS, get_pf_address
from scripts.get_weth import get_weth
from web3 import Web3

amount = 0.1*10**18

def main():
    account = get_account()
    weth_Address = interface.IWeth(
        config['networks'][network.show_active()]['weth_token'])

    if network.show_active() not in LOCAL_BLOCKCHAIN_NETWORKS:
        get_weth()

    # ABI, Address
    # since we need to have the ABI and Address of the lending pool contract (main contract of aave)
    lending_pool = get_lending_pool()

    # We need to approve our sending of ERC20 tokens.
    # Can't use weth gateway since its only a smartcontract for wrapping ETH to WETH and not authorized for approving all types of token, only autorized for WETH.
    # Need a ERC20 contract to approve any Token.
    approve_erc20(amount, lending_pool.address, weth_Address, account)
    # tx_app = weth_Address.approve(
    #    lending_pool.address, amount, {'from': account})
    # tx_app.wait(1)

    print('Depositing...')
    tx = lending_pool.deposit(weth_Address, amount,
                              account.address, 0, {'from': account})
    tx.wait(1)
    print('Deposited!')

    total_debt, availableToBorrow = get_borrowable_data(lending_pool, account)

    print('Lets Borrow!')
    dai_eth_price_feed = get_pf_address('dai_eth')
    dai_eth_price = get_asset_price(dai_eth_price_feed)

    amount_dai_to_borrow = (1/dai_eth_price) * (availableToBorrow)
    #         borrowable_eth -> borrowable_dai, then, * availableToBorrow because there is a set amount we can borrow
    print(f'We are gonna borrow {amount_dai_to_borrow} DAI.')

    dai_address = config['networks'][network.show_active()]['dai_address']
    print(Web3.toWei(amount_dai_to_borrow, 'ether'))
    borrow_tx = lending_pool.borrow(
        dai_address, Web3.toWei(amount_dai_to_borrow, 'ether'), 1, 0, account.address, {'from': account})
    borrow_tx.wait(1)
    print('BORROWED! HELL YEAH.')
    get_borrowable_data(lending_pool, account)

    # now we are gonna repay everything back
    print('We are gonna repay everything back...')
    repay_all(amount, lending_pool, account, dai_address)
    get_borrowable_data(lending_pool, account)

def repay_all(amount, lending_pool, account, dai_Address):
    approve_erc20(amount, lending_pool, dai_Address,
                  account)
    # We have to approve the use of dai tokens since we just borrowed it and not approved it before
    # Like the weth token.

    repay_tx = lending_pool.repay(
        dai_Address, amount, 1, account.address, {'from': account})
    repay_tx.wait(1)
    print('REPAYED! HELL YEAH.')

def get_asset_price(priceFeed_address):
    priceFeed = interface.AggregatorV3Interface(priceFeed_address)
    latest_price = priceFeed.latestRoundData()[1]
    converted_price = Web3.fromWei(latest_price, 'ether')
    print(f'The DAI/ETH Price is {converted_price}')
    return float(converted_price)

def get_borrowable_data(lending_pool, account):
    data = lending_pool.getUserAccountData(account)
    print(f'Collateral ETH: {data[0]/10**18}, Debt: {data[1] / 10**18}, Borrowable Amount: {data[2] / 10**18}, Liquidation Threshold: {data[3]/100}%, Loan_to_value: {data[4]/100}%, Health Factor: {data[5]}')
    return data[1] / 10**18, data[2] / 10**18

def approve_erc20(amount, spender, erc20_address, account):
    '''
    Approves Tokens
    '''
    print('Approving tokens')
    erc20 = interface.IERC20(erc20_address)
    tx = erc20.approve(spender, amount, {'from': account})
    tx.wait(1)
    print('Approved!')
    return tx

def get_lending_pool():
    # Lending pool has a changing address (different markets) so we have a different contract which can provide us the main lending pool address
    # Called LendingPoolAddressProvider
    LenPoolAddProv = interface.ILendingPoolAddressesProvider(
        config['networks'][network.show_active()]['LendingPoolAddressesProvider'])

    LendingPoolAddress = LenPoolAddProv.getLendingPool()
    LendingPool = interface.ILendingPool(LendingPoolAddress)
    return LendingPool


import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk as gtk

class MainWindow(gtk.Window):

	def __init__(self, title):
		gtk.Window.__init__(self, title=title)

		# Box
		self.b = gtk.Box(spacing=100)
		self.add(self.b)

		self.label = gtk.Label()
		self.label.set_label('How u doin!')
		self.label.set_angle(45)
		self.set_halign(gtk.Align.END)
		# self.label.connect('clicked', self.button_clicked)
		self.b.pack_start(self.label, True, True, 0)

		# print(self.label.get_properties('angle'))

		self.button = gtk.Button(label='CLick Here!')
		self.button.connect('clicked', self.button_clicked)
		self.b.pack_start(self.button, True, True, 0)

	def button_clicked(self, widget):
		print('You clicked {} on {}'.format(self.get_properties('title')[0], widget.get_properties('label')[0]))

win = MainWindow('One')
win2 = MainWindow('Two')
win.connect('delete-event', gtk.main_quit)
# win2.connect('delete-event', gtk.main_quit)

win2.show_all()
win.show_all()
gtk.main()


import pygame as pg
from utility import dist

pg.init()
clock = pg.time.Clock()
window = pg.display.set_mode((640,640))
screen = pg.Surface((800,800))

from resources import cursors, font16, scale_imgs

scale_imgs(window.get_size()[0] / 800)

from gui import GUI
from player import Player
from enemy import Enemy
from hero import Hero

cursor = cursors['dwarven_0']
pg.mouse.set_visible(False)

all_sprites = pg.sprite.Group()
gui_group = pg.sprite.Group()
player = Player(all_sprites, scale=window.get_size()[0] / 800)
enemy  = Enemy(all_sprites, scale=window.get_size()[0] / 800)
gui = GUI(player, window.get_size())

back_ph = pg.image.load('assets/img/maps/forest.png').convert()
back_ph = pg.transform.scale(back_ph, (window.get_size()[0],1280 * (window.get_size()[1]/800)))

camera = pg.math.Vector2(0, back_ph.get_size()[1]-window.get_size()[1])
camera_speed = 3800

wave = 1

def finish_combat(player, enemy):
    global wave
    player.in_combat = False
    wave += 1
    enemy.generate(wave)
    player.refresh_buy()

    player.after_combat_tick = pg.time.get_ticks()
    player.after_combat = True

    return pg.math.Vector2(0, back_ph.get_size()[1]-window.get_size()[1])

while True:
    dt = clock.tick() / 1000
    for event in pg.event.get():
        if event.type == pg.QUIT:
            pg.quit()
            exit()
        elif event.type == pg.MOUSEBUTTONDOWN:
            if event.button == 1:
                gui.click(pg.mouse.get_pos())
            if player.in_combat:
                if event.button == 4:
                    camera.y -= camera_speed * dt
                    if camera.y < 0:
                        camera.y = 0
                if event.button == 5:
                    camera.y += camera_speed * dt
                    if camera.y > back_ph.get_size()[1] - window.get_size()[1]:
                        camera.y = back_ph.get_size()[1] - window.get_size()[1]

        elif event.type == pg.KEYDOWN:
            if event.key == pg.K_TAB:
                player.reroll()
                gui.update_buttons()
            elif event.key == pg.K_f:
                player.level_up()
            elif event.key == pg.K_1:
                player.buy(0)
                gui.update_buttons()
            elif event.key == pg.K_2:
                player.buy(1)
                gui.update_buttons()
            elif event.key == pg.K_3:
                player.buy(2)
                gui.update_buttons()
            elif event.key == pg.K_SPACE:
                player.start_combat((window.get_size()[0]/800, window.get_size()[0]/800))
            elif event.key == pg.K_LSHIFT:
                camera_speed = 11400
            elif event.key == pg.K_k:
                for i in range(40):
                    if player.battlefield[i]:
                        player.battlefield[i].modifiers["crit_rate"] += 5
                        print('hero blessed!')
        elif event.type == pg.KEYUP:
            if event.key == pg.K_LSHIFT:
                camera_speed = 3800

    screen.fill((255,0,255))

    screen.blit(back_ph, (0,0) - camera)

    if player.after_combat and pg.time.get_ticks() > player.after_combat_delay + player.after_combat_tick:
        for i, hero in enumerate(player.battlefield):
            if hero:
                hero.heal(.3, True, index=i, group=gui_group)

        player.add_gold(5)
        player.after_combat = False

    if player.in_combat:
        keys = pg.key.get_pressed()
        if keys[pg.K_UP]:
            camera.y -= camera_speed/10 * dt
            if camera.y < 0:
                camera.y = 0
        elif keys[pg.K_DOWN]:
            camera.y += camera_speed/10 * dt
            if camera.y > back_ph.get_size()[1] - window.get_size()[1]:
                camera.y = back_ph.get_size()[1] - window.get_size()[1]
        for sprite in sorted(all_sprites.sprites(), key = lambda sprite: sprite.pos.y):
            if sprite.in_battlefield:
                if enemy.battlefield.count(None) == len(enemy.battlefield):
                    camera = finish_combat(player, enemy)

                if isinstance(sprite, Hero):
                    for sprite2 in all_sprites.sprites():
                        if sprite != sprite2 and isinstance(sprite2, Hero):
                            if pg.sprite.collide_rect(sprite, sprite2):
                                sprite.pos.x -= (sprite2.rect.centerx - sprite.rect.centerx) * dt * 1.5
                                sprite.pos.y -= (sprite2.rect.centery - sprite.rect.centery) * dt * 1.5

                    if not sprite.target:
                        if sprite.enemy:
                            nearest = None
                            for hero in player.battlefield:
                                if hero and nearest and dist(sprite, hero) < dist(sprite, nearest):
                                    nearest = hero
                                elif hero and not nearest:
                                    nearest = hero
                            sprite.target = nearest
                            if nearest:
                                nearest.target_of.append(sprite)
                        else:
                            nearest = None
                            for hero in enemy.battlefield:
                                if hero and nearest and dist(sprite, hero) < dist(sprite, nearest):
                                    nearest = hero
                                elif hero and not nearest:
                                    nearest = hero
                            sprite.target = nearest
                            if nearest:
                                nearest.target_of.append(sprite)

                    if sprite.enemy and sprite.dead:
                        for hero in sprite.target_of:
                            hero.target = None
                        enemy.battlefield[enemy.battlefield.index(sprite)] = None
                        player.add_gold(1)
                        sprite.kill()

                    elif sprite.dead:
                        for hero in sprite.target_of:
                            hero.target = None
                        player.battlefield[player.battlefield.index(sprite)] = None
                        sprite.kill()
                sprite.update(screen, dt, camera)

    gui.update(screen)
    gui_group.update(screen, dt, camera)

    window.blit(screen, (0,0))
    window.blit(cursor, pg.mouse.get_pos())
    window.blit(font16.render( str(round(clock.get_fps(),2)), True, (255, 255, 255), (0, 0, 0) ), (0,0))
    pg.display.flip()


import datetime
import math
import random
from datetime import timedelta
from uuid import uuid4

from civilservant.wikipedia.connections.api import get_mwapi_session, get_auth
from civilservant.wikipedia.queries.sites import get_new_users, get_volunteer_signing_users
from civilservant.models.core import Experiment, ExperimentThing, ExperimentAction
from civilservant.models.wikipedia import WikipediaUser
from civilservant.util import ThingType, PlatformType
from sqlalchemy.orm import aliased

from thanks.utils import _get_experiment_id, _get_num_experiment_actions

import civilservant.logs
from sqlalchemy import func, desc, and_

civilservant.logs.initialize()
import logging

from thanks.creators import BaseSurvey

class ActionFollowUpSurvey(BaseSurvey):
    def __init__(self, db, batch_size, lang, intervention_name, intervention_type, config):
        super().__init__(db, batch_size, lang, intervention_name, intervention_type, config)
        self.extra_metadata_fields = ['public_anonymous_id']

    def _get_completed_interventions(self):
        # join on what should be be the wikipedia user id en EA and ETs
        intervention_on_clause = and_(ExperimentAction.action_object_id == ExperimentThing.thing_id,
                                      ExperimentAction.metadata_json['lang'] ==
                                      ExperimentThing.metadata_json['sync_object']['lang'])

        intervention_completed_qualify = and_(ExperimentAction.experiment_id == self.experiment_id,
                                              ExperimentAction.action_subject_id == self.survey_after_intervention,
                                              ExperimentAction.action_subject_type == self.survey_after_intervention_type,
                                              ExperimentAction.created_dt < self.action_latest_date,
                                              ExperimentAction.metadata_json['action_complete'] == True)

        completed_intervention_users_res = self.db.query(ExperimentAction, ExperimentThing) \
            .join(ExperimentThing, intervention_on_clause) \
            .filter(intervention_completed_qualify).all()

        completed_intervention_users = self._dictify_completed_intervention_users(completed_intervention_users_res)

        return completed_intervention_users

    def _dictify_completed_intervention_users(self, completed_intervention_users_res):
        completed_intervention_users = []
        for ea, et in completed_intervention_users_res:
            ciu = {'user_name':
                       ea.metadata_json['user_name'], }
            completed_intervention_users.append(ciu)
        return completed_intervention_users

    def _get_unsent_survey_recipients(self):
        """
        find which users need need to receive surveys that haven't already
        :rtype dict to map to ExperimentActions
        """
        logging.info(f'Seeking welcomed users that havent been surveyed before {self.action_latest_date}')

        # welcome
        completed_intervention_users = self._get_completed_interventions()

        sent_survey_recipients = super()._get_sent_survey_recipient()

        sent_survey_user_names = [ea.metadata_json['user_name'] for ea in sent_survey_recipients]

        unsent_survey_recipients = [ciu for ciu in completed_intervention_users if
                                    ciu['user_name'] not in sent_survey_user_names]

        for usr in unsent_survey_recipients:
            usr['public_anonymous_id'] = str(uuid4())
        return unsent_survey_recipients

def fr_wiki_welcome_survey(db, batch_size, lang, intervention_name, intervention_type, config):
    afus = ActionFollowUpSurvey(db, batch_size, lang, intervention_name, intervention_type, config)
    return afus.run()


from typing import Sequence
from node import Node
from math import prod

"""
A basic pooling layer, implemented by Thomas Volden.

Sources of inspiration:
- https://www.analyticsvidhya.com/blog/2021/08/beginners-guide-to-convolutional-neural-network-with-implementation-in-python/
- https://en.wikipedia.org/wiki/Convolutional_neural_network
"""
class PoolingStrategy:
    def compute(values:Sequence[Node]) -> Node:
        raise NotImplementedError

class MaxPooling(PoolingStrategy):
    def compute(values: Sequence[Node]) -> Node:
        return max(values) # Return the maximum number of the pool

class Pool:
    def __init__(self, stride:int, shape:int, input_shape:tuple[int, int, int], strategy:PoolingStrategy = MaxPooling()):
        """
        stride: The number of positions to move for each iteration (both left and down)
        shape: The size of the pool (both width and height)
        input_shape: The height, width and number of channels of the input
        stategy: The stategy used for pooling. Standard set to max pooling
        """
        self.stride = stride
        self.shape = shape
        self.input_shape = input_shape
        self.strategy = strategy

    def pool(self, inputs: Sequence[Node]) -> Sequence[Node]:
        """
        inputs: input values from the two dimentional value map and number of channels

        Check to see if the lenght of inputs are equal to the product of the defined shape (x*y*c)
        """
        assert prod(self.input_shape) == len(inputs), "The input must meet the defined shape"
        output = []
        steps_x = int((self.input_shape[0] - self.filter_size) / self.stride) + 1 # Calculate max number of moves to the left giving the strider value
        steps_y = int((self.input_shape[1] - self.filter_size) / self.stride) + 1 # Calculate max number of moves to the bottom giving the strider value

        for c in range(self.input_shape[2]): # For each channel
            for y in range(steps_y): # For each move to the bottom
                for x in range(steps_x): # For each move to the left
                    # Append j,i value from filter bank to x,y value
                    values = []
                    for i in range(self.shape):
                        for j in range(self.shape):
                            channel = c * self.input_shape[0] * self.input_shape[1] # Skip whole areas (height*width)
                            stride_y = y * self.stride # Calculate the stride offset for the y direction
                            stride_x = x * self.stride # Calculate the stride offset for the x direction
                            row = (i + stride_y) * self.input_shape[0] # Jump over a width for each row plus y stride offset
                            col = stride_x + j # The column plus x stride offset
                            values.append(inputs[channel + row + col])
                    # Parse the value through our pooling strategy to get one value
                    output.append(self.strategy.compute(values))
        return output


#! /usr/bin/python3
import pandas as pd;

def guessFirstName(name):
    if '(' in name:
        pIndex1    = name.find('(');
        spaceIndex = name.find(' ', pIndex1);
        pIndex2    = name.find(')', pIndex1);
        endIndex   = spaceIndex if spaceIndex > 0 else pIndex2;
        return name[pIndex1 + 1:endIndex].strip();
    if '.' in name:
        dIndex     =  name.find('.');
        spaceIndex =  name.find(' ', dIndex + 2);
        return name[dIndex + 2:spaceIndex].strip();
    return name;

data = pd.read_csv('titanic.csv', index_col='PassengerId');

hm = dict();
for nm in data[data['Sex'] == 'female']['Name']:
    firstName = guessFirstName(nm);
    if firstName in hm:
        hm[firstName] = hm[firstName] + 1;
    else:
        hm[firstName] = 1;

for nm in hm:
    cnt = hm[nm];
    if cnt > 5:
        print("{}:{}".format(nm, cnt));