"""
The referenceFrames module provides the class :code:`ReferenceFrames` which defines the affine transformation
for bringing one reference frame to another.

.. todo:: The tests in methods like getPassiveTransformationTo using '==' should be looked at again and maybe
          changed into using the 'is' operator. This because we now have __eq__ implemented.

@author: Pierre Royer
"""
import logging
import random
import string
import textwrap

import numpy as np
import transforms3d as t3

import egse.coordinates.transform3d_addon as t3add
from egse.coordinates.point import Point
from egse.coordinates.rotationMatrix import RotationMatrix
from egse.decorators import deprecate
from egse.exceptions import InvalidOperationError

LOGGER = logging.getLogger(__name__)

def transformationToString(transformation):
    """Helper function: prints out a transformation (numpy ndarray) in a condensed form on one line."""

    if isinstance(transformation, np.ndarray):
        if np.allclose(transformation, ReferenceFrame._I):
            return "Identity"
        msg = np.array2string(
            transformation,
            separator=",",
            suppress_small=True,
            formatter={"float_kind": lambda x: "%.2f" % x},
        ).replace("\n", "")
        return msg

    # We do not want to raise an Exception here since this is mainly used in logging messages
    # and doesn't really harm the execution of the program.
    return f"ERROR: expected transformation to be an ndarray, type={type(transformation)}"

class ReferenceFrame(object):
    """
    A Reference Frame defined in reference frame "ref", i.e.
    defined by the affine transformation bringing the reference frame "ref" onto "self".

    By default, "ref" is the master refence frame, defined as the identity matrix.

    :param transformation: 4x4 affine transformation matrix defining this system in "ref" system
    :type transformation: numpy array

    :param ref: reference system in which this new reference frame is defined
    :type ref: ReferenceFrame

    :param name: name the reference frame so it can be referenced, set to 'master' when None
    :type name: str

    :param rot_config:
            * Is set when using creator ReferenceFrame.fromTranslationRotation()
            * In other cases, is set to a default "szyx"
              (rotations around static axes z, y and x in this order)
              In these other cases, it has no real direct influence,
              except for methods returning the rotation vector (e.g. getRotationVector)
              It is therefore always recommended to pass it to the constructor, even when
              constructing the ReferenceFrame directly from a transformation matrix
    :type rot_config: str

    Both the ``transformation`` and the ``ref`` parameters are mandatory.

    If the reference frame is None, the master reference frame is created.

    The master reference frame:

        * is defined by the identity transformation matrix
        * has itself as a reference

    For convenience we provide the following factory methods:

    createMaster()
        Create a Master Reference Frame

    createRotation(..)
        Create a new Reference Frame that is rotated with respect to the given reference frame

    createTranslation(..)
        Create a new Reference Frame that is a translation with respect to the given reference frame
    """

    _I = np.identity(4)
    _MASTER = None
    _ROT_CONFIG_DEFAULT = "sxyz"
    _names_used = [None, "Master"]
    _strict_naming = False
    _ACTIVE_DEFAULT = True

    def __new__(cls, transformation, ref, name=None, rot_config=_ROT_CONFIG_DEFAULT):
        """Create a new ReferenceFrame class."""

        LOGGER.debug(
            f"transformation={transformationToString(transformation)}, ref={ref!r}, name={name}, rot_config={rot_config}"
        )

        if ref is None:
            msg = (
                "No reference frame was given, if you planned to create a Master Reference Frame, "
                "use ReferenceFrame.createMaster(). "
            )
            LOGGER.error(msg)
            raise ValueError(msg, "REF_IS_NONE")

        if not isinstance(ref, cls):
            msg = f"The 'ref' keyword argument is not a ReferenceFrame object, but {type(ref)}"
            LOGGER.error(msg)
            raise ValueError(msg, "REF_IS_NOT_CLS")

        if name == "Master":
            msg = (
                "The 'name' argument cannot be 'Master' unless a Master instance should be created, "
                "in that case, use ReferenceFrame.createMaster()"
            )
            LOGGER.error(msg)
            raise ValueError(msg, "MASTER_NAME_USED")

        if transformation is None:
            msg = "The 'transformation' argument can not be None, please provide a proper transformation for this reference frame."
            LOGGER.error(msg)
            raise ValueError(msg, "TRANSFORMATION_IS_NONE")

        if not isinstance(transformation, np.ndarray):
            msg = f"The 'transformation' argument shall be a Numpy ndarray [not a {type(transformation)}], please provide a proper transformation for this reference frame."
            LOGGER.error(msg)
            raise ValueError(msg, "TRANSFORMATION_IS_NOT_NDARRAY")

        if rot_config is None:
            msg = "The 'rot_config' keyword argument can not be None, do not specify it when you want to use the default value."
            LOGGER.error(msg)
            raise ValueError(msg)

        _instance = super(ReferenceFrame, cls).__new__(cls)

        return _instance

    def __init__(self, transformation, ref, name=None, rot_config=_ROT_CONFIG_DEFAULT):
        """Initialize the ReferenceFrame object """

        self.debug = False

        LOGGER.debug(
            f"transformation={transformationToString(transformation)}, ref={ref!r}, name={name}, rot_config={rot_config}"
        )

        # All argument testing is done in the __new__() method and we should be save here.

        self.ref = ref
        self.name = self.__createName(name)
        self.transformation = transformation
        self.rot_config = rot_config

        self.definition = [self.transformation, self.ref, self.name]

        self.x = self.getAxis("x")
        self.y = self.getAxis("y")
        self.z = self.getAxis("z")

        self.linkedTo = {}
        self.referenceFor = []

        ref.referenceFor.append(self)

        return

    def find_master(self):
        return self.findMaster()

    def findMaster(self):
        """
        Returns the Master frame for this reference frame. The Master frame is always at the end
        of the path following the references.

        Returns:
            The master frame.
        """

        frame = self
        while not frame.isMaster():
            frame = frame.ref
        return frame

    @classmethod
    def createMaster(cls):
        """
        Create a master reference frame.

        A master reference frame is defined with respect to itself and is initialised with the
        identity matrix.

        The master frame is automatically given the name "Master".
        """
        ref_master = super(ReferenceFrame, cls).__new__(cls)
        ref_master.name = "Master"
        ref_master.ref = ref_master
        ref_master.transformation = cls._I
        ref_master.rot_config = cls._ROT_CONFIG_DEFAULT
        ref_master.initialized = True
        ref_master.debug = False
        ref_master.linkedTo = {}
        ref_master.referenceFor = []

        LOGGER.debug(
            f"NEW MASTER CREATED: {id(ref_master)}, ref = {id(ref_master.ref)}, "
            f"name = {ref_master.name}"
        )

        return ref_master

    @classmethod
    def __createName(cls, name: str = None):

        if name is None:
            while name in cls._names_used:
                name = "F" + "".join(random.choices(string.ascii_uppercase, k=3))
            return name

        if cls._strict_naming:

            # generate a unique name

            old_name = name

            while name in cls._names_used:
                name = "F" + "".join(random.choices(string.ascii_uppercase, k=3))

            LOGGER.warning(
                f"name ('{old_name}') is already defined, since strict naming is applied, "
                f"a new unique name was created: {name}"
            )

        else:

            if name in cls._names_used:
                LOGGER.log(
                    0,
                    f"name ('{name}') is already defined, now you have more than one "
                    f"ReferenceFrame with the same name.",
                )

        cls._names_used.append(name)
        return name

    @classmethod
    def fromTranslation(cls, transx, transy, transz, ref, name=None):
        """
        Create a ReferenceFrame from a translation with respect to the given reference frame.

        :param transx: translation along the x-axis
        :type transx: float

        :param transy: translation along the y-axis
        :type transy: float

        :param transz: translation along the z-axis
        :type transz: float

        :param ref: reference frame with respect to which the translation is performed. If no ref is given
            the rotation is with respect to the master reference frame
        :type ref: ReferenceFrame

        :param name: a simple convenient name to identify the reference frame. If no name is provided
                     a random name of four characters starting with 'F' will be generated.
        :type name: str

        :return: a reference frame
        """
        adef = np.identity(4)
        adef[:3, 3] = [transx, transy, transz]

        if ref is None:
            raise ValueError(
                "The ref argument can not be None, provide a master or another reference frame."
            )

        return cls(transformation=adef, ref=ref, name=name)

    @classmethod
    def fromRotation(
        cls,
        rotx,
        roty,
        rotz,
        ref,
        name=None,
        rot_config=_ROT_CONFIG_DEFAULT,
        active=_ACTIVE_DEFAULT,
        degrees=True,
    ):
        """
        Create a ReferenceFrame from a rotation with respect to the given reference frame.

        :param rotx: rotation around the x-axis
        :type rotx: float

        :param roty: rotation around the y-axis
        :type roty: float

        :param rotz: rotation around the z-axis
        :type rotz: float

        :param ref: reference frame with respect to which the rotation is performed. If no ref is given
            the rotation is with respect to the master reference frame
        :type ref: ReferenceFrame

        :param name: a simple convenient name to identify the reference frame. If no name is provided
                     a random name of four characters starting with 'F' will be generated.
        :type name: str

        :return: a reference frame
        """
        # Convention (rotating axes, in order xyz)

        # rot_config = cls._ROT_CONFIG_DEFAULT

        # Rotation amplitude

        # Rotation
        if degrees:
            rotx = np.deg2rad(rotx)
            roty = np.deg2rad(roty)
            rotz = np.deg2rad(rotz)

        rotation = RotationMatrix(rotx, roty, rotz, rot_config, active=active)

        # Defaults for zoom & shear

        zdef = np.array([1, 1, 1])
        sdef = np.array([0, 0, 0])

        translation = [0, 0, 0]

        TT = t3.affines.compose(T=translation, R=rotation.R, Z=zdef, S=sdef)

        if ref is None:
            raise ValueError(
                "The ref argument can not be None, provide a master or another reference frame."
            )

        return cls(transformation=TT, ref=ref, name=name, rot_config=rot_config)

    @staticmethod
    def fromPoints(points, plane="xy", name=None, usesvd=True, verbose=True):
        """
        fromPoints(points, plane="xy", name=None, usesvd=True,verbose=True)

        Finds the best fitting "plane" plane to all points in "points" and builds a ReferenceFrame
        considering the fitted plane as the 'xy' plane of the new system

        fitPlane must be in ['xy','yz','zx']
        """
        return points.bestFittingPlane(fitPlane=plane, usesvd=usesvd, verbose=verbose)

    @classmethod
    def fromTranslationRotation(
        cls,
        translation,
        rotation,
        ref,
        name=None,
        rot_config=_ROT_CONFIG_DEFAULT,
        active=_ACTIVE_DEFAULT,
        degrees=True,
    ):
        """fromTranslationRotation(cls,translation,rotation, ref=None,name=None, rot_config=_ROT_CONFIG_DEFAULT,active=_ACTIVE_DEFAULT, degrees=True)

        Construct a ReferenceFrame from a translation and a rotation (vectors!)

        translation : translation vector : 3 x 1 = tx, ty, tz
        rotation    : rotation vector    : 3 x 1 = rx, ry, rz

        rot_config : convention for the rotation, see rotationMatrix.__doc__

        degrees : if True, input values are assumed in degrees, otherwise radians
        """
        # Translation
        translation = np.array(translation)
        # Zoom - unit
        zdef = np.array([1, 1, 1])
        # Shear
        sdef = np.array([0, 0, 0])
        # Rotation
        if degrees:
            rotation = np.array([np.deg2rad(item) for item in rotation])
        rotx, roty, rotz = rotation
        #
        rmat = RotationMatrix(rotx, roty, rotz, rot_config=rot_config, active=active)
        #
        # transformation = t3.affines.compose(translation,rmat.R,Z=zdef,S=sdef)

        if ref is None:
            raise ValueError(
                "The ref argument can not be None, provide a master or another reference frame."
            )

        return cls(
            transformation=t3.affines.compose(translation, rmat.R, Z=zdef, S=sdef),
            ref=ref,
            name=name,
            rot_config=rot_config,
        )

    def getTranslationVector(self):
        """
        getTranslationVector()

        Returns the translation defining this reference frame (from self.ref to self)
        """
        return self.transformation[:3, 3]

    def getRotationVector(self, degrees=True, active=_ACTIVE_DEFAULT):
        """
        getRotationVector()

        Returns the rotation (vector) defining this reference frame (from self.ref to self)
        degrees : if true, rotation angles are expressed in degrees
        """
        rotation = t3.euler.mat2euler(self.transformation, axes=self.rot_config)
        if degrees:
            rotation = np.array([np.rad2deg(item) for item in rotation])
        return rotation

    def getTranslationRotationVectors(self, degrees=True, active=_ACTIVE_DEFAULT):
        """getTranslationRotationVectors()
        Returns the translation and rotation (vectors) defining this reference frame (from self.ref to self)
        degrees : if true, rotation angles are expressed in degrees
        """
        translation = self.getTranslationVector()
        rotation = self.getRotationVector(degrees=degrees, active=active)
        return translation, rotation

    def getRotationMatrix(self):
        """
        getRotationMatrix()

        Returns the rotation (matrix) defining this reference frame (from self.ref to self)
        """
        result = self.transformation.copy()
        result[:3, 3] = [0.0, 0.0, 0.0]
        return result

    def __repr__(self):
        return f"ReferenceFrame(transformation={transformationToString(self.transformation)}, ref={self.ref.name}, name={self.name}, rot_config={self.rot_config})"

    def __str__(self):
        msg = textwrap.dedent(
            f"""\
                ReferenceFrame
                name          : {self.name}
                reference     : {self.ref.name}
                rot_config    : {self.rot_config}
                links         : {[key.name for key in self.linkedTo.keys()]}
                transformation:
                  [{np.round(self.transformation[0], 3)}
                   {np.round(self.transformation[1], 3)}
                   {np.round(self.transformation[2], 3)}
                   {np.round(self.transformation[3], 3)}]
                translation   : {np.round(self.getTranslationVector(),3)}
                rotation      : {np.round(self.getRotationVector(),3)}"""
        )
        return msg

    @deprecate(
        reason=(
            "I do not see the added value of changing the name and "
            "the current method has the side effect to change the name "
            "to a random string when the name argument is already used."
        ),
        alternative="the constructor argument to set the name already of the object.",
    )
    def setName(self, name=None):
        """
        Set or change the name of a reference frame.

        ..note: this method is deprecated

        :param str name: the new name for the reference frame, if None, a random name will be generated.

        :raises InvalidOperationError: when you try to change the name of the Master reference frame
        """
        if self.isMaster():
            raise InvalidOperationError(
                "You try to change the name of the Master reference frame, which is not allowed."
            )
        self.name = self.__createName(name)

    def addLink(self, ref, transformation=None, _stop=False):
        """
        adds a link between self and ref in self.linkedTo and ref.linkedTo
        """

        # DONE: set the inverse transformation in the ref to this
        #   ref.linkedTo[self] = t3add.affine_inverse(transformation)
        # TODO:
        #   remove the _stop keyword

        # TODO: deprecate transformation as an input variable
        #       linkedTo can become a list of reference frames, with no transformation
        #       associated to the link. The tfo associated to a link is already
        #       checked in real time whenever the link is addressed
        if transformation is None:
            transformation = self.getActiveTransformationTo(ref)
        else:
            LOGGER.info("Deprecation warning: transformation will be automatically set to "
                        "the current relation between {self.name} and {ref.name}")
            LOGGER.debug("Requested:")
            LOGGER.debug(np.round(transformation, decimals=3))
            LOGGER.debug("Auto (enforced):")

            transformation = self.getActiveTransformationTo(ref)

            LOGGER.debug(np.round(transformation, decimals=3))

        self.linkedTo[ref] = transformation

        # TODO simplify this when transformation is deprecated
        #      it becomes ref.linkedTo[self] = ref.getActiveTransformationTo(self)
        ref.linkedTo[self] = t3add.affine_inverse(transformation)

    def removeLink(self, ref):
        """
        Remove the link between self and ref, both ways.
        """

        # First remove the entry in ref to this

        if self in ref.linkedTo:
            del ref.linkedTo[self]

        # Then remove the entry in this to ref

        if ref in self.linkedTo:
            del self.linkedTo[ref]

    def getPassiveTransformationTo(self, targetFrame):
        """
        getPassiveTransformationTo(self,targetFrame)
        == getPointTransformationTo(self,targetFrame)

        returns the transformation to apply to a Point (defined in self) to express it in targetFrame

        Passive transformation : the point is static, we change the reference frame around it
        """
        LOGGER.debug("PASSIVE TO self {self.name} target {targetFrame.name}")
        if targetFrame is self:
            """
            Nothing to do here, we already have the right coordinates
            """
            LOGGER.debug("case 1")
            result = np.identity(4)

        elif targetFrame.ref is self:
            """
            The target frame is defined in self => the requested transformation is the targetFrame definition
            """
            LOGGER.debug("=== 2 start ===")
            result = t3add.affine_inverse(targetFrame.transformation)
            LOGGER.debug("=== 2 end   ===")
        elif targetFrame.ref is self.ref:
            """
            targetFrame and self are defined wrt the same reference frame
            We want
            self --> targetFrame
            We know
            targetFrame.ref --> targetFrame (= targetFrame.transformation)
            self.ref   --> self   (= self.transformation)
            That is
            self --> self.ref is targetFrame.ref --> targetFrame
            inverse(definition)    targetFrame definition

            """
            LOGGER.debug("=== 3 start ===")
            LOGGER.debug(" ref   \n{0}".format(self.ref))
            LOGGER.debug("===")
            LOGGER.debug("self   \n{0}".format(self))
            LOGGER.debug("===")
            LOGGER.debug("targetFrame \n{0}".format(targetFrame))
            LOGGER.debug("===")

            selfToRef = self.transformation
            LOGGER.debug("selfToRef \n{0}".format(selfToRef))

            # refToRef = I

            refToTarget = t3add.affine_inverse(targetFrame.transformation)
            LOGGER.debug("refToTarget \n{0}".format(refToTarget))

            result = np.dot(refToTarget, selfToRef)
            LOGGER.debug("result \n{0}".format(result))
            LOGGER.debug("=== 3 end   ===")
        else:
            """
            We are after the transformation from
            self --> targetFrame
            ==
            self --> self.ref --> targetFrame.ref --> targetFrame

            We know
            targetFrame.ref --> targetFrame (targetFrame.transformation)
            self.ref        --> self (self.transformation)
            but
            targetFrame.ref != self.ref
            so we need
            self.ref --> targetFrame.ref
            then we can compose
            self --> self.ref --> targetFrame.ref --> targetFrame

            Note: the transformation self.ref --> targetFrame.ref is acquired recursively
                  This relies on the underlying assumption that there exists
                  one unique reference frame that source and self can be linked to
                  (without constraints on the number of links necessary), i.e.
                  that, from a frame to its reference or the opposite, there exists
                  a path between self and targetFrame. That is equivalent to
                  the assumption that the entire set of reference frames is connex,
                  i.e. defined upon a unique master reference frame.
            """
            LOGGER.debug("=== 4 start ===")
            selfToRef = self.transformation
            selfRefToTargetRef = self.ref.getPassiveTransformationTo(targetFrame.ref)
            refToTarget = t3add.affine_inverse(targetFrame.transformation)
            result = np.dot(refToTarget, np.dot(selfRefToTargetRef, selfToRef))
            LOGGER.debug("=== 4 end   ===")

        return result

    def getPassiveTranslationRotationVectorsTo(
        self, targetFrame, degrees=True
    ):  # , active=_ACTIVE_DEFAULT):
        """
        getPassiveTranslationRotationVectorsTo(self,ref,degrees=True)

        extracts rotation vector from the result of getPassiveTransformationTo(target)
        """
        transformation = self.getPassiveTransformationTo(targetFrame)
        rotation = t3.euler.mat2euler(transformation, axes=self.rot_config)
        if degrees:
            rotation = np.array([np.rad2deg(item) for item in rotation])
        translation = transformation[:3, 3]
        return translation, rotation

    def getPassiveTranslationVectorTo(self, targetFrame, degrees=True):
        """
        getPassiveTranslationRotationVectorsTo(self,ref,degrees=True)

        extracts translation vector from the result of getPassiveTransformationTo(target)
        """
        return self.getPassiveTranslationRotationVectorsTo(targetFrame, degrees=degrees)[0]

    def getPassiveRotationVectorTo(self, targetFrame, degrees=True):
        """
        getPassiveTranslationRotationVectorsTo(self,targetFrame,degrees=True)

        extracts rotation vector from the result of getPassiveTransformationTo(targetFrame)
        """
        return self.getPassiveTranslationRotationVectorsTo(targetFrame, degrees=degrees)[1]

    def getPassiveTransformationFrom(self, source):
        """
        getPassiveTransformationFrom(self,source)
        ==  getPointTransformationTo(self,source)

        INPUT
        source : is a ReferenceFrame object

        OUTPUT
        returns the transformation matrix  that, applied to a point defined
        in source returns its coordinates in self
        """
        LOGGER.debug("PASSIVE FROM self {self.name} source {source.name}")
        return source.getPassiveTransformationTo(self)

    def getPassiveTranslationRotationVectorsFrom(
        self, source, degrees=True
    ):  # , active=_ACTIVE_DEFAULT):
        """
        getPassiveTranslationRotationVectorsFrom(self,source, degrees=True)

        extracts rotation vector from the result of getPassiveTransformationTo(target)
        """
        transformation = self.getPassiveTransformationFrom(source)
        rotation = t3.euler.mat2euler(transformation, axes=self.rot_config)
        if degrees:
            rotation = np.array([np.rad2deg(item) for item in rotation])
        translation = transformation[:3, 3]
        return translation, rotation

    def getPassiveTranslationVectorFrom(self, source, degrees=True):
        """
        getPassiveTranslationVectorFrom(self,source, degrees=True)

        extracts translation vector from the result of getPassiveTransformationFrom(source)
        """
        return self.getPassiveTranslationRotationVectorsFrom(source, degrees=degrees)[0]

    def getPassiveRotationVectorFrom(self, source, degrees=True):
        """
        getPassiveTranslationRotationVectorsFrom(self,source,degrees=True)

        extracts rotation vector from the result of getPassiveTransformationFrom(source)
        """
        return self.getPassiveTranslationRotationVectorsFrom(source, degrees=degrees)[1]

    def getActiveTransformationTo(self, target):
        """
        Return the transformation matrix from this reference frame (``self``) to
        the target reference frame.

        Applying this transformation to the ``self`` ReferenceFrame would render it
        identical to target.

        Applying this transformation to a point defined in ``self`` would move it to
        the same coordinates in target.

        :param target: is a ReferenceFrame object

        :return: the transformation matrix that defines the reference frame ``target``
            in ``self``, the current reference frame. The transformation from self
            to target

        """
        LOGGER.debug("ACTIVE TO self {self.name} target {target.name}")
        return target.getPassiveTransformationTo(self)

    def getActiveTranslationRotationVectorsTo(
        self, targetFrame, degrees=True
    ):  # ,active=_ACTIVE_DEFAULT):
        """
        getActiveTranslationRotationVectorsTo(self,ref,degrees=True)

        extracts rotation vector from the result of getActiveTransformationTo(target)
        """
        transformation = self.getActiveTransformationTo(targetFrame)
        rotation = t3.euler.mat2euler(transformation, axes=self.rot_config)
        if degrees:
            rotation = np.array([np.rad2deg(item) for item in rotation])
        translation = transformation[:3, 3]
        return translation, rotation

    def getActiveTranslationVectorTo(self, targetFrame, degrees=True):
        """
        getActiveTranslationRotationVectorsTo(self,ref,degrees=True)

        extracts translation vector from the result of getActiveTransformationTo(target)
        """
        return self.getActiveTranslationRotationVectorsTo(targetFrame, degrees=degrees)[0]

    def getActiveRotationVectorTo(self, targetFrame, degrees=True):
        """
        getActiveTranslationRotationVectorsTo(self,targetFrame,degrees=True)

        extracts rotation vector from the result of getActiveTransformationTo(targetFrame)
        """
        return self.getActiveTranslationRotationVectorsTo(targetFrame, degrees=degrees)[1]

    def getActiveTransformationFrom(self, source):
        """
        Applying this transformation to the ``source`` ReferenceFrame
        would render it identical to self.

        Applying this transformation to a point defined in source
        would move it to the same coordinates in self.

        :param source: is a ReferenceFrame object

        :return: the transformation matrix that defines this reference frame in ``source``,
            i.e. the transformation from ``source`` to ``self``

        """
        LOGGER.debug("ACTIVE FROM self {self.name} source {source.name}")
        return self.getPassiveTransformationTo(source)

    def getActiveTranslationRotationVectorsFrom(
        self, source, degrees=True
    ):  # ,active=_ACTIVE_DEFAULT):
        """
        getActiveTranslationRotationVectorsFrom(self,source, degrees=True)

        extracts rotation vector from the result of getActiveTransformationTo(target)
        """
        transformation = self.getActiveTransformationFrom(source)
        rotation = t3.euler.mat2euler(transformation, axes=self.rot_config)
        if degrees:
            rotation = np.array([np.rad2deg(item) for item in rotation])
        translation = transformation[:3, 3]
        return translation, rotation

    def getActiveTranslationVectorFrom(self, source, degrees=True):
        """
        getActiveTranslationVectorFrom(self,source, degrees=True)

        extracts translation vector from the result of getActiveTransformationFrom(source)
        """
        return self.getActiveTranslationRotationVectorsFrom(source, degrees=degrees)[0]

    def getActiveRotationVectorFrom(self, source, degrees=True):
        """
        getActiveTranslationRotationVectorsFrom(self,source,degrees=True)

        extracts rotation vector from the result of getActiveTransformationFrom(source)
        """
        return self.getActiveTranslationRotationVectorsFrom(source, degrees=degrees)[1]

    def _findEnds(self, frame, visited=[], ends=[], verbose=True, level=1):
        """
        PURPOSE
        Identify the 'linked_frames':
            frames that are linked, either directly or indirectly (via mult. links) to "frame"
            --> returned as visited
        Identify subset of 'linked_frames of which the reference does not belong to linked_frames
            --> returned as 'finalEnds'
        """
        LOGGER.debug(
            f"{level:-2d}{2*level*' '} Current: {frame.name} --  ends: {[f.name for f in ends]} -- visited {[f.name for f in visited]}"
        )
        # if verbose: print (f"{level:-2d}{2*level*' '} Current: {frame.name} --  ends: {[f.name for f in ends]} -- visited {[f.name for f in visited]}")

        # Establish the set of 'linked_frames' (variable 'visited')
        # The recursive process below keeps unwanted (non-endFrames), namely the
        # frames that are not directly, but well indirectly linked to their reference
        # This case is solved further down
        if frame not in visited:
            visited.append(frame)
            if verbose and level:
                level += 1
            if frame.ref not in frame.linkedTo:
                ends.append(frame)
                LOGGER.debug(f"{(10+2*level)*' '}{frame.name}: new end")
                # if verbose: LOGGER.info(f"{(10+2*level)*' '}{frame.name}: new end")
            for linkedFrame in frame.linkedTo:
                ends, visited = self._findEnds(
                    linkedFrame, visited=visited, ends=ends, verbose=verbose, level=level
                )

        # If frame.ref was linked to frame via an indirect route, reject it
        finalEnds = []
        for aframe in ends:
            if aframe.ref not in ends:
                finalEnds.append(aframe)
        return finalEnds, visited

    def setTransformation(
        self, transformation, updated=None, preserveLinks=True, _relative=False, verbose=True
    ):
        """
        setTransformation(self,transformation, updated=None, preserveLinks=True,_relative=False, verbose=True)

        Alter the definition of this coordinate system

        If other systems are linked to this one, their definition must be updated accordingly
          The link set between two ref. Frames A & B is the active transformation matrix from A to B
          A.addLink(B, matrix)
          A.getActiveTransformationTo(B) --> matrix

        The way to update the definition of the present system, and of those linked to it
        depends on the structure of those links.

        We define
        - the target frame as the one we want to move / redefine
        - 'linkedFrames' as those directly, or indirectly (i.e. via multiple links)
           linked to the target frame
        - endFrames as the subset of linkedFrames which are not linked to their reference (directly or indirectly)
        - sideFrames as the set of frames whose reference is a linkedFrame, but not themselves belonging to the linkedFrames

        We can demonstrate that updating the endFrames (Block A below) is sufficient to represent
        the movement of the target frame and all frames directly or indirectly linked to it.

        This may nevertheless have perverse effects for sideFrames. Indeed,
        their reference will (directly or implicitely) be redefined, but they shouldn't:
        they are not linked to their reference --> their location in space (e.g. wrt the master frame)
        should not be affected by the movement of the target frame. This is the aim of block B.

        For a completely robust solution, 2 steps must be taken
        BLOCK A. apply the rigt transformation to all "endFrames"
        BLOCK B. Check for frames
                       using any of the "visited" frames as a reference
                       not linked to its reference
            Correct its so that it doesn't move (it shouldn't be affected by the requested movement)
            This demands a "referenceFor" array property

        """
        # Ruthless, enforced redefinition of one system. Know what you do, or stay away.
        # Semi-unpredictible side effets if the impacted frame has links!
        if preserveLinks == False:
            self.transformation = transformation
            return

        if updated is None:
            updated = []

        # visitedFrames = all frames which can be reached from self via invariant links
        # endFrames = subset of visitedFrames that are at the end of a chain, and must be updated
        #             in order to properly represent the requested movement
        endFrames, visitedFrames = self._findEnds(frame=self, visited=[], ends=[], verbose=verbose)
        if verbose:
            LOGGER.info(
                f"Visited sub-system                      {[f.name for f in visitedFrames]}"
            )
            LOGGER.info(f"End-frames (movement necessary)         {[f.name for f in endFrames]}")

        # All updates are done by relative movements
        # so we must first compute the relative movement corresponding to the requested absolute movement
        if _relative == False:
            ## virtual = what self should become after the (absolute) movement
            ## it allows to compute the relative transformation to be applied and work in relative further down
            virtual = ReferenceFrame(
                transformation, ref=self.ref, name="virtual", rot_config=self.rot_config
            )
            request = self.getActiveTransformationTo(virtual)
            del virtual
        else:
            # If this method is called by applyTransformation,
            # we are facing a request for a relative movement
            # In that case the input is directly what we want
            request = transformation

        # BLOCK B. Check for frames that were impacted but shouldn't have been and correct them
        # B1. List of frames demanding a correction
        #     'impacted' are frames having their reference inside the rigid structure moving, but not linked to it
        #     If nothing is done, the movement will implicitely displace them, which is not intended

        ### Impacted shall not contain frames that are linked to self (== to any frame in visitedFrames) via any route...
        ### We check if the impacted frames are in visitedFrames:
        ### it is enough to know it's connected to the entire 'solid body' in which self belongs
        impacted = []
        for frame in visitedFrames:
            for child in frame.referenceFor:
                # Version 1 : too simple (restores too many frames)
                #if child not in frame.linkedTo:

                # Version 2 : overkill
                #child_ends, child_visited = child._findEnds(frame=child,visited=[],ends=[],verbose=verbose)
                #if frame not in child_visited:

                # Version 3 : just check if the child belongs to the rigid structure...
                if child not in visitedFrames:
                    impacted.append(child)

        LOGGER.debug(f"Impacted (not moving, defined in moving) {[f.name for f in impacted]}")

        # B2. save the location of all impacted frames
        # tempReference has the only purpose of avoiding that every frame must know the master
        # It could be any frame without links and defined wrt the master, but the master isn't known here...
        # TODO : confirm that the master isn't known (e.g. via cls._MASTER)
        tempMaster = self.findMaster()
        toRestore = {}
        for frame in impacted:
            toRestore[frame] = ReferenceFrame(
                frame.getActiveTransformationFrom(tempMaster),
                ref=tempMaster,
                name=frame.name + "toRestore",
                rot_config=frame.rot_config,
            )

        # BLOCK A. apply the rigt transformation to all "endFrames"
        """
        ### Ensure that "Untouched" remains unaffected regardless of the update order of the endFrames
        selfUntouched = ReferenceFrame(
            transformation = self.getActiveTransformationFrom(tempMaster),
            ref=tempMaster,
            name=self.name + "_fixed",
            rot_config=self.rot_config,
        )
        """

        selfUntouched = ReferenceFrame(
            transformation=self.transformation,
            ref=self.ref,
            name=self.name + "_fixed",
            rot_config=self.rot_config,
        )

        for bottom in endFrames:

            up = bottom.getActiveTransformationTo(selfUntouched)
            down = selfUntouched.getActiveTransformationTo(bottom)

            relativeTransformation = up @ request @ down

            LOGGER.debug(
                f"\nAdjusting {bottom.name} to {self.name}\nUpdated {[i.name for i in updated]}"
            )
            LOGGER.debug(f"\ninput transformation \n{np.round(transformation,3)}")
            LOGGER.debug(
                f"\nup \n{np.round(up,3)}\ntransformation\n{np.round(request,3)}\ndown\n{np.round(down,3)}"
            )
            LOGGER.debug(f"\nrelativeTransformation \n{np.round(relativeTransformation,3)}")

            bottom.transformation = bottom.transformation @ relativeTransformation

            updated.append(bottom)

        for frame in visitedFrames:
            if frame not in updated:
                updated.append(frame)

        # Block B
        # B3. Correction
        # we must set preserveLinks to False to prevent cascading impact from this update
        # if X1 is impacted with
        #    X1.ref = E1     X1 --> X2 (simple link)    E2.ref = X2
        # where X1 and X2 are "external frames" and E1 and E2 are "endFrames" that will hence move
        # X1 was impacted by the move of E1, but X2 wasn't
        # ==> wrt master, neither X1 nor X2 should have moved, but X1 did (via its ref)
        # and hence its link with X2 is now corrupt
        # We need to move X1 back to its original location wrt master
        # if we preserved the links while doing that,
        # we will move X2, which shouldn't move
        # (it didn't have to, it didn't and the goal is to restore the validity of the links)
        #
        # Direct restoration or the impacted frames at their original location

        for frame in toRestore:
            frame.transformation = frame.ref.getActiveTransformationTo(toRestore[frame])

        del toRestore

        return

    def setTranslationRotation(
        self,
        translation,
        rotation,
        rot_config=_ROT_CONFIG_DEFAULT,
        active=_ACTIVE_DEFAULT,
        degrees=True,
        preserveLinks=True,
    ):
        """
        setTranslationRotation(self,translation,rotation,rot_config=_ROT_CONFIG_DEFAULT, active=_ACTIVE_DEFAULT, degrees=True, preserveLinks=True)

        Same as setTransformation, but input = translation and rotation vectors rather than affine transformation matrix
        """
        # Translation
        translation = np.array(translation)
        # Zoom - unit
        zdef = np.array([1, 1, 1])
        # Shear
        sdef = np.array([0, 0, 0])
        # Rotation
        if degrees:
            rotation = np.array([np.deg2rad(item) for item in rotation])

        rotx, roty, rotz = rotation

        rmat = RotationMatrix(rotx, roty, rotz, rot_config=rot_config, active=active)

        LOGGER.debug(t3.affines.compose(translation, rmat.R, Z=zdef, S=sdef))

        transformation = t3.affines.compose(translation, rmat.R, Z=zdef, S=sdef)

        self.setTransformation(transformation, preserveLinks=preserveLinks, _relative=False)
        return

    def applyTransformation(self, transformation, updated=None, preserveLinks=True):
        """
        applyTransformation(self,transformation)

        Applies the transformation to the current reference frame's definition

        self.transformation := transformation @ self.transformation
        """
        if updated is None:
            updated = []

        self.setTransformation(
            transformation=transformation,
            updated=updated,
            preserveLinks=preserveLinks,
            _relative=True,
        )

    def applyTranslationRotation(
        self,
        translation,
        rotation,
        rot_config=None,
        active=_ACTIVE_DEFAULT,
        degrees=True,
        preserveLinks=True,
    ):
        """
        applyTranslationRotation(self,translation,rotation,rot_config=None,active=_ACTIVE_DEFAULT,degrees=True, preserveLinks=True)

        Builds transformation from input translation and rotation, then applies this transformation
        """
        if rot_config is None:
            rot_config = self.rot_config

        # Translation
        translation = np.array(translation)
        # Zoom - unit
        zdef = np.array([1, 1, 1])
        # Shear
        sdef = np.array([0, 0, 0])
        # Rotation
        if degrees:
            rotation = np.array([np.deg2rad(item) for item in rotation])
        rotx, roty, rotz = rotation
        #
        rmat = RotationMatrix(rotx, roty, rotz, rot_config=rot_config, active=active)
        #
        transformation = t3.affines.compose(translation, rmat.R, Z=zdef, S=sdef)
        #
        self.applyTransformation(transformation, preserveLinks=preserveLinks)
        return

    def getAxis(self, axis, name=None):
        """
        INPUT
        axis : in ['x','y','z']

        OUTPUT
        Returns an object of class Point, corresponding to the vector defining
        the axis of choice in self.
        The output can be used with the Point methods to express that axis in any reference frame
        """
        haxis = {}
        haxis["x"] = [1, 0, 0]
        haxis["y"] = [0, 1, 0]
        haxis["z"] = [0, 0, 1]
        if name is None:
            name = self.name + axis
        return Point(haxis[axis], ref=self, name=name)

    def getNormal(self, name=None):
        """
        getNormal(self,name=None)

        Returns a unit vector normal to the X-Y plane (= [0,0,1] = getAxis('z'))
        """
        return Point([0, 0, 1], ref=self, name=name)

    def getOrigin(self, name=None):
        """
        OUTPUT
        Returns an object of class Point, corresponding to the vector defining the origin in 'self', i.e. [0,0,0]
        The output can be used with the Point methods to express that axis in any reference frame
        """
        return Point([0, 0, 0], ref=self, name=name)

    def is_master(self):
        return self.isMaster()

    def isMaster(self):
        """
        A Master reference frame is a reference frame that refers to itself, and the
        transformation is the Identity matrix.
        """
        tr = self.transformation

        if (
            (self.name == self.ref.name)
            and (tr.shape[0] == tr.shape[1])
            and np.allclose(tr, np.eye(tr.shape[0]))
        ):
            return True
        return False

    def isSame(self, other):
        """
        Returns True if the two reference frames are the same except for their name.

        Two Reference Frames are considered the same when:

        * their transformation matrices are equal
        * the reference frame is equal
        * the rot_config is equal
        * the name may be different

        .. todo:: This needs further work and testing!
        """

        if other is self:
            LOGGER.debug(
                "self and other are the same object (beware: this message might occur with recursion from self.ref != self.other)"
            )
            return True

        if isinstance(other, ReferenceFrame):
            LOGGER.debug(f"comparing {self.name} and {other.name}")
            if not np.array_equal(self.transformation, other.transformation):
                LOGGER.debug("self.transformation not equals other.transformation")
                return False
            if self.rot_config != other.rot_config:
                LOGGER.debug("self.rot_config not equals other.rot_config")
                return False
            # The following tests are here to prevent recursion to go infinite when self and other
            # point to itself
            if self.ref is self and other.ref is other:
                LOGGER.debug("both self.ref and other.ref point to themselves")
                pass
            else:
                LOGGER.debug("one of self.ref or other.ref doesn't points to itself")
                if self.ref != other.ref:
                    LOGGER.debug("self.ref not equals other.ref")
                    return False
            if self.name is not other.name:
                LOGGER.debug(
                    f"When checking two reference frames for equality, only their names differ: '{self.name}' not equals '{other.name}'"
                )
                pass
            return True
        return NotImplemented

    def __eq__(self, other):
        """
        Overrides the default implementation, which basically checks for id(self) == id(other).

        Two Reference Frames are considered equal when:

        * their transformation matrices are equal
        * the reference frame is equal
        * the rot_config is equal
        * do we want to insist on the name being equal?
          YES - for strict testing
          NO  - this might need a new method like isSame(self, other) where the criteria are relaxed

        .. todo:: This needs further work and testing!
        """

        if other is self:
            LOGGER.debug(
                "self and other are the same object (beware: this message might occur with recursion from self.ref != self.other)"
            )
            return True

        if isinstance(other, ReferenceFrame):
            LOGGER.debug(f"comparing {self.name} and {other.name}")
            if not np.array_equal(self.transformation, other.transformation):
                LOGGER.debug("self.transformation not equals other.transformation")
                return False
            if self.rot_config != other.rot_config:
                LOGGER.debug("self.rot_config not equals other.rot_config")
                return False
            # The following tests are here to prevent recursion to go infinite when self and other
            # point to itself
            if self.ref is self and other.ref is other:
                LOGGER.debug("both self.ref and other.ref point to themselves")
                pass
            else:
                LOGGER.debug("one of self.ref or other.ref doesn't points to itself")
                if self.ref != other.ref:
                    LOGGER.debug("self.ref not equals other.ref")
                    return False
            if self.name is not other.name:
                LOGGER.debug(
                    f"When checking two reference frames for equality, only their names differ: '{self.name}' not equals '{other.name}'"
                )
                return False
            return True
        return NotImplemented

    def __hash__(self):
        """Overrides the default implementation"""
        hash_number = (id(self.rot_config) + id(self.ref) + id(self.name)) // 16
        return hash_number

    def __copy__(self):

        LOGGER.debug(
            f'Copying {self!r} unless {self.name} is "Master" in which case the Master itself is returned.'
        )

        if self.isMaster():
            LOGGER.debug(f"Returning Master itself instead of a copy.")
            return self

        return ReferenceFrame(self.transformation, self.ref, self.name, self.rot_config)


# A function that displays text on-screen as if someone was typing it

import sys, time, random

def print_slow(str):
    for letter in str:
        sys.stdout.write(letter)
        sys.stdout.flush()
        time.sleep(0.1)

print_slow("Type whatever you want here")


from flask import Blueprint, g, jsonify, make_response
from flask import current_app as app
from flask_restful import Api

from app.api.resources.config_resource import ConfigResource
from app.api.resources.book_resource import BooksResource, BookResource
from app.api.auth import auth

API_BP = Blueprint('api', __name__)
API = Api(API_BP)

API.add_resource(ConfigResource, '/config')
API.add_resource(BooksResource, '/books')
API.add_resource(BookResource, '/books/<id>')

@API_BP.route('')
def index():
    # Testing logging levels
    app.logger.warning('I AM WARNING***')
    app.logger.debug('I AM DEBUG***')
    app.logger.error('I AM ERROR***')
    app.logger.info('I AM INFO***')
    return jsonify({'status': 'ok'})

@API_BP.route('/auth/token')
@auth.login_required
def get_auth_token():
    token = g.user.generate_auth_token(600)
    return jsonify({'token': token.decode('ascii')})


# Original source: https://github.com/kalise/flask-vote-app.git
import os
import random
import json
import socket

from datetime import datetime
from flask import Flask, request, make_response, render_template, redirect, url_for
from flask_sqlalchemy import SQLAlchemy

def create_app(test_config=None, debug_state=False):
    """Create and configure an instance of the Flask application."""
    hostname = socket.gethostname()
    app = Flask(__name__, instance_relative_config=True)
    app.debug = debug_state
    app.config.from_mapping(
        # a default secret that should be overridden by instance config
        SECRET_KEY='dev',
        # store the database in the instance folder
        DATABASE=None
    )

    # BEGIN: DATABASE local
    basedir = os.path.abspath(os.path.dirname(__file__))
    dbhost  = os.environ.get('DB_HOST', '')
    dbport  = os.environ.get('DB_PORT', '')
    dbname  = os.environ.get('DB_NAME', '')
    dbuser  = os.environ.get('DB_USER', '')
    dbpass  = os.environ.get('DB_PASS', '')
    dbtype  = os.environ.get('DB_TYPE', '')

    # Passed by pytest
    if test_config:
        app.config.update(test_config)

    if app.config['TESTING']:
        local_sql_db = os.path.join(basedir, 'data/testing.db')
        if os.path.exists(local_sql_db):
            os.remove(local_sql_db)
    else:
        local_sql_db = os.path.join(basedir, 'data/app.db')

    # TODO: it can be improved
    if dbtype == 'mysql':
        dburi  = dbtype + '://' + dbuser + ':' + dbpass + '@' + dbhost + ':' + dbport + '/' + dbname
    elif dbtype == 'postgresql':
        dburi  = dbtype + '://' + dbuser + ':' + dbpass + '@' + dbhost + ':' + dbport + '/' + dbname
    else:
        dburi = 'sqlite:///' + local_sql_db

    app.config['SQLALCHEMY_DATABASE_URI'] = dburi
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    app.config['SQLALCHEMY_ECHO'] = False

    db = SQLAlchemy(app)

    class Poll(db.Model):
        id       = db.Column(db.Integer, primary_key=True)
        name     = db.Column(db.String(30), unique=True)
        question = db.Column(db.String(90))
        stamp    = db.Column(db.DateTime)
        options  = db.relationship('Option', backref='option', lazy='dynamic')

        def __init__(self, name, question, stamp=None):
            self.name  = name
            self.question = question
            if not stamp:
                stamp = datetime.utcnow()
            self.stamp = stamp

    class Option(db.Model):
        id      = db.Column(db.Integer, primary_key=True)
        text    = db.Column(db.String(30))
        poll_id = db.Column(db.Integer, db.ForeignKey('poll.id'))
        poll    = db.relationship('Poll', backref=db.backref('poll', lazy='dynamic'))
        votes   = db.Column(db.Integer)

        def __init__(self, text, poll, votes):
            self.text = text
            self.poll = poll
            self.votes = votes
    # END: DATABASE local
    # BEGIN: Creating local database

    db.create_all()
    db.session.commit()
    hostname = socket.gethostname()

    print("Checking if a poll already exists into db")
    poll = Poll.query.first()

    if poll:
        print("Restarting the poll")
        poll.stamp = datetime.utcnow()
        db.session.commit()
    else:
        print("Loading seed data from file")
        try:
            with open(os.path.join(basedir, 'seeds/seed_data.json')) as file:
                seed_data = json.load(file)
                print("Starting a new poll")
                poll = Poll(seed_data['poll'], seed_data['question'])
                db.session.add(poll)
                for i in seed_data['options']:
                    option = Option(i, poll, 0)
                    db.session.add(option)
                db.session.commit()
        except:
            print("Cannot load seed data from file")
            poll = Poll('', '')
    # END: Creating local database

    if test_config is None:
        # load the instance config, if it exists, when not testing
        app.config.from_pyfile('config.py', silent=True)
    else:
        # load the test config if passed in
        app.config.update(test_config)

    # ensure the instance folder exists
    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass

    @app.errorhandler(404)
    def not_found(error):
        return 'Unfortunately, not found!'

    @app.route('/', methods=['GET', 'OPTIONS', 'HEAD'])

    @app.route('/index.html', methods=['GET', 'OPTIONS', 'HEAD'])
    def index():
        if request.method == 'GET':
            return render_template('index.html', hostname=hostname, poll=poll)
        else:
            # To deny OPTIONS and HEAD, you must allow it at the app.route
            # and then block them from here.
            return 'Method now allowed!\n', 405

    @app.route('/vote.html', methods=['GET', 'POST', 'OPTIONS', 'HEAD'])
    def vote():
        if request.method == 'POST':
            vote = request.form['vote']
            voted_option = Option.query.filter_by(poll_id=poll.id,id=vote).first()
            voted_option.votes += 1
            db.session.commit()
            return redirect(url_for('results'), code=302)

        if request.method not in ['GET', 'POST']:
            # To deny OPTIONS and HEAD, you must allow it at the app.route
            # and then block them from here.
            return 'Method now allowed!\n', 405

        options = Option.query.filter_by(poll_id=poll.id).all()
        resp = make_response(render_template('vote.html', hostname=hostname, poll=poll, options=options))
        return resp

    @app.route('/results.html', methods=['GET', 'OPTIONS', 'HEAD'])
    def results():
        if request.method == 'GET':
            results = Option.query.filter_by(poll_id=poll.id).all()
            return render_template('results.html', hostname=hostname, poll=poll, results=results)
        else:
            # To deny OPTIONS and HEAD, you must allow it at the app.route
            # and then block them from here.
            return 'Method now allowed!\n', 405

    return app


import os
import re
import shutil

from django.conf import settings
from django.db import transaction
from django.contrib.auth import get_user_model
from django.core.management import call_command
from django.utils.translation import ugettext_lazy as _
from django.contrib.staticfiles.templatetags import staticfiles

from celery.utils.log import get_task_logger

from geonode.celery_app import app
from geonode.tasks.tasks import (
    AcquireLock,
    FaultTolerantTask)
from geonode import GeoNodeException
from geonode.upload import signals
from geonode.layers.models import (
    Layer, UploadSession)
from geonode.base.models import (
    ResourceBase)
from geonode.utils import (
    is_monochromatic_image,
    set_resource_default_links)
from geonode.geoserver.upload import geoserver_upload
from geonode.catalogue.models import catalogue_post_save

from .helpers import (
    gs_catalog,
    ogc_server_settings,
    gs_slurp,
    set_styles,
    get_sld_for,
    set_layer_style,
    cascading_delete,
    fetch_gs_resource,
    create_gs_thumbnail,
    set_attributes_from_geoserver,
    _invalidate_geowebcache_layer,
    _stylefilterparams_geowebcache_layer)

logger = get_task_logger(__name__)

@app.task(
    bind=True,
    base=FaultTolerantTask,
    name='geonode.geoserver.tasks.geoserver_update_layers',
    queue='geoserver.catalog',
    expires=600,
    acks_late=False,
    autoretry_for=(Exception, ),
    retry_kwargs={'max_retries': 3, 'countdown': 10},
    retry_backoff=True,
    retry_backoff_max=700,
    retry_jitter=True)
def geoserver_update_layers(self, *args, **kwargs):
    """
    Runs update layers.
    """
    lock_id = f'{self.request.id}'
    with AcquireLock(lock_id) as lock:
        if lock.acquire() is True:
            return gs_slurp(*args, **kwargs)

@app.task(
    bind=True,
    base=FaultTolerantTask,
    name='geonode.geoserver.tasks.geoserver_set_style',
    queue='geoserver.catalog',
    expires=30,
    acks_late=False,
    autoretry_for=(Exception, ),
    retry_kwargs={'max_retries': 3, 'countdown': 10},
    retry_backoff=True,
    retry_backoff_max=700,
    retry_jitter=True)
def geoserver_set_style(
        self,
        instance_id,
        base_file):
    """
    Sets styles from SLD file.
    """
    instance = None
    try:
        instance = Layer.objects.get(id=instance_id)
    except Layer.DoesNotExist:
        logger.debug(f"Layer id {instance_id} does not exist yet!")
        raise

    lock_id = f'{self.request.id}'
    with AcquireLock(lock_id) as lock:
        if lock.acquire() is True:
            try:
                sld = open(base_file, "rb").read()
                set_layer_style(
                    instance,
                    instance.alternate,
                    sld,
                    base_file=base_file)
            except Exception as e:
                logger.exception(e)

@app.task(
    bind=True,
    base=FaultTolerantTask,
    name='geonode.geoserver.tasks.geoserver_create_style',
    queue='geoserver.catalog',
    expires=30,
    acks_late=False,
    autoretry_for=(Exception, ),
    retry_kwargs={'max_retries': 3, 'countdown': 10},
    retry_backoff=True,
    retry_backoff_max=700,
    retry_jitter=True)
def geoserver_create_style(
        self,
        instance_id,
        name,
        sld_file,
        tempdir):
    """
    Sets or create styles from Upload Session.
    """
    instance = None
    try:
        instance = Layer.objects.get(id=instance_id)
    except Layer.DoesNotExist:
        logger.debug(f"Layer id {instance_id} does not exist yet!")
        raise

    lock_id = f'{self.request.id}'
    with AcquireLock(lock_id) as lock:
        if lock.acquire() is True and instance:
            if sld_file and os.path.exists(sld_file) and os.access(sld_file, os.R_OK):
                f = None
                if os.path.isfile(sld_file):
                    try:
                        f = open(sld_file, 'r')
                    except Exception:
                        pass
                elif tempdir and os.path.exists(tempdir):
                    if os.path.isfile(os.path.join(tempdir, sld_file)):
                        try:
                            f = open(os.path.join(tempdir, sld_file), 'r')
                        except Exception:
                            pass
                if f:
                    sld = f.read()
                    f.close()
                    if not gs_catalog.get_style(name=name, workspace=settings.DEFAULT_WORKSPACE):
                        style = gs_catalog.create_style(
                            name,
                            sld,
                            raw=True,
                            workspace=settings.DEFAULT_WORKSPACE)
                        gs_layer = gs_catalog.get_layer(name)
                        _default_style = gs_layer.default_style
                        gs_layer.default_style = style
                        gs_catalog.save(gs_layer)
                        set_styles(instance, gs_catalog)
                        try:
                            gs_catalog.delete(_default_style)
                        except Exception as e:
                            logger.exception(e)
                else:
                    get_sld_for(gs_catalog, instance)
            else:
                get_sld_for(gs_catalog, instance)

@app.task(
    bind=True,
    base=FaultTolerantTask,
    name='geonode.geoserver.tasks.geoserver_finalize_upload',
    queue='geoserver.events',
    expires=600,
    acks_late=False,
    autoretry_for=(Exception, ),
    retry_kwargs={'max_retries': 3, 'countdown': 10},
    retry_backoff=True,
    retry_backoff_max=700,
    retry_jitter=True)
def geoserver_finalize_upload(
        self,
        import_id,
        instance_id,
        permissions,
        created,
        xml_file,
        sld_file,
        sld_uploaded,
        tempdir):
    """
    Finalize Layer and GeoServer configuration:
     - Sets Layer Metadata from XML and updates GeoServer Layer accordingly.
     - Sets Default Permissions
    """
    instance = None
    try:
        instance = Layer.objects.get(id=instance_id)
    except Layer.DoesNotExist:
        logger.debug(f"Layer id {instance_id} does not exist yet!")
        raise

    lock_id = f'{self.request.id}'
    with AcquireLock(lock_id) as lock:
        if lock.acquire() is True:
            from geonode.upload.models import Upload
            upload = Upload.objects.get(import_id=import_id)
            upload.layer = instance
            upload.save()

            try:
                # Update the upload sessions
                geonode_upload_sessions = UploadSession.objects.filter(resource=instance)
                geonode_upload_sessions.update(processed=False)
                instance.upload_session = geonode_upload_sessions.first()
            except Exception as e:
                logger.exception(e)

            # Sanity checks
            if isinstance(xml_file, list):
                if len(xml_file) > 0:
                    xml_file = xml_file[0]
                else:
                    xml_file = None
            elif not isinstance(xml_file, str):
                xml_file = None

            if xml_file and os.path.exists(xml_file) and os.access(xml_file, os.R_OK):
                instance.metadata_uploaded = True

            try:
                gs_resource = gs_catalog.get_resource(
                    name=instance.name,
                    store=instance.store,
                    workspace=instance.workspace)
            except Exception:
                try:
                    gs_resource = gs_catalog.get_resource(
                        name=instance.alternate,
                        store=instance.store,
                        workspace=instance.workspace)
                except Exception:
                    try:
                        gs_resource = gs_catalog.get_resource(
                            name=instance.alternate or instance.typename)
                    except Exception:
                        gs_resource = None

            if gs_resource:
                # Updating GeoServer resource
                gs_resource.title = instance.title
                gs_resource.abstract = instance.abstract
                gs_catalog.save(gs_resource)
                if gs_resource.store:
                    instance.storeType = gs_resource.store.resource_type
                    if not instance.alternate:
                        instance.alternate = f"{gs_resource.store.workspace.name}:{gs_resource.name}"

            if sld_uploaded:
                geoserver_set_style(instance.id, sld_file)
            else:
                geoserver_create_style(instance.id, instance.name, sld_file, tempdir)

            logger.debug(f'Finalizing (permissions and notifications) Layer {instance}')
            instance.handle_moderated_uploads()

            if permissions is not None:
                logger.debug(f'Setting permissions {permissions} for {instance.name}')
                instance.set_permissions(permissions, created=created)

            instance.save(notify=not created)

            try:
                logger.debug(f"... Cleaning up the temporary folders {tempdir}")
                if tempdir and os.path.exists(tempdir):
                    shutil.rmtree(tempdir)
            except Exception as e:
                logger.warning(e)
            finally:
                upload.complete = True
                upload.save()

            signals.upload_complete.send(sender=geoserver_finalize_upload, layer=instance)

@app.task(
    bind=True,
    base=FaultTolerantTask,
    name='geonode.geoserver.tasks.geoserver_post_save_layers',
    queue='geoserver.catalog',
    expires=3600,
    acks_late=False,
    autoretry_for=(Exception, ),
    retry_kwargs={'max_retries': 3, 'countdown': 10},
    retry_backoff=True,
    retry_backoff_max=700,
    retry_jitter=True)
def geoserver_post_save_layers(
        self,
        instance_id,
        *args, **kwargs):
    """
    Runs update layers.
    """
    from geonode.geoserver.signals import geoserver_post_save_complete

    instance = None
    try:
        instance = Layer.objects.get(id=instance_id)
    except Layer.DoesNotExist:
        logger.debug(f"Layer id {instance_id} does not exist yet!")
        raise

    lock_id = f'{self.request.id}'
    with AcquireLock(lock_id) as lock:
        if lock.acquire() is True:
            # Don't run this signal if is a Layer from a remote service
            if getattr(instance, "remote_service", None) is not None or instance.storeType == "remoteStore":
                # Creating Layer Thumbnail by sending a signal
                geoserver_post_save_complete.send(
                    sender=instance.__class__, instance=instance, update_fields=['thumbnail_url'])
                return

            # Don't run this signal handler if it is a tile layer or a remote store (Service)
            #    Currently only gpkg files containing tiles will have this type & will be served via MapProxy.
            if hasattr(instance, 'storeType') and getattr(instance, 'storeType') in ['tileStore', 'remoteStore']:
                # Creating Layer Thumbnail by sending a signal
                geoserver_post_save_complete.send(
                    sender=instance.__class__, instance=instance, update_fields=['thumbnail_url'])
                return instance

            if isinstance(instance, ResourceBase):
                if hasattr(instance, 'layer'):
                    instance = instance.layer
                else:
                    return

            geonode_upload_sessions = UploadSession.objects.filter(resource=instance)
            geonode_upload_sessions.update(processed=False)
            instance.set_dirty_state()

            gs_resource = None
            values = None
            _tries = 0
            _max_tries = getattr(ogc_server_settings, "MAX_RETRIES", 2)

            # If the store in None then it's a new instance from an upload,
            # only in this case run the geoserver_upload method
            if not instance.store or getattr(instance, 'overwrite', False):
                base_file, info = instance.get_base_file()

                # There is no need to process it if there is no file.
                if base_file is None:
                    return
                gs_name, workspace, values, gs_resource = geoserver_upload(
                    instance,
                    base_file.file.path,
                    instance.owner,
                    instance.name,
                    overwrite=True,
                    title=instance.title,
                    abstract=instance.abstract,
                    charset=instance.charset
                )

            values, gs_resource = fetch_gs_resource(instance, values, _tries)
            while not gs_resource and _tries < _max_tries:
                values, gs_resource = fetch_gs_resource(instance, values, _tries)
                _tries += 1

            # Get metadata links
            metadata_links = []
            for link in instance.link_set.metadata():
                metadata_links.append((link.mime, link.name, link.url))

            if gs_resource:
                logger.debug(f"Found geoserver resource for this layer: {instance.name}")
                gs_resource.metadata_links = metadata_links
                instance.gs_resource = gs_resource

                # Update Attribution link
                if instance.poc:
                    # gsconfig now utilizes an attribution dictionary
                    gs_resource.attribution = {
                        'title': str(instance.poc),
                        'width': None,
                        'height': None,
                        'href': None,
                        'url': None,
                        'type': None}
                    profile = get_user_model().objects.get(username=instance.poc.username)
                    site_url = settings.SITEURL.rstrip('/') if settings.SITEURL.startswith('http') else settings.SITEURL
                    gs_resource.attribution_link = site_url + profile.get_absolute_url()

                # Iterate over values from geoserver.
                for key in ['alternate', 'store', 'storeType']:
                    # attr_name = key if 'typename' not in key else 'alternate'
                    # print attr_name
                    setattr(instance, key, values[key])

                try:
                    if settings.RESOURCE_PUBLISHING:
                        if instance.is_published != gs_resource.advertised:
                            gs_resource.advertised = 'true'

                    if any(instance.keyword_list()):
                        keywords = gs_resource.keywords + instance.keyword_list()
                        gs_resource.keywords = list(set(keywords))

                    # gs_resource should only be called if
                    # ogc_server_settings.BACKEND_WRITE_ENABLED == True
                    if getattr(ogc_server_settings, "BACKEND_WRITE_ENABLED", True):
                        gs_catalog.save(gs_resource)
                except Exception as e:
                    msg = (f'Error while trying to save resource named {gs_resource} in GeoServer, '
                           f'try to use: "{e}"')
                    e.args = (msg,)
                    logger.exception(e)

                # store the resource to avoid another geoserver call in the post_save
                """Get information from geoserver.
                The attributes retrieved include:
                * Bounding Box
                * SRID
                """
                try:
                    # This is usually done in Layer.pre_save, however if the hooks
                    # are bypassed by custom create/updates we need to ensure the
                    # bbox is calculated properly.
                    srid = gs_resource.projection
                    bbox = gs_resource.native_bbox
                    instance.set_bbox_polygon([bbox[0], bbox[2], bbox[1], bbox[3]], srid)
                except Exception as e:
                    logger.exception(e)
                    srid = instance.srid
                    bbox = instance.bbox

                if instance.srid:
                    instance.srid_url = f"http://www.spatialreference.org/ref/{instance.srid.replace(':', '/').lower()}/"
                elif instance.bbox_polygon is not None:
                    # Guessing 'EPSG:4326' by default
                    instance.srid = 'EPSG:4326'
                else:
                    raise GeoNodeException(_("Invalid Projection. Layer is missing CRS!"))

                to_update = {
                    'title': instance.title or instance.name,
                    'abstract': instance.abstract or "",
                    'alternate': instance.alternate
                }

                if is_monochromatic_image(instance.thumbnail_url):
                    to_update['thumbnail_url'] = staticfiles.static(settings.MISSING_THUMBNAIL)

                # Save all the modified information in the instance without triggering signals.
                try:
                    with transaction.atomic():
                        ResourceBase.objects.filter(
                            id=instance.resourcebase_ptr.id).update(
                            **to_update)

                        # to_update['name'] = instance.name,
                        to_update['workspace'] = gs_resource.store.workspace.name
                        to_update['store'] = gs_resource.store.name
                        to_update['storeType'] = instance.storeType
                        to_update['typename'] = instance.alternate

                        Layer.objects.filter(id=instance.id).update(**to_update)

                        # Dealing with the BBOX: this is a trick to let GeoDjango storing original coordinates
                        instance.set_bbox_polygon([bbox[0], bbox[2], bbox[1], bbox[3]], 'EPSG:4326')
                        Layer.objects.filter(id=instance.id).update(
                            bbox_polygon=instance.bbox_polygon, srid=srid)

                        # Refresh from DB
                        instance.refresh_from_db()
                except Exception as e:
                    logger.exception(e)

                try:
                    with transaction.atomic():
                        match = re.match(r'^(EPSG:)?(?P<srid>\d{4,6})$', str(srid))
                        instance.bbox_polygon.srid = int(match.group('srid')) if match else 4326
                        Layer.objects.filter(id=instance.id).update(
                            ll_bbox_polygon=instance.bbox_polygon, srid=srid)

                        # Refresh from DB
                        instance.refresh_from_db()
                except Exception as e:
                    logger.warning(e)
                    try:
                        with transaction.atomic():
                            instance.bbox_polygon.srid = 4326
                            Layer.objects.filter(id=instance.id).update(
                                ll_bbox_polygon=instance.bbox_polygon, srid=srid)

                            # Refresh from DB
                            instance.refresh_from_db()
                    except Exception as e:
                        logger.warning(e)

                # Refreshing CSW records
                logger.debug(f"... Updating the Catalogue entries for Layer {instance.title}")
                try:
                    catalogue_post_save(instance=instance, sender=instance.__class__)
                except Exception as e:
                    logger.exception(e)

                # Refreshing layer links
                logger.debug(f"... Creating Default Resource Links for Layer {instance.title}")
                try:
                    set_resource_default_links(instance, instance, prune=True)
                except Exception as e:
                    logger.exception(e)

                # Save layer attributes
                logger.debug(f"... Refresh GeoServer attributes list for Layer {instance.title}")
                try:
                    set_attributes_from_geoserver(instance)
                except Exception as e:
                    logger.exception(e)

                # Save layer styles
                logger.debug(f"... Refresh Legend links for Layer {instance.title}")
                try:
                    set_styles(instance, gs_catalog)
                except Exception as e:
                    logger.exception(e)

                # Invalidate GeoWebCache for the updated resource
                try:
                    _stylefilterparams_geowebcache_layer(instance.alternate)
                    _invalidate_geowebcache_layer(instance.alternate)
                except Exception:
                    pass

                # Creating Layer Thumbnail by sending a signal
                geoserver_post_save_complete.send(
                    sender=instance.__class__, instance=instance, update_fields=['thumbnail_url'])
            try:
                geonode_upload_sessions = UploadSession.objects.filter(resource=instance)
                geonode_upload_sessions.update(processed=True)
            except Exception as e:
                logger.exception(e)
            finally:
                instance.clear_dirty_state()

            # Updating HAYSTACK Indexes if needed
            if settings.HAYSTACK_SEARCH:
                call_command('update_index')

@app.task(
    bind=True,
    base=FaultTolerantTask,
    name='geonode.geoserver.tasks.geoserver_create_thumbnail',
    queue='geoserver.events',
    expires=30,
    acks_late=False,
    autoretry_for=(Exception, ),
    retry_kwargs={'max_retries': 3, 'countdown': 10},
    retry_backoff=True,
    retry_backoff_max=700,
    retry_jitter=True)
def geoserver_create_thumbnail(self, instance_id, overwrite=True, check_bbox=True):
    """
    Runs create_gs_thumbnail.
    """
    instance = None
    try:
        instance = ResourceBase.objects.get(id=instance_id).get_real_instance()
    except Exception:
        logger.error(f"Resource id {instance_id} does not exist yet!")
        raise

    lock_id = f'{self.request.id}'
    with AcquireLock(lock_id) as lock:
        if lock.acquire() is True:
            try:
                create_gs_thumbnail(instance, overwrite=overwrite, check_bbox=check_bbox)
                logger.debug(f"... Created Thumbnail for Layer {instance.title}")
            except Exception as e:
                geoserver_create_thumbnail.retry(exc=e)

@app.task(
    bind=True,
    base=FaultTolerantTask,
    name='geonode.geoserver.tasks.geoserver_cascading_delete',
    queue='cleanup',
    expires=600,
    acks_late=False,
    autoretry_for=(Exception, ),
    retry_kwargs={'max_retries': 3, 'countdown': 10},
    retry_backoff=True,
    retry_backoff_max=700,
    retry_jitter=True)
def geoserver_cascading_delete(self, *args, **kwargs):
    """
    Runs cascading_delete.
    """
    lock_id = f'{self.request.id}'
    with AcquireLock(lock_id) as lock:
        if lock.acquire() is True:
            return cascading_delete(*args, **kwargs)


from pydantic import Field, validator
from ayon_server.settings import (
    BaseSettingsModel,
    ensure_unique_names,
)

from .imageio import TVPaintImageIOModel
from .workfile_builder import WorkfileBuilderPlugin
from .create_plugins import CreatePluginsModel, DEFAULT_CREATE_SETTINGS
from .publish_plugins import (
    PublishPluginsModel,
    LoadPluginsModel,
    DEFAULT_PUBLISH_SETTINGS,
)

class PublishGUIFilterItemModel(BaseSettingsModel):
    _layout = "compact"
    name: str = Field(title="Name")
    value: bool = Field(True, title="Active")

class PublishGUIFiltersModel(BaseSettingsModel):
    _layout = "compact"
    name: str = Field(title="Name")
    value: list[PublishGUIFilterItemModel] = Field(default_factory=list)

    @validator("value")
    def validate_unique_outputs(cls, value):
        ensure_unique_names(value)
        return value

class TvpaintSettings(BaseSettingsModel):
    imageio: TVPaintImageIOModel = Field(
        default_factory=TVPaintImageIOModel,
        title="Color Management (ImageIO)"
    )
    stop_timer_on_application_exit: bool = Field(
        title="Stop timer on application exit")
    create: CreatePluginsModel = Field(
        default_factory=CreatePluginsModel,
        title="Create plugins"
    )
    publish: PublishPluginsModel = Field(
        default_factory=PublishPluginsModel,
        title="Publish plugins")
    load: LoadPluginsModel = Field(
        default_factory=LoadPluginsModel,
        title="Load plugins")
    workfile_builder: WorkfileBuilderPlugin = Field(
        default_factory=WorkfileBuilderPlugin,
        title="Workfile Builder"
    )
    filters: list[PublishGUIFiltersModel] = Field(
        default_factory=list,
        title="Publish GUI Filters")

    @validator("filters")
    def validate_unique_outputs(cls, value):
        ensure_unique_names(value)
        return value

DEFAULT_VALUES = {
    "stop_timer_on_application_exit": False,
    "create": DEFAULT_CREATE_SETTINGS,
    "publish": DEFAULT_PUBLISH_SETTINGS,
    "load": {
        "LoadImage": {
            "defaults": {
                "stretch": True,
                "timestretch": True,
                "preload": True
            }
        },
        "ImportImage": {
            "defaults": {
                "stretch": True,
                "timestretch": True,
                "preload": True
            }
        }
    },
    "workfile_builder": {
        "create_first_version": False,
        "custom_templates": []
    },
    "filters": []
}


from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import QApplication
import vtk
from vtk.qt4.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
import sys
from PyQt4.Qt import QGridLayout

class Ui_MainWindow(QtGui.QMainWindow):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1280, 800)
        self.centralWidget = QtGui.QWidget(MainWindow)

        self.gridlayout = QtGui.QGridLayout(self.centralWidget)
        self.vtkWidget = QVTKRenderWindowInteractor(self.centralWidget)
        self.vtkWidget2 = QVTKRenderWindowInteractor(self.centralWidget)
        #self.hbox = QtGui.QHBoxLayout(self.centralWidget)

        label1= QtGui.QFrame(self)
        label1.setFrameShape(QtGui.QFrame.StyledPanel)

        label2= QtGui.QFrame(self)
        label2.setFrameShape(QtGui.QFrame.StyledPanel)

        label3= QtGui.QFrame(self)
        label3.setFrameShape(QtGui.QFrame.StyledPanel)

        #toolbar
        exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.triggered.connect(QtGui.qApp.quit)

        saveAction = QtGui.QAction(QtGui.QIcon('4.png'),'Save',self)
        saveAction.setShortcut('Ctrl+S')
        saveAction.triggered.connect(QtGui.qApp.quit)

        cutAction = QtGui.QAction(QtGui.QIcon('5.png'),'Cut',self)
        cutAction.setShortcut('Ctrl+K')
        cutAction.triggered.connect(QtGui.qApp.quit)

        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(exitAction)
        self.toolbar.addAction(saveAction)
        self.toolbar.addAction(cutAction)

        #############image reading
        label = QtGui.QLabel()
        pixmap = QtGui.QPixmap('x.JPG')
        scaledpix = pixmap.scaled(430,610)
        label.setPixmap(scaledpix)
        ############## finish img

        self.splitter1 = QtGui.QSplitter(QtCore.Qt.Vertical)
        self.splitter1.addWidget(label3)
        self.splitter1.addWidget(label1)

        label4= QtGui.QFrame(self)
        label4.setFrameShape(QtGui.QFrame.StyledPanel)

        label5 = QtGui.QFrame(self)
        label5.setFrameShape(QtGui.QFrame.StyledPanel)

        splitter2 = QtGui.QSplitter(QtCore.Qt.Horizontal)
        splitter2.addWidget(self.splitter1)
        splitter2.addWidget(label4)

        splitter3 = QtGui.QSplitter(QtCore.Qt.Vertical)
        splitter3.addWidget(splitter2)
        splitter3.addWidget(label5)

        self.gridlayout.addWidget(label,1,0)
        self.gridlayout.addWidget(self.vtkWidget, 1, 1)
        self.gridlayout.addWidget( self.toolbar , 0, 0, 1, 2)
        self.gridlayout.addWidget( splitter3 , 1, 3 )

        MainWindow.setCentralWidget(self.centralWidget)

class Example(QtGui.QMainWindow):

    def __init__(self):
        super(Example, self).__init__()

        self.initUI()

    def initUI(self):

        exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.triggered.connect(QtGui.qApp.quit)

        saveAction = QtGui.QAction(QtGui.QIcon('4.png'),'Save',self)
        saveAction.setShortcut('Ctrl+S')
        saveAction.triggered.connect(QtGui.qApp.quit)

        cutAction = QtGui.QAction(QtGui.QIcon('5.png'),'Cut',self)
        cutAction.setShortcut('Ctrl+K')
        cutAction.triggered.connect(QtGui.qApp.quit)

        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(exitAction)
        self.toolbar.addAction(saveAction)
        self.toolbar.addAction(cutAction)

        self.setGeometry(300, 300, 1280, 720)
        self.setWindowTitle('Toolbar')
        self.show()

class ViewShape(QtGui.QMainWindow):

    def __init__(self, parent = None):
        QtGui.QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.ren = vtk.vtkRenderer()
        self.ui.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
        self.iren = self.ui.vtkWidget.GetRenderWindow().GetInteractor()

        filename = "organ.stl"
        # Create a reader
        reader = vtk.vtkSTLReader()
        reader.SetFileName(filename)

        # Create a mapper
        mapper = vtk.vtkPolyDataMapper()
        if vtk.VTK_MAJOR_VERSION <= 5:
            mapper.SetInput(reader.GetOutput())
        else:
            mapper.SetInputConnection(reader.GetOutputPort())

        # Create an actor
        actor = vtk.vtkActor()
        actor.SetMapper(mapper)
        actor.RotateX(-90)

        self.ren.AddActor(actor)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    #eg = Example()
    window = ViewShape()
    window.show()
    window.iren.Initialize() # Need this line to actually show the render inside Qt
    sys.exit(app.exec_())


'''
file name   : directoryTree_toJson.py

description : This file converts directory structure into its
            corresponding json format.

input       :
        path of directory,
        variable.py file contains details of files and folders that
        are to be excluded while creating the json format.

output      :
        directoryTree.json: json file format for a given directory.

author      : Aryan Mehta <aryancountry@gmail.com>

'''
import os
import json
#from variable import *

JSON_FILE_PATH='directoryTree.json'
DIRECTORY_PATH='.\RootFolder'

def path_to_dict(path):
    '''
    function name: path_to_dict
    description  : A recursive function to generate a dynamic dictionary
                  which holds folder structure.
    input        : path of directory
    output       : returns dictionary (d)
    author       : Aryan Mehta <aryancountry@gmail.com>
    '''
    name=os.path.basename(path)
    d={}
    if name in excludeFolders or name in excludeFiles:
        return None
    d = {'name': name}
    if os.path.isdir(path):
        d['type'] = "directory"
        d['path']=path
        d['features']=[]
        d['children'] = [path_to_dict(os.path.join(path,x)) for x in os.listdir(path)]
    else:
        d['type'] = "file"
        d['path']=path
        filename=os.path.basename(path)
        if filename.endswith('.c') or filename.endswith('.cpp'):
            d['features']=[]
    print(d)
    return d

with open(JSON_FILE_PATH,'w') as directoryTree:
    json.dump(path_to_dict(DIRECTORY_PATH),directoryTree,indent=4)


import os
import subprocess
from funcs import funcs
path = ""

# Get system path
ost = ''
while ost != "\\"or ost != "/":
  ost = input("\nIs this being run on windows, or linux? ")
  if ost.lower() == "windows":
    ost = "\\"
    break
  elif ost.lower() == "linux":
    ost = "/"
    break
  else:
    print("Input failed.")

inp = os.path.dirname(os.path.abspath(__file__))

os.system("clear")
inp = inp + ost
inp = inp[0].upper() + inp[1:]
fncs = funcs(inp+"settings.json") #setup funcs

fncs.dprint(inp)
path = inp

def imports():
  try:
    #x = subprocess.check_output("python3 --version",shell=True)
    x = subprocess.check_output("python3.10 --version",shell=True)
  except subprocess.CalledProcessError as err:
    #fncs.log(err)
    pass
  try:
    x = x.split(" ")
    y = x[1].split(".")
    if int(y[1]) >= 6:
      os.system("python3 -m pip install -r requirements.txt")
  except Exception as err:
    str(err)
    os.system("python -m pip install -r requirements.txt")
  finally:
    with open(f"{path}log.txt","w") as fp:
      fp.write(f"[{fncs.ptime()}] Finished Install Packages and created setup_done.yay file.\n")

def replace_line(file_name, line_num, text): # Yes i know this is a dumb way to solve it but it works
    lines = open(file_name, 'r').readlines()
    lines[line_num] = text
    out = open(file_name, 'w')
    out.writelines(lines)
    out.close()

def fix_files():
  os.system("clear")

  global inp
  my_file = os.path.exists(f"{inp}setup_done.yay")
  if my_file:
    print("Packages Already Imported, Exiting!")
  else:
    imports()
    with open("setup_done.yay","w") as file:
      pass
    os.system("clear")
  print("Updating file paths...")
  inp = r"{}".format(inp)
  replace_line(f"{inp}.env",0,r'{}PATH={}settings.json{}'.format("\r",inp,"\n"))
  fncs.dprint(inp)
  replace_line(f"{inp}settings.json",7,r'  "home-dir": "{}",{}'.format(inp,"\n"))

def settings():
  with open(f"{path}settings.json","r") as fp:
    settings = fp.read()
    settings = settings.split("\n")
    arr = []
    c = 0
    for i in settings:
      if i != "" and i != "{" and i != "}":
        i = i.split('":')
        i[0] = i[0] + '"'
        inp = input(f"{i[0]}:")
        if inp != "":
          i[1] = inp
          if c == 9 or c == 11 or c == 13:
            if i[1].lower().startswith("f"):
              i[1] = "false,"
            elif i[1].lower().startswith("t"):
              i[1] = "true,"
          elif c == 14:
            if i[1].lower().startswith("f"):
              i[1] = "false"
            elif i[1].lower().startswith("t"):
              i[1] = "true"
          else:
            i[1] = f'"{i[1]}"'
        i = ":".join(i)
      print(i)
      arr.append(i)
      c += 1

  text = "\n".join(arr)
  if input(f"{text}\n\nSave? (Y/N)").lower() == "y":
    with open(f"{path}settings.json","w") as fp:
      fp.write("\r"+text+"\n")

if __name__ == "__main__":
  fix_files()
  inp2 = input(f"\nSetup is Done at {fncs.ptime()}!\nPlease change the settings.json file to suit your needs or type 'y' to start editing it now.")
  if inp2 != "":
    settings()

  with open(f"{path}log.txt","w") as fp:
    fp.write(f"[{fncs.ptime()}] Finished Setup with no errors.\n")
  os.system("clear")


#!/usr/bin/env python
# coding: utf8

import argparse
import logging
import os
import random
import sys

from Bio import SeqIO

VERSION = "0.5.0"

# Init.
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    datefmt="%d-%m-%Y %H:%M",
)
random.seed(0)

description = "Compute test file for HmnTrimmer"
parser = argparse.ArgumentParser(description=description)
parser.add_argument("--input-forward", required=True, help="Input forward")
parser.add_argument("--input-reverse", required=True, help="Input forward")
parser.add_argument("--indices", nargs="+", help="Indices to keep, 1-based")
# Output.
parser.add_argument("--outdir", required=True, help="Outdir")
parser.add_argument("--basename", required=True, help="Basename")

if len(sys.argv) < 1:
    print("No args provided")
    sys.exit()
else:
    args = parser.parse_args()

input_forward = args.input_forward
input_reverse = args.input_reverse
indices = args.indices

basename = args.basename
outdir = args.outdir

recs_f = [x for x in SeqIO.parse(input_forward, "fastq")]
recs_r = [x for x in SeqIO.parse(input_reverse, "fastq")]

recs_f_new, recs_r_new = [], []
for i in indices:
    i = int(i) - 1
    recs_f_new.append(recs_f[i])
    recs_r_new.append(recs_r[i])

recs_f = recs_f_new
recs_r = recs_r_new

recs_inter = []
for rec_f, rec_r in zip(recs_f, recs_r):
    rec_f.id = rec_f.id + "\\1"
    rec_f.description = ""
    rec_f.name = ""
    recs_inter.append(rec_f)
    rec_r.id = rec_r.id + "\\2"
    rec_r.description = ""
    rec_r.name = ""
    recs_inter.append(rec_r)

with open(os.path.join(outdir, basename + ".R1.fastq"), "w") as fod:
    SeqIO.write(recs_f, fod, "fastq")
with open(os.path.join(outdir, basename + ".R2.fastq"), "w") as fod:
    SeqIO.write(recs_r, fod, "fastq")
with open(os.path.join(outdir, basename + ".Interleaved.fastq"), "w") as fod:
    SeqIO.write(recs_inter, fod, "fastq")


from flask import Blueprint, jsonify, request, session
from flask_login import login_required, current_user
from app.models import db, User, WatchList, friends

friend_routes = Blueprint('friends', __name__)

def validation_errors_to_error_messages(validation_errors):
    """
    Simple function that turns the WTForms validation errors into a simple list
    """
    errorMessages = []
    for field in validation_errors:
        for error in validation_errors[field]:
            errorMessages.append(f'{field} : {error}')
    return errorMessages

# fetches all friends of a user to place in state
@friend_routes.route('/', methods=['GET'])
@login_required
def getAllFriends():
    userId = current_user.id
    all_friends = db.session.query(friends).all()
    friend_obj = {}
    for friend in all_friends:
        if (friend[0] == userId):
            friend_obj[friend[1]] = friend[0]
    return {'friends': friend_obj}

# adds friend on both sides of relationship
@friend_routes.route('/', methods=['POST'])
@login_required
def postFriend():
    userId = current_user.id
    data = request.get_json()["friend"]
    friendId = data["id"]
    user = User.query.filter(User.id == userId).first()
    friend = User.query.filter(User.id == friendId).first()
    user.friends.append(friend)
    friend.friends.append(user)
    db.session.commit()
    return 'posted'

# deletes friend of a particular user
@friend_routes.route('/<int:friendId>', methods=["DELETE"])
@login_required
def deleteFriend(friendId):
    userId = current_user.id
    user = User.query.filter(User.id == userId).first()
    friend = User.query.filter(User.id == friendId).first()
    user.friends.remove(friend)
    friend.friends.remove(user)
    db.session.commit()
    return 'deleted'