#!/usr/bin/env python3

import math
import warnings
import copy
import rospy
from nav_msgs.srv import GetPlan, GetMap
from nav_msgs.msg import GridCells, OccupancyGrid, Path
from geometry_msgs.msg import Point, Pose, PoseStamped

import priority_queue

class PathPlanner:

    def __init__(self):
        """
        Class constructor
        """
        ### REQUIRED CREDIT
        ## Initialize the node and call it "path_planner"
        rospy.init_node("lab3_path_node")

        ## Create a new service called "plan_path" that accepts messages of
        ## type GetPlan and calls self.plan_path() when a message is received
        self.plan_path_service = rospy.Service('plan_path', GetPlan, self.plan_path)

        ## Create a publisher for the C-space (the enlarged occupancy grid)
        ## The topic is "/path_planner/cspace", the message type is GridCells
        self.cspace_pub = rospy.Publisher('/path_planner/cspace', GridCells, queue_size=10)

        ## Create publishers for A* (expanded cells, frontier, ...)
        ## Choose the topic names, the message type is GridCells
        self.expanded_pub = rospy.Publisher('/path_planner/expanded', GridCells, queue_size=10)
        self.frontier_pub = rospy.Publisher('/path_planner/frontier', GridCells, queue_size=10)
        self.unexplored_pub = rospy.Publisher('/path_planner/unexplored', GridCells, queue_size=10)

        self.path_pub = rospy.Publisher('/path_planner/path', Path, queue_size=10)

        self.cspace_value = 3

        ## Sleep to allow roscore to do some housekeeping
        rospy.sleep(1.0)
        rospy.loginfo("Path planner node ready")

    @staticmethod
    def grid_to_index(mapdata, x, y):
        """
        Returns the index corresponding to the given (x,y) coordinates in the occupancy grid.
        :param x [int] The cell X coordinate.
        :param y [int] The cell Y coordinate.
        :return  [int] The index.
        """
        ### REQUIRED CREDIT
        within_bound = x < mapdata.info.width and \
                        y < mapdata.info.height and \
                            min(x,y) >= 0
        if not within_bound:
            warn_str = f"Error: grid out of bound {(x,y)}"
            warnings.warn(warn_str)
            return 0

        index = y * mapdata.info.width + x
        return index

    @staticmethod
    def index_to_grid(mapdata, index):
        width = mapdata.info.width
        height = mapdata.info.height
        map_array = mapdata.data
        if index >= len(map_array):
            warn_str = f"Error: index out of grid {index}"
            warnings.warn(warn_str)
            return (0,0)

        y = index // width
        x = index % height
        return (x,y)

    @staticmethod
    def euclidean_distance(x1, y1, x2, y2):
        """
        Calculates the Euclidean distance between two points.
        :param x1 [int or float] X coordinate of first point.
        :param y1 [int or float] Y coordinate of first point.
        :param x2 [int or float] X coordinate of second point.
        :param y2 [int or float] Y coordinate of second point.
        :return   [float]        The distance.
        """
        ### REQUIRED CREDIT
        return math.sqrt((x1-x2) ** 2 + (y1-y2) ** 2)

    @staticmethod
    def grid_to_world(mapdata, x, y):
        """
        Transforms a cell coordinate in the occupancy grid into a world coordinate.
        :param mapdata [OccupancyGrid] The map information.
        :param x       [int]           The cell X coordinate.
        :param y       [int]           The cell Y coordinate.
        :return        [Point]         The position in the world.
        """
        ### REQUIRED CREDIT
        point = Point()
        point.x = (x+0.5) * mapdata.info.resolution + mapdata.info.origin.position.x
        point.y = (y+0.5) * mapdata.info.resolution + mapdata.info.origin.position.y
        return point

    @staticmethod
    def world_to_grid(mapdata, wp):
        """
        Transforms a world coordinate into a cell coordinate in the occupancy grid.
        :param mapdata [OccupancyGrid] The map information.
        :param wp      [Point]         The world coordinate.
        :return        [(int,int)]     The cell position as a tuple.
        """
        ### REQUIRED CREDIT
        gc_x = int((wp.x - mapdata.info.origin.position.x) / mapdata.info.resolution)
        gc_y = int((wp.y - mapdata.info.origin.position.y) / mapdata.info.resolution)
        return (gc_x, gc_y)

    @staticmethod
    def path_to_poses(mapdata, path):
        """
        Converts the given path into a list of PoseStamped.
        :param mapdata [OccupancyGrid] The map information.
        :param  path   [[(int,int)]]   The path as a list of tuples (cell coordinates).
        :return        [[PoseStamped]] The path as a list of PoseStamped (world coordinates).
        """
        ### REQUIRED CREDIT
        world = []

        for coord in path:
            # Convert path tuple to world coord (converted is a Point())
            newPose = PoseStamped()
            converted = PathPlanner.grid_to_world(mapdata, coord[0], coord[1])
            newPose.pose.position.x = converted.x
            newPose.pose.position.y = converted.y
            newPose.header.frame_id = 'map'

            world.append(newPose)
        return world

    # TESTED
    @staticmethod
    def is_cell_walkable(mapdata, x, y):
        """
        A cell is walkable if all of these conditions are true:
        1. It is within the boundaries of the grid;
        2. It is free (not unknown, not occupied by an obstacle)
        :param mapdata [OccupancyGrid] The map information.
        :param x       [int]           The X coordinate in the grid.
        :param y       [int]           The Y coordinate in the grid.
        :return        [boolean]       True if the cell is walkable, False otherwise
        """
        ### REQUIRED CREDIT
        # walkable = False;
        # index = PathPlanner.grid_to_index(mapdata, x, y) # Converts the coordinates to indices in grid
        # if (index < mapdata.info.width and index > 0):
        #   walkable = True

        within_bound = x < mapdata.info.width and \
                        y < mapdata.info.height and \
                            min(x,y) >= 0

        index = PathPlanner.grid_to_index(mapdata, x, y)
        is_free = not mapdata.data[index]
        return within_bound & is_free

    @staticmethod
    def is_cell_free(mapdata, x, y):
        index = PathPlanner.grid_to_index(mapdata, x, y)
        is_free = mapdata.data[index] == 0 or mapdata.data[index] == -1
        return is_free

    @staticmethod
    def is_cell_not_wall(mapdata, x, y):
        within_bound = x < mapdata.info.width and \
                        y < mapdata.info.height and \
                            min(x,y) >= 0

        index = PathPlanner.grid_to_index(mapdata, x, y)
        is_wall = mapdata.data[index] == 100
        return within_bound and not is_wall

    # TESTED
    @staticmethod
    def neighbors_of_4(mapdata, x, y):
        """
        Returns the walkable 4-neighbors cells of (x,y) in the occupancy grid.
        :param mapdata [OccupancyGrid] The map information.
        :param x       [int]           The X coordinate in the grid.
        :param y       [int]           The Y coordinate in the grid.
        :return        [[(int,int)]]   A list of walkable 4-neighbors.
        """
        ### REQUIRED CREDIT
        possible_neighbors = []
        walkable_neighbors = []
        possible_neighbors.append((x+1, y))
        possible_neighbors.append((x-1, y))
        possible_neighbors.append((x, y+1))
        possible_neighbors.append((x, y-1))

        for neighbor in possible_neighbors:
            if (PathPlanner.is_cell_walkable(mapdata, neighbor[0], neighbor[1])):
                walkable_neighbors.append(neighbor)
        return walkable_neighbors

    # TESTED
    @staticmethod
    def neighbors_of_8(mapdata, x, y):
        """
        Returns the walkable 8-neighbors cells of (x,y) in the occupancy grid.
        :param mapdata [OccupancyGrid] The map information.
        :param x       [int]           The X coordinate in the grid.
        :param y       [int]           The Y coordinate in the grid.
        :return        [[(int,int)]]   A list of walkable 8-neighbors.
        """
        ### REQUIRED CREDIT
        possible_neighbors = []
        walkable_neighbors = PathPlanner.neighbors_of_4(mapdata, x, y)

        possible_neighbors.append((x+1, y+1))
        possible_neighbors.append((x-1, y+1))
        possible_neighbors.append((x+1, y-1))
        possible_neighbors.append((x-1, y-1))

        for neighbor in possible_neighbors:
            if (PathPlanner.is_cell_walkable(mapdata, neighbor[0], neighbor[1])):
                walkable_neighbors.append(neighbor)
        return walkable_neighbors

    # TESTED
    @staticmethod
    def request_map():
        """
        Requests the map from the map server.
        :return [OccupancyGrid] The grid if the service call was successful,
                                None in case of error.
        """
        ### REQUIRED CREDIT
        rospy.loginfo("Requesting the map")
        try:
            rospy.wait_for_service('dynamic_map') # Block until service is available
            grid = rospy.ServiceProxy('dynamic_map', GetMap)
        except Exception:
            print(f"Error when requesting map\n{Exception}")
            return None

        return grid().map

    # TESTED
    def calc_cspace(self, mapdata, padding):
        """
        Calculates the C-Space, i.e., makes the obstacles in the map thicker.
        Publishes the list of cells that were added to the original map.
        :param mapdata [OccupancyGrid] The map data.
        :param padding [int]           The number of cells around the obstacles.
        :return        [OccupancyGrid] The C-Space.
        """
        ### REQUIRED CREDIT
        rospy.loginfo("Calculating C-Space")
        cspace = OccupancyGrid()

        ## Go through each cell in the occupancy grid
        ## Inflate the obstacles where necessary
        width = mapdata.info.width
        height = mapdata.info.height
        resolution = mapdata.info.resolution
        paded_cell_list = []
        new_map_array = []

        for y in range(height):
            for x in range(width):

                # is wall, copy
                if not PathPlanner.is_cell_walkable(mapdata, x, y):
                    new_map_array.append(mapdata.data[PathPlanner.grid_to_index(mapdata, x, y)])
                    continue

                neighbor_all_free = True
                for pad_x in range(x - padding, x + padding+1):
                    if not neighbor_all_free:
                        break

                    for pad_y in range(y - padding, y + padding+1):
                        if not neighbor_all_free:
                            break

                        # see if neighbor is wall
                        within_bound = pad_x < mapdata.info.width and \
                            pad_y < mapdata.info.height and \
                            min(pad_x,pad_y) >= 0

                        if within_bound:
                            neighbor_all_free &= PathPlanner.is_cell_free(mapdata, pad_x, pad_y)
                # not neighbor with wall, unchanged
                if neighbor_all_free:
                    new_map_array.append(mapdata.data[PathPlanner.grid_to_index(mapdata, x, y)])
                else:
                    # value = 10 for cspace
                    new_map_array.append(10)
                    paded_cell_list.append((x,y))

        cspace.header.frame_id = "map"
        cspace.info = mapdata.info
        cspace.data = new_map_array
        # PathPlanner.print_map(cspace)

        ## Create a GridCells message and publish it
        grid_cell = GridCells()
        grid_cell.header.frame_id = "map"
        grid_cell.cell_width = resolution
        grid_cell.cell_height = resolution
        point_list = []

        for cell in paded_cell_list:
            point_list.append(PathPlanner.grid_to_world(mapdata, cell[0], cell[1]))
        grid_cell.cells = point_list

        self.cspace_pub.publish(grid_cell)
        ## Return the C-space
        return cspace

    def go_out_of_cspace(self, mapdata, coord):
        """if robot in C space, add a path to go out"""
        search_dir_list = [(1,0), (0,1), (-1,0), (0,-1), (1,1), (1,-1), (-1,-1), (-1,1)]

        for dir in search_dir_list:
            for dist in range(self.cspace_value):
                x = coord[0] + dir[0] * (dist+1)
                y = coord[1] + dir[1] * (dist+1)
                # is cell is wall, discard this direction
                if not PathPlanner.is_cell_not_wall(mapdata, x, y):
                    break
                if PathPlanner.is_cell_walkable(mapdata, x, y):
                    print(f"Out of cspace: {coord} -> {(x,y)}")
                    return (x, y)
        warn_str = f"Error: {coord} is trapped"
        warnings.warn(warn_str)
        return None

    def a_star(self, mapdata, start, goal):
        """
        return list of tuples (x,y) as path
        """
        ### REQUIRED CREDIT
        rospy.loginfo("Executing A* from (%d,%d) to (%d,%d)" % (start[0], start[1], goal[0], goal[1]))

        width = mapdata.info.width
        height = mapdata.info.height
        map_array = mapdata.data

        start_path = [start]
        if not PathPlanner.is_cell_walkable(mapdata, start[0], start[1]):
            warn_str = f"Error: Start grid is not walkable {start}"
            warnings.warn(warn_str)
            out_start = self.go_out_of_cspace(mapdata, start)
            if out_start is None:
                return []
            else:
                start_path = [start, out_start]
                start = out_start

        if not PathPlanner.is_cell_walkable(mapdata, goal[0], goal[1]):
            warn_str = f"Error: Goal grid is not walkable {goal}"
            warnings.warn(warn_str)
            return []

        h_list = [None] * (width * height)
        g_list = [None] * (width * height)
        f_list = [None] * (width * height)
        from_list = [None] * (width * height)
        visited_list = [None] * (width * height)
        all_path_list = [None] * (width * height)

        pq = priority_queue.PriorityQueue()

        start_index = PathPlanner.grid_to_index(mapdata, start[0], start[1])

        # calculate h
        for i in range(len(map_array)):
            x,y = PathPlanner.index_to_grid(mapdata, i)
            h_list[i] = (PathPlanner.euclidean_distance(x, y, goal[0], goal[1]))
        print("h calculated")

        # fill in start node
        g_list[start_index] = 0
        f_list[start_index] = g_list[start_index] + h_list[start_index]
        all_path_list[start_index] = start_path
        pq.put(start, f_list[start_index])

        frontier_plot_list = []
        visited_plot_list = []

        while not pq.empty():
            # pop current from frontier
            current = pq.get()
            current_i = PathPlanner.grid_to_index(mapdata, current[0], current[1])

            # retrieve final path
            if current == goal:
                rospy.loginfo("A* Path Found")

                path_list = all_path_list[current_i]
                print(f"Path: {path_list}")

                path_msg = self.path_to_message(mapdata, path_list)

                self.path_pub.publish(path_msg)
                # PathPlanner.print_num_value(mapdata, g_list)
                rospy.sleep(0.5)
                return path_list

            # get neighbors
            # neighbors = PathPlanner.neighbors_of_4(mapdata, current[0], current[1])
            neighbors = PathPlanner.neighbors_of_8(mapdata, current[0], current[1])

            # process neighbors
            for neib in neighbors:
                neib_i = PathPlanner.grid_to_index(mapdata,neib[0], neib[1])
                if visited_list[neib_i]:
                    continue

                # construct path from start to neib
                neighbor_path = copy.deepcopy(all_path_list[current_i])
                neighbor_path.append(neib)

                path_value = neighbor_path
                g_value = PathPlanner.calc_g_value(neighbor_path)
                x,y = PathPlanner.index_to_grid(mapdata, neib_i)
                h_list[neib_i] = (PathPlanner.euclidean_distance(x, y, goal[0], goal[1]))

                f_value = g_value + h_list[neib_i]

                # if successfully put, return True
                if pq.put(neib, f_value):
                    from_list[neib_i] = current
                    g_list[neib_i] = g_value
                    f_list[neib_i] = f_value
                    all_path_list[neib_i] = path_value
                    frontier_plot_list.append(neib)

            # current add to visited
            visited_list[current_i] = True
            visited_plot_list.append(current)

            # # plot process in Rviz
            # PathPlanner.publish_grid_cell(mapdata, self.frontier_pub, frontier_plot_list)
            # PathPlanner.publish_grid_cell(mapdata, self.expanded_pub, visited_plot_list)

            # rospy.sleep(0.02)
        return []

    @staticmethod
    def calc_g_value(from_path):
        """calculat g value based on real distance and number of turns"""
        turn_weight = 0.7
        walk_distance = 0
        turn_count = 0
        for i in range(1,len(from_path)):
            last = from_path[i-1]
            this = from_path[i]
            # calculate distance walked
            this_dist = math.sqrt((this[0]-last[0])**2 + (this[1]-last[1])**2)
            walk_distance += this_dist

            if i == len(from_path)-1:
                continue
            # calculate turns
            next = from_path[i+1]
            if not (this[0]-last[0] == next[0]-this[0] and \
                this[1]-last[1] == next[1]-this[1]):
                turn_count += 1

        g_value = walk_distance + turn_weight * turn_count
        return g_value

    @staticmethod
    def optimize_path(path):
        """
        Optimizes the path, removing unnecessary intermediate nodes.
        :param path [[(x,y)]] The path as a list of tuples (grid coordinates)
        :return     [[(x,y)]] The optimized path as a list of tuples (grid coordinates)
        """
        ### EXTRA CREDIT
        rospy.loginfo("Optimizing path")

        if len(path) == 0:
            warnings.warn("Path is empty")
            return []

        new_path = []
        new_path.append(path[0])
        for i in range(1,len(path)-1):
            last = path[i-1]
            this = path[i]
            next = path[i+1]
            dx1 = this[0]-last[0]
            dy1 = this[1]-last[1]
            dx2 = next[0]-this[0]
            dy2 = next[1]-this[1]

            # there is a turn
            if dx1 - dx2 != dy1 - dy2:
                new_path.append(this)

        new_path.append(path[-1])
        print(new_path)
        return new_path

    def path_to_message(self, mapdata, path):
        """
        Takes a path on the grid and returns a Path message.
        :param path [[(int,int)]] The path on the grid (a list of tuples)
        :return     [Path]        A Path message (the coordinates are expressed in the world)
        """
        ### REQUIRED CREDIT
        rospy.loginfo("Returning a Path message")

        msg = Path() # Create path msg

        msg.header.stamp = rospy.Time.now()
        msg.header.frame_id = 'map'
        msg.poses = PathPlanner.path_to_poses(mapdata, path) # Store poses using path_to_poses()

        return msg

    def plan_path(self, msg):
        """
        Plans a path between the start and goal locations in the requested.
        Internally uses A* to plan the optimal path.
        :param req
        """
        ## Request the map
        ## In case of error, return an empty path
        mapdata = PathPlanner.request_map()
        if mapdata is None:
            return Path()
        ## Calculate the C-space and publish it
        cspacedata = self.calc_cspace(mapdata, self.cspace_value)
        ## Execute A*
        start = PathPlanner.world_to_grid(mapdata, msg.start.pose.position)
        goal  = PathPlanner.world_to_grid(mapdata, msg.goal.pose.position)
        path  = self.a_star(cspacedata, start, goal)
        ## Optimize waypoints
        waypoints = PathPlanner.optimize_path(path)
        ## Return a Path message
        return self.path_to_message(mapdata, waypoints)

###################################### utils ##################################

    @staticmethod
    def print_map(mapdata):
        """Print map in terminal"""
        width = mapdata.info.width
        height = mapdata.info.height
        map_array = mapdata.data

        for i in range(height):
            for j in range(width):
                # print is up side down, need to print top to bottom
                value = map_array[PathPlanner.grid_to_index(mapdata, j, (height-i-1))]
                if value == 100:
                    print("#", end=" ")
                elif value == 0:
                    print(" ", end=" ")
                else:
                    print("?", end=" ")
            print("")

    @staticmethod
    def print_num_value(mapdata, value_list):
        """Print a numerical value for the whole map in terminal"""
        width = mapdata.info.width
        height = mapdata.info.height

        for i in range(height):
            for j in range(width):
                # print is up side down, need to print top to bottom
                value = value_list[PathPlanner.grid_to_index(mapdata, j, (height-i-1))]
                if value is None:
                    print("-", end="\t")
                # elif value == 0:
                #     print("0.0", end="\t")
                else:
                    print(round(value,1), end="\t")
                # if value == 100:
                #     print("#", end=" ")
                # elif value == 0:
                #     print(" ", end=" ")
                # else:
                #     print("?", end=" ")
            print("\n", end="")

    @staticmethod
    def modify_map(mapdata, x, y, value):
        # print(f"@ modify_map {(x,y,value)}")
        map_array = list(mapdata.data)
        map_array[PathPlanner.grid_to_index(mapdata, x, y)] = value

        new_mapdata = OccupancyGrid()
        new_mapdata.header = mapdata.header
        new_mapdata.info = mapdata.info
        new_mapdata.data = map_array

        return new_mapdata

    @staticmethod
    def get_test_map():
        mapdata = OccupancyGrid()
        mapdata.info.width = 4
        mapdata.info.height = 4
        mapdata.data = (0,0,0,0, 0,0,0,0, 100,0,0,0, 0,0,100,100)

        return mapdata

    @staticmethod
    def publish_grid_cell(mapdata, publisher, grid_list):
        grid_cell_msg = GridCells()
        grid_cell_msg.header.frame_id = "map"
        grid_cell_msg.cell_width = mapdata.info.resolution
        grid_cell_msg.cell_height = mapdata.info.resolution
        point_list = []
        for coord in grid_list:
            point_list.append(PathPlanner.grid_to_world(mapdata, coord[0], coord[1]))

        # print(f"Published Points: {point_list}")
        grid_cell_msg.cells = point_list

        publisher.publish(grid_cell_msg)
        rospy.sleep(0.005)

    def run(self):
        """
        Runs the node until Ctrl-C is pressed.
        """

        rospy.spin()

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


from rauth import OAuth2Service
from flask import current_app, url_for, request, redirect, json

class OAuthSignIn(object):
    providers = None

    def __init__(self, provider_name):
        self.provider_name = provider_name
        credentials = current_app.config['OAUTH_CREDENTIALS'][provider_name]
        self.consumer_id = credentials['id']
        self.consumer_secret = credentials['secret']

    def authorize(self):
        pass

    def callback(self):
        pass

    def get_callback_url(self):
        return url_for('auth.oauth_callback',
                       provider=self.provider_name,
                       _external=True)

    @classmethod
    def get_provider(self, provider_name):
        if self.providers is None:
            self.providers = {}
            for provider_class in self.__subclasses__():
                provider = provider_class()
                self.providers[provider.provider_name] = provider
        return self.providers[provider_name]

class FacebookSignIn(OAuthSignIn):
    def __init__(self):
        super(FacebookSignIn, self).__init__('facebook')
        self.service = OAuth2Service(
            name='facebook',
            client_id=self.consumer_id,
            client_secret=self.consumer_secret,
            authorize_url='https://graph.facebook.com/oauth/authorize',
            access_token_url='https://graph.facebook.com/oauth/access_token',
            base_url='https://graph.facebook.com/'
        )

    def authorize(self):
        return redirect(self.service.get_authorize_url(
            scope='email',
            response_type='code',
            redirect_uri=self.get_callback_url())
        )

    def callback(self):
        if 'code' not in request.args:
            return None, None, None
        oauth_session = self.service.get_auth_session(
            data={'code': request.args['code'],
                  'grant_type': 'authorization_code',
                  'redirect_uri': self.get_callback_url()},
            decoder=json.loads,
        )
        me = oauth_session.get('me?fields=id,email,name').json()
        return (
            'facebook$' + me['id'],
            me.get('name'),
            me.get('email'),
        )

class GoogleSignIn(OAuthSignIn):
    def __init__(self):
        super(GoogleSignIn, self).__init__('google')
        self.service = OAuth2Service(
            name='google',
            client_id=self.consumer_id,
            client_secret=self.consumer_secret,
            authorize_url='https://accounts.google.com/o/oauth2/v2/auth',
            base_url='https://www.googleapis.com/oauth2/v3/userinfo',
            access_token_url='https://www.googleapis.com/oauth2/v4/token'
        )

    def authorize(self):
        return redirect(self.service.get_authorize_url(
            scope='email',
            response_type='code',
            redirect_uri=self.get_callback_url())
        )

    def callback(self):
        if 'code' not in request.args:
            return None, None, None
        oauth_session = self.service.get_auth_session(
                data={'code': request.args['code'],
                      'grant_type': 'authorization_code',
                      'redirect_uri': self.get_callback_url()},
                decoder=json.loads
        )
        me = oauth_session.get('').json()

        if 'sub' not in me:
            raise ValueError("Error with OAuth callback: {}".format(me))

        return (
            'google$' + me['sub'],
            me['name'],
            me['email']
        )

class GithubSignIn(OAuthSignIn):
    def __init__(self):
        super(GithubSignIn, self).__init__('github')
        self.service = OAuth2Service(
            name='github',
            client_id=self.consumer_id,
            client_secret=self.consumer_secret,
            authorize_url='https://github.com/login/oauth/authorize',
            access_token_url='https://github.com/login/oauth/access_token',
            base_url='https://api.github.com/'
        )

    def authorize(self):
        return redirect(self.service.get_authorize_url(
            scope='user:email',
            response_type='code',
            redirect_uri=self.get_callback_url())
        )

    def callback(self):
        if 'code' not in request.args:
            return None, None, None
        oauth_session = self.service.get_auth_session(
            data={'code': request.args['code'],
                  'grant_type': 'authorization_code',
                  'redirect_uri': self.get_callback_url()},
        )
        me = oauth_session.get('user').json()

        email = me.get('email')
        if not email:
            me_emails = oauth_session.get('user/emails').json()
            email = next(iter(me_emails))
            email = email.get('email')

        return (
            'github$%s' % me['id'],
            me.get('name'),
            email,
        )


import sys
sys.path.append("../")
import pango
import pyspdf as pdf

text1 = "The quick brown fox jumps over the lazy dog"

text2 = """Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enimad minim veniam, quis nostrud exercitation ullamco laboris nisi utaliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nullapariatur. Excepteur sint occaecat cupidatat non proident, sunt inculpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enimad minim veniam, quis nostrud exercitation ullamco laboris nisi utaliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nullapariatur. Excepteur sint occaecat cupidatat non proident, sunt inculpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enimad minim veniam, quis nostrud exercitation ullamco laboris nisi utaliquip ex ea commodo consequat.
Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nullapariatur. Excepteur sint occaecat cupidatat non proident, sunt inculpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enimad minim veniam, quis nostrud exercitation."""

text3 = " ".join([text2]*10)

class SamplePDF(pdf.PDF):

    def __init__(self):
        pdf.PDF.__init__(self)
        self.text_list = []

    def _paginate(self, op, ctx):

        # create multiple text elements for long text
        self.text_list = pdf.Text(ctx,
            text3,
            fmt=pdf.TextFormat(size=10, justify=True, line_spacing=2,
                width=self.w-20),
        ).split(max_height=[100,self.h-30])

        self._set_page_count(len(self.text_list))

    def _draw_page(self, op, ctx, no):

        # show page number
        page_no = pdf.Text(ctx,
            "{} / {}".format(no+1, self._get_page_count()),
            pdf.TextFormat(size=8)
        )
        page_no.draw((self.w-page_no.w)/2, self.h-10)

        # first page
        if no == 0:
            pdf.Text(ctx,
                text1,
                fmt=pdf.TextFormat(size=20),
            ).draw(10,10)

            pdf.Text(ctx,
                text1,
                fmt=pdf.TextFormat(font="monospace", size=10, style="italic",
                    color="#ff0000"),
            ).draw(50,50)

            pdf.Text(ctx,
                text2,
                fmt=pdf.TextFormat(font="CMU Sans Serif", size=10,
                line_spacing=2, wrap=pango.WRAP_WORD, justify=True,
                width=self.w-20),
            ).draw(10,70)

            self.text_list[no].draw(10,self.h-20-100)

        # all other pages
        else:
            self.text_list[no].draw(10,10)

try:
    samplepdf = SamplePDF()
    samplepdf.save_to_file("example_text.pdf")
except pdf.RenderError as e:
    print(e.code, e.args)


from Combat import Combat
from Nodcv import NodiatisCV
import time as Clock
import random, gc

from PyQt5.QtCore import pyqtSignal, QThread

#
COUNT_DOWN = 5

#
GAME_PAUSE = False

#
TOTAL_KILLS = 0

#
CURRENT_KILLS = 0

"""
"
"
" Parent: None
"""
class Game (QThread):

    """
    " This function initializes the Game class
    "
    " Parent: Game
    """
    def __init__(self, logger):
        super(Game, self).__init__()
        self.CMB = Combat(logger)
        self.nod_cv = NodiatisCV(logger)
        self.nod_log = logger

    """
    " This function automates nodiatis combat
    "
    " Parent: Game
    """
    def GameLoop(self):
        global CURRENT_KILLS

        self.startCountDown()

        while 1:
            while not GAME_PAUSE:
                try:
                    if self.nod_cv.doScreenMatch(self.nod_cv.SSQueries.get("ooc")) is not None:
                        self.CMB.start()
                    else:
                        exit = self.nod_cv.doScreenMatch(self.nod_cv.SSQueries.get("exit"))
                        if exit is not None:
                            self.randomBreak()
                            self.CMB.end(exit)
                            CURRENT_KILLS += 1
                        else:
                            self.CMB.inProcess()

                except Exception as e:
                    self.nod_log.logDebug("Exception occurred: ")
                    self.nod_log.logDebug(e)
                    # print e

                gc.collect() # initiate garbage collector > prevents fragmentation

            Clock.sleep(2) # wait for game to resume

    """
    " This function takes a random length break after a random number of kills
    "
    " Parent: Game
    """
    def randomBreak(self):
        global TOTAL_KILLS
        global CURRENT_KILLS

        #Random Break / Pause.. maybe
        rand_kills = random.randint(15, 40)
        rand_sleep = random.randint(10, 25)

        if CURRENT_KILLS > rand_kills:
            self.nod_log.logOutput("Taking break for %ds" %rand_sleep)
            Clock.sleep(rand_sleep)
            TOTAL_KILLS += CURRENT_KILLS
            CURRENT_KILLS = 0;

    """
    " This function prints the count down timer when the script will start
    "
    " Parent: Game
    """
    def startCountDown(self):
        lCount = COUNT_DOWN
        while(lCount > 0):
            self.nod_log.logOutput("Starting in %d seconds" %lCount)
            Clock.sleep(1)
            lCount -= 1

    """
    " This function toggles game loop status
    "
    " Parent: Game
    """
    def toggleGameStatus(self):
        global GAME_PAUSE

        if GAME_PAUSE:
            self.nod_log.logOutput("Resuming game")
            GAME_PAUSE = False
        else:
            self.nod_log.logOutput("Pausing game")
            GAME_PAUSE = True

    """
    " This function toggles which class ability is used in combat
    "
    " Parent: Game
    """
    def toggleSpecialMoveButton(self, aVal):
        self.CMB.special_move_button = aVal
        self.nod_log.logOutput("Class Ability changed to (%s)" %aVal)


import unittest
import mesh
from os import path
from os.path import dirname
from nose.plugins.attrib import attr

@attr(level = 'standard')
class TestMesh(unittest.TestCase):

    def setUp(self):
        pass

    def test_make_simple_hexagonal_mesh(self):
        """create a 2 by 2 hexagonal mesh and test the number
        of elements, their number of nodes, etc"""
        simple_mesh = mesh.creation.generate_hexagonal(2,2)

        # plotting doesn't work without ids assigned to it

        simple_mesh.plot(path.join(dirname(__file__),'output','hexagonal_mesh.pdf'))
        self.assertEqual(simple_mesh.get_num_nodes(), 16)
        self.assertEqual(simple_mesh.get_num_elements(), 4)
        for element in simple_mesh.elements:
            self.assertEqual(element.get_num_nodes(), 6)

    def test_nodes_are_ordered_counter_clockwise_in_each_element(self):
        """Create a simple mesh and make sure that the nodes are ordered clockwise
        in each element.
        """
        simple_mesh = mesh.creation.generate_hexagonal(7,10)
        for element in simple_mesh.elements:
            self.assertEqual(element.calculate_area(), 1.0)

        second_simple_mesh = mesh.creation.generate_hexagonal(3,3)
        for element in second_simple_mesh.elements:
            self.assertEqual(element.calculate_area(), 1.0)

    def test_generate_vornoi_padding_centroids(self):
        """test whether the random mesh generator draws the padding centroids correctly"""

        random_mesh_generator = mesh.creation.RandomTesselationGenerator(5,3)
        padding_centroids = random_mesh_generator.generate_padding_voronoi_centroids()

        for centroid in padding_centroids:
            assert( ( centroid[0] < -3.5 and centroid[0] > -5.5 ) or
                    ( centroid[0] > 9.5 and centroid[0] < 11.5 ) or
                    ( centroid[1] < -3.5 and centroid[1] > -5.5 ) or
                    ( centroid[1] > 7.5 and centroid[1] < 9.5 ) )

            if ( centroid[0] < -3.5 and centroid[0] > -5.5 ) :
                assert( centroid[1] > -5.5 and centroid[1] < 9.5 )
            elif ( ( centroid[0] > 9.5 and centroid[0] < 11.5 ) ):
                assert( centroid[1] > -5.5 and centroid[1] < 9.5 )
            elif ( ( centroid[1] < -3.5 and centroid[1] > -5.5 ) ):
                assert( centroid[0] > -5.505 and centroid[1] < 11.5 )
            elif ( ( centroid[1] > 7.5 and centroid[1] < 9.5 ) ):
                assert( centroid[0] > -5.505 and centroid[1] < 11.5 )

    def test_create_random_tesselation(self):
        """Create a mesh following Luisma Escuderos algorithm for
        mesh creation using voronoi tesselations"""

        random_mesh = mesh.creation.generate_random_tesselation(15,10, 5)

        random_mesh.plot(path.join(dirname(__file__),'output','random_mesh.pdf'))
        for element in random_mesh.elements:
            self.assertGreater(element.calculate_area(), 0.0)
            for node in element.nodes:
                self.assertNotAlmostEqual(node.position[0], -10.101)
                self.assertNotAlmostEqual(node.position[1], -10.101)
            this_centroid = element.calculate_centroid()
            self.assertGreater(this_centroid[0] , 0.0)
            self.assertGreater(this_centroid[1] , 0.0)
            self.assertLess(this_centroid[0] , 15.5)
            self.assertLess(this_centroid[1] , 10.5)

        average_cell_area = random_mesh.calculate_average_element_area()
        self.assertGreater(average_cell_area, 0.9)
        self.assertLess(average_cell_area, 1.1)

        total_area = random_mesh.calculate_total_area()
        self.assertGreater(total_area, 135)
        self.assertLess(total_area, 165.0)

        self.assertGreater(random_mesh.get_num_elements(), 135)
        self.assertLess(random_mesh.get_num_elements(), 165)


import datetime
import sys
import struct
import pyqtgraph as pg
from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
    QMainWindow, QApplication, QWidget, QPushButton, QGridLayout, QComboBox,
    QLineEdit, QMessageBox, QTextEdit, QLabel, QCheckBox
)
import serial
import serial.tools.list_ports as port_find

# 帧头
frame_b_1 = b'\xfa'
frame_b_2 = b'\xac'

# 帧尾
frame_e_1 = b'\xcc'
frame_e_2 = b'\x89'

def get_serial_com():
    portlist = list(port_find.comports())
    return [port[0] for port in portlist]

class SerialThread(QThread):
    data_received = pyqtSignal(bytes)

    def __init__(self, port, baud_rate):
        super().__init__()
        self.new_data = None
        self.port = port
        self.baud_rate = baud_rate
        self.running = True

    def run(self):
        with serial.Serial(
                port=self.port, baudrate=self.baud_rate,
                parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=None
        ) as ser:
            while self.running:
                self.new_data = ser.read(10)
                self.data_received.emit(self.new_data)

    def stop(self):
        self.running = False

class SerialDebugger(QMainWindow):
    def __init__(self):
        super().__init__()
        self.data_frame = None
        self.poltch = None
        self.bound_rate = None
        self.plot_widget = None
        self.ser = None
        self.serialbox = None
        self.data_buffer = b''
        self.init_ui()
        self.plot_data = [[], [], [], []]
        self.serial_thread = None

    def open_serial(self):
        port = self.serialbox.currentText()
        bound_str = self.bound_rate.text()
        try:
            bound = int(bound_str)
            self.serial_thread = SerialThread(port, bound)
            self.serial_thread.data_received.connect(self.process_received_data)
            self.serial_thread.start()
            QMessageBox.information(None, 'Success', '串口已打开')
        except ValueError:
            QMessageBox.critical(None, 'Error', '波特率为整数')

    def process_received_data(self, new_data):
        self.data_buffer += new_data
        self.process_data_buffer()

        current_time = datetime.datetime.now().strftime('%H:%M:%S.%f')[:-3]
        data_str = f'[{current_time}] \
            {" ".join(format(byte, "02x").upper() for byte in new_data)}
'

        if self.rx_show.isChecked():
            self.text_edit.insertHtml(data_str)
            self.scroll_to_bottom()

    def closeEvent(self, event):
        if self.serial_thread and self.serial_thread.isRunning():
            self.serial_thread.stop()
            self.serial_thread.wait()

        super().closeEvent(event)

    def init_ui(self):
        central_widget = QWidget(self)
        grid = QGridLayout()
        central_widget.setLayout(grid)
        self.setCentralWidget(central_widget)
        self.setWindowTitle("Serial Debugger")
        self.setWindowIcon(QIcon('main.png'))
        self.setGeometry(200, 100, 1200, 720)
        grid.setSpacing(10)

        self.serialbox = QComboBox()
        self.serialbox.addItems(get_serial_com())

        button = QPushButton("打开串口")
        button.clicked.connect(self.open_serial)

        self.bound_rate = QLineEdit()
        self.bound_rate.setPlaceholderText("波特率")

        for i in range(15):
            grid.setColumnStretch(i, 1)

        for i in range(10):
            grid.setRowStretch(i, 1)

        self.plot_widget = pg.PlotWidget()
        self.plot_widget.showGrid(x=True, y=True)
        self.plot_widget.setBackground('w')

        self.poltch = QComboBox()
        self.poltch.addItems(["0", "1", "2", "3", "4"])

        chlabel = QLabel()
        chtxt = f'选择数据通道数'
        chlabel.setText(chtxt)
        chlabel.setAlignment(Qt.AlignBottom)

        self.rx_show = QCheckBox('RX ON')
        self.tx_show = QCheckBox('TX ON')

        self.text_edit = QTextEdit()
        self.text_edit.setReadOnly(True)

        self.tx_edit = QLineEdit()

        button_tx = QPushButton("发送")
        button_tx.clicked.connect(self.serial_send)

        self.sendtypebox = QComboBox()
        self.sendtypebox.addItems(["ABC", "HEX"])

        grid.addWidget(self.sendtypebox, *[9, 13], 1, 1)
        grid.addWidget(button_tx, *[9, 14], 1, 1)
        grid.addWidget(self.text_edit, *[7, 1], 2, 14)
        grid.addWidget(self.tx_edit, *[9, 1], 1, 12)
        grid.addWidget(self.plot_widget, *[0, 1], 7, 14)
        grid.addWidget(self.serialbox, *[0, 0])
        grid.addWidget(self.bound_rate, *[1, 0])

        grid.addWidget(button, *[2, 0])
        grid.addWidget(self.poltch, *[4, 0])
        grid.addWidget(chlabel, *[3, 0])
        grid.addWidget(self.rx_show, *[7, 0])
        grid.addWidget(self.tx_show, *[9, 0])

        self.ch1 = self.plot_widget.getPlotItem()
        self.ch2 = self.plot_widget.getPlotItem()
        self.ch3 = self.plot_widget.getPlotItem()
        self.ch4 = self.plot_widget.getPlotItem()

        self.plot_data = [[], [], [], []]
        self.plot_curve1 = self.ch1.plot(self.plot_data[0])
        self.plot_curve1.setPen(pg.mkPen(color='red', width=2, style=pg.QtCore.Qt.SolidLine))
        self.plot_curve2 = self.ch2.plot(self.plot_data[1])
        self.plot_curve2.setPen(pg.mkPen(color='blue', width=2, style=pg.QtCore.Qt.SolidLine))
        self.plot_curve3 = self.ch3.plot(self.plot_data[2])
        self.plot_curve3.setPen(pg.mkPen(color='green', width=2, style=pg.QtCore.Qt.SolidLine))
        self.plot_curve4 = self.ch4.plot(self.plot_data[3])
        self.plot_curve4.setPen(pg.mkPen(color='black', width=2, style=pg.QtCore.Qt.SolidLine))

        self.show()
        self.timer = self.startTimer(10)
        self.plot_timer = QTimer(self)
        self.plot_timer.timeout.connect(self.plot_update)
        self.plot_timer.start(100)

    def scroll_to_bottom(self):
        scrollbar = self.text_edit.verticalScrollBar()
        scrollbar.setValue(scrollbar.maximum())

    def process_data_buffer(self):
        start_index_1 = self.data_buffer.find(frame_b_1)
        start_index_2 = self.data_buffer.find(frame_b_2)
        end_index_1 = self.data_buffer.find(frame_e_1)
        end_index_2 = self.data_buffer.find(frame_e_2)

        if start_index_2 - start_index_1 == 1 and end_index_2 - end_index_1 == 1:
            self.data_frame = self.data_buffer[start_index_2 + 1:end_index_1]
            self.data_buffer = self.data_buffer[end_index_2 + 1:]
            print(self.data_frame)
        else:
            self.data_buffer = self.data_buffer[end_index_2 + 1:]
        # if len(self.data_buffer) > 1000:
        #     self.data_buffer = b''

    def serial_send(self):
        if self.ser != None:
            if self.ser.isOpen():
                txtype = self.sendtypebox.currentText()
                txcontent = self.tx_edit.text()
                if txtype == "HEX":
                    content = bytes.fromhex(txcontent)
                    self.ser.write(content)
                    current_time = datetime.datetime.now().strftime('%H:%M:%S.%f')[:-3]
                    data_str = f'[{current_time}] \
                                    {" ".join(format(byte, "02x").upper() for byte in content)}
'
                    if self.tx_show.isChecked():
                        self.text_edit.insertHtml(data_str)
                    print(content)
                if txtype == "ABC":
                    content = bytes(txcontent, "UTF-8")
                    self.ser.write(content)
                    current_time = datetime.datetime.now().strftime('%H:%M:%S.%f')[:-3]
                    data_str = f'[{current_time}] \
                                    {" ".join(format(byte, "02x").upper() for byte in content)}
'
                    if self.tx_show.isChecked():
                        self.text_edit.insertHtml(data_str)
                    print(content)
        else:
            QMessageBox.information(None, "ATTITION", '先打开串口哦')

    def plot_update(self):
        ch = self.poltch.currentIndex()
        if ch != 0 and self.data_frame != None:
            if len(self.data_frame) != 0:
                data = struct.unpack("h" * ch, self.data_frame[:ch * 2])
                data_len = len(data)
                for i in range(data_len):
                    self.plot_data[i].insert(0, data[i])

                self.plot_curve1.setData(self.plot_data[0])
                self.plot_curve2.setData(self.plot_data[1])
                self.plot_curve3.setData(self.plot_data[2])
                self.plot_curve4.setData(self.plot_data[3])

if __name__ == "__main__":
    app = QApplication(sys.argv)
    sd = SerialDebugger()
    sys.exit(app.exec_())


from django.urls import path

from movie.views import (CinemaDetail, CinemaList, MovieDetail, MovieList,
                         ShowtimeDetail, ShowtimeList)

urlpatterns = [
    path("", MovieList.as_view(), name="movies"),
    path("<int:pk>/", MovieDetail.as_view(), name="movie-detail"),
    path("cinema/", CinemaList.as_view(), name="cinemas"),
    path("cinema/<int:pk>/", CinemaDetail.as_view(), name="cinema-detail"),
    path("showtime/", ShowtimeList.as_view(), name="showtimes"),
    path("showtime/<int:pk>/", ShowtimeDetail.as_view(), name="showtime-detail"),
]


# -*- coding: utf-8 -*-
"""Module containing the FieldPlot class for scalar fields on the Earth

This class plots scalar variables (e.g. temperature) on the globe. Optionally a
overlayed vector field (e.g. wind) can be plotted as well."""
from itertools import izip
import numpy as np
import netCDF4 as nc
import matplotlib.pyplot as plt
from matplotlib.tri import Triangulation
import matplotlib as mpl
from .._basemap import Basemap
from copy import deepcopy
from ..formatoptions import FieldFmt, FmtBase, WindFmt, _get_fmtkeys_formatted
from ..warning import warnings, Nc2MapWarning, warn
from .._cmap_ops import get_cmap
from _map_base import MapBase, _props, returnbounds
from _windplot import WindPlot

class FieldPlot(MapBase):
    """Class to visualize two-dimensional scalar fields on a map. Optionally
    you can also combine this with a nc2map.mapos.WindPlot instance (see
    __init__ methods for details."""
    # ------------------ define properties here -----------------------
    var = _props.dim('var', """String. Variable name in the reader""")

    @property
    def meta(self):
        meta = super(FieldPlot, self).meta
        if self.wind is not None:
            for key, val in self.wind.meta.items():
                meta.setdefault(key, val)
        return meta

    def __init__(self, reader, var, u=None, v=None, fmt={}, **kwargs):
        """
        Input:
            - var: string. Name of the variable which shall be plotted and red
                the netCDF file
            - u: string (Default: None). Name of the zonal wind variable if a
                 WindPlot shall be visualized (set u and v to also plot the
                 wind above the data of var)
            - v: string (Default: None). Name of the meridional wind variable
                 if a WindPlot shall be visualized
            - fmt: dictionary containing the formatoption keywords as keys and
                 settings as value.
        Keywords inherited from MapBase initialization are: %(mapbase)s
        Possible formatoption keywords are (see nc2map.show_fmtdocs() for
        details): %(fmt)s
        And the windplot specific options are (see nc2map.show_fmtdocs('wind')
        for details): %(windfmt)s
        """
        # docstring is extended below with the formatoption keywords
        self.name = kwargs.get('name', 'mapo')
        self.set_logger()

        kwargs_keys = ['var', 'u', 'v', 'fmt']
        max_kwarg_len = max(map(len, kwargs_keys)) + 2
        self.logger.debug("Original kwargs:")
        for key in kwargs_keys:
            self.logger.debug('    %s:'.ljust(max_kwarg_len)  + '%s',
                              key, str(locals()[key]))

        super(FieldPlot, self).__init__(reader, fmt=fmt, var=var, **kwargs)
        # define formatoptions
        if isinstance(fmt, dict):
            var = fmt.get('var', var)  # update from variables
            updatewind = u is not None and v is not None
            self.fmt = FieldFmt(**dict(
                item for item in fmt.items() + [('updatewind', updatewind)]
                if item[0] not in self.dims))
            self.logger.debug(
                "Dimensions set up by formatoptions dictionary:")

        elif not fmt:
            self.fmt = FieldFmt()
        else:
            if not isinstance(fmt, FieldFmt):
                warn('Expected FieldFmt instance but found %s for '
                     'formatoptions!' % str(type(fmt)))
            self.fmt = fmt

        # create basemap
        self.logger.debug(
            "Initialize Basemap instance with %s", self.fmt._projops)
        self.mapproj = Basemap(**self.fmt._projops)

        self.data = self.get_data(var)
        self._reinitialized = 1
        if u is not None and v is not None:
            self.wind = WindPlot(
                self.reader, time=self.time, level=self.level, ax=self._ax,
                fmt=self.fmt.windplot, u=u, v=v, mapsin=self._mapsin,
                name=self.name, mapproj=self.mapproj)
            self.wind._draw_mapproj = 0
        else:
            self.wind = None

    def _reinit(self, u=None, v=None, **kwargs):
        """Function to reinitialize the data"""
        # set _reinitialized for unstructured grids in make_plot
        self._reinitialized = 1
        self._reinitialize = 0
        super(FieldPlot, self)._reinit(**kwargs)
        zipped_kwargs = izip(['u', 'v'], [u, v])
        not_none_kwargs = (key for key, val in zipped_kwargs
                           if val is not None)
        self.logger.debug('Reinit %s', ', '.join(not_none_kwargs))
        self.data = self.get_data(self.var)
        if self.wind is not None:
            self.wind.mapproj = self.mapproj
            self.wind._reinit(u=u, v=v)

    def update(self, todefault=False, plot=True, force=False, **kwargs):
        """Update the MapBase instance, formatoptions, variable, time and
        level.
        Possible keywords (kwargs) are
          - all keywords as set by formatoptions (see initialization __init__)
          - time: integer. Sets the time of the MapBase instance and the data
          - level: integer. Sets the level of the MapBase instance and the data
          - var: string. Sets the variable of the MapBase instance and the data
        Additional keys in the windplot dictionary are:
          - u: string. Sets the variable of the WindPlot instance and reloads
              the data
          - v: Sets the variable of the WindPlot instance and reloads the data
          - time, level and var as above
        """
        if not hasattr(self, '_basemapops'):
            self.logger.info("Setting up projection...")
            self._setupproj()
        # set current axis
        plt.sca(self.ax)
        self.logger.debug('Updating...')
        self.logger.debug('Plot after finishing: %s', plot)
        # patterns for warnings that shall be depreciated

        # check the keywords
        dims = self.dims.copy()
        dimsorig = self.dims_orig
        for dim in self.data.dims.dtype.fields.keys():
            dims.setdefault(dim, 0)
            dimsorig.setdefault(dim, 0)
        possible_keys = self.fmt._default.keys() + dims.keys()
        for key in kwargs:
            self.fmt.check_key(key, possible_keys=possible_keys)

        # save current formatoptions
        old_fmt = self.fmt.asdict()

        # delete formatoptions which are already at the wished state
        if not todefault and not force:

            kwargs = {key: value for key, value in kwargs.items()
                      if (not isinstance(value, dict) and
                          np.all(value != old_fmt.get(
                          key, dims.get(key, self.fmt._default.get(key)))))}
        elif not force:
            self.logger.debug('Update to default...')
            oldkwargs = kwargs.copy()
            defaultitems = self.fmt._default.items()
            kwargs = {
                key: kwargs.get(key, value) for key, value in defaultitems
                if key != 'windplot' and (
                    (key not in kwargs and np.all(
                        value != getattr(self.fmt, key)))
                    or (key in kwargs and np.all(
                        kwargs[key] != getattr(self.fmt, key))))}
            if not self.fmt._enablebounds:
                kwargs = {key: value for key, value in kwargs.items()
                          if key not in ['cmap', 'bounds']}
            # update dimensions
            kwargs.update({
                key: oldkwargs.get(key, value)
                for key, value in dimsorig.items()
                if (dims[key] != dimsorig[key]
                    or (key in oldkwargs and oldkwargs[key] != dims[key]))})
            if 'windplot' in oldkwargs:
                kwargs['windplot'] = oldkwargs['windplot']
            else:
                kwargs['windplot'] = {}

        self.logger.debug("Update to ")
        try:
            max_kwarg_len = max(map(len, kwargs.keys())) + 3
        except ValueError:
            max_kwarg_len = 0
        for key, val in kwargs.items():
            self.logger.debug('    %s:'.ljust(max_kwarg_len)  + '%s',
                              key, str(val))

        try:  # False: Remove plot, True: make plot, None: do nothing
            enable = bool(kwargs['enable'])
            self.logger.debug('Updating enable to %s', enable)
            self.fmt.enable = enable
        except KeyError:
            enable = None  # no change

        # update plotting of cbar properties and close cbars
        if 'plotcbar' in kwargs:
            self.logger.debug(
                "Found 'plotcbar' in kwargs --> remove non-used cbars")
            if kwargs['plotcbar'] in [False, None]:
                kwargs['plotcbar'] = ''
            if kwargs['plotcbar'] is True:
                kwargs['plotcbar'] = 'b'
            cbars2close = [cbar for cbar in self.fmt.plotcbar
                          if cbar not in kwargs['plotcbar']]
            self._removecbar(cbars2close)

        # update extend and ticklabels
        if ('extend' in kwargs
                or kwargs.get('ticklabels', False) is None):
            self.logger.debug(
                "Found 'extend' or 'ticklabels' in kwargs --> remove cbars")
            self._removecbar(resize=False)

        # update masking options
        maskprops = {key: value for key, value in kwargs.items()
                     if key in self.fmt._maskprops}
        if maskprops:
            self.logger.debug(
                'Found masking properties: %s. --> reinit',
                ', '.join(maskprops.keys()))
            self.fmt.update(**maskprops)

        # update basemap properties
        bmprops = {key: value for key, value in kwargs.items()
                   if key in self.fmt._bmprops}
        if bmprops:
            self.logger.debug(
                'Found basemap properties: %s. --> reinit',
                ', '.join(bmprops.keys()))
            with warnings.catch_warnings():
                if self.wind is None and not 'windplot' in kwargs:
                    warnings.filterwarnings(
                        "ignore", 'Stereographic', Nc2MapWarning,
                        'nc2map.formatoptions._windfmt', 0)
                self.fmt.update(**bmprops)

        # update mapobject dimensions and reinitialize
        newdims = {key: value for key, value in kwargs.items()
                   if key in dims.keys()}
        if newdims:
            self.logger.debug(
            'Found new dimensions: %s. --> reinit',
            ', '.join(newdims.keys()))

        if newdims or bmprops or maskprops or enable or self._reinitialize:
            if bmprops:
                self.mapproj = Basemap(**self.fmt._projops)
            self._reinit(**newdims)
            self._make_plot = 1
        if ((bmprops or not hasattr(self, '_basemapops'))
                and plot):
            self._setupproj()

        self._make_plot = self._make_plot or set(kwargs) & set(
            ['cmap', 'bounds', 'norm', 'opacity', 'rasterized', 'grid'])

        # color oceans and land
        if any(key in kwargs for key in ['ocean_color', 'land_color']):
            self._draw_lsmask(kwargs.get('ocean_color', self.fmt.ocean_color),
                              kwargs.get('land_color', self.fmt.land_color))

        # handle plot enabling changing
        if enable is False:
            self.logger.debug("Found 'enable' in kwargs --> remove plot")
            self._removeplot()

        if ('grid' in kwargs or self._make_plot) and hasattr(self, 'grid'):
            for line in self.grid:
                line.remove()
            del self.grid

        # update rest
        self.logger.debug('Update fmtBase instance')
        fmt_update_kwargs = {key: value for key, value in kwargs.items()
                             if key not in self.fmt._bmprops + dims.keys()}
        for key in dims.keys():
            try:
                fmt_update_kwargs['windplot'].pop(key)
            except KeyError:
                pass
        self.fmt.update(**fmt_update_kwargs)

        # update ticks
        self._update_ticks(kwargs)

        # update map projection
        self._update_mapproj(kwargs=kwargs, plot=plot)

        # update wind
        if self.wind is not None or 'windplot' in kwargs:
            if 'windplot' in kwargs:
                self.logger.debug("Found 'windplot' in kwargs...")
                windops = kwargs['windplot']
                # if WindPlot is currently not set, enable it now
                if (windops.get('u', None) is not None
                        and windops.get('v', None) is not None
                        and self.fmt.windplot.enable
                        and self.wind is None):
                    self.logger.debug("No windplot set so far --> Set it now")
                    self.wind = WindPlot(
                        reader=self.reader, time=self.time, level=self.level,
                        ax=self._ax, fmt=self.fmt.windplot, u=windops['u'],
                        v=windops['v'], mapsin=self._mapsin, name=self.name,
                        mapproj=self.mapproj)
                    self.wind._draw_mapproj = 0
                for key in ['time', 'level']:
                    if key in kwargs:
                        windops.setdefault(key, kwargs[key])
            else:
                windops = {}
            if self.wind is not None:
                self.logger.debug('Updating wind...')
                windops.update({key: getattr(self.fmt, '_'+key) for key in
                                    self.fmt._bmprops})
                self.wind.update(plot=False, todefault=todefault, **windops)
                self.wind._make_plot = self._make_plot or self.wind._make_plot
        if plot:
            self.make_plot()
        else:
            self._make_plot = 0

        self.logger.debug('Update Done.')

    def _mask_data(self, data):
        data = super(FieldPlot, self)._mask_data(data)
        if self.fmt.maskless is not None:
            self.logger.debug('Masking below %s', self.fmt.maskless)
            data[:] = np.ma.masked_less(data[:], self.fmt.maskless, copy=True)
        if self.fmt.maskleq is not None:
            self.logger.debug('Masking below or equal to %s',
                              self.fmt.maskleq)
            data[:] = np.ma.masked_less_equal(data[:], self.fmt.maskleq,
                                           copy=True)
        if self.fmt.maskgreater is not None:
            self.logger.debug('Masking above %s', self.fmt.maskgreater)
            data[:] = np.ma.masked_greater(data[:], self.fmt.maskgreater,
                                           copy=True)
        if self.fmt.maskgeq is not None:
            self.logger.debug('Masking above or equal to %s',
                              self.fmt.maskgeq)
            data[:] = np.ma.masked_greater_equal(data[:], self.fmt.maskgeq,
                                              copy=True)
        if self.fmt.maskbetween is not None:
            self.logger.debug('Masking between %s', str(self.fmt.maskbetween))
            data[:] = np.ma.masked_inside(data[:], self.fmt.maskbetween[0],
                                       self.fmt.maskbetween[1], copy=True)
        return data

    def _moviedata(self, times, nowind=False, **kwargs):
        """generator to get the data for the movie"""
        try:
            windops = kwargs.pop('windplot')
        except KeyError:
            windops = {}
        kwargs = {key: iter(val) for key, val in kwargs.items()}
        windops = {key: iter(val) for key, val in windops.items()}
        for i, time in enumerate(times):
            # yield time step, data, formatoptions
            fmt = {key: next(value) for key, value in kwargs.items()}
            fmt.update({key: next(value) for key, value in windops.items()})
            yield (time, fmt)

    def _setupproj(self):
        # docstring is set equal to MapBase _setupproj method below
        super(FieldPlot, self)._setupproj()
        if self.wind is not None:
            self.wind.mapproj = self.mapproj

    def _runmovie(self, args):
        """Function to update the movie with args from self._moviedata"""
        if 'windplot' not in args[-1]:
            args[-1]['windplot'] = {}
        self.update(time=args[0], **args[-1])
        return

    def _removeplot(self):
        """Removes the plot from the axes and deletes the plot property from
        the instance"""
        self.logger.debug("    Removing plot...")
        if hasattr(self, 'plot'):
            try:
                self.plot.remove()
            except:
                pass
            try:
                for line in self.grid:
                    line.remove()
                del self.grid
            except AttributeError:
                pass
            del self.plot
        self._removecbar()

    def make_plot(self):
        """Make the plot with the current settings and the WindPlot. Use it
        after reinitialization and _setupproj. Don't use this function
        to update the plot but rather the update function!"""
        self.logger.debug('Making plot...')
        if self.fmt.enable and self._make_plot:
            if not hasattr(self, '_basemapops'):
                self.logger.info("Setting up projection...")
                self._setupproj()
            plt.sca(self.ax)
            if not self.fmt._enablebounds:
                pass
            elif self.fmt.bounds[0] in ['rounded', 'sym', 'minmax',
                                      'roundedsym']:
                self.logger.debug('    Calculate bounds with %s',
                                  str(self.fmt.bounds))
                self._bounds = returnbounds(self.data[:], self.fmt.bounds)
            else:
                self.logger.debug('   Found manually set bounds')
                self._bounds = self.fmt.bounds
            if self.fmt._enablebounds:
                if self.fmt.norm == 'bounds':
                    self._cmap = get_cmap(self.fmt.cmap, len(self._bounds)-1)
                    self._norm = mpl.colors.BoundaryNorm(self._bounds,
                                                         self._cmap.N)
                else:
                    self._cmap = get_cmap(self.fmt.cmap)
                    if self.fmt.norm is None:
                        self._norm = mpl.colors.Normalize()
                        self._norm.vmin = self._bounds.min()
                        self._norm.vmax = self._bounds.max()
                    else:
                        self._norm = self.fmt.norm
                if self.fmt.opacity is not None:
                    self._calculate_opacity()
            if hasattr(self, 'plot'):
                self.logger.debug('    Remove plot')
                self.plot.remove()
                # does not work atm
                # self.plot.set_cmap(self._cmap)
                # self.plot.set_norm(self._norm)
                # self.plot.set_array(self.data[:-1,:-1].ravel())
            # else:
                # make plot
            self.logger.debug('    Draw plot')
            if (self.reader._udim(self.get_var())
                    and self.fmt.plottype == 'quad'):
                self.logger.debug(
                    "Found unstructured variable. I set the 'plottype' "
                    "keyword to 'tri'")
                self.fmt.plottype = 'tri'
            if self.fmt.plottype == 'quad':
                if self.fmt.grid:
                    settings = {'edgecolors': self.fmt.grid}
                else:
                    settings = {'edgecolors': 'face'}
                lat2d, lon2d = self.data.grid
                self.plot = self.mapproj.pcolormesh(
                    lon2d, lat2d, self.data[:], cmap=self._cmap,
                    norm=self._norm, rasterized=self.fmt.rasterized,
                    latlon=self.fmt.latlon, **settings)
            elif self.fmt.plottype == 'tri':
                if self.data.triangles:
                    triangles = self.data.triangles
                    self.plot = self.mapproj.tripcolor(
                        triangles, self.data[:], cmap=self._cmap,
                        norm=self._norm, rasterized=self.fmt.rasterized,
                        alpha=None,
                        latlon=False if not self._reinitialized else \
                            self.fmt.latlon)
                else:
                    triangles = Triangulation(self.data.lon, self.data.lat)
                    self.plot = self.mapproj.tripcolor(
                        triangles, self.data[:], cmap=self._cmap,
                        norm=self._norm, rasterized=self.fmt.rasterized,
                        latlon=self.fmt.latlon)
                if self.fmt.grid:
                    try:
                        self.grid = self.mapproj.triplot(
                            triangles, latlon=False, **self.fmt.grid)
                    except TypeError:
                        self.grid = self.mapproj.triplot(
                            triangles, self.fmt.grid, latlon=False)

            else:
                raise ValueError("Unknown plottype %s" % self.fmt.plottype)

        if self.fmt.enable and not (
                self.fmt.plotcbar == '' or self.fmt.plotcbar == ()
                or self.fmt.plotcbar is None or self.fmt.plotcbar is False):
            self._draw_colorbar()

        if self.wind is not None:
            if self.wind.fmt.enable:
                self.logger.debug('Wind is enabled --> make windplot')
                if not hasattr(self, "_basemapops"):
                    self.wind._setupproj()
                self.wind.make_plot()

        self._configureaxes()
        self._make_plot = 0
        self._reinitialized = 0
        if self.fmt.tight:
            self.logger.debug("'tight' is True --> make tight_layout")
            plt.tight_layout()

    def close(self, num=0):
        """Close the MapBase instance.
        Arguments may be
        - 'data': To delete all data (but not close the netCDF4.MFDataset
        instance)
        - 'figure': To close the figure and the figures of the colorbars.
        Without any arguments, everything will be closed (including the
        netCDF4.MFDataset instance) and deleted.
        """
        self.logger.debug("Closing...")
        super(FieldPlot, self).close(num)
        if not num % 7:
            num /= 7
        if self.wind is not None:
            self.wind.close(num)

    def asdict(self, **kwargs):
        """Returns a dictionary containing the current formatoptions and (if
        the time, level or var changed compared to the original
        initialization) the time, level or var"""
        fmt = super(FieldPlot, self).asdict(**kwargs)
        #if self.var != self.varorig:
            #fmt.update({'var': self.var})
        if self.wind is not None:
            fmt.update({'windplot': self.wind.asdict()})
        return fmt

    def copy(self, *args, **kwargs):
        """Copy method. Docstring is set below to be equal to the MapBase.copy
        method"""
        mapo = super(FieldPlot, self).copy(*args, **kwargs)
        if self.wind is not None:
            mapo.wind = self.wind.copy(*args, **kwargs)
        return mapo

    # ------------------ modify docstrings here --------------------------
    __init__.__doc__ += __init__.__doc__ % {
        'fmt': '\n' + _get_fmtkeys_formatted(),
        'windfmt': '\n' + _get_fmtkeys_formatted('wind', 'windonly'),
        'mapbase': '\n' + MapBase.__init__.__doc__}
    _setupproj.__doc__ = MapBase._setupproj.__doc__
    copy.__doc__ = MapBase.copy.__doc__


import RPi.GPIO as GPIO
from flask import Flask, render_template, Response, request
from time import sleep
import serial
from MotorModule import Motor
from LaneDetectionModule import getLaneCurve
import WebcamModule
import cv2
import multiprocessing

initi = 0
userValue = 0
kart = 0
i = 0
app = Flask(__name__)

def main1():
    motor = Motor(13,19,16,20)
    while True:
        img = WebcamModule.getImg()
        curveVal = getLaneCurve(img,1)
        sleep(0.01)
        sen = 5 # SENSITIVITY
        maxVAl= 0.35 # MAX SPEED
        if curveVal>maxVAl:curveVal = maxVAl
        if curveVal<-maxVAl: curveVal =-maxVAl
        print('curvel')
        print(curveVal)
        if curveVal>0:
            if curveVal<0.05: curveVal=0
        else:
            if curveVal>-0.08: curveVal=0
        motor.move(2,curveVal*sen,0.1)
        print('motor commend sent')
        cv2.waitKey(1)

process = multiprocessing.Process(target=main1, args=())

@app.route("/", methods=["GET", "POST"])
def home():
    global initi
    global userValue
    global motor
    global kart
    print('/')
    global i
#     motor.stop(0,0,0)
    TES_pin = 26
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(TES_pin, GPIO.OUT)
    GPIO.output(TES_pin, GPIO.LOW)
    print("Pump Off")
    initi = 0

    if kart == 1:
        print("trying to stop thread")
        process.terminate()
        sleep(2)
       # process.close()
        i = 0
        motor = Motor(13,19,16,20)
        motor.stop()
        kart = kart -1;

    return render_template("index.html")  # nothing happen

    # To Power on the motor, pump and etc
@app.route('/on', methods=["GET", "POST"])

def on():
    global initi
    global userValue
    global curveVal
    global i
    global kart
    sen = 5 # SENSITIVITY

    if i == 0:
        process.start()
        kart = 1
        i = i + 1 ;

    if  initi == 0:
        RelayControl = 26
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(RelayControl, GPIO.OUT)
        GPIO.output(RelayControl, GPIO.HIGH)
        initi = 1
        print('on1')
        sleep(2)
        return render_template('on.html')

    elif request.method == "POST":
        print('Post Request')
        userValue = request.form["val"]  # get the value of val from html file and store as str
        print(userValue)
        SMotor_Control(userValue)
        return render_template('on.html')

    elif request.method == "GET":
        print("Get")
        Data = WaterLevelRead()
        return render_template('on.html',data=Data)

def SMotor_Control(degree):
    print("Spray Motor Control")
    val1 = SetAngle(degree)
    servoM1_pin = 6  # PWM pin
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(servoM1_pin, GPIO.OUT)
    m1 = GPIO.PWM(servoM1_pin, 50)
    m1.start(0)
    m1.ChangeDutyCycle(12)
    sleep(4)
    m1.ChangeDutyCycle(val1)
    sleep(2)
    #GPIO.cleanup()

def SetAngle(angle):
    angle1 = float(angle)
    print(angle1)
    angle1 += 90
    print(angle1)
    duty = 12 - (angle1 / 180) * 10
    result = int(duty)
    print(duty)
    print(result)
    return result

def WaterLevelRead():
    ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
    ser.flush()
    sleep(1)
    Line = ser.readline().decode('utf-8').rstrip()
    print(Line)
    ser.close()
    return Line

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=False)


import logging
from datetime import timedelta

from django.core.management import BaseCommand
from django.db.models import Max
from django.utils import timezone

from apps.booking.emails import send_pre_booking_cancelled
from apps.booking.models import Booking, Status, update_status_log

PREBOOKING_LIFETIME = 48

logger = logging.getLogger(__name__)

class Command(BaseCommand):
    help = (
        f'Меняет статус бронирования на cancelled, если бронирование '
        'находится в статусе inwork или pending больше {PREBOOKING_LIFETIME} '
        'часов'
    )

    def handle(self, *args, **options):
        # print(logger)
        # self.stdout.write(f"----- Checking bookings... -----")
        logger.debug("Checking bookings...")
        treshold = timezone.now() - timedelta(hours=PREBOOKING_LIFETIME)
        expired_prebookings = Booking.objects.annotate(
            last_record=Max('statuses__created_at')
        ).filter(
            status__in=[Status.inwork, Status.pending],
            last_record__lt=treshold
        )
        # print(prebookings.values())
        # print(f"{treshold=}")
        for booking in expired_prebookings:
            logger.debug(f'Cancelling booking {booking}...')
            booking.status = Status.cancelled
            booking.save()
            logger.debug('Booking cancelled')
            update_status_log(booking=booking, status=Status.cancelled)
            send_pre_booking_cancelled(booking)
            logger.info(f"Автоматическая отмена бронирования '{booking}'")


# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render, redirect, HttpResponse
from django.contrib.messages import error
# from datetime import datetime

from .models import User

# Create your VIEWS here.
#Views consist the bulk of your logic. I have basically none but I try.
#Use print statements ALL OVER your code to make sure things are working as they should.
def index(request):
    print ('GGGRRRR... THIS IS THE INDEX!!!!!!!')
    context = {
        'users': User.objects.all()
    } #Dictionary to hold a QUERY for use in the index.html page. We used User.objects.all() because we want to see ALL the data!
    return render(request, 'users_app/index.html', context) #Renders the template and CONTEXT is stated here because you want to pass that info onto the template

def new(request):
    print ('THIS IS THE FORM!!!!') #BLEH!
    return render(request, 'users_app/new.html') #Renders the template

def edit(request, id): #id is stated here because you want the id to be passed onto the URL
#FORM RENDERS BUT DOES NOT DO ANYTHING. EDIT AND UPDATE VIEWS NEEDS WORK!
    print ('THIS IS EDITING!!!!')
    user = User.objects.get(id=id) #QUERY to get specific id and attach it to a variable
    context = {
        'user': user
    } #Dictionary containing user information for use in this template/view
    return render(request, 'users_app/edit.html', context)

def show(request, id): #id is stated here because you want the id to be passed onto the URL
    print ('THIS IS ME SHOWING YOU STUFF!!!!!')
    user = User.objects.get(id=id) #QUERY to get the id of the specific person to be displayed
    context = {
        'user': user
    } #Dictionary containing user information for use in this template/view
    return render(request, 'users_app/show.html', context) #Renders a template and CONTEXT is stated here because you want to pass that info onto the template

def create(request):
#USER IS CREATED BUT ERROR MESSAGES DO NOT APPEAR. FIX!
    errors = User.objects.basic_validator(request.POST)
    if len(errors):
        print ('CREATE IF PATH')
        for error in errors.iteritems():
            messages.error(request, error)
        return redirect('/new') #If errors are prsent, stay in the new.html to flash the error messages
    else: #If there are NO errors, this code will execute and proceed to create a new user which will be added to the index.html
        print ('CREATE ELSE PATH')
        User.objects.create(
            first_name=request.POST['first_name'],
            last_name=request.POST['last_name'],
            email=request.POST['email'],
        ) #QUERY that assigns variable names using the form inputs the user provided
        return redirect('/') #As soon the code is executes, a user is created, go back to index.html

def destroy(request, id): #The id is needed to attach to the URL to keep track of who's being deleted
    user = User.objects.get(id=id) #QUERY to get the specific id
    user.delete() #User with the id is deleted
    return redirect('/') #Goes back to index.html once successful

def update(request, id):
    errors = User.objects.basic_validator(request.POST)
    if len(errors):
        print ('UPDATE IF PATH')
        for error in errors.iteritems():
            messages.error(request, error)
    else:
        print ('UPDATE ELSE PATH')
        user = User.objects.get(id = id)
        user.first_name = request.POST['first_name']
        user.last_name = request.POST['last_name']
        user.email = request.POST['email']
        user.save()
        return redirect('show/')


"""
Write a program, which will find all such numbers between 1000 and 3000 (both included) such that each digit of the number is an even number.
The numbers obtained should be printed in a comma-separated sequence on a single line.

In case of input data being supplied to the question, it should be assumed to be a console input.
"""

def main() -> None:

    num_range = range(1000, 3000 + 1)
    special_nums = each_digit_even(num_range)
    print(",".join(special_nums))

def each_digit_even(num_range):
    """Return a list of numbers such that each digit is an even number"""

    each_digit_even_num = []

    for num in num_range:
        num = str(num)

        all_digits_even = True

        for char in num:
            char = int(char)

            if char % 2 != 0:
                all_digits_even = False

        if all_digits_even:
            each_digit_even_num.append(num)

    return each_digit_even_num

if __name__ == "__main__":
    main()


import unittest
from functools import partial
import numpy as np
import bayes
import tensorflow as tf
import tensorflow_probability as tfp

tfd = tfp.distributions
tfb = tfp.bijectors

class BayesTestCase(unittest.TestCase):
    def test_batch(self):

        n=4
        x = np.random.normal(loc=10, scale=2, size=(n,351,1)).astype(np.float32)
        model = bayes.poisson_mixture_regression(x, 2)

        s = model.sample(n)

        @tf.function(jit_compile=True)
        def f(x):
            return model.log_prob(x)

        f(s)
        return 0
    def test_no_batch(self):

        n=4
        x = np.random.normal(loc=10, scale=2, size=(351,1)).astype(np.float32)
        model = bayes.poisson_mixture_regression(x, 2)

        s = model.sample()

        @tf.function(jit_compile=True)
        def f(x):
            return model.log_prob(x)

        f(s)
        return 0

class TupleModelTestCase(unittest.TestCase):

    def test_prior(self):
        prior = bayes.PoissonMixtureRegression.prior(2, tf.float64)

        n = 4
        sample_model = bayes.PoissonMixtureRegression(*map(partial(tfd.Distribution.sample,sample_shape=n), prior))
        x = np.random.normal(loc=10, scale=2, size=(n,351, 1))
        sample_model(x)
        ...
    def test_z(self):
        prior = bayes.PoissonMixtureRegression.prior(2, tf.float64)

        n = 4
        sample_model = bayes.PoissonMixtureRegression(*map(partial(tfd.Distribution.sample,sample_shape=n), prior))
        x = np.random.normal(loc=10, scale=2, size=(n,351, 1))
        sample_model.Z(x,tf.round(x))

    def test_jdc(self):
        n = 4
        x = np.random.normal(loc=10, scale=2, size=(n, 351, 1))
        Root = tfd.JointDistributionCoroutine.Root
        def makejdc():
            @tfd.JointDistributionCoroutine
            def model():
                params=[]
                for p in bayes.PoissonMixtureRegression.prior(2,tf.float64):
                    param = yield Root(p)
                    params.append(param)
                yield bayes.PoissonMixtureRegression(*params)(x)
            return model

        model = makejdc()
        model.log_prob(model.sample())
        ...

if __name__ == '__main__':
    unittest.main()


def htmlEsc(val):
    """Escape certain HTML characters by HTML entities.

    To prevent them to be interpreted as HTML
    in cases where you need them literally.
    """

    return (
        ""
        if val is None
        else (
            str(val)
            .replace("&", "&")
            .replace("<", "<")
        )
    )

class AttrDict(dict):
    """Turn a dict into an object with attributes

    If non-existing attributes are accessed for reading, `None` is returned.

    See:
    https://stackoverflow.com/questions/4984647/accessing-dict-keys-like-an-attribute

    And:
    https://stackoverflow.com/questions/16237659/python-how-to-implement-getattr
    (especially the remark that

    > `__getattr__` is only used for missing attribute lookup

    )
    """

    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

    def __getattr__(self, key, *args, **kwargs):
        return None


class Request:
    def __init__(self, raw: str) -> None:
        end_of_first_line = raw.index("\r\n")
        first_line = raw[:end_of_first_line]
        self.method, self.path, self.protocol = first_line.split(" ")

        raw = raw[end_of_first_line + 2 :]
        headers, self.body = raw.split("\r\n\r\n")
        self.headers = {}
        for header in headers.split("\r\n"):
            first_colon = header.index(":")
            name = header[:first_colon].strip()
            value = header[first_colon + 1 :].strip()
            self.headers[name] = value


# -*- coding: utf-8 -*-
"""
Created on Wed Feb 26 19:23:13 2020

@author: espeg
"""

userInput = ""
validInput = "hlc"
highNumber = 100
lowNumber = 0

print("Please think of a number between 0 and 100!")
while True:
    guess = (highNumber + lowNumber)//2
    print("Is your secret number " + str(guess) + "?")
    userInput = input("Enter 'h' to indicate the guess is too high. Enter 'l' to indicate the guess is too low. Enter 'c' to indicate I guessed correctly.")
    if userInput not in validInput:
        print("Sorry, I did not understand your input.")
        continue
    elif userInput == "l":
        lowNumber = guess
    elif userInput == "h":
        highNumber = guess
    else:
        print("Game over. Your secret number was: " + str(guess))
        break


from input_values import *

def compose_query_CB_get(request):
  v_req_keys = [i for i in request.args.keys()]

  query = 'SELECT * FROM cocktailbar'
  v_q = []
  for rk in v_req_keys:
    if rk not in v_possible_keys_CB_GET:
      continue
    value = request.args.get(rk)
    if type(value) == str:
      v_q.append(rk + " = '" + request.args.get(rk) + "'")
    else:
      v_q.append(rk + " = " + request.args.get(rk))

  for i, q in enumerate(v_q):
    if i == 0:
      query += ' WHERE'
    else:
      query += ' AND'
    query += ' ' + q

  query += ' ORDER BY name ASC'
  return query


from PyQt5.QtWidgets import QListWidget
from PyQt5.QtCore import Qt

class ListBoxWidget(QListWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self.itemsList = set()

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
           event.setDropAction(Qt.CopyAction)
           event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()

            for url in event.mimeData().urls():
                if url.isLocalFile():
                    localFile = str(url.toLocalFile())

                    if localFile.endswith((".png", ".jpg", ".jpeg")):
                       self.itemsList.add(localFile)
                else:
                    continue

            self.clear()
            self.addItems(self.itemsList)
        else:
            event.ignore()


'''
izhikevich verison of #14
although network is chaotic, starting to produce some interesting results..

'''
import os,sys
parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,parentdir)
from pyN import *

#substantia nigra releases dopamine. Stimulate this when good things happen. This one is special. because it is excitatory towards Go and inhibitory towards NoGo.
#tonic bursting parameters
SNc      = IzhikevichPopulation(name='snc',N=60,a=0.02,b=0.25, c=-50,d=2,v0=-70, connectivity="sparse-random")

Go_add   = IzhikevichPopulation(name='Go+', N=40, connectivity="sparse-random")
Go_sub   = IzhikevichPopulation(name='Go-', N=40, connectivity="sparse-random")
NoGo_add = IzhikevichPopulation(name='NoGo+',N=40, connectivity="sparse-random")
NoGo_sub = IzhikevichPopulation(name='NoGo-',N=40, connectivity="sparse-random")

#these inhibitory populations act to balance out activity and prevent go/nogo from going nuts when (not) driven by dopamine.
#Go_inhib = IzhikevichPopulation(name='go_inhib',N=8,connectivity="sparse-random")
#NoGo_inhib = IzhikevichPopulation(name='nogo_inhib',N=8,connectivity="sparse-random")

GPi = IzhikevichPopulation(name='gpi',N=40, connectivity="sparse-random")
GPe  = IzhikevichPopulation(name='gpe',N=40, connectivity="sparse-random")

thal_in = IzhikevichPopulation(name='thal_in',N=120,connectivity="sparse-random", scale=2.0)#where sensory info goes in
thal_add = IzhikevichPopulation(name='thal+',N=40, connectivity="sparse-random")#where motor output comes out
thal_sub = IzhikevichPopulation(name='thal-',N=40, connectivity="sparse-random")

PFC  = IzhikevichPopulation(name='pfc', N=340, connectivity="sparse-random")
#gain modulator for PFC
PFC_inhib = IzhikevichPopulation(name='pfc_inhib',N=60,connectivity="sparse-random")
brain = UnsegDopaNetwork(populations=[SNc, Go_add, NoGo_add, Go_sub, NoGo_sub, GPi, GPe, thal_in, thal_add, thal_sub, PFC, PFC_inhib])
#set up the excitatory connections between the populations

#SNc -> excitatory to Go+/- and inhibitory to NoGo +/-
brain.connect(pre='snc', post='Go+', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)
brain.connect(pre='snc', post='Go-', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)
brain.connect(pre='snc', post='NoGo+', synapses="sparse-random", mode='inhibitory', delay=2.25,std=0.25)
brain.connect(pre='snc', post='NoGo-', synapses="sparse-random", mode='inhibitory', delay=2.25,std=0.25)

#Go/NoGo Balanced by inhibitory population

#frontal cortex -> excitatory connections to all the go populations
brain.connect(pre='pfc', post='Go+', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)
brain.connect(pre='pfc', post='NoGo+', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)
brain.connect(pre='pfc', post='Go-', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)
brain.connect(pre='pfc', post='NoGo-', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)

#Go inhibits GPi
brain.connect(pre='Go+',post='gpi', synapses='sparse-random', mode='inhibitory', delay=2.25, std=0.25)
brain.connect(pre='Go-',post='gpi', synapses='sparse-random', mode='inhibitory', delay=2.25, std=0.25)

#NoGo inhibits Gpe (disinhibited)
brain.connect(pre='NoGo+',post='gpe', synapses='sparse-random', mode='inhibitory', delay=2.25, std=0.25)
brain.connect(pre='NoGo-',post='gpe', synapses='sparse-random', mode='inhibitory', delay=2.25, std=0.25)

brain.disconnect(pre='NoGo+',post='gpe')
brain.disconnect(pre='NoGo-',post='gpe')

#GPe inhibits GPi
brain.connect(pre='gpe', post='gpi', synapses="sparse-random", mode='inhibitory', delay=2.25,std=0.25)

#GPi inhibits Thalamus (disinhibited)
brain.connect(pre='gpi', post='thal+', synapses="sparse-random", mode='inhibitory', delay=2.25,std=0.25)
brain.connect(pre='gpi', post='thal-', synapses="sparse-random", mode='inhibitory', delay=2.25,std=0.25)
brain.disconnect(pre='gpi', post='thal+')
brain.disconnect(pre='gpi', post='thal-')

#thalamus bidirectional activity to PFC
brain.connect(pre='pfc', post='thal+', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)
brain.connect(pre='pfc', post='thal-', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)
brain.connect(pre='pfc', post='thal_in', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)
brain.connect(pre='thal+', post='pfc', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)
brain.connect(pre='thal-', post='pfc', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)
brain.connect(pre='thal_in', post='pfc', synapses="sparse-random", mode='excitatory', delay=2.25,std=0.25)

#PFC 15% inhibition gain modulation
brain.connect(pre='pfc', post='pfc_inhib', synapses="sparse-random", mode='excitatory')
brain.connect(pre='pfc_inhib', post='pfc', synapses="sparse-random", mode='inhibitory')

#####
#run the network for a goal of 10 seconds
#####

results1 = brain.simulate(experiment_name='seg-iz-unsegregated', T=30000, dt=0.25, save_data='/data/people/evjang/pyN_data/', properties_to_save=['v'],stdp=True)
save_data(results1,'./')
#results2 = brain.simulate(experiment_name='modelock-nocrossinhib-longer', T=30000, dt=0.250, save_data='/data/people/evjang/pyN_data/', properties_to_save=['v','psc','spike_raster','I_rec'],stdp=True)
#save_data(results2,'./')


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
- author: Lkeme
- contact: Useri@live.cn
- file: app
- time: 2019/10/4 7:28
- desc:
"""
import requests
import time
import re

try:
    from urllib import urlencode
except:
    from urllib.parse import urlencode
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def printer(info, *args):
    at_now = int(time.time())
    time_arr = time.localtime(at_now)
    format_time = time.strftime("%Y-%m-%d %H:%M:%S", time_arr)
    # flag = "," if len(args) else " "
    content = f'[{format_time}] {info} {" ".join(f"{str(arg)}" for arg in args)}'
    print(content)

def format_time():
    return f"[{str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))}]"

def match_csrf(cookie):
    temp = re.search(r"bili_jct=(.{32})", cookie)
    csrf = str(temp.group(1))
    return csrf

# 心跳E
def heart_beat_e(room_id=23058):
    url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E'
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Origin': 'https://live.bilibili.com',
        'Referer': f'https://live.bilibili.com/{room_id}',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
        'Cookie': cookie,
    }
    payload = {
        'id': [1, 34, 0, 23058],
        'device': '["c4ca4238a0b923820dcc509a6f75849b","55e2620e-a2b9-4086-bd9a-bc399ba13480"]',
        'ts': int(time.time()) * 1000,
        'is_patch': 0,
        'heart_beat': [],
        'ua': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
        'csrf_token': match_csrf(cookie),
        'csrf': match_csrf(cookie),
        'visit_id': ''
    }
    data = urlencode(payload)
    # {"code":0,"message":"0","ttl":1,"data":{"timestamp":1595342828,"heartbeat_interval":300,"secret_key":"seacasdgyijfhofiuxoannn","secret_rule":[2,5,1,4],"patch_status":2}}
    response = session.post(url, headers=headers, data=data,
                            verify=False).json()
    printer("E心跳", response)
    payload['ets'] = response['data']['timestamp']
    payload['secret_key'] = response['data']['secret_key']
    payload['heartbeat_interval'] = response['data']['heartbeat_interval']
    return payload

# 心跳X
def heart_beat_x(index, payload, room_id=23058):
    url = 'https://live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X'
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Origin': 'https://live.bilibili.com',
        'Referer': f'https://live.bilibili.com/{room_id}',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
        'Cookie': cookie,
    }
    s_data = {
        "t": {
            'id': [1, 34, index, 23058],
            "device": payload['device'],  # LIVE_BUVID
            "ets": payload['ets'],
            "benchmark": payload['secret_key'],
            "time": payload['heartbeat_interval'],
            "ts": int(time.time()) * 1000,
            "ua": payload['ua']
        },
        "r": [2, 5, 1, 4]
    }
    t = s_data['t']
    payload = {
        's': generate_s(s_data),
        'id': t['id'],
        'device': t['device'],
        'ets': t['ets'],
        'benchmark': t['benchmark'],
        'time': t['time'],
        'ts': t['ts'],
        "ua": t['ua'],
        'csrf_token': match_csrf(cookie),
        'csrf': match_csrf(cookie),
        'visit_id': '',
    }
    # printer(payload)
    payload = urlencode(payload)
    # {"code":0,"message":"0","ttl":1,"data":{"heartbeat_interval":300,"timestamp":1595346846,"secret_rule":[2,5,1,4],"secret_key":"seacasdgyijfhofiuxoannn"}}
    response = session.post(url, headers=headers, data=payload,
                            verify=False).json()
    printer("X心跳", response)
    return response

# 加密s
def generate_s(data):
    url = 'http://127.0.0.1:3000/enc'
    response = requests.post(url, json=data).json()
    printer("S加密", response['s'])
    return response['s']

# 加密s
def generate_s_test():
    url = 'http://127.0.0.1:3000/enc'
    data = {
        "t": {
            "id": [1, 34, 0, 23058],
            "device": [
                "c4ca4238a0b923820dcc509a6f75849b",
                "55e2620e-a2b9-4086-bd9a-bc399ba13480"
            ],
            "ets": 1570562610,
            "benchmark": "seacasdgyijfhofiuxoannn",
            "time": 120,
            "ts": 1570562730734
        },
        "r": [2, 5, 1, 4]
    }
    response = requests.post(url, json=data).json()
    printer("S加密", response)
    return response['s']

# X心跳 {'code': 1012002, 'message': 'bad timestamp request,timestamp:1584604393,watchtime:300,userstatus:1584604393,roomInterval:300,serverTime:1584604393,ispatch:0', 'ttl': 1, 'data': {'heartbeat_interval': 300, 'timestamp': 1584604393, 'secret_rule': [2, 5, 1, 4], 'secret_key': 'seacasdgyijfhofiuxoannn'}}
# 41.1b7a7f2689ea751d75d4.js:574 {"id":"","device":"[\"c4ca4238a0b923820dcc509a6f75849b\",\"55e2620e-a2b9-4086-bd9a-bc399ba13480\"]","ets":1570562610,"benchmark":"seacasdgyijfhofiuxoannn","time":120,"ts":1570562730734}
# 41.1b7a7f2689ea751d75d4.js:575 打印r
# 41.1b7a7f2689ea751d75d4.js:576
# 41.1b7a7f2689ea751d75d4.js:577 (4) [2, 5, 1, 4]
# 41.1b7a7f2689ea751d75d4.js:587 T类型 string
# 41.1b7a7f2689ea751d75d4.js:588 R类型 object
# 41.1b7a7f2689ea751d75d4.js:608 打印加密后
# 41.1b7a7f2689ea751d75d4.js:609 cdc04cffba0b1f9f7bfa145b27fc5a4430c95d83d526002371ea363f42c608792798170e3f23dd3eda69480553699ad1b002a8ba7fc60070ae77153930a86e5e

if __name__ == '__main__':
    cookie = ""
    session = requests.Session()
    data = heart_beat_e(room_id=23058)
    for index in [1, 2, 3, 4, 5, 6, 7]:
        printer(f"Interval {data['heartbeat_interval']} 后开始第 {index} 次")
        time.sleep(data['heartbeat_interval'])
        response = heart_beat_x(index, data, room_id=23058)
        data['ets'] = response['data']['timestamp']
        data['secret_key'] = response['data']['secret_key']
        data['heartbeat_interval'] = response['data']['heartbeat_interval']


from sys import stdin
input = stdin.readline

number_computer = int(input())
number_link = int(input())

graph = [[] for i in range(number_computer+1)]

for i in range(number_link):
    (a,b) = list(map(int,input().split()))
    graph[a].append(b)
    graph[b].append(a)

def virus(graph, visited, start):
    if visited[start] == 1:
        return
    visited[start] = 1

    for dest in graph[start]:
        virus(graph, visited, dest)
    return sum(visited)-1

visited = [0]*(number_computer+1)

print(virus(graph,visited, 1 ))