# Author: yf-yang <directoryyf@gmail.com>

import heapq
from collections import defaultdict
from boltons.dictutils import OrderedMultiDict
from ..common import node, multicast_group
import pprint

import logging
logger = logging.getLogger(__name__)

# infinity
INFTY = float('inf')

class GlobalPolicySimulation(object):
    def __init__(self, adj, utraffic, mtraffic, dst_src_combination):
        """ Initiliza the graph with adjacent matrix and total node count.

        Parameters
        ----------
        adj : dict
            Adjacent matrix of a directed graph.
            It is represented as a mapping, where the value is a list of tuples
            that represent different links and the key is the node that all the
            links in the list go toward.
            The structure is like:
            {
                to: [
                    (
                        hop,
                        from, to,
                        speed,
                        from_port, from_port_bit, to_port, to_port_bit,
                        linkID
                    )
                ]
            }
            It could also be considered as:
            {
                from: [
                    (
                        hop,
                        to, from,
                        speed,
                        to_port, to_port_bit, from_port, from_port_bit,
                        linkID
                    )
                ]
            }
        utraffic : dict
            Clustered traffic dictionary for UNICAST.
            All the traffic clustered first by destination, then source.
            The structure is like:
            {
                destination: {
                    source: {
                        {
                            "traffic": [
                                {
                                    "ID": trafficID,
                                    "bandwidth": bandwidth,
                                }
                            ],
                            "bandwidth": total bandwidth,
                        }
                    }
                }
            }
            We cluster them because there is no need to compute the same route
            for different message that has the same destination and source.
            Destination is at higher level because ARL table is a mapping from
            destination to port, so in order to avoid loop, we actually generate
            trees for each destination, where each destination is the root and
            leaves are different sources.
        mtraffic: dict
            Clustered traffic dictionary for MULTICAST.
            The structure is similar as utraffic, the only difference is that
            destination is a frozenset of node IDs instead of a single node ID.
        dst_src_combination: dict
            A mapping from destination to all the sources that send traffic to
            it, including both UNICAST and MULTICAST.
        """
        self.adj = adj
        self.utraffic = utraffic
        self.mtraffic = mtraffic
        self.dst_src_combination = dst_src_combination
        self.switches = {
            nodeID for nodeID, n in node.items()
                if n['type'] == 'SWITCH'
        }
        self.gen_connected_components()
        self.primary_routes = self.gen_routes()

    def gen_connected_components(self):
        """
        Detect all the cut edges / cut endpoints (Tarjan Algorithm).
        Split the graph into different connected components.
        """

        # ----- temporary variables
        # temp: endpoints haven't been traversed
        self._tmp_untraversed = self.switches.copy()
        # temp: order of a node in the DFS
        self._tmp_order = defaultdict(lambda: INFTY)
        # temp: the lowest depth a node is connected to
        self._tmp_low = defaultdict(lambda: INFTY)
        # temp: parent of a node in the DFS
        self._tmp_parent = defaultdict(lambda: -1)

        # Initialize every ETH node with a distinct number
        node_connected_components = OrderedMultiDict(
            (nodeID, i) for i, (nodeID, n) in enumerate(node.items())
            if n['protocol'] == 'ETH')

        link_connected_components = OrderedMultiDict()

        # Initialize every ETH non-switch as type 2
        entity_type = OrderedMultiDict(
            (nodeID, 't2') for nodeID, n in node.items()
            if n['type'] != 'SWITCH' and n['protocol'] == 'ETH')

        while self._tmp_untraversed:
            # temp: all links of a connected components
            self._tmp_links = set()
            # temp: all endpoints of a connected components
            self._tmp_nodes = set()

            # ----- special links/endpoints
            ############################# Explanation ##########################
            """
            We categorize all the links and endpoints by 2 dimensions , so there
            are 2 * 2 = 4 categories in total. Suppose a link/endpoint is down,
            then there would be 4 possible results:

                                          New Routes are needed

                                            No      |       Yes
                                                    |
                                    No      ---     |     Type I
            Some traffic will not                   |
            be reachable            ----------------+----------------
                                                    |
                                    Yes   Type II   |    Type III
                                                    |

            I'll use an example to explain these categories, and why we care
            about these scenarios:
            """

            ###################################################################
            #                                                                 #
            #                             A                                   #
            #                             |                                   #
            #                             |                                   #
            #                             |                                   #
            #                             1 ----- 2                           #
            #                              \     /                            #
            #                               \   /                             #
            #                                \ /                              #
            #                                 3                               #
            #                                 |                               #
            #                                 |                               #
            #                                 4                               #
            #                                / \                              #
            #                               /   \                             #
            #                              /     \                            #
            #                     C ----- 5       6 ------┐                   #
            #                             |       |       |                   #
            #                             |       |       B                   #
            #   A,B,C - DEVICES           |       |       |                   #
            #   1 ~ 8 - SWITCHES          7 ----- 8 ------┘                   #
            #                                                                 #
            ###################################################################

            """
            Type I: When the link/endpoint is down, new routes are needed, all
            traffic are still reachable
            - Endpoints: 2, 6, 7, 8
            - Links: 1-2, 1-3, 2-3, 4-5, 5-7, 7-8, 8-6, 6-4, 8-B, 6-B

            When one of these links/endpoints is down, the network will function
            as good, but traffic should take failover routes. Net Configurator
            will generate alternative routes for all of these scenarios.

            In the algorithm, such links/endpoints are not cut edges / cut
            vertices.

            ------

            Type II: When the link/endpoint is down, every traffic will still
            take the primary route, while some of them will not be reachable.
            - Endpoints: A, B, C
            - Links: 3-4, 1-A, 5-C

            When one of these links/endpoints is down, some of the traffic will
            be dropped somewhere, but if a traffic is still reachable, then they
            will still take the primary route, so we will not generate new routes
            for these scenarios to reduce the ARL table size.

            In the algorithm, such links are cut edges; such endpoints are cut
            vertices, but not all cut vertices are type II endpoints. To be more
            precise, if an endpoint E connects to exactly one another endpoint,
            or all the other endpoints that E connects to are not connected
            w.r.t. any other endpoints except for E itself, then E is a type II
            endpoint.

            Note that in this example, if endpoint 5 is down, traffic between A
            and B will not need a failover route, but 5 is not categorized as a
            type II endpoint, since 4 and 7 are connected w.r.t. some other
            links. Actually, we are not able to categorize 5 as type II to save
            memory, since connected components detection and link/node
            categorization step is done before routes are calculated. Due to
            that reason, 1 and 5 are not type II endpoints.

            I'm not sure if type II should exist. Although we can save memory
            in such scenarios, unreachable traffic will still take up the
            bandwidth and resources of links/switches until they are dropped at
            the broken link/switch unless applications on top of IP Layer stops
            to send these traffic. Maybe we will decide to use more memory, but
            save runtime resources. There is a tradeoff.

            ------

            Type III: When the endpoint is down, some traffic will take failover
            routes while some may not and some traffic will be unreachable.
            - Endpoints: 1, 3, 4, 5

            When one of these endpoints is down, some traffic will be dropped
            and even some traffic are still reachable, they may need a failover
            route. In this scenario, we need to generate failover routes.

            In the algorithm, if an endpoint E connects to some endpoints that
            connect to each other w.r.t. links other than E, then E is a
            type III endpoint.

            Note that no links are categorized as type III, since if a link is
            down, either all the devices are still connected in the same
            connected component, or some are disconnected but we could use the
            primary route and drop unreachable traffics somewhere.

            -----

            It will never happen that when a link/endpoint is down, no traffics
            are dropped and no failover route is needed. In this toy example,
            switch 2 is such an endpoint, but we still categorize it as type I
            because connected components detection and link/node categorization
            step is done before routes are calculated.

            When we are calculating the shortest route tree, all the switches and
            links that not appears in the tree will be recorded. They will be a
            special part of type I that no failover routes will be generated for
            these links/switches. Endpoint 2, 7, 8 and links 1-2, 2-3, 5-7, 6-8,
            7-8, 8-B are such links/switches in this example.

            """

            # temp: non-cut links
            self._tmp_type1_links = set()
            # temp: cut links
            self._tmp_type2_links = set()

            # temp: non-cut endpoints
            self._tmp_type1_nodes = set()
            # temp: type 2 cut endpoints
            self._tmp_type2_nodes = set()
            # temp: type 3 cut endpoints
            self._tmp_type3_nodes = set()

            # order counter
            self.__order = 0

            # randomly choose an endpoint as DFS root
            DFS_ROOT = self._tmp_untraversed.pop()

            self.bridge_update(DFS_ROOT)

            self._tmp_type1_links = self._tmp_links - self._tmp_type2_links

            ccID = node_connected_components[DFS_ROOT]

            node_connected_components.update({
                nodeID: ccID for nodeID in self._tmp_nodes
            })

            link_connected_components.update({
                linkID: ccID for linkID in self._tmp_links
            })

            entity_type.update({
                nodeID: 't1' for nodeID in self._tmp_type1_nodes
            })

            entity_type.update({
                nodeID: 't2' for nodeID in self._tmp_type2_nodes
            })

            entity_type.update({
                nodeID: 't3' for nodeID in self._tmp_type3_nodes
            })

            entity_type.update({
                linkID: 't1' for linkID in self._tmp_type1_links
            })

            entity_type.update({
                linkID: 't2' for linkID in self._tmp_type2_links
            })

            self._tmp_untraversed -= self._tmp_nodes

        self.node_connected_components = node_connected_components
        self.link_connected_components = link_connected_components
        self.entity_type = entity_type

    def bridge_update(self, nodeID):
        """Update a node status
        Parameters
        ----------
        nodeID : str
            ID of the node being traversed.
        """

        self._tmp_nodes.add(nodeID)

        self._tmp_order[nodeID] = self.__order
        self._tmp_low[nodeID] = self.__order

        child_orders = []

        # traverse every child
        for _, child, _, _, _, _, _, _, linkID in self.adj[nodeID]:
            self._tmp_links.add(linkID)

            # not traversed
            if self._tmp_order[child] == INFTY:
                self.__order += 1
                self._tmp_parent[child] = nodeID
                self.bridge_update(child)
                self._tmp_low[nodeID] = min(
                    self._tmp_low[child], self._tmp_low[nodeID]
                )

                child_orders.append(
                    cmp(self._tmp_low[child], self._tmp_order[nodeID])
                )

            elif child != self._tmp_parent[nodeID]:
                self._tmp_low[nodeID] = min(
                    self._tmp_low[child], self._tmp_low[nodeID]
                )

            if self._tmp_low[child] > self._tmp_order[nodeID]:
                self._tmp_type2_links.add(linkID)

        if self._tmp_parent[nodeID] != -1:
            # endpoint is not DFS_ROOT
            if all(order == -1 for order in child_orders):
                self._tmp_type1_nodes.add(nodeID)
            elif all(order == 1 for order in child_orders):
                self._tmp_type2_nodes.add(nodeID)
            else:
                self._tmp_type3_nodes.add(nodeID)
        else:
            # endpoint is DFS_ROOT
            if len(child_orders) == 1 and child_orders[0] == 0:
                self._tmp_type1_nodes.add(nodeID)
            elif all(order == 1 for order in child_orders):
                self._tmp_type2_nodes.add(nodeID)
            else:
                self._tmp_type3_nodes.add(nodeID)

    def gen_routes(self, disabled_link=None, disabled_node=None):
        """Get shortest route trees start from all destinations.

        Parameters:
        disabled_link : str
            Link ID of disabled link.

        disabled_node : int
            Node ID of disabled node.

        Returns
        -------
        routes : dict
            Mapping from target to multiple routes.
        """

        if disabled_link is not None and disabled_node is not None:
            raise Exception('TBD')

        if disabled_link is not None:
            if self.entity_type[disabled_link] == 't2':
                return self.primary_routes

        if disabled_node is not None:
            if self.entity_type[disabled_node] == 't2':
                return self.primary_routes

        self.shortest_route_trees = {
            dst: self.get_shortest_route_tree(
                dst,
                disabled_link = disabled_link,
                disabled_node = disabled_node
            ) for dst in self.dst_src_combination
        }

        routes = {
            udst: self.get_unicast_global_routes(
                udst
            ) for udst in self.utraffic
        }
        routes.update({
            mdst: self.get_multicast_global_routes(
                mdst
            ) for mdst in self.mtraffic
        })
        return routes

    def get_shortest_route_tree(self,
            dst, disabled_link=None, disabled_node=None):
        """Get the shortest route tree with Dijkstra Algorithm.

        Parameters
        ----------
        dst : str
            ID of destination node, which is the root of the tree.

        disabled_link : str
            Link ID of disabled link.

        disabled_node : str
            Node ID of disabled node.

        Returns
        -------
        tree : dict
            Mapping from each node to its parent and actual link ID.
        """
        tree = {}
        tree_nodes = set()

        ccID = self.node_connected_components[dst]
        cc_nodes = set(self.node_connected_components.inverted().getlist(ccID))

        srcs = self.dst_src_combination[dst] & cc_nodes

        pq = PriorityQueue(dst)

        if disabled_node is not None:
            # remove the disabled endpoint from the graph
            adj = {
                to_node: [
                    link_info for link_info in links
                        if link_info[1] != disabled_node
                ] for to_node, links in self.adj.items()
                    if to_node != disabled_node
            }
        else:
            adj = self.adj

        if disabled_link is not None:
            # remove the disabled link from the graph
            adj = {
                to_node: [
                    link_info for link_info in links
                        if link_info[-1] != disabled_link
                ] for to_node, links in self.adj.items()
                    if to_node != disabled_link
            }

        # ------------ Note of the Algorithm ------------
        # We will treat the graph as a directed graph, even though it is
        # actually an undirected graph, because dijkstra is a greedy search
        # algorithm which accidentally achieves global optimum. That way, the
        # other direction will be disposed and will not affect the algorithm.

        # Initialize the priority queue
        for (
                weight,
                from_node, to_node,
                speed,
                from_port, from_port_bit, to_port, to_port_bit,
                linkID
            ) in adj[dst]:

            pq.push(
                (
                    # order of links:
                    #   weight (lower first)
                    #   speed (higher first)
                    #   inbound port (lower first)
                    (weight, -speed, to_port_bit),
                    # source node
                    from_node,
                    # destination node and linkID
                    (to_node, linkID),
                )
            )

        while srcs:
            l = pq.pop()
            if l is None:
                # If some of the destinations are not reachable, then the loop
                # terminates
                break
            (cost, _, _), from_node, path = l
            tree[from_node] = path

            # if get an source
            if from_node in srcs:
                srcs.remove(from_node)

                # add all the parent nodes to the set
                while from_node not in tree_nodes and from_node != dst:
                    tree_nodes.add(from_node)
                    from_node, _ = tree[from_node]

            # now the source becomes the new destination
            # add all links that go to the source to the priority queue
            new_dst = from_node
            for (
                weight,
                from_node, to_node,
                speed,
                from_port, from_port_bit, to_port, to_port_bit,
                linkID
            ) in adj[new_dst]:
                pq.push(
                    (
                        # order of links:
                        #   weight (lower first)
                        #   speed (higher first)
                        #   inbound port (lower first)
                        (weight+cost, -speed, to_port_bit),
                        # source node
                        from_node,
                        # destination node and linkID
                        (to_node, linkID),
                    )
                )

        tree = {
            from_node: path
                for from_node, path in tree.items() if from_node in tree_nodes
        }

        return tree

    def get_unicast_global_routes(self, dst):
        """Generate routes to the given destination from every sources.

        Parameters
        ----------
        dst: str
            ID of unicast destination node, which is the root of the tree.

        Returns
        -------
        tree: dict
            A map from each source to a tuple of route information including:
                - next hop node
                - linkID
        """
        return self.shortest_route_trees[dst]

    def get_multicast_global_routes(self, mgID):
        """Generate routes to the given multicast group from the source

        Parameters
        ----------
        mgID: str
            ID of multicast destination group, which is the root of the tree.

        Returns
        -------
        tree: dict
            A map from each source to a set that contains tuples of:
                - next hop node
                - linkID
        """
        dsts = multicast_group[mgID]['devices']
        tree = defaultdict(set)

        # a multicast group should only have one traffic, so although we don't
        # add any validation before this statement, it should work well
        # if an excepetion occurs here, that means the validation that there is
        # exactly one source for the multicast group is missing
        src, = self.mtraffic[mgID]

        for dst in dsts:
            route = self.shortest_route_trees[dst]

            # source may be in a different connected component
            if src not in route:
                continue

            from_node = src
            while from_node != dst:
                hop_info = route[from_node]
                tree[from_node].add(hop_info)
                from_node, _ = hop_info

        return tree

    def get_overview(self, disabled_link=None, disabled_node=None):
        overview = defaultdict(
            lambda: defaultdict(
                lambda: {'traffic': [], 'bandwidth': 0.0}))

        # if disabled_node is None and disabled_link is None:
        #     routes = self.primary_routes
        # else:
        routes = self.gen_routes(
            disabled_link=disabled_link,
            disabled_node=disabled_node)

        for dst, srcs in self.utraffic.items():
            shortest_route_tree = routes[dst]

            for src, traffic_info in srcs.items():
                # source may be in a different connected component
                if src not in shortest_route_tree:
                    continue
                from_node = src

                while from_node != dst:
                    to_node, linkID = shortest_route_tree[from_node]

                    if to_node == disabled_node or linkID == disabled_link:
                        break

                    l = overview[linkID][from_node, to_node]
                    l['traffic'] += traffic_info['traffic']
                    l['bandwidth'] += traffic_info['bandwidth']

                    from_node = to_node

        for mgID, srcs in self.mtraffic.items():
            shortest_route_tree = routes[mgID]

            # a multicast group should only have one traffic, so although we
            # don't add any validation before this statement, it should work
            # well if an excepetion occurs here, that means the validation that
            # there is exactly one source for the multicast group is missing
            (src, traffic_info), = srcs.items()

            # source may be in a different connected component
            if src not in shortest_route_tree:
                continue

            queue = [src]

            # add every link to the returned information in a BFS manner
            while queue:
                from_node = queue.pop(0)
                for to_node, linkID in shortest_route_tree[from_node]:
                    if to_node == disabled_node or linkID == disabled_link:
                        continue
                    l = overview[linkID][from_node, to_node]
                    l['traffic'] += traffic_info['traffic']
                    l['bandwidth'] += traffic_info['bandwidth']

                    # if to_node is not destination, add it to the queue
                    if to_node in shortest_route_tree:
                        queue.append(to_node)

        return routes, overview

class PriorityQueue(object):
    def __init__(self, root):
        self.__heap = []
        self.traversed = {root} # root is already traversed

    def push(self, item):
        _, from_node = item[:2]
        if from_node not in self.traversed:
            heapq.heappush(self.__heap, item)

    def pop(self):
        while len(self.__heap) > 0:
            item = heapq.heappop(self.__heap)
            _, from_node = item[:2]
            if from_node not in self.traversed:
                self.traversed.add(from_node)
                return item
        return None

def cmp(a, b):
    # https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons
    return (a > b) - (a < b)


import json

from PySide import QtGui, QtCore

from .modes_base import GridViewMouseModeBase, mouse_mode_filtered

class SelectItemsMode(GridViewMouseModeBase):
    def __init__(self, *args, **kargs):
        super().__init__(*args, **kargs)

        self._undo_group_scene = None
        self._drag_start_pos = None

    def mouse_enter(self):
        super().mouse_enter()
        self.setDragMode(QtGui.QGraphicsView.RubberBandDrag)
        self.scene().setSelectionAllowed(True)
        self.scene().set_active(False)

    def mouse_leave(self):
        super().mouse_leave()

        self.setDragMode(QtGui.QGraphicsView.NoDrag)
        self.scene().setSelectionAllowed(False)
        self.scene().set_active(True)

        if self._undo_group_scene is not None:
            self._undo_group_scene.endUndoRedoGroup()
            self._undo_group_scene = None

    def _mask_drag_mode(self, func, mouse_event):
        """
        disables rubber band mode when the left mouse button is not pressed,
        then calls func(mouse_event)
        """
        drag_mode = self.dragMode()
        try:
            if drag_mode is QtGui.QGraphicsView.RubberBandDrag and \
                    not mouse_event.button() & QtCore.Qt.LeftButton:
                # disable rubber band drag
                self.setDragMode(QtGui.QGraphicsView.NoDrag)
            return func(mouse_event)
        finally:
            # restore old drag mode
            self.setDragMode(drag_mode)

    @mouse_mode_filtered
    def mousePressEvent(self, event):
        # call parent with masked drag mode
        self._mask_drag_mode(super().mousePressEvent, event)

        if event.button() == QtCore.Qt.LeftButton:
            self._undo_group_scene = self.scene()
            self._undo_group_scene.beginUndoGroup()

            self._drag_start_pos = event.pos()

    @mouse_mode_filtered
    def mouseMoveEvent(self, event):
        # TODO: merge code with the one in library_view

        is_draged = False

        if event.buttons() & QtCore.Qt.LeftButton and \
                (event.pos() - self._drag_start_pos).manhattanLength() >= \
                QtGui.QApplication.startDragDistance() and False:

            # get selected items & start drag&drop
            gpos = self.mapToScene(self._drag_start_pos)
            # TODO: create method instead of accessing _selection_item
            gpos_items = self.scene().items(gpos)
            sel_items = self.scene().selectedItems()
            if len(sel_items) > 0 and \
                    self.scene()._selection_item in gpos_items:

                drag_pos = self.mapToScene(self._drag_start_pos)
                data = {'drag_pos': drag_pos.toTuple(),
                        'items': [item.metadata() for item in sel_items]}

                mimeData = QtCore.QMimeData()
                mimeData.setData('application/x-components',
                                 json.dumps(data))

                drag = QtGui.QDrag(self)
                drag.setMimeData(mimeData)

                # set items temporary
                for item in sel_items:
                    item.set_temporary(True)

                drop_action = drag.exec_(QtCore.Qt.CopyAction |
                                         QtCore.Qt.MoveAction)

                if drop_action == QtCore.Qt.MoveAction:
                    # delete all items
                    for item in sel_items:
                        self.scene().removeItem(item)
                    is_draged = True
                elif drop_action == QtCore.Qt.CopyAction:
                    # unset temporary
                    for item in sel_items:
                        item.set_temporary(False)
                    is_draged = True

                if is_draged:
                    # TODO: refactor
                    self._undo_group_scene.endUndoGroup()
                    self._undo_group_scene = None
                    self._drag_start_pos = None

        if not is_draged:
            super().mouseMoveEvent(event)

    @mouse_mode_filtered
    def mouseDoubleClickEvent(self, event):
        self.mousePressEvent(event)

    @mouse_mode_filtered
    def mouseReleaseEvent(self, event):
        super().mouseReleaseEvent(event)

        if event.button() == QtCore.Qt.LeftButton and \
                self._undo_group_scene is not None:
            self._undo_group_scene.endUndoGroup()
            self._undo_group_scene = None


"""
Author: Ed Murphy
File: realsense435i.py
Date: April 14 2019
Notes: Donkeycar part for the Intel Realsense depth cameras D435 and D435i.
"""
import argparse
import time
import logging
import sys

import numpy as np
import pyrealsense2 as rs

#
# NOTE: Jetson Nano users should clone the Jetson Hacks project
#       https://github.com/JetsonHacksNano/installLibrealsense
#       and _build librealsense from source_ in order to get the python bindings.
#       ./buildLibrealsense.sh
#

#
# The Realsense D435 will not output images with arbitrarily chosen dimensions.
# The dimensions must be from the allowed list.  I've chosen a reasonable
# resolution that I know is supported by the camera.
# If the part is initialized with different sizes, then opencv
# will be used to resize the image before it is returned.
#
WIDTH = 424
HEIGHT = 240
CHANNELS = 3

class RealSense435i(object):
    """
    Donkeycar part for the Intel Realsense depth cameras D435 and D435i.
    The Intel Realsense D435i camera is a device which uses an imu, twin fisheye cameras,
    and an Movidius chip to stream a depth map along with an rgb image and optionally,
    accelerometer and gyro data (the 'i' variant has an IMU, the non-i variant does not)
    NOTE: this ALWAYS captures 424 pixels wide x 240 pixels high x RGB at 60fps.
          If an image width, height or depth are passed with different values,
          the the outputs will be scaled to the requested size on the way out.
    """

    def __init__(self, width = WIDTH, height = HEIGHT, channels = CHANNELS, enable_rgb=True, enable_depth=True, enable_imu=False, device_id = None):
        self.device_id = device_id  # "923322071108" # serial number of device to use or None to use default
        self.enable_imu = enable_imu
        self.enable_rgb = enable_rgb
        self.enable_depth = enable_depth

        self.width = width
        self.height = height
        self.channels = channels
        self.resize = (width != WIDTH) or (height != height) or (channels != CHANNELS)
        if self.resize:
            print("The output images will be resized from {} to {}.  This requires opencv.".format((WIDTH, HEIGHT, CHANNELS), (self.width, self.height, self.channels)))

        # Configure streams
        self.imu_pipeline = None
        if self.enable_imu:
            self.imu_pipeline = rs.pipeline()
            imu_config = rs.config()
            if None != self.device_id:
                imu_config.enable_device(self.device_id)
            imu_config.enable_stream(rs.stream.accel, rs.format.motion_xyz32f, 63)  # acceleration
            imu_config.enable_stream(rs.stream.gyro, rs.format.motion_xyz32f, 200)  # gyroscope
            imu_profile = self.imu_pipeline.start(imu_config)
            # eat some frames to allow imu to settle
            for i in range(0, 5):
                self.imu_pipeline.wait_for_frames()

        self.pipeline = None
        if self.enable_depth or self.enable_rgb:
            self.pipeline = rs.pipeline()
            config = rs.config()

            # if we are provided with a specific device, then enable it
            if None != self.device_id:
                config.enable_device(self.device_id)

            if self.enable_depth:
                config.enable_stream(rs.stream.depth, 848, 480, rs.format.z16, 60)  # depth

            if self.enable_rgb:
                config.enable_stream(rs.stream.color, 424, 240, rs.format.rgb8, 60)  # rgb

            # Start streaming
            profile = self.pipeline.start(config)

            # Getting the depth sensor's depth scale (see rs-align example for explanation)
            if self.enable_depth:
                depth_sensor = profile.get_device().first_depth_sensor()
                depth_scale = depth_sensor.get_depth_scale()
                print("Depth Scale is: ", depth_scale)
                if self.enable_rgb:
                    # Create an align object
                    # rs.align allows us to perform alignment of depth frames to others frames
                    # The "align_to" is the stream type to which we plan to align depth frames.
                    align_to = rs.stream.color
                    self.align = rs.align(align_to)

            # eat some frames to allow auto-exposure to settle
            for i in range(0, 5):
                self.pipeline.wait_for_frames()

        time.sleep(2)   # let camera warm up

        # initialize frame state
        self.color_image = None
        self.depth_image = None
        self.acceleration_x = None
        self.acceleration_y = None
        self.acceleration_z = None
        self.gyroscope_x = None
        self.gyroscope_y = None
        self.gyroscope_z = None
        self.frame_count = 0
        self.start_time = time.time()
        self.frame_time = self.start_time

        self.running = True

    def _stop_pipeline(self):
        if self.imu_pipeline is not None:
            self.imu_pipeline.stop()
            self.imu_pipeline = None
        if self.pipeline is not None:
            self.pipeline.stop()
            self.pipeline = None

    def _poll(self):
        last_time = self.frame_time
        self.frame_time = time.time() - self.start_time
        self.frame_count += 1

        #
        # get the frames
        #
        try:
            if self.enable_imu:
                imu_frames = self.imu_pipeline.wait_for_frames()

            if self.enable_rgb or self.enable_depth:
                frames = self.pipeline.wait_for_frames()
        except Exception as e:
            logging.error(e)
            return

        #
        # convert camera frames to images
        #
        if self.enable_rgb or self.enable_depth:
            # Align the depth frame to color frame
            aligned_frames = self.align.process(frames) if self.enable_depth and self.enable_rgb else None
            depth_frame = aligned_frames.get_depth_frame() if aligned_frames is not None else frames.get_depth_frame()
            color_frame = aligned_frames.get_color_frame() if aligned_frames is not None else frames.get_color_frame()

            # Convert depth to 16bit array, RGB into 8bit planar array
            self.depth_image = np.asanyarray(depth_frame.get_data(), dtype=np.uint16) if self.enable_depth else None
            self.color_image = np.asanyarray(color_frame.get_data(), dtype=np.uint8) if self.enable_rgb else None

            if self.resize:
                import cv2
                if self.width != WIDTH or self.height != HEIGHT:
                    self.color_image = cv2.resize(self.color_image, (self.width, self.height), cv2.INTER_NEAREST) if self.enable_rgb else None
                    self.depth_image = cv2.resize(self.depth_image, (self.width, self.height), cv2.INTER_NEAREST) if self.enable_depth else None
                if self.channels != CHANNELS:
                    self.color_image = cv2.cvtColor(self.color_image, cv2.COLOR_RGB2GRAY) if self.enable_rgb else None

        #
        # output imu data as discrete values in the same order as the Mpu6050 code
        # so we can be compatible with any other parts that consume imu data.
        #
        if self.enable_imu:
            acceleration = imu_frames.first_or_default(rs.stream.accel, rs.format.motion_xyz32f).as_motion_frame().get_motion_data()
            self.acceleration_x = acceleration.x
            self.acceleration_y = acceleration.y
            self.acceleration_z = acceleration.z
            gyroscope = imu_frames.first_or_default(rs.stream.gyro, rs.format.motion_xyz32f).as_motion_frame().get_motion_data()
            self.gyroscope_x = gyroscope.x
            self.gyroscope_y = gyroscope.y
            self.gyroscope_z = gyroscope.z
            logging.debug("imu frame {} in {} seconds: \n\taccel = {}, \n\tgyro = {}".format(
                str(self.frame_count),
                str(self.frame_time - last_time),
                str((self.acceleration_x, self.acceleration_y, self.acceleration_z)),
                str((self.gyroscope_x, self.gyroscope_y, self.gyroscope_z))))

    def update(self):
        """
        When running threaded, update() is called from the background thread
        to update the state.  run_threaded() is called to return the latest state.
        """
        while self.running:
            self._poll()

    def run_threaded(self):
        """
        Return the lastest state read by update().  This will not block.
        All 4 states are returned, but may be None if the feature is not enabled when the camera part is constructed.
        For gyroscope, x is pitch, y is yaw and z is roll.
        :return: (rbg_image: nparray, depth_image: nparray, acceleration: (x:float, y:float, z:float), gyroscope: (x:float, y:float, z:float))
        """
        return self.color_image, self.depth_image, self.acceleration_x, self.acceleration_y, self.acceleration_z, self.gyroscope_x, self.gyroscope_y, self.gyroscope_z

    def run(self):
        """
        Read and return frame from camera.  This will block while reading the frame.
        see run_threaded() for return types.
        """
        self._poll()
        return self.run_threaded()

    def shutdown(self):
        self.running = False
        time.sleep(2) # give thread enough time to shutdown

        # done running
        self._stop_pipeline()

#
# self test
#
if __name__ == "__main__":

    parser = argparse.ArgumentParser()

    parser.add_argument("--rgb", default=False, action='store_true', help="Stream RGB camera")
    parser.add_argument("--depth", default=False, action='store_true', help="Stream depth camera")
    parser.add_argument("--imu", default=False, action='store_true', help="Stream IMU to console")
    parser.add_argument("--device_id", help="Camera id (if more than one camera connected)")
    args = parser.parse_args()

    if not (args.rgb or args.depth or args.imu):
        print("Must specify one or more of --rgb, --depth, --imu")
        parser.print_help()
        sys.exit(0)

    show_opencv_window = args.rgb or args.depth # True to show images in opencv window: note that default donkeycar environment is not configured for this.
    if show_opencv_window:
        import cv2

    enable_rgb = args.rgb
    enable_depth = args.depth
    enable_imu = args.imu
    device_id = args.device_id

    width = 212
    height = 120
    channels = 3

    profile_frames = 0 # set to non-zero to calculate the max frame rate using given number of frames

    try:
        #
        # for D435i, enable_imu can be True, for D435 enable_imu should be false
        #
        camera = RealSense435i(
            width=width, height=height, channels=channels,
            enable_rgb=enable_rgb, enable_depth=enable_depth, enable_imu=enable_imu, device_id=device_id)

        frame_count = 0
        start_time = time.time()
        frame_time = start_time
        while True:
            #
            # read data from camera
            #
            color_image, depth_image, acceleration_x, acceleration_y, acceleration_z, gyroscope_x, gyroscope_y, gyroscope_z = camera.run()

            # maintain frame timing
            frame_count += 1
            last_time = frame_time
            frame_time = time.time()

            if enable_imu and not profile_frames:
                print("imu frame {} in {} seconds: \n\taccel = {}, \n\tgyro = {}".format(
                    str(frame_count),
                    str(frame_time - last_time),
                    str((acceleration_x, acceleration_y, acceleration_z)),
                    str((gyroscope_x, gyroscope_y, gyroscope_z))))

            # Show images
            if show_opencv_window and not profile_frames:
                cv2.namedWindow('RealSense', cv2.WINDOW_AUTOSIZE)
                if enable_rgb or enable_depth:
                    # make sure depth and color images have same number of channels so we can show them together in the window
                    if 3 == channels:
                        depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(depth_image, alpha=0.03), cv2.COLORMAP_JET) if enable_depth else None
                    else:
                        depth_colormap = cv2.cvtColor(cv2.applyColorMap(cv2.convertScaleAbs(depth_image, alpha=0.03), cv2.COLORMAP_JET), cv2.COLOR_RGB2GRAY) if enable_depth else None

                    # Stack both images horizontally
                    images = None
                    if enable_rgb:
                        images = np.hstack((color_image, depth_colormap)) if enable_depth else color_image
                    elif enable_depth:
                        images = depth_colormap

                    if images is not None:
                        cv2.imshow('RealSense', images)

                # Press esc or 'q' to close the image window
                key = cv2.waitKey(1)
                if key & 0xFF == ord('q') or key == 27:
                    cv2.destroyAllWindows()
                    break
            if profile_frames > 0:
                if frame_count == profile_frames:
                    print("Aquired {} frames in {} seconds for {} fps".format(str(frame_count), str(frame_time - start_time), str(frame_count / (frame_time - start_time))))
                    break
            else:
                time.sleep(0.05)
    finally:
        camera.shutdown()


import sys
from collections import deque

input = sys.stdin.readline

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

def check_island(i, j):
    global cnt
    q = deque()
    q.append([i, j])
    island[i][j] = True
    board[i][j] = cnt

    while q:
        x, y = q.popleft()
        for k in range(4):
            nx = x + dx[k]
            ny = y + dy[k]
            if 0 <= nx < n and 0 <= ny < n and board[nx][ny] == 1 and not island[nx][ny]:
                island[nx][ny] = True
                board[nx][ny] = cnt
                q.append([nx, ny])

# 바다를 건너며 가장 짧은 거리를 구한다.
def solve(z):
    global answer
    dist = [[-1] * n for _ in range(n)] # 거리가 저장될 배열
    q = deque()

    for i in range(n):
        for j in range(n):
            if board[i][j] == z:
                q.append([i, j])
                dist[i][j] = 0

    while q:
        x, y = q.popleft()
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            if nx < 0 or nx >= n or ny < 0 or ny >= n:
                continue
            if board[nx][ny] > 0 and board[nx][ny] != z:
                answer = min(answer, dist[x][y])
                return
            if board[nx][ny] == 0 and dist[nx][ny] == -1:
                dist[nx][ny] = dist[x][y] + 1
                q.append([nx, ny])

n = int(input())

board = [list(map(int, input().split())) for _ in range(n)]
island = [[False] * n for _ in range(n)]
cnt = 1         #섬개수 체크
answer = 1e9

for i in range(n):
    for j in range(n):
        if not island[i][j] and board[i][j] == 1:
            check_island(i, j)
            cnt += 1

for i in range(1, cnt):
    solve(i)

print(answer)

백준/Gold/2146. 다리 만들기/다리 만들기.py


import os
import sys
import logging
from ctypes import *

sys.path.append("\\\\VBOXSVR\\setup\\lib\\")

import cuckoo.defines
from cuckoo.paths import CUCKOO_DLL_PATH
from cuckoo.inject import cuckoo_inject

def cuckoo_resumethread(h_thread = -1):
    """
    Resumes the thread of a process created in suspended mode.
    @param h_thread: handle to the thread to resume
    """
    log = logging.getLogger("Monitor.ResumeThread")

    cuckoo.defines.KERNEL32.Sleep(2000)
    # If the resume fails we need to abort the analysis, as there won't be
    # any activity monitored.
    if not cuckoo.defines.KERNEL32.ResumeThread(h_thread):
        log.error("Unable to resume thread with handle \"0x%08x\" (GLE=%d)."
                  % (h_thread, cuckoo.defines.KERNEL32.GetLastError()))
        return False
    else:
        log.info("Resumed thread with handle \"0x%08x\"." % h_thread)

    return True

def cuckoo_monitor(pid = -1, h_thread = -1, suspended = False, dll_path = None):
    """
    Invokes injection and resume of the specified process.
    @param pid: PID of the process to monitor
    @param h_thread: handle of the thread of the process to monitor
    @param suspended: boolean value enabling or disabling the resume of the
                      specified process from suspended mode
    @param dll_path: path to the DLL to inject, if none is specified it will use
                     the default DLL
    """
    log = logging.getLogger("Monitor.Monitor")

    # The package run function should return the process id, if it's valid
    # I can inject it with Cuckoo's DLL or specified custom DLL.
    if pid > -1:
        if not dll_path or dll_path == CUCKOO_DLL_PATH:
            dll_path = CUCKOO_DLL_PATH
            log.info("Using default Cuckoo DLL \"%s\"." % dll_path)
        else:
            log.info("Using custom DLL \"%s\"." % dll_path)

        if not cuckoo_inject(pid, dll_path):
            log.error("Unable to inject process with ID \"%d\" with DLL " \
                      "\"%s\" (GLE=%s)."
                      % (pid, dll_path, cuckoo.defines.KERNEL32.GetLastError()))
            return False
        else:
            log.info("Original process with PID \"%d\" successfully injected."
                     % pid)

    # Resume the process in case it was created in suspended mode.
    if suspended and h_thread > -1:
        if not cuckoo_resumethread(h_thread):
            return False

    return True


""" Tests for the spatial module. """

import numpy as np
import pytest
import rasterio as rio
from shapely.geometry import Polygon, Point, LineString
import earthpy.spatial as es

def test_crop_image_with_gdf(basic_image_tif, basic_geometry_gdf):
    """Cropping with a GeoDataFrame works when all_touched=True.

    Cropping basic_image_tif file with the basic geometry fixture returns
    all of the cells that have the value 1 in the basic_image_tif fixture

    These fixtures are described in conftest.py
    """
    with rio.open(basic_image_tif) as src:
        img, meta = es.crop_image(src, basic_geometry_gdf, all_touched=True)
    assert np.sum(img) == 9

def test_crop_image_with_gdf_touch_false(basic_image_tif, basic_geometry_gdf):
    """Cropping with a GeoDataFrame works when all_touched=False."""
    with rio.open(basic_image_tif) as src:
        img, meta = es.crop_image(src, basic_geometry_gdf, all_touched=False)
    assert np.sum(img) == 4

def test_crop_image_with_geometry(basic_image_tif, basic_geometry):
    """Cropping with a geometry works with all_touched=True."""
    with rio.open(basic_image_tif) as src:
        img, meta = es.crop_image(src, [basic_geometry], all_touched=True)
    assert np.sum(img) == 9

def test_crop_image_with_geojson_touch_false(basic_image_tif, basic_geometry):
    """Cropping with GeoJSON works when all_touched=False."""
    geojson = basic_geometry.__geo_interface__
    with rio.open(basic_image_tif) as src:
        img, meta = es.crop_image(src, [geojson], all_touched=False)
    assert np.sum(img) == 4

def test_crop_image_when_poly_bounds_image_extent(basic_image_tif):
    """When an image is fully contained in a larger polygon, dont crop."""
    big_polygon = Polygon([(-1, -1), (11, -1), (11, 11), (-1, 11), (-1, -1)])
    with rio.open(basic_image_tif) as src:
        img, meta = es.crop_image(src, [big_polygon])
        src_array = src.read()
    assert np.array_equal(img, src_array)

def test_crop_image_with_one_point_raises_error(basic_image_tif):
    """Cropping an image with one point should raise an error."""
    point = Point([(1, 1)])
    with rio.open(basic_image_tif) as src:
        with pytest.raises(ValueError, match="Input shapes do not overlap "):
            es.crop_image(src, [point])

def test_crop_image_with_1d_extent_raises_error(basic_image_tif):
    """Cropping with a horizontal or vertical line raises an error."""
    line = LineString([(1, 1), (2, 1), (3, 1)])
    with rio.open(basic_image_tif) as src:
        with pytest.raises(ValueError, match="Input shapes do not overlap "):
            es.crop_image(src, [line])

def test_crop_image_fails_two_rasters(basic_image_tif, basic_geometry):
    """crop_image should raise an error if provided two rasters."""
    with rio.open(basic_image_tif) as src:
        with pytest.raises(TypeError):
            es.crop_image(src, src)

def test_crop_image_swapped_args(basic_image_tif, basic_geometry):
    """If users provide a polygon instead of raster raise an error."""
    with pytest.raises(AttributeError):
        es.crop_image(basic_geometry, basic_image_tif)
    with pytest.raises(AttributeError):
        es.crop_image(basic_geometry, basic_geometry)

def test_crop_image_fails_empty_list(basic_image_tif, basic_geometry):
    """If users provide empty list as arg, crop_image fails."""
    with pytest.raises(AttributeError):
        es.crop_image(list(), basic_geometry)
    with rio.open(basic_image_tif) as src:
        with pytest.raises(ValueError):
            es.crop_image(src, list())


#coding:utf-8
"""
基于redis的消息队列
"""
import redis
import cPickle

class Queue(object):
    """An abstract FIFO queue"""
    def __init__(self):
        self.r = redis.Redis(host='localhost', port=6379, db=1)
        self.id_name = "queue"
        #self.id_name = "queue:%s" %(self.r.incr("queue_space"))

    def push(self, element):
        """插入一个元素到队列的尾部"""
        value = cPickle.dumps(element)
        push_element = self.r.lpush(self.id_name,value)
    def pop(self):
        """从队列的头部取出一条记录"""
        popped_element = self.r.rpop(self.id_name)
        try:
            return cPickle.loads(popped_element)
        except:
            return None
"""
Demo

from queue.py import Queue
q = Queue()
添加队列
q.push('123')
提取队列
print q.pop()

"""


from requests import get, utils
from decimal import Decimal
from datetime import datetime

response = get('http://www.cbr.ru/scripts/XML_daily.asp')
content = response.content.decode(encoding=utils.get_encoding_from_headers(response.headers))

def currency_rates(code, cool_vision=False, site=content):
    date_site_2 = site[site.find('Date="') + len('Date="'):site.find('" name="Foreign')]
    date_site = datetime.strptime(date_site_2, '%d.%m.%Y').date()
    for s in site.split('<CharCode>'):
        if code.upper() == s[0:3]:
            nominal = s[s.rfind('<Nominal>') + len('<Nominal>'):s.find('</Nominal>')]
            name = s[s.rfind('<Name>') + len('<Name>'):s.find('</Name>')]
            value = Decimal(s[s.rfind('<Value>') + len('<Value>'):s.find('</Value>')].replace(',', '.')).quantize(Decimal('1.00'))
            if cool_vision is False:
                return f'{value} {date_site}'
            else:
                return f'Получите {nominal} {name} за {value} руб на {date_site}'


import sys
sys.path.append('../code')
import argparse
import GPUtil
import os
from pyhocon import ConfigFactory
import torch
import numpy as np
import cvxpy as cp
from PIL import Image
import math

import utils.general as utils
import utils.plots as plt
from utils import rend_util
from utils import vis_util
from model.sg_render import compute_envmap
import imageio
# import pyexr

def evaluate(**kwargs):
    torch.set_default_dtype(torch.float32)

    conf = ConfigFactory.parse_file(kwargs['conf'])
    exps_folder_name = kwargs['exps_folder_name']
    evals_folder_name = kwargs['evals_folder_name']

    expname = conf.get_string('train.expname') + '-' + kwargs['expname']

    if kwargs['timestamp'] == 'latest':
        if os.path.exists(os.path.join('../', kwargs['exps_folder_name'], expname)):
            timestamps = os.listdir(os.path.join('../', kwargs['exps_folder_name'], expname))
            if (len(timestamps)) == 0:
                print('WRONG EXP FOLDER')
                exit()
            else:
                timestamp = sorted(timestamps)[-1]
        else:
            print('WRONG EXP FOLDER')
            exit()
    else:
        timestamp = kwargs['timestamp']

    utils.mkdir_ifnotexists(os.path.join('../', evals_folder_name))
    expdir = os.path.join('../', exps_folder_name, expname)
    evaldir = os.path.join('../', evals_folder_name, expname, os.path.basename(kwargs['data_split_dir']))

    model = utils.get_class(conf.get_string('train.model_class'))(conf=conf.get_config('model'))
    if torch.cuda.is_available():
        model.cuda()

    eval_dataset = utils.get_class(conf.get_string('train.dataset_class'))(kwargs['gamma'],
                                                                           kwargs['data_split_dir'],
                                                                           train_cameras=False)

    eval_dataloader = torch.utils.data.DataLoader(eval_dataset,
                                                  batch_size=1,
                                                  shuffle=False,
                                                  collate_fn=eval_dataset.collate_fn
                                                  )
    total_pixels = eval_dataset.total_pixels
    img_res = eval_dataset.img_res

    old_checkpnts_dir = os.path.join(expdir, timestamp, 'checkpoints')
    ckpt_path = os.path.join(old_checkpnts_dir, 'ModelParameters', str(kwargs['checkpoint']) + ".pth")
    saved_model_state = torch.load(ckpt_path)
    model.load_state_dict(saved_model_state["model_state_dict"])
    epoch = saved_model_state['epoch']
    print('Loaded checkpoint: ', ckpt_path)

    if kwargs['geometry'].endswith('.pth'):
        print('Reloading geometry from: ', kwargs['geometry'])
        geometry = torch.load(kwargs['geometry'])['model_state_dict']
        geometry = {k: v for k, v in geometry.items() if 'implicit_network' in k}
        print(geometry.keys())
        model_dict = model.state_dict()
        model_dict.update(geometry)
        model.load_state_dict(model_dict)

    #####################################################################################################
    # reset lighting
    #####################################################################################################
    relight = False
    if kwargs['light_sg'].endswith('.npy'):
        print('Loading light from: ', kwargs['light_sg'])
        model.envmap_material_network.load_light(kwargs['light_sg'])
        evaldir = evaldir + '_relight'
        relight = True

    edit_diffuse = False
    if len(kwargs['diffuse_albedo']) > 0:
        print('Setting diffuse albedo to: ', kwargs['diffuse_albedo'])
        evaldir = evaldir + '_editdiffuse'
        edit_diffuse = True

    utils.mkdir_ifnotexists(evaldir)
    print('Output directory is: ', evaldir)

    with open(os.path.join(evaldir, 'ckpt_path.txt'), 'w') as fp:
        fp.write(ckpt_path + '\n')

    ####################################################################################################################
    print("evaluating...")
    model.eval()

    # extract mesh
    if (not edit_diffuse) and (not relight) and eval_dataset.has_groundtruth:
        with torch.no_grad():
            mesh = plt.get_surface_high_res_mesh(
                sdf=lambda x: model.implicit_network(x)[:, 0],
                resolution=kwargs['resolution']
            )

            # Taking the biggest connected component
            components = mesh.split(only_watertight=False)
            areas = np.array([c.area for c in components], dtype=np.float)
            mesh_clean = components[areas.argmax()]
            mesh_clean.export('{0}/mesh.obj'.format(evaldir), 'obj')

    # generate images
    images_dir = evaldir

    all_frames = []
    psnrs = []
    for data_index, (indices, model_input, ground_truth) in enumerate(eval_dataloader):
        if eval_dataset.has_groundtruth:
            out_img_name = os.path.basename(eval_dataset.image_paths[indices[0]])[:-4]
        else:
            out_img_name = '{}'.format(indices[0])

        if len(kwargs['view_name']) > 0 and out_img_name != kwargs['view_name']:
            print('Skipping: ', out_img_name)
            continue

        print('Evaluating data_index: ', data_index, len(eval_dataloader))
        model_input["intrinsics"] = model_input["intrinsics"].cuda()
        model_input["uv"] = model_input["uv"].cuda()
        model_input["object_mask"] = model_input["object_mask"].cuda()
        model_input['pose'] = model_input['pose'].cuda()

        split = utils.split_input(model_input, total_pixels)
        res = []
        for s in split:
            out = model(s)
            res.append({
                'points': out['points'].detach(),
                'idr_rgb_values': out['idr_rgb_values'].detach(),
                'sg_rgb_values': out['sg_rgb_values'].detach(),
                'normal_values': out['normal_values'].detach(),
                'network_object_mask': out['network_object_mask'].detach(),
                'object_mask': out['object_mask'].detach(),
                'sg_diffuse_albedo_values': out['sg_diffuse_albedo_values'].detach(),
                'sg_diffuse_rgb_values': out['sg_diffuse_rgb_values'].detach(),
                'sg_specular_rgb_values': out['sg_specular_rgb_values'].detach(),
            })

        batch_size = ground_truth['rgb'].shape[0]
        model_outputs = utils.merge_output(res, total_pixels, batch_size)

        ### re-render with updated diffuse albedo
        if edit_diffuse:
            diffuse_albedo = imageio.imread(kwargs['diffuse_albedo']).astype(np.float32)[:, :, :3]
            if not kwargs['diffuse_albedo'].endswith('.exr'):
                diffuse_albedo /= 255.
            diffuse_albedo = torch.from_numpy(diffuse_albedo).cuda().reshape((-1, 3))

            ray_dirs, _ = rend_util.get_camera_params(model_input["uv"],
                                                            model_input['pose'],
                                                            model_input["intrinsics"])
            sg_ret = model.render_sg_rgb(mask=model_outputs['network_object_mask'],
                                         normals=model_outputs['normal_values'],
                                         view_dirs=-ray_dirs.reshape((-1, 3)),
                                         diffuse_albedo=diffuse_albedo)
            for x in sorted(sg_ret.keys()):
                assert (x in model_outputs)
                model_outputs[x] = sg_ret[x]

        tonemap_img = lambda x: np.power(x, 1./eval_dataset.gamma)
        clip_img = lambda x: np.clip(x, 0., 1.)

        assert (batch_size == 1)

        if kwargs['write_idr']:
            rgb_eval = model_outputs['idr_rgb_values']
            rgb_eval = rgb_eval.reshape(batch_size, total_pixels, 3)
            rgb_eval = plt.lin2img(rgb_eval, img_res).detach().cpu().numpy()[0]
            rgb_eval = rgb_eval.transpose(1, 2, 0)
            rgb_eval = clip_img(tonemap_img(rgb_eval))
            img = Image.fromarray((rgb_eval * 255).astype(np.uint8))
            img.save('{0}/idr_rgb_{1}.png'.format(images_dir, out_img_name))

        rgb_eval = model_outputs['sg_rgb_values']
        rgb_eval = rgb_eval.reshape(batch_size, total_pixels, 3)
        rgb_eval = plt.lin2img(rgb_eval, img_res).detach().cpu().numpy()[0]
        rgb_eval = rgb_eval.transpose(1, 2, 0)
        if kwargs['save_exr']:
            imageio.imwrite('{0}/sg_rgb_{1}.exr'.format(images_dir, out_img_name), rgb_eval)
            # pyexr.write('{0}/sg_rgb_{1}.exr'.format(images_dir, out_img_name), rgb_eval)
            # np.save('{0}/sg_rgb_{1}.npy'.format(images_dir, out_img_name), rgb_eval)

        else:
            rgb_eval = clip_img(tonemap_img(rgb_eval))
            img = Image.fromarray((rgb_eval * 255).astype(np.uint8))
            img.save('{0}/sg_rgb_{1}.png'.format(images_dir, out_img_name))

            all_frames.append(np.array(img))

        # network_object_mask = model_outputs['network_object_mask']
        # network_object_mask = network_object_mask.reshape(batch_size, total_pixels, 3)
        # network_object_mask = plt.lin2img(network_object_mask, img_res).detach().cpu().numpy()[0]
        # network_object_mask = network_object_mask.transpose(1, 2, 0)
        # img = Image.fromarray((network_object_mask * 255).astype(np.uint8))
        # img.save('{0}/object_mask_{1}.png'.format(images_dir, out_img_name))

        normal = model_outputs['normal_values']
        normal = normal.reshape(batch_size, total_pixels, 3)
        normal = (normal + 1.) / 2.
        normal = plt.lin2img(normal, img_res).detach().cpu().numpy()[0]
        normal = normal.transpose(1, 2, 0)
        if kwargs['save_exr']:
            imageio.imwrite('{0}/normal_{1}.exr'.format(images_dir, out_img_name), normal)
            # pyexr.write('{0}/normal_{1}.exr'.format(images_dir, out_img_name), normal)
            # np.save('{0}/normal_{1}.npy'.format(images_dir, out_img_name), normal)

        else:
            img = Image.fromarray((normal * 255).astype(np.uint8))
            img.save('{0}/normal_{1}.png'.format(images_dir, out_img_name))

        if (not relight) and eval_dataset.has_groundtruth:
            depth = torch.ones(batch_size * total_pixels).cuda().float()
            network_object_mask = model_outputs['network_object_mask'] & model_outputs['object_mask']
            depth_valid = rend_util.get_depth(model_outputs['points'].reshape(batch_size, total_pixels, 3),
                                              model_input['pose']).reshape(-1)[network_object_mask]
            depth[network_object_mask] = depth_valid
            depth[~network_object_mask] = 0.98 * depth_valid.min()
            assert (batch_size == 1)
            network_object_mask = network_object_mask.float().reshape(img_res[0], img_res[1]).cpu()
            depth = depth.reshape(img_res[0], img_res[1]).cpu()

            if kwargs['save_exr']:
                depth = depth * network_object_mask
                depth = depth.numpy()
                imageio.imwrite('{0}/depth_{1}.exr'.format(images_dir, out_img_name), depth)
                # pyexr.write('{0}/depth_{1}.exr'.format(images_dir, out_img_name), depth)
                # np.save('{0}/depth_{1}.npy'.format(images_dir, out_img_name), depth)

            else:
                depth = vis_util.colorize(depth, cmap_name='jet')
                depth = depth * network_object_mask.unsqueeze(-1) + (1. - network_object_mask.unsqueeze(-1))
                depth = depth.numpy()
                img = Image.fromarray((depth * 255).astype(np.uint8))
                img.save('{0}/depth_{1}.png'.format(images_dir, out_img_name))

            # write lighting and materials
            envmap = compute_envmap(lgtSGs=model.envmap_material_network.get_light(), H=256, W=512, upper_hemi=model.envmap_material_network.upper_hemi)
            envmap = envmap.cpu().numpy()
            imageio.imwrite(os.path.join(images_dir, 'envmap.exr'), envmap)

            roughness, specular_reflectance = model.envmap_material_network.get_base_materials()
            with open(os.path.join(images_dir, 'relight_material.txt'), 'w') as fp:
                for i in range(roughness.shape[0]):
                    fp.write('Material {}:\n'.format(i))
                    fp.write('\troughness: {}\n'.format(roughness[i, 0].item()))
                    fp.write('\tspecular_reflectance: ')
                    for j in range(3):
                        fp.write('{}, '.format(specular_reflectance[i, j].item()))
                    fp.write('\n\n')

            rgb_gt = ground_truth['rgb']
            rgb_gt = plt.lin2img(rgb_gt, img_res).numpy()[0].transpose(1, 2, 0)
            if kwargs['save_exr']:
                imageio.imwrite('{0}/gt_{1}.exr'.format(images_dir, out_img_name), rgb_gt)
                # pyexr.write('{0}/gt_{1}.exr'.format(images_dir, out_img_name), rgb_gt)
                # np.save('{0}/gt_{1}.npy'.format(images_dir, out_img_name), rgb_gt)

            else:
                rgb_gt = clip_img(tonemap_img(rgb_gt))
                img = Image.fromarray((rgb_gt * 255).astype(np.uint8))
                img.save('{0}/gt_{1}.png'.format(images_dir, out_img_name))

            mask = model_input['object_mask']
            mask = plt.lin2img(mask.unsqueeze(-1), img_res).cpu().numpy()[0]
            mask = mask.transpose(1, 2, 0)
            rgb_eval_masked = rgb_eval * mask
            rgb_gt_masked = rgb_gt * mask

            psnr = calculate_psnr(rgb_eval_masked, rgb_gt_masked, mask)
            psnrs.append(psnr)

            # verbose mode
            rgb_eval = model_outputs['sg_diffuse_albedo_values']
            rgb_eval = rgb_eval.reshape(batch_size, total_pixels, 3)
            rgb_eval = plt.lin2img(rgb_eval, img_res).detach().cpu().numpy()[0]
            rgb_eval = rgb_eval.transpose(1, 2, 0)
            if kwargs['save_exr']:
                imageio.imwrite('{0}/sg_diffuse_albedo_{1}.exr'.format(images_dir, out_img_name), rgb_eval)
                # pyexr.write('{0}/sg_diffuse_albedo_{1}.exr'.format(images_dir, out_img_name), rgb_eval)
                # np.save('{0}/sg_diffuse_albedo_{1}.npy'.format(images_dir, out_img_name), rgb_eval)

            else:
                rgb_eval = clip_img(rgb_eval)
                img = Image.fromarray((rgb_eval * 255).astype(np.uint8))
                img.save('{0}/sg_diffuse_albedo_{1}.png'.format(images_dir, out_img_name))

            rgb_eval = model_outputs['sg_diffuse_rgb_values']
            rgb_eval = rgb_eval.reshape(batch_size, total_pixels, 3)
            rgb_eval = plt.lin2img(rgb_eval, img_res).detach().cpu().numpy()[0]
            rgb_eval = rgb_eval.transpose(1, 2, 0)
            if kwargs['save_exr']:
                imageio.imwrite('{0}/sg_diffuse_rgb_{1}.exr'.format(images_dir, out_img_name), rgb_eval)
                # pyexr.write('{0}/sg_diffuse_rgb_{1}.exr'.format(images_dir, out_img_name), rgb_eval)
                # np.save('{0}/sg_diffuse_rgb_{1}.npy'.format(images_dir, out_img_name), rgb_eval)

            else:
                rgb_eval = clip_img(rgb_eval)
                img = Image.fromarray((rgb_eval * 255).astype(np.uint8))
                img.save('{0}/sg_diffuse_rgb_{1}.png'.format(images_dir, out_img_name))

            rgb_eval = model_outputs['sg_specular_rgb_values']
            rgb_eval = rgb_eval.reshape(batch_size, total_pixels, 3)
            rgb_eval = plt.lin2img(rgb_eval, img_res).detach().cpu().numpy()[0]
            rgb_eval = rgb_eval.transpose(1, 2, 0)
            if kwargs['save_exr']:
                imageio.imwrite('{0}/sg_specular_rgb_{1}.exr'.format(images_dir, out_img_name), rgb_eval)
                # pyexr.write('{0}/sg_specular_rgb_{1}.exr'.format(images_dir, out_img_name), rgb_eval)
                # np.save('{0}/sg_specular_rgb_{1}.npy'.format(images_dir, out_img_name), rgb_eval)

            else:
                rgb_eval = clip_img(rgb_eval)
                img = Image.fromarray((rgb_eval * 255).astype(np.uint8))
                img.save('{0}/sg_specular_rgb_{1}.png'.format(images_dir, out_img_name))

    if not kwargs['save_exr']:
        imageio.mimwrite(os.path.join(images_dir, 'video_rgb.mp4'), all_frames, fps=15, quality=9)
        print('Done rendering', images_dir)

    if len(psnrs) > 0:
        psnrs = np.array(psnrs).astype(np.float64)
        # print("RENDERING EVALUATION {2}: psnr mean = {0} ; psnr std = {1}".format("%.2f" % psnrs.mean(), "%.2f" % psnrs.std(), scan_id))
        print("RENDERING EVALUATION: psnr mean = {0} ; psnr std = {1}".format("%.2f" % psnrs.mean(), "%.2f" % psnrs.std()))

def get_cameras_accuracy(pred_Rs, gt_Rs, pred_ts, gt_ts,):
    ''' Align predicted pose to gt pose and print cameras accuracy'''

    # find rotation
    d = pred_Rs.shape[-1]
    n = pred_Rs.shape[0]

    Q = torch.addbmm(torch.zeros(d, d, dtype=torch.double), gt_Rs, pred_Rs.transpose(1, 2))
    Uq, _, Vq = torch.svd(Q)
    sv = torch.ones(d, dtype=torch.double)
    sv[-1] = torch.det(Uq @ Vq.transpose(0, 1))
    R_opt = Uq @ torch.diag(sv) @ Vq.transpose(0, 1)
    R_fixed = torch.bmm(R_opt.repeat(n, 1, 1), pred_Rs)

    # find translation
    pred_ts = pred_ts @ R_opt.transpose(0, 1)
    c_opt = cp.Variable()
    t_opt = cp.Variable((1, d))

    constraints = []
    obj = cp.Minimize(cp.sum(
        cp.norm(gt_ts.numpy() - (c_opt * pred_ts.numpy() + np.ones((n, 1), dtype=np.double) @ t_opt), axis=1)))
    prob = cp.Problem(obj, constraints)
    prob.solve()
    t_fixed = c_opt.value * pred_ts.numpy() + np.ones((n, 1), dtype=np.double) * t_opt.value

    # Calculate transaltion error
    t_error = np.linalg.norm(t_fixed - gt_ts.numpy(), axis=-1)
    t_error = t_error
    t_error_mean = np.mean(t_error)
    t_error_medi = np.median(t_error)

    # Calculate rotation error
    R_error = compare_rotations(R_fixed, gt_Rs)

    R_error = R_error.numpy()
    R_error_mean = np.mean(R_error)
    R_error_medi = np.median(R_error)

    print('CAMERAS EVALUATION: R error mean = {0} ; t error mean = {1} ; R error median = {2} ; t error median = {3}'
          .format("%.2f" % R_error_mean, "%.2f" % t_error_mean, "%.2f" % R_error_medi, "%.2f" % t_error_medi))

    # return alignment and aligned pose
    return R_opt.numpy(), t_opt.value, c_opt.value, R_fixed.numpy(), t_fixed

def compare_rotations(R1, R2):
    cos_err = (torch.bmm(R1, R2.transpose(1, 2))[:, torch.arange(3), torch.arange(3)].sum(dim=-1) - 1) / 2
    cos_err[cos_err > 1] = 1
    cos_err[cos_err < -1] = -1
    return cos_err.acos() * 180 / np.pi

def calculate_psnr(img1, img2, mask):
    # img1 and img2 have range [0, 1]
    img1 = img1.astype(np.float64)
    img2 = img2.astype(np.float64)
    mse = np.mean((img1 - img2)**2) * (img2.shape[0] * img2.shape[1]) / mask.sum()
    if mse == 0:
        return float('inf')
    return 20 * math.log10(1.0 / math.sqrt(mse))

if __name__ == '__main__':

    parser = argparse.ArgumentParser()
    parser.add_argument('--conf', type=str, default='./confs/default.conf')
    parser.add_argument('--data_split_dir', type=str, default='')
    parser.add_argument('--gamma', type=float, default=1., help='gamma correction coefficient')

    parser.add_argument('--save_exr', default=False, action="store_true", help='')

    parser.add_argument('--light_sg', type=str, default='', help='')
    parser.add_argument('--geometry', type=str, default='', help='')
    parser.add_argument('--diffuse_albedo', type=str, default='', help='')
    parser.add_argument('--view_name', type=str, default='', help='')

    parser.add_argument('--expname', type=str, default='', help='The experiment name to be evaluated.')
    parser.add_argument('--exps_folder', type=str, default='exps', help='The experiments folder name.')
    parser.add_argument('--timestamp', default='latest', type=str, help='The experiemnt timestamp to test.')
    parser.add_argument('--checkpoint', default='latest',type=str,help='The trained model checkpoint to test')

    parser.add_argument('--write_idr', default=False, action="store_true", help='')

    parser.add_argument('--resolution', default=512, type=int, help='Grid resolution for marching cube')
    parser.add_argument('--is_uniform_grid', default=False, action="store_true", help='If set, evaluate marching cube with uniform grid.')

    parser.add_argument('--gpu', type=str, default='auto', help='GPU to use [default: GPU auto]')

    opt = parser.parse_args()

    if opt.gpu == "auto":
        deviceIDs = GPUtil.getAvailable(order='memory', limit=1, maxLoad=0.5, maxMemory=0.5, includeNan=False, excludeID=[], excludeUUID=[])
        gpu = deviceIDs[0]
    else:
        gpu = opt.gpu

    if (not gpu == 'ignore'):
        os.environ["CUDA_VISIBLE_DEVICES"] = '{0}'.format(gpu)

    evaluate(conf=opt.conf,
             write_idr=opt.write_idr,
             gamma=opt.gamma,
             data_split_dir=opt.data_split_dir,
             expname=opt.expname,
             exps_folder_name=opt.exps_folder,
             evals_folder_name='evals',
             timestamp=opt.timestamp,
             checkpoint=opt.checkpoint,
             resolution=opt.resolution,
             save_exr=opt.save_exr,
             light_sg=opt.light_sg,
             geometry=opt.geometry,
             view_name=opt.view_name,
             diffuse_albedo=opt.diffuse_albedo,
             )


#!/usr/bin/env python3

import functools
import json
import os
import sys
import collections
import pystache
import click

FieldType = collections.namedtuple('FieldType', ['safe_name', 'name', 'type', 'required'])

@click.command()
@click.option('--output-dir')
@click.option('--input')
@click.option('--templates')
@click.option('--file-suffix')
@click.option('--case')
@click.option('--model')
def main(output_dir, input, templates, file_suffix, case, model=None):
    with open(input) as f:
        content = json.load(f)

    def basedirReducer(init, val):
        if val[1] != 'protos':
            return init
        return val[0]

    if '.' not in file_suffix:
        file_suffix = '.' + file_suffix

    protos_index = functools.reduce(basedirReducer, enumerate(input.split('/')), None)
    basedir = input.split('/')[protos_index+1:-1]
    api_name = input.split('/')[-1].split('.')[0]
    basedir[0] = "_"+basedir[0]
    result_dir = os.path.join(output_dir, *basedir)
    result_package = '.'.join(basedir)

    if model is None:
        model = "model" + file_suffix

    create_models(result_dir, result_package, content, templates, file_suffix, case, model)
    create_api(result_dir, result_package, api_name, content, templates, file_suffix, case)

def create_api(result_dir, result_package, api_name, content, templates, file_suffix, case):
    if 'paths' not in content:
        return

    paths = []
    operations = []
    for path, p_content in content['paths'].items():
        for op, op_content in p_content.items():
            operation_id = op_content['operationId']
            operations.append((operation_id, path+"", op+""))
    operations = sorted(operations, key=lambda x: x[0])

    operations_info = []

    for (operation_id, path, op) in operations:
            op_content = content['paths'][path][op]
            parameters = []
            query = []
            path_components = []
            got_body_parameter = False
            body_type = create_typedef(any=True)
            required = []

            for param_def in op_content.get('parameters', []):
                name = param_def['name']
                safe_name = to_language_case(name.replace('.', '_'), case)
                param = FieldType(safe_name, name, dict(resolve_type(param_def), var_name=safe_name), param_def.get('required', False))
                parameters.append(param)

                if param_def.get('required', False):
                    required.append(parameters[-1])

                if param_def['in'] == 'body':
                    got_body_parameter = True
                    body_type = dict(resolve_type(param_def), var_name=safe_name)
                elif param_def['in'] == 'query':
                    query.append(param)
                elif param_def['in'] == 'path':
                    path = path.replace('{%s}' % name, "$%s" % safe_name)
                else:
                    # print(path)
                    raise ValueError(param_def['in'])

            parameters = sorted(parameters, key=lambda x: (not x.required, x.safe_name))

            # TODO
            success_response = None
            code_mapping = {}
            for code, definition in op_content['responses'].items():
                if code == "200":
                    success_response = resolve_type(definition)
                else:
                    code_mapping[code] = resolve_type(definition)

            operations_info.append({
                'operation_id': operation_id,
                'success_type': success_response,
                'query': [{
                    'name': q.name,
                    'safe_name': q.safe_name,
                    'type': q.type,
                    'required': q.required,
                    'last': i == len(query)-1
                } for i, q in enumerate(query)],
                'body_present': got_body_parameter,
                'body_type': body_type,
                'parameters': [{
                    'name': p.name,
                    'safe_name': p.safe_name,
                    'type': p.type,
                    'required': p.required,
                    'last': i == len(parameters)-1
                } for i, p in enumerate(parameters)],
                'required_parameters': [{
                    'name': q.name,
                    'safe_name': q.safe_name,
                    'type': q.type,
                    'last': i == len(required)-1
                } for i, q in enumerate(required)],
                'op': op.upper(),
                'path': path,
            })

    os.makedirs(os.path.join(result_dir, 'api'), exist_ok=True)

    with open(os.path.join(templates, 'api'+file_suffix)) as f:
        template = f.read()

    info = {
        'package': result_package,
        'api_name': api_name,
        'base_path': content.get('basePath', ''),
        'operations': operations_info,
    }

    with open(os.path.join(result_dir, 'api', api_name+'Api'+file_suffix), 'w') as f:
        f.write(pystache.Renderer(partials=load_partials(templates)).render(template, info))

def load_partials(templates_folder):
    ret = {}
    for _, _, files in os.walk(templates_folder):
        for f in files:
            parts = f.split('.')
            if parts[1] == 'mustache':
                ret[parts[0]] = open(os.path.join(templates_folder, f)).read()

    return ret

def keyword_safe(s):
    if s in ['type']:
        return '`%s`' % s
    return s

def create_models(result_dir, result_package, content, templates, file_suffix, case, model):
    enums = []
    os.makedirs(os.path.join(result_dir, 'model'), exist_ok=True)
    for k, v in content['definitions'].items():
        if 'enum' in v:
            enums.append(capitalize_first(k))

    enums = sorted(enums)
    for k, v in content['definitions'].items():
        create_model(result_dir, result_package, k, v, enums, templates, file_suffix, case, model)
    if len(content['definitions']) == 0:
        filename = os.path.join(result_dir, 'model', 'dummy'+file_suffix)
        with open(filename, 'w') as f:
            f.write('''
// THIS FILE IS AUTO-GENERATED. DO NOT EDIT
package ai.verta.swagger.%s.model
case class dummy()
''' % result_package)

def create_model(result_dir, result_package, definition_name, definition, enums, templates, file_suffix, case, model):
    capitalized_definition_name = capitalize_first(definition_name)
    filename = os.path.join(result_dir, 'model', capitalized_definition_name+file_suffix)

    with open(os.path.join(templates, model)) as f:
        template = f.read()

    required = definition.get('required', list())
    properties = definition.get('properties', dict())
    properties = [{'name': keyword_safe(to_language_case(k, case)),
                   'required': k in required,
                   'type': dict(resolve_type(v), var_name=keyword_safe(to_language_case(k, case)))
                  } for k, v in properties.items()]
    properties = sorted(properties, key=lambda x: x['name'])
    for i, p in enumerate(properties):
        p.update({'last': i == len(properties)-1})

    info = {
        '__object_flag': False,
        '__enum_flag': False,
        'package': result_package,
        'class_name': capitalized_definition_name,
        'properties': properties,
        'object': True,
        'enums': [{'name': e} for e in enums],
        'enum_values': [{'name': e} for e in definition.get('enum', list())],
    }

    type_flag = "__"+definition['type']+"_flag"

    if definition['type'] == 'object':
        info['__object_flag'] = True
    elif 'enum' in definition:
        info['__enum_flag'] = True
    else:
        raise ValueError(definition['type'])

    with open(filename, 'w') as f:
        f.write(pystache.Renderer(partials=load_partials(templates)).render(template, info))

def create_typedef(**kwargs):
    return dict(kwargs, **{
        'is_list': kwargs.get('is_list', False),
        'is_basic': kwargs.get('is_basic', False),
        'is_map': kwargs.get('is_map', False),
        'string': kwargs.get('string', False),
        'integer': kwargs.get('integer', False),
        'long': kwargs.get('long', False),
        'double': kwargs.get('double', False),
        'float': kwargs.get('float', False),
        'any': kwargs.get('any', False),
        'is_custom': kwargs.get('custom', None) is not None,
    })

# TODO: generalize to types besides Scala
def resolve_type(typedef):
    if '$ref' in typedef:
        ref = typedef['$ref'].split('/')[-1]
        return create_typedef(custom={'name': capitalize_first(ref)})

    if 'schema' in typedef:
        return resolve_type(typedef['schema'])

    if typedef['type'] == 'string':
        if 'format' in typedef:
            # TODO: support long?
            if typedef['format'] == 'uint64':
                return create_typedef(long=True, is_basic=True)
            if typedef['format'] == 'int64':
                return create_typedef(long=True, is_basic=True)
        return create_typedef(string=True, is_basic=True)
    elif typedef['type'] == 'boolean':
        return create_typedef(boolean=True, is_basic=True)
    elif typedef['type'] == 'integer':
        if typedef['format'] == 'int32':
            return create_typedef(integer=True, is_basic=True)
        else:
            raise ValueError(typedef['format'])
    elif typedef['type'] == 'number':
        if typedef['format'] == 'double':
            return create_typedef(double=True, is_basic=True)
        elif typedef['format'] == 'float':
            return create_typedef(float=True, is_basic=True)
        else:
            raise ValueError(typedef['format'])
    elif typedef['type'] == 'array':
        return create_typedef(is_list=True, list_type=resolve_type(typedef['items']))
    elif typedef['type'] == 'object' and len(typedef) == 1:
        return create_typedef(generic=True)
    elif typedef['type'] == 'object' and 'additionalProperties' in typedef and 'properties' not in typedef:
        return create_typedef(is_map=True, map_key_type=resolve_type({'type': 'string'}), map_val_type=resolve_type(typedef['additionalProperties']))
    else:
        raise ValueError(typedef['type'])

def capitalize_first(s):
    return s[0].upper() + s[1:]

def to_language_case(base, case):
    if case == 'camel':
        return to_camel_case(base)
    if case == 'capital':
        return to_capital_case(base)
    if case == 'snake':
        return base
    raise ValueError(case)

def to_camel_case(snake_str):
    components = snake_str.split('_')
    # We capitalize the first letter of each component except the first one
    # with the 'title' method and join them together.
    return components[0] + ''.join(x.title() for x in components[1:])

def to_capital_case(snake_str):
    components = snake_str.split('_')
    # We capitalize the first letter of each component except the first one
    # with the 'title' method and join them together.
    return ''.join(x.title() for x in components)

if __name__ == "__main__":
    main()


# https://pythonbasics.org/transcribe-audio/

import speech_recognition as sr
import os
from pydub import AudioSegment
from pydub.utils import make_chunks
from os import path
import glob

def wav_to_text(prefix, filename, counter):

    output_filename = prefix + '_txt/' + prefix + '_' + str(counter) + '.txt'

    if path.exists(output_filename):
        # Skip already completed files
        return

    print('Beginning ' + output_filename)
    output_txt = open(output_filename, "w+")

    audio = AudioSegment.from_wav(filename)
    # recognize_sphinx works best on smaller chunks
    chunks = make_chunks(audio, 30000)

    for chunk in chunks:
        chunk_filename = 'temp_chunked_audio_wav/chunk.wav'
        chunk.export(chunk_filename, format='wav')

        r = sr.Recognizer()
        with sr.AudioFile(chunk_filename) as source:
            audio = r.record(source)  # read the chunk
            rec = r.recognize_sphinx(audio)  # recognize the chunk as text
            output_txt.write(rec)
        os.remove(chunk_filename)

    output_txt.close()

if __name__ == '__main__':

    SHAPIRO_PATH = 'ben_shapiro_wav'
    PSA_PATH = 'psa_wav'

    # This repetition could be refactored to obey DRY, being lazy...
    for i, file in enumerate(glob.glob(SHAPIRO_PATH + '/*.wav')):
        wav_to_text('ben_shapiro', file, i + 1)

    for i, file in enumerate(glob.glob(PSA_PATH + '/*.wav')):
        wav_to_text('psa', file, i + 1)


import cv2

f_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
image = cv2.imread('1.jpg')

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

faces = f_cascade.detectMultiScale(gray_image, scaleFactor=1.05, minNeighbors=5)

print(type(faces))
print(faces)

for x, y, w, h in faces:
    image = cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 3)

cv2.imshow("Example", image)

cv2.waitKey(0)

cv2.destroyAllWindows()


import pymysql

db = pymysql.connect('localhost', 'root', '', 'timeuser')
cursor = db.cursor()
cursor.execute("DROP TABLE IF EXISTS EMPLOYEE")
# 使用预处理语句创建表
sql = """CREATE TABLE EMPLOYEE (
         FIRST_NAME  CHAR(20) NOT NULL,
         LAST_NAME  CHAR(20),
         AGE INT,
         SEX CHAR(1),
         INCOME FLOAT )"""

cursor.execute(sql)
# 关闭数据库连接
db.close()

数据库/创建数据库表.py


#!/usr/bin/env python

import cgi
import sqlite3
import datetime

class HtmlResponse:

    def __init__(self):
        pass

    def build_response(self):
        html_text = """Content-type: text/html

        <!DOCTYPE html>

        <meta charset="utf-8">
        <title>entrance group | BioLabCorp</title>
        <link rel="stylesheet" href="../html/css/entrance_group.css">

            <form action="entrance_group.py" method="POST">

                    <input class="groupName" type="text" placeholder="Group name" name="groupName">

                    <input class="roomKeyNumber" type="password" placeholder="Room key number" name="keyNumber">

                    <button type="submit" class="back" onclick="location.href='../html/select_menu.html'">< Back</button>
                    <button type="submit" class="join">Join</button>

            </form>

"""
        return html_text

class DataBase:

    def __init__(self):
        # フルパスじゃないと上手くいかない...?
        # self.db_path = "/home/hal/Biolabcorp/Test/cgi-bin/db/test_db.sqlite"
        self.db_path = "./cgi-bin/db/test_db.sqlite"

    # DB の全データを読み込んで返す
    def read_db(self):
        conn = sqlite3.connect(self.db_path)
        cur = conn.cursor()

        ret = []
        try:
            for row in cur.execute("SELECT * FROM group_table"):
                ret.append(row)
        except:
            pass

        conn.close()
        return ret

def check_group(group_name, key_number):
    if group_name == "" or key_number == "":
        return False

    data_base = DataBase()
    for row in data_base.read_db():
        if row[1] == group_name and row[2] == key_number:
            return True

    return False

if __name__ == "__main__":
    form = cgi.FieldStorage()

    html_response = HtmlResponse()

    group_name = form.getvalue("groupName", "")
    key_number = form.getvalue("keyNumber", "")
    if check_group(group_name, key_number):
        html_text = """Content-type: text/html

        <!DOCTYPE html>

                <meta http-equiv="refresh" content="0; url=login.py">

        """
        print(html_text)
    else:
        print(html_response.build_response())


"""
Find IOTA lattice chromaticity by tracking
Intended for profiling
"""
import time
from pathlib import Path

import ocelot
import pyIOTA.lattice as lat
import pyIOTA.sixdsim as sixdsim
from ocelot.cpbd.elements import *

lattice, correctors, monitors, info, variables = sixdsim.parse_lattice(Path('IOTA_1NL_100MeV_v8.6.1.4.6ds'),
                                                                       verbose=False)
method = ocelot.MethodTM()
method.global_method = ocelot.SecondTM
box = lat.LatticeContainer(name='IOTA', lattice=lattice, correctors=correctors,
                           monitors=monitors, info=info, variables=variables, silent=False, method=method)
box.remove_elements(box.get_all(CouplerKick))
box.transmute_elements(box.get_all(Cavity), Drift)
box.transmute_elements(box.filter_elements('oq.*', Quadrupole), Drift)
box.transmute_elements(box.filter_elements('nl.*', Quadrupole), Drift)
box.merge_drifts()
box.update_element_positions()
box.lattice.update_transfer_maps()

tws = box.update_twiss()

print('Starting tracking...')
start = time.time()
chroma = lat.chromaticity(box.lattice, tws[0], method='track', method_kwargs={'n_turns': 2048})
print(f'Perf: ({time.time()-start : .4f}) - ({(time.time()-start)/1024 :.4f}) per turn')
print(chroma)


"""
Good example of:
    - type validation for lists of dictionaries
    - type validation for complex types (datetime, timedelta)
    - error handling
    - spaces/characters in key names (dictionary['indexing'] and namespace.indexing)
"""

from __future__ import annotations

import argparse
import json
from datetime import datetime, timedelta
from pathlib import Path

import ruamel.yaml
import tomli

import cargparse

def parse_config(filename: Path | str) -> cargparse.Namespace:
    def min_sec(arg: str) -> timedelta:
        try:
            minutes, seconds = arg.split(":")
        except:
            # nicely formatted error message including the key (--duration; keys are otherwise inaccessible)
            raise argparse.ArgumentTypeError(f"{arg} failed to parse (expected MM:SS)")
        return timedelta(minutes=int(minutes), seconds=int(seconds))

    def year_month_day(arg: str) -> datetime:
        return datetime.strptime(arg, "%Y %m %d")

    def list_member_namespace(args: str) -> list[cargparse.Namespace]:
        args = eval(args)  # we expect args to be a list
        parser = argparse.ArgumentParser()
        parser.add_argument("--name", type=str)
        parser.add_argument("--dob", type=year_month_day)
        # use the same parser for each element in the list
        return [cargparse.Cargparse(parser).parse_dict(item) for item in args]

    mode = "rb" if filename.suffix == ".toml" else "r"
    with open(filename, mode) as f:
        if filename.suffix == ".yaml":
            config_str = ruamel.yaml.safe_load(f)
        elif filename.suffix == ".json":
            config_str = json.load(f)
        elif filename.suffix == ".toml":
            config_str = tomli.load(f)

    parser = argparse.ArgumentParser()
    parser.add_argument("--title", type=str)
    parser.add_argument("--artist", type=str)
    parser.add_argument("--label", type=str)
    parser.add_argument("--genre", type=cargparse.list_str)
    parser.add_argument("--duration", type=min_sec)
    parser.add_argument("--release date", type=year_month_day)
    parser.add_argument("--members", type=list_member_namespace)  # or cargparse.list_dict for list[dict[str, str]]
    return cargparse.Cargparse(parser).parse_dict(config_str)

if __name__ == "__main__":

    extensions = ["json", "toml", "yaml"]
    configs = [parse_config(Path(__file__).parent / f"twice_fancy.{ext}") for ext in extensions]

    try:
        assert configs[0] == configs[1] == configs[2]
        print("All good!\n")
    except AssertionError as e:
        print(e.__class__.__name__)
        exit(1)

    config = configs[2]

    print(f"{config['release date'].strftime('%Y %m %d')} - TWICE's smash hit 'Fancy' released")
    print(f"{datetime.now().strftime('%Y %m %d')} - Today's date")

    years = (datetime.now() - config["release date"]).days // 365
    days = (datetime.now() - config["release date"]).days % 365
    print(f"-> {years} years and {days} day{'s' if days != 1 else ''} have elapsed since then\n")

    print("How old is each TWICE member?")
    ages = {member: datetime.now().year - member.dob.year for member in config.members}
    for member, age in sorted(ages.items(), key=lambda x: x[1], reverse=True):
        msg = f"{age} - {member.name}"
        if datetime.now().month == member.dob.month and datetime.now().day == member.dob.day:
            msg += " (today is her birthday!)"
        print(msg)


from torch import optim
from torch.optim import lr_scheduler
from .train import train
from .test import test

def learner(model, train_loader, test_loader, lambda_l1, epochs, device):
    train_losses = []
    test_losses = []
    train_acc = []
    test_acc = []

    optimizer = optim.SGD(model.parameters(), lr=0.015,
                          momentum=0.7)

    scheduler = lr_scheduler.StepLR(optimizer, step_size=9, gamma=0.1)
    epoch = epochs

    for epoch in range(1, epoch + 1):
        print(f'Epoch {epoch}:')

        train(model, device, train_loader, optimizer,
                train_acc, train_losses, lambda_l1)
        scheduler.step()
        test(model, device, test_loader, test_acc, test_losses)

    return (train_acc, train_losses, test_acc, test_losses), model


import logging
from enum import Enum, unique,auto

### Logging

logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s - %(levelname)s - %(message)s")

class Logging_level(Enum):
    debug = 1
    info = 2
    error = 3
    critical = 4
    warning = 5

def set_log(message, level=Logging_level.debug):
    check_type(Logging_level, level)
    switcher={
        Logging_level.debug: _logging_debug,
        Logging_level.info : _logging_info,
        Logging_level.error: _logging_error,
        Logging_level.critical: _logging_critical,
        Logging_level.warning: _logging_warning
    }
    func = switcher.get(level, lambda message: _logging_default(message))
    return func(message)

def _logging_debug(message):
    logging.debug(message)

def _logging_warning(message):
    logging.warning(message)

def _logging_critical(message):
    logging.critical(message)

def _logging_info(message):
    logging.info(message)

def _logging_error(message):
    logging.error(message)

def _logging_default(message):
    logging.warning(f"Logging level is not correctly set default value is warning :\n {message}")

def check_type(t, *args, allow_None=False):
    if allow_None:
        wrong_arg = [x for x in args if type(x) != t and x != None ]
    else:
        wrong_arg = [x for x in args if type(x) != t]
    if len(wrong_arg) > 0:
        raise TypeError(f" Arguments value {wrong_arg} should be an instance of {t}")

    return True


'''
Created on Jan 15, 2015

@author: Jay <smile665@gmail.com>
'''

from django.views.decorators.csrf import csrf_exempt
from rest_framework.request import Request
from lib.response import JSONResponse
from user.qq_user import get_user, add_update_user

@csrf_exempt
def get_user_by_openid(request):
    '''
    @summary: get a user by QQ openid.
    '''
    if request.method == 'GET':
        req = Request(request)
        openid = req.QUERY_PARAMS.get('openid', default='123abc')
        data = get_user(openid)
        return JSONResponse(data=data, status=200)
    else:
        return JSONResponse({'error': 'It only support HTTP GET method.'},
            status=200)

@csrf_exempt
def add_user(request):
    '''
    @summary: add (update) a user  from QQ login or .
    '''
    if request.method == 'POST':
        req = Request(request)
        data = req.DATA
        force = data.get('force', default=False)
        if force and force != '0':
            force = True
        else:
            force = False
        openid = add_update_user(data, force=force)
        return JSONResponse(data={'openid': openid}, status=200)
    else:
        return JSONResponse({'error': 'It only support HTTP POST method.'},
            status=200)