from grid import Grid, Square, Box
from sudoku_generator import SudokuGenerator
import pygame
from helper_utils import *

SQUARE_TOTAL = 81

class Sudoku:
    """
    A representation of a Sudoku game.
    """

    def __init__(self, grid: Grid, grid_pos: tuple[int, int], sprite_groups: dict[str, pygame.sprite.Group],
                 screen: pygame.Surface):
        """
        Initializes a Sudoku object.

        :param grid: The Grid object to be used in the Sudoku game.
        :param grid_pos: The position of the Grid object.
        """
        self.sprite_groups = sprite_groups
        self.screen = screen
        self.grid = grid
        self.grid_pos = grid_pos
        self.empty_squares = SQUARE_TOTAL
        self.solved = False

    def update_screen(self):
        draw(self.screen, "white", self.sprite_groups,
             "grid",
             "box",
             "background",
             "square",
             "number",
             "game_ui",
             )
        pygame.display.flip()

    def puzzle_to_grid(self, matrix: list[list[int]]) -> None:
        self.reset()
        for row_idx, row in enumerate(matrix):
            for col_idx, value in enumerate(row):
                if value != -1:
                    square = self._get_square(row_idx, col_idx)
                    square.set_value(value)
                    square.set_static(True)

    def grid_to_list(self) -> list[list[int]]:
        """
        Converts the Sudoku grid into a list of lists representation.

        :return: The grid represented as a list of lists.
        """
        grid_list = []
        for row in range(9):
            row_list = []
            for col in range(9):
                value = self.get_number(row, col)
                row_list.append(value)
            grid_list.append(row_list)
        return grid_list

    def generate_puzzle(self, difficulty: str) -> None:
        if difficulty == "EASY":
            hints = 45
        elif difficulty == "MEDIUM":
            hints = 35
        elif difficulty == "HARD":
            hints = 29
        else:
            raise ValueError("Invalid difficulty level!")
        generator = SudokuGenerator()
        puzzle = generator.generate_puzzle(hints)[0]
        self.puzzle_to_grid(puzzle)
        self.empty_squares = SQUARE_TOTAL - hints

    def demo(self) -> None:
        hard_sudoku = [
            [5, 3, -1, -1, 7, -1, -1, -1, -1],
            [6, -1, -1, 1, 9, 5, -1, -1, -1],
            [-1, 9, 8, -1, -1, -1, -1, 6, -1],
            [8, -1, -1, -1, 6, -1, -1, -1, 3],
            [4, -1, -1, 8, -1, 3, -1, -1, 1],
            [7, -1, -1, -1, 2, -1, -1, -1, 6],
            [-1, 6, -1, -1, -1, -1, 2, 8, -1],
            [-1, -1, -1, 4, 1, 9, -1, -1, 5],
            [-1, -1, -1, -1, 8, -1, -1, 7, 9]
        ]
        self.puzzle_to_grid(hard_sudoku)
        self.solve()

    def solve(self) -> bool:
        """
        Solves the Sudoku using backtracking.
        """
        self.grid_matrix = self.grid_to_list()  # Translate the grid to a matrix
        return self._solve_matrix()

    def _solve_matrix(self) -> bool:
        """
        Solves the Sudoku grid matrix using backtracking.

        :return: True if the Sudoku grid matrix can be solved, False otherwise.
        """
        for row in range(9):
            for col in range(9):
                if self.grid_matrix[row][col] == -1:  # Check if the square is empty
                    for num in range(1, 10):  # Try numbers from 1 to 9
                        if self._is_number_valid_matrix(row, col, num):  # Check if the number is valid
                            # Assign the number to the square and insert it in the grid
                            self.grid_matrix[row][col] = num
                            self._get_square(row, col).set_value(num)
                            self.update_screen()

                            if self._solve_matrix():  # Recursively try to solve the rest of the grid
                                return True

                            # If the recursive call fails, remove the number from the square and the grid
                            self.grid_matrix[row][col] = -1
                            self._get_square(row, col).reset()
                            # self.update_screen()

                    return False  # If no number can be inserted in the current square, backtrack

        # If all the squares are filled, the Sudoku is solved
        self.update_square_validities()
        self.solved = self.is_grid_valid()
        return self.solved

    def _is_number_valid_matrix(self, row: int, col: int, num: int) -> bool:
        """
        Checks if a number is valid to be inserted in a specified row and
        column of the Sudoku grid matrix according to the rules of Sudoku.

        :param row: The row to check.
        :param col: The column to check.
        :param num: The number to check.
        :return: True if the number is valid, False otherwise.
        """
        # Check if the number is already in the same row or column
        if num in self.grid_matrix[row] or num in (self.grid_matrix[i][col] for i in range(9)):
            return False

        # Check if the number is already in the same 3x3 box
        box_start_row, box_start_col = 3 * (row // 3), 3 * (col // 3)
        for i in range(3):
            for j in range(3):
                if self.grid_matrix[i + box_start_row][j + box_start_col] == num:
                    return False

        return True

    def get_number(self, row: int, col: int) -> int:
        """
        Gets the number at the specified row and column of the Sudoku grid.

        :param row: The row of the number.
        :param col: The column of the number.
        :return: The number at the specified position, or -1 if there is no number.
        """
        square = self._get_square(row, col)
        return square.value

    def manually_insert_number(self, square: Square, value: int, hint: bool = False):
        if square.static:
            return False

        if square.value == -1:
            self.empty_squares -= 1
        if value == -1:
            self.empty_squares += 1
        square.set_value(value)
        if self.empty_squares == 0:  # If this was the last empty square
            self.update_square_validities()  # Update validities to make sure the grid is valid
            if self.is_grid_valid():
                self.solved = True  # Set the win condition to True

        return True

    def auto_insert_number(self, square: Square, value: int, force: bool = False):
        """
        Inserts a number in a specified square of the Sudoku grid.

        :param square: The square in which the number should be inserted.
        :param value: The value of the number to be inserted.
        :param force: If True, the number will be inserted even if it's not valid.
        :return: True if the number was successfully inserted, False otherwise.
        """
        if square.static:
            return False
        # if value not in range(1, 10):
        # return False

        """if force or self.is_number_valid(square.row, square.col, value):
            if square.value == -1:  # Decrease the number of empty squares only if the square was empty
                self.empty_squares -= 1

            square.set_validity(True)
            square.set_value(value)
        else:
            square.set_validity(False)
            square.set_value(value)"""
        square.set_value(value)
        # self.update_screen()
        return True

    def remove_number(self, row: int, col: int):
        """
        Removes a number from a specified row and column of the Sudoku grid.
        Skips hints.

        :param row: The row from which the number should be removed.
        :param col: The column from which the number should be removed.
        """
        square = self._get_square(row, col)
        if not square.static:
            square.reset()

    def clear(self):
        """
        Removes all the values from Squares in the grid, except hints.
        """
        for row in range(9):
            for col in range(9):
                self.remove_number(row, col)
        self.update_square_validities()
        solved = self.is_grid_valid()
        self.solved = solved

    def reset(self):
        for row in range(9):
            for col in range(9):
                square = self._get_square(row, col)
                square.reset()
        self.solved = False

    def is_number_valid(self, row: int, col: int, num: int) -> bool:
        """
        Checks if a number is valid to be inserted in a specified row and
        column of the Sudoku grid according to the rules of Sudoku.

        :param row: The row to check.
        :param col: The column to check.
        :param num: The number to check.
        :return: True if the number is valid, False otherwise.
        """
        # Check if the number is already in the same row or column
        if self._value_in_row(row, num, exclude_col=col):
            return False
        if self._value_in_col(col, num, exclude_row=row):
            return False

        # Check if the number is already in the same box
        box = self._get_box(self._get_square(row, col))
        return not self._value_in_box(box, num, exclude_row=row, exclude_col=col)

    def find_empty_square(self) -> Square | None:
        """
        Finds the next empty square in the Sudoku grid.
        :return: The next empty Square, or None if there are no more empty squares.
        """
        for row in range(9):
            for col in range(9):
                if self.get_number(row, col) == -1:
                    return self._get_square(row, col)
        return None

    def is_grid_valid(self) -> bool:
        """
        Checks the whole grid and returns True if all Squares are valid, False otherwise.
        Squares are only valid if they are not empty and have a valid number.

        :return: True if all Squares are valid, False otherwise.
        """
        for row in range(9):
            for col in range(9):
                square = self._get_square(row, col)
                if not square.valid:
                    return False
        return True

    def update_square_validities(self):
        """
        Updates all the Square's validities by calling is_number_valid on all of them.
        WARNING: Destructive, since it will set previously valid Squares as invalid based
        on later insertions.
        """
        for row in range(9):
            for col in range(9):
                value = self.get_number(row, col)
                if value != -1:
                    validity = self.is_number_valid(row, col, value)
                    self._get_square(row, col).set_validity(validity)

    def _get_square(self, row: int, col: int) -> Square:
        """
        Returns the Square object at a specified row and column of the Sudoku grid.

        :param row: The row of the Square object.
        :param col: The column of the Square object.
        :return: The Square object at the specified position.
        """
        return self.grid.boxes[row // 3][col // 3].squares[row % 3][col % 3]

    def _get_box(self, square: Square) -> Box:
        """
        Returns the Box object to which a specified Square object belongs.

        :param square: The Square object.
        :return: The Box object to which the specified Square object belongs.
        """
        box_row = square.row // 3
        box_col = square.col // 3
        return self.grid.boxes[box_row][box_col]

    def _value_in_row(self, row: int, num: int, exclude_col: int = None) -> bool:
        """
        Checks if a number is already in the same row.

        :param row: The row to check.
        :param num: The number to check.
        :param exclude_col: The column to exclude from the check.
        :return: True if the number is already in the same row, False otherwise.
        """
        for i in range(9):
            if i != exclude_col and self.get_number(row, i) == num:
                return True
        return False

    def _value_in_col(self, col: int, num: int, exclude_row: int = None) -> bool:
        """
        Checks if a number is already in the same column.

        :param col: The column to check.
        :param num: The number to check.
        :param exclude_row: The row to exclude from the check.
        :return: True if the number is already in the same column, False otherwise.
        """
        for i in range(9):
            if i != exclude_row and self.get_number(i, col) == num:
                return True
        return False

    def _value_in_box(self, box: Box, value: int, exclude_row: int = None, exclude_col: int = None) -> bool:
        """
        Checks if a value is already in any of the Squares of a specified Box object.

        :param box: The Box object.
        :param value: The value to check.
        :param exclude_row: The row to exclude from the check.
        :param exclude_col: The column to exclude from the check.
        :return: True if the value is already in the Box object, False otherwise.
        """
        for row_idx, square_row in enumerate(box.squares):
            for col_idx, square in enumerate(square_row):
                if (row_idx != exclude_row % 3 or col_idx != exclude_col % 3) and square.value == value:
                    return True
        return False


import asyncio
import logging

from sqlalchemy import select

from accounting import database
from accounting.models import Account, Payment, PaymentStatus, Transaction, BillingCycle

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

async def main():
    await database.setup(database.Settings())
    async with database.create_session() as session:
        pending_payment_ids = (await session.execute(
            select(Payment.id).where(Payment.status == PaymentStatus.pending)
        )).scalars().all()
    for payment_id in pending_payment_ids:
        async with database.create_session() as session:
            payment, transaction, billing_cycle, account = (await session.execute(
                select(
                    Payment,
                    Transaction,
                    BillingCycle,
                    Account,
                ).join(
                    Transaction, onclause=Transaction.id == Payment.transaction_id,
                ).join(
                    BillingCycle, onclause=BillingCycle.id == Transaction.billing_cycle_id,
                ).join(
                    Account, onclause=Account.id == BillingCycle.account_id,
                ).where(
                    Payment.id == payment_id,
                    Payment.status == PaymentStatus.pending,
                ).with_for_update(
                    of=Payment,
                )
            )).one()
        logger.info(
            'Pay %s$ to %s for %s - %s',
            (transaction.credit - transaction.debit) / 100,
            account.full_name,
            billing_cycle.opened_at,
            billing_cycle.closed_at,
        )
        payment.status = PaymentStatus.completed
        session.add(payment)
        await session.commit()

if __name__ == '__main__':
    asyncio.run(main(), debug=True)


# -*- coding: utf-8 -*-
"""
Downloads WSR-88D Archive level 2 files using AWS
Plots reflectivity using pyart and then saves images

Prequisites:

    shape_path  :  A path to the shapefile you want to plot
         plts   :  Dictionary of colormaps (cmaps) and information about how
                   to plot ( e.g., value range, colorbar settings, etc.)

"""

import sys
import os
import configlocal as cfg
from aws_catalog import NexradLevel2
import matplotlib.pyplot as plt
import pyart
import cartopy.crs as ccrs
# from metpy.plots import USCOUNTIES
from radar_cmaps import plts
from datetime import datetime, timedelta

sys.path.append(os.path.join(cfg.resources_dir))

data_dir = cfg.data_dir

image_dir = cfg.images_dir

placefile_dir = cfg.placefile_dir

archive_dir = cfg.archive_dir

gis_dir = cfg.gis_dir
shape_path = os.path.join(gis_dir, 'c_02ap19', 'c_02ap19.shp')

py_call = cfg.py_call

def get_places(xmin, xmax, ymin, ymax):

    src = os.path.join(cfg.gis_dir, "places_conus.csv")

    places = []
    with open(src) as fp:
        content = fp.read().splitlines()
        for line in content:
            test = line.split(',')
            place = test[0]
            lat = test[-2][:6]
            lon = test[-1][:7]
            try:
                float(lon)
                if float(lon) > xmin and float(lon) < xmax:
                    if float(lat) > ymin and float(lat) < ymax:
                        places.append([place, lon, lat])
            except Exception:
                pass

    return places

def extract_sweeps(orig_list, cut):
    """

    Parameters
    ----------
    orig_list : list of floats representing elevations for every sweep

    cut : integer representing desired cut to plot
          possible values are 5, 9, 13, 18, 24, 35

    Returns
    -------
    cut_list : list of integers
        indices representing sweep numbers associated with CS cuts

    """
    # multiply elevations by 10 and round to ensure that the possible
    # values listed above are represented
    sweep_list = [int(round(x*10)) for x in orig_list]
    cut_list = []
    found_cut = False
    for t in range(0, len(sweep_list)):

        if sweep_list[t] == cut and found_cut is False:

            cut_list.append(t)
            found_cut = True
        elif sweep_list[t] == cut and found_cut is True:
            found_cut = False

    return cut_list

def pyart_plot_reflectivity(filepath, filename, dx=1, dy=1):
    """

    Parameters
    ----------
    filepath : str
        full local path to Archive 2 radar file
    filename : str
        just the filename instead of the full path
    dx : float, optional
        Number of degrees longitude on each side of the radar location to plot.
        The default is 1.
    dy : float, optional
        Number of degrees latitude on each side of the radar location to plot.
        The default is 1.

    Dependencies
    ----------
    shapefile  : str
        shape_path - full local path to shapefile to add.
                     This needs to be staged in advance.

         cmap  : str
                 plts dictionary that needs to be imported.
                 Can also just use default cmap by removing vmin,vmax, cmap
                 arguments below

    Returns
    -------
    Nothing. Just exits after creating and saving plot.

    """
    # create datetime object from filename
    file_timestamp = datetime.strptime(filename[4:19], '%Y%m%d_%H%M%S')
    # convert datetime object from UTC to local time
    local_dt_obj = file_timestamp - time_shift
    # create string for title based on new datetime object
    title = datetime.strftime(local_dt_obj, '%a %b %d, %Y\n%I:%M %p EDT')
    radar = pyart.io.read_nexrad_archive(filepath)
    display = pyart.graph.RadarMapDisplay(radar)

    rda_lon = radar.longitude['data'][0]
    rda_lat = radar.latitude['data'][0]

    # add or subtract 0.25 degrees to ensure names stay within plot panel
    xmin = rda_lon - dx + 0.25
    xmax = rda_lon + dx - 0.25
    ymin = rda_lat - dy + 0.25
    ymax = rda_lat + dy - 0.25

    locations = get_places(xmin, xmax, ymin, ymax)

    # extract sweeps for the 0.5 degree cut
    angles = list(radar.fixed_angle['data'])
    desired_sweeps = extract_sweeps(angles, 5)

    for s in desired_sweeps:

        # look up first radial of the sweep
        sweep_start = radar.sweep_start_ray_index['data'][s]
        # retrieve number of seconds elapsed since very first sweep
        sweep_start_seconds = int(round(radar.time['data'][sweep_start]))

        new_time = file_timestamp + timedelta(seconds=sweep_start_seconds)
        full_title = display.generate_title('reflectivity', s)
        # example of how temp_title looks
        # 'KILX 0.5 Deg. 2019-06-16T03:01:10.802000Z \n
        # Equivalent reflectivity factor'
        title_parts = full_title.split(' ')
        rda_str = title_parts[0]
        elevation_str = title_parts[1]
        new_title = "{} {} Degrees Reflectivity\n".format(rda_str,
                                                          elevation_str)

        time_title = datetime.strftime(new_time, '%a %b %d, %Y\n%I:%M %p UTC')
        image_time = datetime.strftime(new_time, '_%Y%m%d_%H%M_UTC.png')
        image_filename = rda_str + image_time
        title = new_title + time_title
        fig = plt.figure(figsize=(6, 6))
        projection = ccrs.LambertConformal(central_latitude=rda_lat,
                                           central_longitude=rda_lon)
        display.plot_ppi_map('reflectivity', int(s), vmin=-30, vmax=80,
                             cmap=plts['Ref']['cmap'],
                             title=title, title_flag=True,
                             colorbar_flag=True,
                             colorbar_label=plts['Ref']['cblabel'],
                             min_lon=xmin, max_lon=xmax, min_lat=ymin,
                             max_lat=ymax,
                             resolution='50m', projection=projection,
                             shapefile=shape_path,
                             shapefile_kwargs={'facecolor': 'none',
                                               'edgecolor': 'gray',
                                               'linewidth': 0.7},
                             lat_lines=[0], lon_lines=[0],  # omit grid lines
                             fig=fig, lat_0=rda_lat, lon_0=rda_lon)

        # ax = display.ax
        # ax.add_feature(USCOUNTIES.with_scale('5m'), edgecolor='gray',
        #                linewidth=0.7)

        for p in range(0, len(locations)):
            place = locations[p][0]
            lat = float(locations[p][2])
            lon = float(locations[p][1])
            plt.plot(lon, lat, 'o', color='black',
                     transform=ccrs.PlateCarree(), zorder=10)
            plt.text(lon, lat, place, horizontalalignment='center',
                     verticalalignment='top', transform=ccrs.PlateCarree())

        # image_dst_path = os.path.join(this_image_dir, filename + '.png')
        image_dst_path = os.path.join(this_image_dir, image_filename)

        plt.savefig(image_dst_path, format='png', bbox_inches="tight", dpi=150)
        print('  Image saved at  ' + image_dst_path)
        plt.close()

    return None

# TODO: Move other GIS files to an external location, and eventually
# provide via a GUI.
# Define radar, date, hours in which to acquire files and plot data
###########################################
radar = 'KGRR'
start_date = datetime(2020, 4, 8, 0, 0)
end_date = datetime(2020, 4, 8, 0, 10)
###########################################

# Go to AWS or other location and get list of available files for date range
nexradlist = NexradLevel2(radar, start_date, end_date)
filelist = nexradlist.filelist()

# Assuming we'll do central time for Chicago
# my klunky way of subtracting 6 hours from UTC, to get local time
# This of course changes with transition between standard and daylight time.
# Been meaning to include more robust handling of this, but since I'm focused
# more on research, UTC has always been sufficient
# maybe this is a good project for someone else :)

time_shift = timedelta(hours=5)

# create a string of format YYYYmmdd based on inputs above
# (example: '20190719')
# then, use this string to create sub-directory in image directory
# to save images
YYYY = start_date.year
mm = start_date.month
dd = start_date.day
ymd_str = f'{YYYY:.0f}{mm:02.0f}{dd:02.0f}'
this_image_dir = os.path.join(image_dir, ymd_str, 'radar')
os.makedirs(this_image_dir, exist_ok=True)

this_data_dir = os.path.join(data_dir, ymd_str, radar, 'raw')

radar_files_already_downloaded = nexradlist.download(filelist, this_data_dir)

# Eventually most of this can go away since downloads now occur elsewhere.
if radar_files_already_downloaded is False:
    print("Data files not found or some were missing.")

else:
    # this_data_dir = 'C:/data/20190720/radar' # where arc2 files are
    files = os.listdir(this_data_dir)
    for file in files:
        if 'V06' in file:
            full_filepath = os.path.join(this_data_dir, file)
            print('  Beginning plot of ' + full_filepath)
            locations = pyart_plot_reflectivity(full_filepath, file)


import os
import tempfile
import unittest
import stat

# from unittest.mock import patch

from mackup import utils

def convert_to_octal(file_name):
    """
    Using os.stat, returns file permissions (read, write, execute) as an octal.
    """
    return oct(os.stat(file_name)[stat.ST_MODE])[-3:]

class TestMackup(unittest.TestCase):
    def test_confirm_yes(self):
        # Override the input used in utils
        def custom_input(_):
            return "Yes"

        utils.input = custom_input
        assert utils.confirm("Answer Yes to this question")

    def test_confirm_no(self):
        # Override the input used in utils
        def custom_input(_):
            return "No"

        utils.input = custom_input
        assert not utils.confirm("Answer No to this question")

    def test_confirm_typo(self):
        # Override the input used in utils
        def custom_input(_):
            return "No"

        utils.input = custom_input
        assert not utils.confirm("Answer garbage to this question")

    def test_delete_file(self):
        # Create a tmp file
        tfile = tempfile.NamedTemporaryFile(delete=False)
        tfpath = tfile.name
        tfile.close()

        # Make sure the created file exists
        assert os.path.isfile(tfpath)

        # Check if mackup can really delete it
        utils.delete(tfpath)
        assert not os.path.exists(tfpath)

    def test_delete_folder_recursively(self):
        # Create a tmp folder
        tfpath = tempfile.mkdtemp()

        # Let's put a file in it just for fun
        tfile = tempfile.NamedTemporaryFile(dir=tfpath, delete=False)
        filepath = tfile.name
        tfile.close()

        # Let's put another folder in it
        subfolder_path = tempfile.mkdtemp(dir=tfpath)

        # And a file in the subfolder
        tfile = tempfile.NamedTemporaryFile(dir=subfolder_path, delete=False)
        subfilepath = tfile.name
        tfile.close()

        # Make sure the created files and folders exists
        assert os.path.isdir(tfpath)
        assert os.path.isfile(filepath)
        assert os.path.isdir(subfolder_path)
        assert os.path.isfile(subfilepath)

        # Check if mackup can really delete it
        utils.delete(tfpath)
        assert not os.path.exists(tfpath)
        assert not os.path.exists(filepath)
        assert not os.path.exists(subfolder_path)
        assert not os.path.exists(subfilepath)

    def test_copy_file(self):
        # Create a tmp file
        tfile = tempfile.NamedTemporaryFile(delete=False)
        srcfile = tfile.name
        tfile.close()

        # Create a tmp folder
        dstpath = tempfile.mkdtemp()
        # Set the destination filename
        dstfile = os.path.join(dstpath, "subfolder", os.path.basename(srcfile))

        # Make sure the source file and destination folder exist and the
        # destination file doesn't yet exist
        assert os.path.isfile(srcfile)
        assert os.path.isdir(dstpath)
        assert not os.path.exists(dstfile)

        # Check if mackup can copy it
        utils.copy(srcfile, dstfile)
        assert os.path.isfile(srcfile)
        assert os.path.isdir(dstpath)
        assert os.path.exists(dstfile)

        # Let's clean up
        utils.delete(dstpath)

    def test_copy_fail(self):
        # Create a tmp FIFO file
        tfile = tempfile.NamedTemporaryFile()
        srcfile = tfile.name
        tfile.close()
        os.mkfifo(srcfile)

        # Create a tmp folder
        dstpath = tempfile.mkdtemp()
        # Set the destination filename
        dstfile = os.path.join(dstpath, "subfolder", os.path.basename(srcfile))

        # Make sure the source file and destination folder exist and the
        # destination file doesn't yet exist
        assert not os.path.isfile(srcfile)
        assert stat.S_ISFIFO(os.stat(srcfile).st_mode)
        assert os.path.isdir(dstpath)
        assert not os.path.exists(dstfile)

        # Check if mackup can copy it
        self.assertRaises(ValueError, utils.copy, srcfile, dstfile)
        assert not os.path.isfile(srcfile)
        assert stat.S_ISFIFO(os.stat(srcfile).st_mode)
        assert os.path.isdir(dstpath)
        assert not os.path.exists(dstfile)

        # Let's clean up
        utils.delete(srcfile)
        utils.delete(dstpath)

    def test_copy_file_to_dir(self):
        """Copies a file to a destination folder that already exists."""
        # Create a tmp folder
        srcpath = tempfile.mkdtemp()

        # Create a tmp file
        tfile = tempfile.NamedTemporaryFile(delete=False, dir=srcpath)
        srcfile = tfile.name
        tfile.close()

        # Create a tmp folder
        dstpath = tempfile.mkdtemp()

        # Set the destination filename
        srcpath_basename = os.path.basename(srcpath)
        dstfile = os.path.join(
            dstpath, "subfolder", srcpath_basename, os.path.basename(srcfile)
        )
        # Make sure the source file and destination folder exist and the
        # destination file doesn't yet exist
        assert os.path.isdir(srcpath)
        assert os.path.isfile(srcfile)
        assert os.path.isdir(dstpath)
        assert not os.path.exists(dstfile)

        # Check if mackup can copy it
        utils.copy(srcfile, dstfile)
        assert os.path.isdir(srcpath)
        assert os.path.isfile(srcfile)
        assert os.path.isdir(dstpath)
        assert os.path.exists(dstfile)

        # Let's clean up
        utils.delete(srcpath)
        utils.delete(dstpath)

    def test_copy_dir(self):
        """Copies a directory recursively to the destination path."""
        # Create a tmp folder
        srcpath = tempfile.mkdtemp()

        # Create a tmp file
        tfile = tempfile.NamedTemporaryFile(delete=False, dir=srcpath)
        srcfile = tfile.name
        tfile.close()

        # Create a tmp folder
        dstpath = tempfile.mkdtemp()

        # Set the destination filename
        srcpath_basename = os.path.basename(srcpath)
        dstfile = os.path.join(dstpath, srcpath_basename, os.path.basename(srcfile))
        # Make sure the source file and destination folder exist and the
        # destination file doesn't yet exist
        assert os.path.isdir(srcpath)
        assert os.path.isfile(srcfile)
        assert os.path.isdir(dstpath)
        assert not os.path.exists(dstfile)

        # Check if mackup can copy it
        utils.copy(srcpath, dstfile)
        assert os.path.isdir(srcpath)
        assert os.path.isfile(srcfile)
        assert os.path.isdir(dstpath)
        assert os.path.exists(dstfile)

        # Let's clean up
        utils.delete(srcpath)
        utils.delete(dstpath)

    def test_link_file(self):
        # Create a tmp file
        tfile = tempfile.NamedTemporaryFile(delete=False)
        srcfile = tfile.name
        tfile.close()

        # Create a tmp folder
        dstpath = tempfile.mkdtemp()
        # Set the destination filename
        dstfile = os.path.join(dstpath, "subfolder", os.path.basename(srcfile))

        # Make sure the source file and destination folder exist and the
        # destination file doesn't yet exist
        assert os.path.isfile(srcfile)
        assert os.path.isdir(dstpath)
        assert not os.path.exists(dstfile)

        # Check if mackup can link it and the link points to the correct place
        utils.link(srcfile, dstfile)
        assert os.path.isfile(srcfile)
        assert os.path.isdir(dstpath)
        assert os.path.exists(dstfile)
        assert os.readlink(dstfile) == srcfile

        # Let's clean up
        utils.delete(dstpath)

    def test_chmod_file(self):
        # Create a tmp file
        tfile = tempfile.NamedTemporaryFile(delete=False)
        file_name = tfile.name

        # Create a tmp directory with a sub folder
        dir_name = tempfile.mkdtemp()
        nested_dir = tempfile.mkdtemp(dir=dir_name)

        # # File Tests

        # Change the tmp file stats to S_IWRITE (200), write access only
        os.chmod(file_name, stat.S_IWRITE)
        assert convert_to_octal(file_name) == "200"

        # Check to make sure that utils.chmod changes the bits to 600,
        # which is read and write access for the owner
        utils.chmod(file_name)
        assert convert_to_octal(file_name) == "600"

        # # Directory Tests

        # Change the tmp folder stats to S_IREAD (400), read access only
        os.chmod(dir_name, stat.S_IREAD)
        assert convert_to_octal(dir_name) == "400"

        # Check to make sure that utils.chmod changes the bits of all
        # directories to 700, which is read, write, and execute access for the
        # owner
        utils.chmod(dir_name)
        assert convert_to_octal(dir_name) == "700"
        assert convert_to_octal(nested_dir) == "700"

        # Use an "unsupported file type". In this case, /dev/null
        self.assertRaises(ValueError, utils.chmod, os.devnull)

    def test_error(self):
        test_string = "Hello World"
        self.assertRaises(SystemExit, utils.error, test_string)

    def test_failed_backup_location(self):
        """
        Tests for the error that should occur if the backup folder cannot be
        found for Dropbox, Google, and Copy
        """
        # Hack to make our home folder some temporary folder
        temp_home = tempfile.mkdtemp()
        utils.os.environ["HOME"] = temp_home

        # Check for the missing Dropbox folder
        assert not os.path.exists(os.path.join(temp_home, ".dropbox/host.db"))
        self.assertRaises(SystemExit, utils.get_dropbox_folder_location)

        # Check for the missing Google Drive folder
        assert not os.path.exists(
            os.path.join(
                temp_home, "Library/Application Support/Google/Drive/sync_config.db"
            )
        )
        self.assertRaises(SystemExit, utils.get_google_drive_folder_location)

        # Check for the missing Copy Folder
        assert not os.path.exists(
            os.path.join(temp_home, "Library/Application Support/Copy Agent/config.db")
        )
        self.assertRaises(SystemExit, utils.get_copy_folder_location)

    def test_is_process_running(self):
        # A pgrep that has one letter and a wildcard will always return id 1
        assert utils.is_process_running("a*")
        assert not utils.is_process_running("some imaginary process")

    def test_can_file_be_synced_on_current_platform(self):
        # Any file path will do, even if it doesn't exist
        path = "some/file"

        # Force the Mac OSX Test using lambda magic
        utils.platform.system = lambda *args: utils.constants.PLATFORM_DARWIN
        assert utils.can_file_be_synced_on_current_platform(path)

        # Force the Linux Test using lambda magic
        utils.platform.system = lambda *args: utils.constants.PLATFORM_LINUX
        assert utils.can_file_be_synced_on_current_platform(path)

        # Try to use the library path on Linux, which shouldn't work
        path = os.path.join(os.environ["HOME"], "Library/")
        assert not utils.can_file_be_synced_on_current_platform(path)


# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
from odoo.exceptions import Warning
import datetime

class SaleOrderLine(models.Model):
    _inherit = "sale.order.line"

    # @api.depends('product_id')
    # def _compute_purchase_order_line_id(self):
    #     for obj in self:
    #         filtre=[
    #             ('is_sale_order_line_id','=',obj.id),
    #         ]
    #         order_lines=self.env['purchase.order.line'].search(filtre)
    #         order_id = False
    #         for line in order_lines:
    #             if line.order_id.state!='cancel':
    #                 order_id=line.id
    #                 break
    #         obj.is_purchase_order_line_id = order_id

    # is_purchase_order_line_id = fields.Many2one('purchase.order.line', string=u'Ligne commande fournisseur', compute='_compute_purchase_order_line_id', readonly=True, store=False)

    is_purchase_line_id = fields.Many2one('purchase.order.line', string=u'Ligne commande fournisseur', index=True, copy=False)

    is_date_reception         = fields.Date(string=u'Date réception')
    is_livraison_directe      = fields.Boolean(string=u'Livraison directe', help=u"Si cette case est cochée, une commande fournisseur spécifique pour ce client sera créée",default=False)

    def get_fournisseur_par_defaut(self):
        now = datetime.date.today()
        suppliers=self.env['product.supplierinfo'].search([('product_tmpl_id', '=', self.product_id.product_tmpl_id.id)])
        partner=False
        for s in suppliers:
            if now>=s.date_start and now<= s.date_end:
                partner=s.name
                break
        return partner

    @api.onchange('product_id','is_livraison_directe')
    def onchange_product_id_for_date_reception(self):
        if self.is_livraison_directe:
            if self.order_id.delivery_date:
                self.is_date_reception = str(self.order_id.delivery_date)[:10]
        else:
            partner = self.get_fournisseur_par_defaut()
            if partner and partner.is_date_reception:
                self.is_date_reception = partner.is_date_reception

    @api.onchange('is_date_reception')
    def onchange_is_date_reception(self):
        if self.is_date_reception:
            partner = self.get_fournisseur_par_defaut()
            if partner:
                partner.write({'is_date_reception': self.is_date_reception})

    @api.multi
    def acceder_commande_fournisseur(self, vals):
        for obj in self:
            res= {
                'name': 'Ligne commande fournisseur',
                'view_mode': 'tree,form',
                'view_type': 'form',
                'res_model': 'purchase.order.line',
                'type': 'ir.actions.act_window',
                'domain': [
                    ('id','=',obj.is_purchase_line_id.id),
                ],
            }
            return res

class SaleOrder(models.Model):
    _inherit = "sale.order"

    is_commande_soldee = fields.Boolean(string=u'Commande soldée', default=False, copy=False, help=u"Cocher cette case pour indiquer qu'aucune nouvelle livraison n'est prévue sur celle-ci")

    @api.depends('order_line')
    def _compute_is_creer_commande_fournisseur_vsb(self):
        for obj in self:
            vsb = False
            for line in obj.order_line:
                if not line.is_purchase_line_id.id:
                    vsb=True
                    break
            obj.is_creer_commande_fournisseur_vsb=vsb

    is_creer_commande_fournisseur_vsb = fields.Boolean(string=u'Créer commande fournisseur', compute='_compute_is_creer_commande_fournisseur_vsb', readonly=True, store=False)

    @api.multi
    def creer_commande_fournisseur_action(self):
        for obj in self:
            if not len(obj.order_line):
                raise Warning(u"Il n'y a aucune ligne de commandes à traiter !")
            for line in obj.order_line:
                if not line.is_date_reception:
                    raise Warning(u"La date de réception n'est pas renseignée sur toutes les lignes")
            now = datetime.date.today()
            for line in obj.order_line:
                suppliers=self.env['product.supplierinfo'].search([('product_tmpl_id', '=', line.product_id.product_tmpl_id.id)])
                partner_id=False
                supplierinfo=False
                for s in suppliers:
                    if now>=s.date_start and now<= s.date_end:
                        supplierinfo=s
                        break
                if supplierinfo:
                    partner_id = supplierinfo.name.id
                    date_reception = str(line.is_date_reception)
                    date_planned  = date_reception+' 08:00:00'
                    filtre=[
                        ('partner_id'  ,'='   , partner_id),
                        ('state'       ,'='   , 'draft'),
                        ('date_planned','>=', date_reception+' 00:00:00'),
                        ('date_planned','<=', date_reception+' 23:59:59'),
                    ]
                    if line.is_livraison_directe:
                        filtre.append(('delivery_adress','=', obj.partner_shipping_id.id))
                    orders=self.env['purchase.order'].search(filtre,limit=1)
                    if orders:
                        order=orders[0]
                    else:
                        vals={
                            'partner_id'  : partner_id,
                        }
                        order=self.env['purchase.order'].create(vals)
                        if order:
                            order.onchange_partner_id()
                            if line.is_livraison_directe:
                                order.delivery_adress = obj.partner_shipping_id.id
                    print(order,order.name)

                    #** Création des lignes ************************************
                    filtre=[
                        ('order_id'  ,'=', order.id),
                        ('product_id','=', line.product_id.id),
                    ]
                    order_lines=self.env['purchase.order.line'].search(filtre,limit=1)
                    if not order_lines:
                        if order:
                            vals={
                                'order_id'    : order.id,
                                'product_id'  : line.product_id.id,
                                'name'        : line.name,
                                'product_qty' : line.product_uom_qty,
                                'product_uom' : line.product_uom.id,
                                'date_planned': date_planned,
                                'price_unit'  : line.price_unit,
                                #'is_sale_order_line_id': line.id,
                            }
                            order_line=self.env['purchase.order.line'].create(vals)
                            order_line.onchange_product_id()
                    else:
                        order_line = order_lines[0]

                    if order_line:
                        line.is_purchase_line_id=order_line.id
                        filtre=[
                            ('is_purchase_line_id'  ,'=', order_line.id),
                        ]
                        order_lines=self.env['sale.order.line'].search(filtre)
                        qty=0
                        for l in order_lines:
                            qty+=l.product_uom_qty
                        order_line.product_qty = qty
                    # ***********************************************************

    # @api.multi
    # def creer_commande_fournisseur_action(self):
    #     for obj in self:
    #         if not len(obj.order_line):
    #             raise Warning(u"Il n'y a aucune ligne de commandes à traiter !")
    #         for line in obj.order_line:
    #             if not line.is_date_reception:
    #                 raise Warning(u"La date de réception n'est pas renseignée sur toutes les lignes")
    #         now = datetime.date.today()
    #         for line in obj.order_line:
    #             suppliers=self.env['product.supplierinfo'].search([('product_tmpl_id', '=', line.product_id.product_tmpl_id.id)])
    #             partner_id=False
    #             supplierinfo=False
    #             for s in suppliers:
    #                 if now>=s.date_start and now<= s.date_end:
    #                     supplierinfo=s
    #                     break
    #             if supplierinfo:
    #                 partner_id = supplierinfo.name.id
    #                 date_reception = str(line.is_date_reception)
    #                 date_planned  = date_reception+' 08:00:00'
    #                 filtre=[
    #                     ('partner_id'  ,'='   , partner_id),
    #                     ('state'       ,'='   , 'draft'),
    #                     ('date_planned','>=', date_reception+' 00:00:00'),
    #                     ('date_planned','<=', date_reception+' 23:59:59'),
    #                 ]
    #                 if line.is_livraison_directe:
    #                     filtre.append(('delivery_adress','=', obj.partner_shipping_id.id))
    #                 orders=self.env['purchase.order'].search(filtre,limit=1)
    #                 if orders:
    #                     order=orders[0]
    #                 else:
    #                     vals={
    #                         'partner_id'  : partner_id,
    #                     }
    #                     order=self.env['purchase.order'].create(vals)
    #                     if order:
    #                         order.onchange_partner_id()
    #                         if line.is_livraison_directe:
    #                             order.delivery_adress = obj.partner_shipping_id.id

    #                 #** Création des lignes ************************************
    #                 filtre=[
    #                     ('order_id'  ,'='   , order.id),
    #                     ('is_sale_order_line_id','=',line.id),
    #                 ]
    #                 order_lines=self.env['purchase.order.line'].search(filtre)
    #                 if not order_lines:
    #                     if order:
    #                         vals={
    #                             'order_id'    : order.id,
    #                             'product_id'  : line.product_id.id,
    #                             'name'        : line.name,
    #                             'product_qty' : line.product_uom_qty,
    #                             'product_uom' : line.product_uom.id,
    #                             'date_planned': date_planned,
    #                             'price_unit'  : line.price_unit,
    #                             'is_sale_order_line_id': line.id,
    #                         }
    #                         order_line=self.env['purchase.order.line'].create(vals)
    #                         order_line.onchange_product_id()
    #                         order_line.product_qty = line.product_uom_qty
    #                 #***********************************************************


import math
from typing import Callable, Tuple, Optional
from functools import partial
from torch import nn, Tensor
from torch.nn import functional as F
from torchvision.ops import StochasticDepth

class Lambda(nn.Module):
    """An utility Module, it allows custom function to be passed

    Args:
        lambd (Callable[Tensor]): A function that does something on a tensor

    Examples:
        >>> add_two = Lambda(lambd x: x + 2)
        >>> add_two(Tensor([0])) // 2
    """

    def __init__(self, lambd: Callable[[Tensor], Tensor]):

        super().__init__()
        self.lambd = lambd

    def forward(self, x: Tensor) -> Tensor:
        return self.lambd(x)

class Conv2dPad(nn.Conv2d):
    """2D Convolutions with different padding modes.

    'auto' will use the kernel_size to calculate the padding
    'same' same padding as TensorFLow. It will dynamically pad the image based on its size

    Args:
        mode (str, optional): [description]. Defaults to 'auto'.
    """

    def __init__(self, *args, mode: str = "auto", padding: int = 0, **kwargs):

        super().__init__(*args, **kwargs)
        self.mode = mode
        # dynamic add padding based on the kernel_size
        if self.mode == "auto":
            self.padding = (
                self._get_padding(padding)
                if padding != 0
                else (self.kernel_size[0] // 2, self.kernel_size[1] // 2)
            )

    def _get_padding(self, padding: int) -> Tuple[int, int]:
        return (padding, padding)

    def forward(self, x: Tensor) -> Tensor:
        if self.mode == "same":
            ih, iw = x.size()[-2:]
            kh, kw = self.weight.size()[-2:]
            sh, sw = self.stride
            # change the output size according to stride
            oh, ow = math.ceil(ih / sh), math.ceil(iw / sw)
            pad_h = max(
                (oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0
            )
            pad_w = max(
                (ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0
            )
            if pad_h > 0 or pad_w > 0:
                x = F.pad(
                    x, [pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2]
                )
            return F.conv2d(
                x,
                self.weight,
                self.bias,
                self.stride,
                self.padding,
                self.dilation,
                self.groups,
            )

        else:
            return super().forward(x)

class ConvNormAct(nn.Sequential):
    """Utility module that stacks one convolution layer, a normalization layer and an activation function.

    Example:
        >>> ConvNormAct(32, 64, kernel_size=3)
            ConvNormAct(
                (conv): Conv2dPad(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
                (norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
                (act): ReLU()
            )

        >>> ConvNormAct(32, 64, kernel_size=3, normalization = None )
            ConvNormAct(
                (conv): Conv2dPad(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
                (act): ReLU()
            )

        >>> ConvNormAct(32, 64, kernel_size=3, activation = None )
            ConvNormAct(
                (conv): Conv2dPad(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
                (norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            )

    We also provide additional modules built on top of this one: `ConvBn`, `ConvAct`, `Conv3x3BnAct`
    Args:
            out_features (int): Number of input features
            out_features (int): Number of output features
            conv (nn.Module, optional): Convolution layer. Defaults to Conv2dPad.
            normalization (nn.Module, optional): Normalization layer. Defaults to nn.BatchNorm2d.
            activation (nn.Module, optional): Activation function. Defaults to nn.ReLU.
    """

    def __init__(
        self,
        in_features: int,
        out_features: int,
        conv: nn.Module = Conv2dPad,
        activation: Optional[nn.Module] = nn.ReLU,
        normalization: Optional[nn.Module] = nn.BatchNorm2d,
        bias: bool = False,
        **kwargs
    ):
        super().__init__()
        self.add_module("conv", conv(in_features, out_features, **kwargs, bias=bias))
        if normalization:
            self.add_module("norm", normalization(out_features))
        if activation:
            self.add_module("act", activation())

class ConvNormRegAct(nn.Sequential):
    """Utility module that stacks one convolution layer, a normalization layer, a regularization layer and an activation function.

    Example:
        >>> ConvNormDropAct(32, 64, kernel_size=3)
            ConvNormDropAct(
                (conv): Conv2dPad(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
                (norm): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
                (reg): StochasticDepth(p=0.2)
                (act): ReLU()
            )
    """

    def __init__(
        self,
        in_features: int,
        out_features: int,
        conv: nn.Module = Conv2dPad,
        activation: Optional[nn.Module] = nn.ReLU,
        normalization: Optional[nn.Module] = nn.BatchNorm2d,
        regularization: Optional[nn.Module] = partial(StochasticDepth, mode="batch"),
        p: float = 0.2,
        bias: bool = False,
        **kwargs
    ):
        super().__init__()
        self.add_module("conv", conv(in_features, out_features, **kwargs, bias=bias))
        if normalization:
            self.add_module("norm", normalization(out_features))
        if regularization:
            self.add_module("reg", regularization(p=p))
        if activation:
            self.add_module("act", activation())

ReLUInPlace = partial(nn.ReLU, inplace=True)

class NormActConv(nn.Sequential):
    """A Sequential layer composed by a normalization, an activation and a convolution layer. This is usually known as a 'Preactivation Block'

    Args:
        in_features (int): Number of input features
        out_features (int): Number of output features
        conv (nn.Module, optional): [description]. Defaults to Conv2dPad.
        normalization (nn.Module, optional): [description]. Defaults to nn.BatchNorm2d.
        activation (nn.Module, optional): [description]. Defaults to nn.ReLU.
    """

    def __init__(
        self,
        in_features: int,
        out_features: int,
        conv: nn.Module = Conv2dPad,
        normalization: Optional[nn.Module] = nn.BatchNorm2d,
        activation: Optional[nn.Module] = ReLUInPlace,
        *args,
        **kwargs
    ):
        super().__init__()
        if normalization:
            self.add_module("norm", normalization(in_features))
        if activation:
            self.add_module("act", activation())
        self.add_module("conv", conv(in_features, out_features, *args, **kwargs))

ConvBnAct = partial(ConvNormAct, normalization=nn.BatchNorm2d)
ConvBn = partial(ConvBnAct, activation=None)
ConvAct = partial(ConvBnAct, normalization=None, bias=True)
Conv3x3BnAct = partial(ConvBnAct, kernel_size=3)
BnActConv = partial(NormActConv, normalization=nn.BatchNorm2d)
ConvBnDropAct = partial(
    ConvNormRegAct, normalization=nn.BatchNorm2d, regularization=nn.Dropout2d
)


import pygame
import random
import math
import time

# Initializing pygame and the screen
pygame.init()
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("RRT with Obstacles")

# Colors
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)

# Static Obstacles
obstacles = [
    # Benches on the platform
    (120, 430, 50, 15),
    (190, 430, 50, 15),
    (260, 430, 50, 15),
    (330, 430, 50, 15),
    (400, 430, 50, 15),
    (470, 430, 50, 15),
    (540, 430, 50, 15),
    (610, 430, 50, 15),

    # Pillars/supports
    (200, 50, 15, 350),
    (300, 50, 15, 350),
    (400, 50, 15, 350),
    (500, 50, 15, 350),
    (600, 50, 15, 350)
]

def draw_obstacles(screen, obstacles):
    for obstacle in obstacles:
        pygame.draw.rect(screen, black, pygame.Rect(obstacle[0], obstacle[1], obstacle[2], obstacle[3]))

# New DynamicObstacle Class
class DynamicObstacle:
    def __init__(self, x, y, w, h, vel_x, vel_y):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.vel_x = vel_x
        self.vel_y = vel_y

    def move(self, static_obstacles):
        # Tentative movement
        new_x = self.x + self.vel_x
        new_y = self.y + self.vel_y
        new_rect = (new_x, new_y, self.w, self.h)

        # Checking if the new rectangle will collide with any static obstacles
        for obs in static_obstacles:
            if self.check_collision(new_rect, obs):
                # Randomize new direction
                self.vel_x = random.choice([-1, 1]) * abs(self.vel_x)
                self.vel_y = random.choice([-1, 1]) * abs(self.vel_y)
                return

        # Applying movement if no collision
        self.x = new_x
        self.y = new_y

    def as_rect(self):
        return (self.x, self.y, self.w, self.h)

    def check_collision(self, rect1, rect2):
        return (rect1[0] < rect2[0] + rect2[2] and rect1[0] + rect1[2] > rect2[0] and
                rect1[1] < rect2[1] + rect2[3] and rect1[1] + rect1[3] > rect2[1])

# RRT Algorithm
class Node:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.parent = None

def distance(a, b):
    return math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2)

def step_from_to(node1, node2, stepSize):
    if distance(node1, node2) < stepSize:
        return node2
    else:
        theta = math.atan2(node2.y - node1.y, node2.x - node1.x)
        return Node(node1.x + stepSize * math.cos(theta), node1.y + stepSize * math.sin(theta))

def is_inside_obstacle(point, obstacle):
    """
    Check if the point is inside the obstacle rectangle.
    """
    x, y = point
    x0, y0, w, h = obstacle
    return x0 <= x <= x0 + w and y0 <= y <= y0 + h

def is_path_clear(start, end, obstacles):
    """
    Checking if the path between start and end points is clear of obstacles.
    :param start: tuple (x, y), start point
    :param end: tuple (x, y), end point
    :param obstacles: list of tuples (x, y, width, height), representing obstacles
    :return: boolean, True if path is clear, False otherwise
    """
    for obstacle in obstacles:
        # Calculating the rectangle's (obstacle's) vertices
        top_left = (obstacle[0], obstacle[1])
        top_right = (obstacle[0] + obstacle[2], obstacle[1])
        bottom_left = (obstacle[0], obstacle[1] + obstacle[3])
        bottom_right = (obstacle[0] + obstacle[2], obstacle[1] + obstacle[3])

        # Checking all sides of the rectangle
        if do_intersect(start, end, top_left, top_right):
            return False
        if do_intersect(start, end, top_right, bottom_right):
            return False
        if do_intersect(start, end, bottom_right, bottom_left):
            return False
        if do_intersect(start, end, bottom_left, top_left):
            return False

    # If we've not returned False yet, path is clear
    return True

def do_intersect(p1, q1, p2, q2):
    """
    Checking if two line segments intersect.
    :param p1: tuple (x, y), start point of line segment 1
    :param q1: tuple (x, y), end point of line segment 1
    :param p2: tuple (x, y), start point of line segment 2
    :param q2: tuple (x, y), end point of line segment 2
    :return: boolean, True if lines intersect, False otherwise
    """
    def orientation(p, q, r):
        """
        Finding orientation of ordered triplet (p, q, r).
        :param p: tuple (x, y), first point
        :param q: tuple (x, y), second point
        :param r: tuple (x, y), third point
        :return: int, 0 --> p, q and r are colinear, 1 --> Clockwise, 2 --> Counterclockwise
        """
        val = (float(q[1] - p[1]) * (r[0] - q[0])) - (float(q[0] - p[0]) * (r[1] - q[1]))
        if val == 0: return 0  # colinear
        elif val > 0: return 1  # clockwise
        else: return 2  # counterclockwise

    def on_segment(p, q, r):
        """
        Checking if point q lies on line segment 'pr'
        :param p: tuple (x, y), first point
        :param q: tuple (x, y), second point
        :param r: tuple (x, y), third point
        :return: boolean, True if q lies on segment pr, False otherwise
        """
        if q[0] <= max(p[0], r[0]) and q[0] >= min(p[0], r[0]) and q[1] <= max(p[1], r[1]) and q[1] >= min(p[1], r[1]):
            return True
        return False

    # Finding the four orientations needed for the general and special cases
    o1 = orientation(p1, q1, p2)
    o2 = orientation(p1, q1, q2)
    o3 = orientation(p2, q2, p1)
    o4 = orientation(p2, q2, q1)

    # In general case
    if o1 != o2 and o3 != o4:
        return True

    # Special cases: when any three points are colinear
    if o1 == 0 and on_segment(p1, p2, q1): return True
    if o2 == 0 and on_segment(p1, q2, q1): return True
    if o3 == 0 and on_segment(p2, p1, q2): return True
    if o4 == 0 and on_segment(p2, q1, q2): return True

    return False  # Doesn't fall

class Person:
    def __init__(self):
        self.width = 10
        self.height = 10
        self.speed = 2
        self.size = 5
        self.buffer_distance = 10
        self.x, self.y = self.random_free_space()
        self.dx = random.choice([-1, 1])
        self.dy = random.choice([-1, 1])

    def random_free_space(self):
        while True:
            x = random.randint(self.width + self.buffer_distance, width - self.width - self.buffer_distance)
            y = random.randint(self.height + self.buffer_distance, height - self.height - self.buffer_distance)
            if not any(is_inside_obstacle((x, y), obstacle) for obstacle in obstacles):
                return x, y

    def in_obstacle(self, pos, include_buffer=False):
        buffer = self.buffer_distance if include_buffer else 0
        for obs in obstacles:
            if ((pos[0] - self.size - buffer > obs[0] and pos[0] + self.size + buffer < obs[0] + obs[2]) and
               (pos[1] - self.size - buffer > obs[1] and pos[1] + self.size + buffer < obs[1] + obs[3])):
                return True
        return False

    def move(self):
        self.x += self.dx * self.speed
        self.y += self.dy * self.speed

        if self.x < 0 or self.x > width or self.y < 0 or self.y > height or any(is_inside_obstacle((self.x, self.y), obstacle) for obstacle in obstacles):
            while True:  # Keep generating new directions until a valid one is found
                self.dx = random.choice([-1, 1])
                self.dy = random.choice([-1, 1])
                new_x = self.x + self.dx * self.speed
                new_y = self.y + self.dy * self.speed
                if not (new_x < 0 or new_x > width or new_y < 0 or new_y > height or any(is_inside_obstacle((new_x, new_y), obstacle) for obstacle in obstacles)):
                    break

    def draw(self, screen):
        pygame.draw.rect(screen, red, pygame.Rect(self.x - self.width/2, self.y - self.height/2, self.width, self.height))

    def collides(self, rect):
        if (self.x > rect[0] and self.x < rect[0] + rect[2] and
            self.y > rect[1] and self.y < rect[1] + rect[3]):
            return True
        return False

    def render(self, screen):
        pygame.draw.circle(screen, red, (self.x, self.y), self.size)

# Lets reate some people to contribute towards dynamic obstacles
people = [Person() for _ in range(15)]

def main():
    average_time = 0
    rrt_loops = 5
    for i in range(rrt_loops):

        running = True
        nodes = []
        stepSize = 20
        FinalProx = 20
        start = Node(50, 50)
        nodes.append(start)
        goal = Node(550, 550)

        start_time = time.time()

        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False

            screen.fill(white)
            draw_obstacles(screen, obstacles)

            # Operations on dynamic obstacles
            for person in people:
                person.move()
                pygame.draw.circle(screen, red, (int(person.x), int(person.y)), person.size)

            # Drawing existing nodes and edges
            for n in nodes:
                pygame.draw.circle(screen, black, (int(n.x), int(n.y)), 3)
                if n.parent:
                    pygame.draw.line(screen, green, (n.x, n.y), (n.parent.x, n.parent.y))

            # Drawing goal
            pygame.draw.circle(screen, blue, (int(goal.x), int(goal.y)), 5)

            # RRT Algorithm
            rand = Node(random.random()*width, random.random()*height)
            nn = nodes[0]
            for p in nodes:
                if distance(p, rand) < distance(nn, rand):
                    nn = p

            newnode = step_from_to(nn, rand, stepSize)

            # Checking if the path between the nearest node and the new node is clear
            if is_path_clear((nn.x, nn.y), (newnode.x, newnode.y), obstacles):
                for person in people:
                    if person.collides((newnode.x, newnode.y, 5, 5)):
                        break
                else:
                    newnode.parent = nn
                    nodes.append(newnode)

            # Checking for completion
            if distance(newnode, goal) < FinalProx:
                # Drawing final path
                while newnode.parent:
                    pygame.draw.line(screen, blue, (newnode.x, newnode.y), (newnode.parent.x, newnode.parent.y), 3)
                    newnode = newnode.parent
                running = False

            pygame.display.flip()
            # pygame.time.wait(50)

        finish_time = time.time()

        print("Time taken for loop ", i+1, "/", rrt_loops, " : ", finish_time-start_time)
        average_time += finish_time-start_time
        pygame.time.wait(2000)

    print("Average time : ", average_time/rrt_loops)
    pygame.quit()

if __name__ == "__main__":
    main()


from django.conf.urls import url
from gale import views

urlpatterns = [
              url(r'^$',views.home),
              url(r'^web_crawler/$', views.web_crawler, ),
              url(r'^route/$', views.route, ),
              url(r'^routedetail/$', views.create_excel, ),
              url(r'^predefined_routes/$', views.noptimize, ),
              url(r'^dist/$', views.distance_matrix, ),
              url(r'^report_info/$', views.ReportInfo, ),
            url(r'^barcode/$', views.barcode, ),
             url(r'^inventory_mongo/$', views.inventory, ),
              url(r'^inventory_data/$', views.inventory_data, ),
               url(r'^price_mongo/$', views.price_mongo, ),
                url(r'^route_mongo/$', views.route_mongo, ),
                url(r'^redeliver/$', views.redeliver, ),
                url(r'^redelivery_points/$', views.redelivery_points, ),

             ]


from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
def home_view(request,*args,**kwargs):
    # return HttpResponse("
Hello World
")
    return render(request,"home.html",{})

def about_view(request,*args,**kwargs):
    info={
        "name":"Sid",
        "address":"Baneshwor",
        "mobile":9808877669,
        "list":["a","b","c"]
    }
    return render(request,"about.html",info)


from typing import Any, Dict, List, Type, TypeVar, Union

import attr

from ..models.template_layout import TemplateLayout
from ..types import UNSET, Unset

T = TypeVar("T", bound="PageForm")

@attr.s(auto_attribs=True)
class PageForm:
    """
    Attributes:
        theme_id (Union[Unset, str]):
        title (Union[Unset, str]):
        host_id (Union[Unset, str]):
        layout (Union[Unset, TemplateLayout]):
        anonymous_layout (Union[Unset, bool]):
    """

    theme_id: Union[Unset, str] = UNSET
    title: Union[Unset, str] = UNSET
    host_id: Union[Unset, str] = UNSET
    layout: Union[Unset, TemplateLayout] = UNSET
    anonymous_layout: Union[Unset, bool] = UNSET
    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        theme_id = self.theme_id
        title = self.title
        host_id = self.host_id
        layout: Union[Unset, Dict[str, Any]] = UNSET
        if not isinstance(self.layout, Unset):
            layout = self.layout.to_dict()

        anonymous_layout = self.anonymous_layout

        field_dict: Dict[str, Any] = {}
        field_dict.update(self.additional_properties)
        field_dict.update({})
        if theme_id is not UNSET:
            field_dict["themeId"] = theme_id
        if title is not UNSET:
            field_dict["title"] = title
        if host_id is not UNSET:
            field_dict["hostId"] = host_id
        if layout is not UNSET:
            field_dict["layout"] = layout
        if anonymous_layout is not UNSET:
            field_dict["anonymousLayout"] = anonymous_layout

        return field_dict

    @classmethod
    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
        d = src_dict.copy()
        theme_id = d.pop("themeId", UNSET)

        title = d.pop("title", UNSET)

        host_id = d.pop("hostId", UNSET)

        _layout = d.pop("layout", UNSET)
        layout: Union[Unset, TemplateLayout]
        if isinstance(_layout, Unset):
            layout = UNSET
        else:
            layout = TemplateLayout.from_dict(_layout)

        anonymous_layout = d.pop("anonymousLayout", UNSET)

        page_form = cls(
            theme_id=theme_id,
            title=title,
            host_id=host_id,
            layout=layout,
            anonymous_layout=anonymous_layout,
        )

        page_form.additional_properties = d
        return page_form

    @property
    def additional_keys(self) -> List[str]:
        return list(self.additional_properties.keys())

    def __getitem__(self, key: str) -> Any:
        return self.additional_properties[key]

    def __setitem__(self, key: str, value: Any) -> None:
        self.additional_properties[key] = value

    def __delitem__(self, key: str) -> None:
        del self.additional_properties[key]

    def __contains__(self, key: str) -> bool:
        return key in self.additional_properties


# -*- coding: utf-8 -*-
from flask import request, render_template, redirect, current_app
from flask_login import login_required, current_user

from . import api
from .models import Client
from ...extensions import oauth

def get_current_user():
    User = current_app.user_model
    return User.query.get(current_user.id)

@api.route('/oauth/authorize', methods=['GET', 'POST'])
@login_required
@oauth.authorize_handler
def oauth_authorize(*args, **kwargs):
    user = get_current_user()
    if not user:
        return redirect('/')

    if request.method == 'GET':
        client_id = kwargs.get('client_id')
        client = Client.query.filter_by(client_id=client_id).first()
        kwargs['client'] = client
        kwargs['user'] = user
        return render_template('authorize.html', **kwargs)

    confirm = request.form.get('confirm', 'no')
    return confirm == 'yes'

@api.route('/oauth/token', methods=['GET', 'POST'])
@oauth.token_handler
def oauth_access_token():
    return None


import datetime as dt

import azure.mgmt.costmanagement.aio

import util.cache
import util.azure

class CostClient:
    def __init__(self):
        creds, subscription_id = util.azure.get_aio_credentials()
        self._subscription_id = subscription_id
        self._cost_management = azure.mgmt.costmanagement.aio.CostManagementClient(
            credential=creds,
            subscription_id=subscription_id
        )

    @util.cache.async_cache_ttl(ttl=60 * 60, skip_n=1)
    async def report(
        self, *,
        scope=None,
        timeframe='custom',
        from_date: dt.datetime=None,
        to_date: dt.datetime=None,
        dataset: dict={}
    ):
        scope = scope or f'/subscriptions/{self._subscription_id}'

        return await self._query_usage(
            scope,
            query_type='ActualCost',
            timeframe=timeframe,
            from_date=from_date,
            to_date=to_date,
            dataset=dataset
        )

    async def _query_usage(
        self, scope, *,
        query_type: str='ActualCost',
        timeframe: str='custom',
        from_date: dt.datetime=None,
        to_date: dt.datetime=None,
        dataset: dict={}
    ):
        query = {
            'type': query_type,
            'dataset': dataset
        }

        if timeframe != 'last_invoice':
            query['timeframe'] = timeframe

        if timeframe == 'custom':
            query['timePeriod'] = {
                'from': from_date,
                'to':to_date
            }

        return await self._cost_management.query.usage(scope, query)


from unittest.mock import patch
from uuid import UUID

import pytest
import random
import requests
from jsonrpc_requests import TransportError

from wacryptolib.exceptions import KeystoreDoesNotExist
from wacryptolib.jsonrpc_client import JsonRpcProxy, status_slugs_response_error_handler

def test_waserver_wsgi_application(db):
    from waserver.wsgi import application

    with pytest.raises(KeyError, match="REQUEST_METHOD"):
        application(environ={}, start_response=lambda *args, **kwargs: None)

def test_robots_txt(client):
    response = client.get("/robots.txt")
    assert response.status_code == 200
    assert "User-agent" in response.content.decode("utf8")

def test_jsonrpc_invalid_method(live_server):
    jsonrpc_url = live_server.url + random.choice(["/gateway/jsonrpc/", "/trustee/jsonrpc/"])

    gateway_proxy = JsonRpcProxy(url=jsonrpc_url, response_error_handler=status_slugs_response_error_handler)

    with pytest.raises(TransportError, match="Got non-200 response from server, status code: 404"):
        gateway_proxy.unexistingmethod()

def test_jsonrpc_invalid_http_get_request(live_server):
    jsonrpc_url = live_server.url + random.choice(["/gateway/jsonrpc/", "/trustee/jsonrpc/"])

    response = requests.get(jsonrpc_url)
    assert response.headers["Content-Type"] == "application/json"
    assert response.json() == {
        "error": {
            "code": {"$numberInt": "-32600"},
            "data": None,
            "message": "InvalidRequestError: The method you are trying to access is not available by GET requests",
            "name": "InvalidRequestError",
        },
        "id": None,
    }

def test_waserver_abnormal_error_masking(live_server):
    jsonrpc_url = live_server.url + "/gateway/jsonrpc/"

    gateway_proxy = JsonRpcProxy(url=jsonrpc_url, response_error_handler=status_slugs_response_error_handler)

    keystore_uid = UUID("cac682a8-809f-4de5-bbfd-72b533a37a21")

    with pytest.raises(KeystoreDoesNotExist):
        gateway_proxy.get_public_authenticator(keystore_uid=keystore_uid)

    # Patch the IMPORTED callable of view.py!
    with patch("waserver.apps.wagateway.views.get_public_authenticator", side_effect=KeyError("wrong key ABC")):

        with pytest.raises(TransportError):  # Server error NOT translated!
            gateway_proxy.get_public_authenticator(keystore_uid=keystore_uid)


import cv2

import numpy as np
import tensorflow as tf
import keras.backend as K

def load_weights(model, weight_file):
    wf = open(weight_file, 'rb')

    major, minor, revision, seen, _ = np.fromfile(wf, dtype=np.int32, count=5)
    j = 0
    for i in range(75):
        conv_layer_name = 'conv2d_%d' % i if i > 0 else 'conv2d'
        bn_layer_name = 'batch_normalization_%d' % j if j > 0 else 'batch_normalization'

        conv_layer = model.get_layer(conv_layer_name)
        filters = conv_layer.filters
        k_size = conv_layer.kernel_size[0]
        in_dim = conv_layer.input_shape[-1]

        if i not in [58, 66, 74]:
            # darknet weights: [beta, gamma, mean, variance]
            bn_weights = np.fromfile(wf, dtype=np.float32, count=4 * filters)
            bn_weights = bn_weights.reshape((4, filters))[[1, 0, 2, 3]]
            bn_layer = model.get_layer(bn_layer_name)

            j += 1

        else:
            conv_bias = np.fromfile(wf, dtype=np.float32, count=filters)

        # darknet shape is (out_dim, in_dim, height,width)
        conv_shape = (filters, in_dim, k_size, k_size)
        conv_weights = np.fromfile(wf, dtype=np.float32, count=np.product(conv_shape))

        # tf shpae (height, width, in_dim, out_dim)
        conv_weights = conv_weights.reshape(conv_shape).transpose([2, 3, 1, 0])

        if i not in [58, 66, 74]:
            conv_layer.set_weights([conv_weights])
            bn_layer.set_weights(bn_weights)
        else:
            conv_layer.set_weights([conv_weights, conv_bias])

    assert len(wf.read(0)) == 0, 'failed to read all data'
    wf.close()

    return model

def read_class_names(class_file_name):
    names = {}
    with open(class_file_name, 'r') as data:
        for ID, name in enumerate(data):
            names[ID] = name.strip('\n')
    return names

def transform_images(x_train, size):
    x_train = tf.image.resize(x_train, (size, size))
    x_train = x_train / 255
    return x_train

def box_detector(pred):
    center_x, center_y, width, height, confidence, classes = tf.split(pred, [1, 1, 1, 1, 1, -1], axis=-1)
    top_left_x = (center_x - width / 2.) / 416
    top_left_y = (center_y - height / 2.0) / 416.0
    bottom_right_x = (center_x + width / 2.0) / 416.0
    bottom_right_y = (center_y + height / 2.0) / 416.0

    boxes = tf.concat([top_left_y, top_left_x, bottom_right_y, bottom_right_x], axis=-1)
    scores = confidence * classes
    scores = np.array(scores)

    scores = scores.max(axis=-1)
    class_index = np.argmax(classes, axis=-1)

    final_indexes = tf.image.non_max_suppression(boxes, scores, max_output_size=20)
    final_indexes = np.array(final_indexes)
    class_names = class_index[final_indexes]
    boxes = np.array(boxes)
    scores = np.array(scores)
    class_names = np.array(class_names)
    boxes = boxes[final_indexes, :]

    scores = scores[final_indexes]
    boxes = boxes * 416

    return boxes, class_names, scores

def drawbox(boxes, class_names, scores, names, img):
    data = np.concatenate([boxes, scores[:, np.newaxis], class_names[:, np.newaxis]], axis=-1)
    data = data[np.logical_and(data[:, 0] >= 0, data[:, 0] <= 416)]
    data = data[np.logical_and(data[:, 1] >= 0, data[:, 1] <= 416)]
    data = data[np.logical_and(data[:, 2] >= 0, data[:, 2] <= 416)]
    data = data[np.logical_and(data[:, 3] >= 0, data[:, 3] <= 416)]
    data = data[data[:, 4] > 0.3]

    img = cv2.resize(img, (416, 416))
    for i, row in enumerate(data):
        img = cv2.rectangle(img, (int(row[1]), int(row[0])), (int(row[3]), int(row[2])), (170, 1, 130), 1)
        img = cv2.putText(img, (list(names.keys())[int(row[5])] + ": " + "{:.2f}".format(row[4])),
                          (int(row[1]), int(row[0] - 5)),
                          cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)

    return img

def iou(box1, box2):
    intersect_x_min = np.max([box1[0], box2[0]])
    intersect_y_min = np.max([box1[1], box2[1]])
    intersect_x_max = np.min([box1[2], box2[2]])
    intersect_y_max = np.min([box1[3], box2[3]])

    intersect_area = (intersect_x_max - intersect_x_min) * (intersect_y_max - intersect_y_min)
    box1_area = (box1[2] - box1[0]) * (box1[3] - box1[1])
    box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1])
    iou_score = intersect_area / (box1_area + box2_area - intersect_area)

    return iou_score

def iou_batch_array(actual, pred):
    '''
    Parameters
    ----------
    box: shape(batch,n,n,3,4)s

    Returns
    --------
    iou_score: shape(batch,n,n,3)
    '''

    box1 = []
    box2 = []

    box1.append(actual[..., 0:1] - actual[..., 2:3] / 2)
    box1.append(actual[..., 1:2] - actual[..., 3:4] / 2)
    box1.append(actual[..., 0:1] + actual[..., 2:3] / 2)
    box1.append(actual[..., 1:2] + actual[..., 3:4] / 2)
    box2.append(pred[..., 0:1] - pred[..., 2:3] / 2)
    box2.append(pred[..., 1:2] - pred[..., 3:4] / 2)
    box2.append(pred[..., 0:1] + pred[..., 2:3] / 2)
    box2.append(pred[..., 1:2] + pred[..., 3:4] / 2)

    box1 = tf.stack(box1, axis=-1)
    box2 = tf.stack(box2, axis=-1)

    intersect_x_min = K.maximum(box1[..., 0], box2[..., 0])
    intersect_y_min = K.maximum(box1[..., 1], box2[..., 1])
    intersect_x_max = K.minimum(box1[..., 2], box2[..., 2])
    intersect_y_max = K.minimum(box1[..., 3], box2[..., 3])

    intersect_area = (intersect_x_max - intersect_x_min) * (intersect_y_max - intersect_y_min)

    box1_area = (box1[..., 2] - box1[..., 0]) * (box1[..., 3] - box1[..., 1])
    box2_area = (box2[..., 2] - box2[..., 0]) * (box2[..., 3] - box2[..., 1])

    iou_score = intersect_area / (box1_area + box2_area - intersect_area)

    return iou_score


'''
readData_V3.py
written by: Hannah Holt

NEEDS PYTHON 2.7 TO COMPILE AND RUN

readData.py reads in a specified text file containing UT, gas number densities, and density errors for a certain orbit of the DE-2 satellite.
File contains values for O, He, N2, N and Ar as well as other orbit info

VERSION 2.0 UPDATES
    - removed anything that had to do with combining plots. We don't need this tool
    - changed the normalization feature to use a pure helium scale height instead of MSIS.

VERSION 3.0 UPDATES
    - after talking to Alan Burns and Wenbin Wang, suggestions were made to instead of normalizing He to a specific altitude, compare 2 days before
    and two days after a major ETA event and subtract those from the main event to see
    - added in the ability to plot helium densities vs invariant latitude. This gets rid of magnetic equator errors based on
    the orbits being on different longitudes (even though they have same LT)
'''

from __future__ import print_function, division
import sys, os
from pyglow.pyglow import Point  # for MSIS total mass density
from datetime import datetime
import numpy as np
import matplotlib.pyplot as plt
import csv

def print_header():
    print('\n**********************************************')
    print(os.path.basename(__file__))
    print('Written by: Hannah Holt')
    print('Last updated: 6/21/2018')
    print('**********************************************\n')

def plotData(x_values, y_values, filenames, LT_name):
    '''
    This function plots the given x and y values for a given gas and orbit number file.

    :param x_values:    array of values to plot on x axis. This could be lat, lon, LST, etc.
    :param y_values:    array of y values for the specific gas. This will usually be density
    :param filenames:    array of input files for graph title

    :return: plots the data and returns 0
    '''

    print("Done reading files. Plotting...")

    name = []

    # GRAPH LABELING....
    for i in range(0, len(filenames)):
        temp = filenames[i]
        temp = temp[4:]
        temp = temp.replace("_orbit", " (#")
        temp = temp.replace(".txt", ")")
        temp = temp.replace("_", "Day ")
        name.append(temp)

    y_label = whatplot + ' ' + y_name + ' Difference ' + ' [1/cm^3]'
    x_label = x_name + ' [deg]'
    graphTitle = 'Density Change of ' + name[1] + ' - ' + name[0] + '\nLT ~ ' + str(LT_name)

    if average:
        plt.plot(x_values, y_values, label='Raw DE2')
        plt.plot(x_values_avg, y_values_avg, label='Avg')
    else:
        plt.plot(x_values, y_values, label='Raw DE2')

    if x_lower != 0:
        plt.xlim(x_lower, x_upper)
    if y_lower != 0:
        plt.ylim(y_lower, y_upper)

    plt.ticklabel_format(style='sci', axis='y', scilimits=(0, 0))
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.title(graphTitle)
    plt.legend()
    plt.grid()
    plt.show()

    return 0

def dens_difference(dens_1, dens_2, lat_1, lat_2, lon_1, lon_2, LST_1, LST_2, split_1, split_2):
    """
    Takes in two density arrays and finds the realtive difference between the values for the same latitude

    Arguments:
        dens_1  :   density arr of first file [cm^-3]
        dens_2  :   density arr of second file [cm^-3]
        lat_1   :   latitude arr of first file [deg]
        lat_2   :   latitude arr of second file [deg]
        LST_1   :   local time arr of first file
        LST_2   :   local time arr of second file
        split_1 :   LT split integer for first file
        split_2 :   LT split integer for second file

    Returns:
        lat_diff    :   even though the latitudes will be roughly the same for consecutive files, this returns the average
        dens_diff   :   relative difference between the density values
        LT_name     :   the average LT value for the two orbits. Used later for plotting
    """

    # check to see if we want MLAT instead
    if x_name == "Magnetic Latitude":
        print('Changing to Geomagnetic Coordinates...')
        lat_1 = get_MLAT(lat_1, lon_1)
        lat_2 = get_MLAT(lat_2, lon_2)

    # only use the LT around 18
    if LST_1[split_1] > 12:
        length_1 = len(LST_1[split_1:])
        start1 = split_1
    else:
        length_1 = len(LST_1[:split_1])
        start1 = 0
        print("*** UPDATE: Changing 1st file split...")
        print("New LT1 = ", LST_1[start1])

    if LST_2[split_2] > 12:
        length_2 = len(LST_2[split_2:])
        start2 = split_2
    else:
        length_2 = len(LST_2[:split_2])
        start2 = 0
        print("*** UPDATE: Changing 2nd file split...")
        print("New LT2 = ", LST_2[start2])

    # now compare lengths - difference density array will be size of the smaller one
    if length_1 < length_2:
        size = length_1
    else:
        size = length_2

    # print('1at1[start1], lat2[start2] = ', lat_1[start1], lat_2[start2])
    # print('size = ', size)
    # print('start1 start2 =', start1, start2)
    # print('length to the end of 1 =', len(dens_1[start1:]))
    # print('length to the end of 2 =', len(dens_2[start2:]))

    # need to find latitude bin number where they start to overlap
    if lat_1[start1] > lat_2[start2]:
        for i in range(0, size):
            if lat_1[start1 + i] <= lat_2[start2]:
                start1 += i
                if len(dens_1[start1:start1+size]) < size:
                    size = len(dens_1[start1:start1+size])                                 # size of overlap shrinks
                break
    else:
        for i in range(0, size):
            if lat_2[start2 + i] <= lat_1[start1]:
                start2 += i
                if len(dens_2[start2:start2+size]) < size:
                    size = len(dens_2[start2:start2+size])                                 # size of overlap shrinks
                break

    # print("\nNEW VALUES.....")
    # print('1at1[start1], lat2[start2] = ', lat_1[start1], lat_2[start2])
    # print('size = ', size)
    # print('start1 start2 =', start1, start2)
    # print('length to the end of 1 =', len(dens_1[start1:]))
    # print('length to the end of 2 =', len(dens_2[start2:]))
    #
    # print('lat2[start stop] = ', lat_2[start2: start2+size])
    # print('lat1[start stop] = ', lat_1[start1: start1 + size])

    dens_diff = np.zeros(size)                      # difference between densities at same latitude
    lat_overlap = np.zeros(size)
    LT_value = (LST_1[start1] + LST_2[start2])/2

    # now go through and iterate to find the differences
    for i in range(0, size):
        dens_diff[i] = dens_2[start2 + i] - dens_1[start1 + i]

        # DE satellite will have basically the same latitude for the overlapping parts
        lat_overlap[i] = 0.5 * (lat_2[start2 + i] + lat_1[start1 + i])

    if compare_dens:
        plt.plot(lat_1[start1: start1+size], dens_1[start1:start1+size], label='Orbit 1608')
        plt.plot(lat_2[start2: start2+size], dens_2[start2:start2+size], label='Orbit 1614')
        plt.xlabel('Latitude [deg]')
        plt.ylabel('He Density [1/cm^3]')
        plt.grid()
        plt.legend()
        plt.show()

    return lat_overlap, dens_diff, LT_value

def find_split(LST_arr):
    '''
    Since DE2 is a polar orbiter, it will drastically change local times (or longitude) when it passes over the poles. We want to find when this happens

    :param LST_arr: the array of LT values for a specific orbit. Find when LT_n+1 - LT_n > x.
    :return: an integer that marks the first location AFTER the split in the array from 0 to len(arr)
    '''

    dLT = 1  # the LT difference cutoff. I arbitrarily choose this
    split = 0
    for i in range(1, len(LST_arr)):
        if (abs(LST_arr[i] - LST_arr[i - 1]) > dLT):
            split = i
            break
    print("LT split = ", LST_arr[split])
    return split

def avg(array, num_points):
    '''
    This function averages the given array over the specified number of points
    :param array: a numpy array of values
    :param num_points: an integer that tells how many points you would like to average over.
    :return: an array with len(array) - num_points + 1 values
    '''
    average = np.zeros(len(array) - num_points + 1)

    for i in range(0, len(average)):
        sum = 0.0
        for j in range(0, num_points):
            sum += array[i+j]
        average[i] = sum/num_points

    return average

def check_lon(lon_1, lat_1, lon_2, lat_2):
    '''

    :param lon_1: longitude values of first orbit file
    :param lat_1: geographical latitude values of first orbit file
    :param lon_2: "       "          second orbit file
    :param lat_2: "       "          second orbit file
    :return: plots the  latitude of the magnetic equator and two orbits (where they overlap) as a function of longitude
    '''

    # read in the magnetic coordinates
    mag_filename = 'mag_eq_lon_lat.txt'
    mag_lon = []
    mag_lat = []
    with open(mag_filename) as csvfile:
        readCSV = csv.reader(csvfile, delimiter=',')
        for line in readCSV:
            mag_lon.append(float(line[0]))
            mag_lat.append(float(line[1]))
    csvfile.close()

    # plot
    fig, ax = plt.subplots()
    label = []                              # legend labels
    for i in range(0, len(filenames)):
        temp = filenames[i]
        temp = temp[4:]
        temp = temp.replace("_orbit", " (#")
        temp = temp.replace(".txt", ")")
        temp = temp.replace("_", "Day ")
        label.append(temp)

    plt.plot(mag_lon, mag_lat, label='Magnetic Eq.')
    plt.plot(lon_1, lat_1, label=label[0])
    plt.plot(lon_2, lat_2, label=label[1])
    plt.xlabel("Geographic Longitude [deg]")
    plt.ylabel("Geographic Latitude [deg]")
    plt.title('DE 2 Orbit Comparison')
    plt.xlim(-180, 180)
    plt.ylim(-90, 90)
    ax.set_xticks(np.arange(-180, 181, step=20))
    plt.grid()
    plt.legend()
    plt.show()

    return 0

def get_MLAT(DE_lat, DE_lon):
    '''

    :param DE_lat: array of DE geographical latitudes for the orbit
    :param DE_lon: corresponding DE geographical longitudes for each of the latitudes
    :return: MLAT:  an array that gives the corresponding magnetic latitude for each of the longitudes
    '''

    #coordinates for the dip equator
    mag_lon_eq = []
    mag_lat_eq = []

    MLAT = np.zeros(len(DE_lat))        # magnetic latitude values to return
    # lat_values = np.zeros(len(DE_lat))

    mag_filename = 'mag_eq_lon_lat.txt'
    with open(mag_filename) as csvfile:
        readCSV = csv.reader(csvfile, delimiter=',')
        for line in readCSV:
            mag_lon_eq.append(float(line[0]))
            mag_lat_eq.append(float(line[1]))
    csvfile.close()

    for i in range(0, len(DE_lon)):
        for j in range(0, len(mag_lon_eq)):
            if int(DE_lon[i]) == mag_lon_eq[j]:
                MLAT[i] = DE_lat[i] - mag_lat_eq[j]
                # lat_values[i] = mag_lat_eq[j]

    # plt.plot(DE_lon, DE_lat, label='DE Lat')
    # plt.plot(DE_lon, lat_values, label='Mag. Eq.')
    # plt.plot(DE_lon, MLAT, label='DE MLat')
    # plt.xlabel("Longitude [deg]")
    # plt.legend()
    # plt.grid()
    # plt.show()

    return MLAT

# ----------------------- VARIABLES TO CHANGE -------------------------------------------------------
# ----------------------------------------------------------------------------------------------------

directory = './NACSfiles/outputs/'
filenames = ['1983_030_orbit8273.txt', '1983_029_orbit8257.txt']                # subtracts first file data from second
whatplot = "HE"                 # to specify what species to plot, can be: O, N2, HE, or AR
x_name = "Latitude"             # can be: 'Latitude' or 'Magnetic Latitude'
y_name = "Density"              # can be: Density
average = True                  # if you want to average the density values
npts = 75                       # number of points to average over
compare_lon = True              # if you want to plot the longitudinal comparison
compare_dens = True             # if you want to compare the individual densities
x_lower = 40                    # upper and lower x limits for graphing, zero = autoscale
x_upper = -40
y_upper = 0
y_lower = 0
# ----------------------------------------------------------------------------------------------------

print_header()

delimiter = '\t'                # file delimiter
hour = 3600000                  # ms in an hr
numFiles = len(filenames)       # number of files to read

if numFiles != 2:
    print('ERROR: Program needs two data files to compare.')
    sys.exit()

for i in range(0, numFiles):
    if not os.path.isfile(directory + filenames[i]):
        print("ERROR. Read file", filenames[i], "does not exist.")
        sys.exit()

for j in range(0, numFiles):
    fread = open(directory + filenames[j], 'r')
    print('.....')
    print('Opening file', filenames[j])

    # get number of lines and initialize data. Time is in UT [ms], densities are 1/cm^3, errors are %
    data_lines = 0
    for line in fread:
        data_lines += 1
    fread.close()
    data_lines -= 2  # don't care about first two lines (header)

    time = np.zeros(data_lines)
    O_dens, O_err = np.zeros(data_lines), np.zeros(data_lines)
    N2_dens, N2_err = np.zeros(data_lines), np.zeros(data_lines)
    He_dens, He_err = np.zeros(data_lines), np.zeros(data_lines)
    N_dens, N_err = np.zeros(data_lines), np.zeros(data_lines)
    Ar_dens, Ar_err = np.zeros(data_lines), np.zeros(data_lines)
    alt, lat, lon, LST = np.zeros(data_lines), np.zeros(data_lines), np.zeros(data_lines), np.zeros(data_lines)

    # ----------- READ IN DATA-----------------------------------------------------------------
    # -----------------------------------------------------------------------------------------

    fread = open(directory + filenames[j], 'r')

    # skip header
    fread.next()
    fread.next()

    k = 0
    # read in all the values from the file
    for line in fread:
        for i in range(0, 16):
            if i == 0:
                time[k] = float((line.split(delimiter)[i])) / hour
            elif i == 1:
                O_dens[k] = float(line.split(delimiter)[i])
            elif i == 2:
                O_err[k] = float(line.split(delimiter)[i])
            elif i == 3:
                N2_dens[k] = float(line.split(delimiter)[i])
            elif i == 4:
                N2_err[k] = float(line.split(delimiter)[i])
            elif i == 5:
                He_dens[k] = float(line.split(delimiter)[i])
            elif i == 6:
                He_err[k] = float(line.split(delimiter)[i])
            elif i == 7:
                N_dens[k] = float(line.split(delimiter)[i])
            elif i == 8:
                N_err[k] = float(line.split(delimiter)[i])
            elif i == 9:
                Ar_dens[k] = float(line.split(delimiter)[i])
            elif i == 10:
                Ar_err[k] = float(line.split(delimiter)[i])
            elif i == 11:
                continue  # don't care about orbit number
            elif i == 12:
                alt[k] = float(line.split(delimiter)[i])
            elif i == 13:
                lat[k] = float(line.split(delimiter)[i])
            elif i == 14:
                lon[k] = float(line.split(delimiter)[i])
            elif i == 15:
                LST[k] = float(line.split(delimiter)[i])
            # elif i == 18:
            #     inv_lat[k] = float(line.split(delimiter)[i])
            else:
                continue
        k += 1

    fread.close()

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

    # we are on the first file
    if j == 0:
        alt_1 = alt
        lat_1 = lat
        lon_1 = lon
        LST_1 = LST
        split_1 = find_split(LST)
        print("First file split occurred at:", split_1, '\n')

        if y_name == 'Density':
            if whatplot == 'HE':
                dens_1 = He_dens
            elif whatplot == 'O':
                dens_1 = O_dens
            elif whatplot == 'N2':
                dens_1 = N2_dens
            elif whatplot == 'N':
                dens_1 = N_dens
            elif whatplot == 'Ar':
                dens_1 = Ar_dens
            else:
                print('ERROR: Invalid gas constituent.')
                sys.exit()
        else:
            print('ERROR: Invalid y axis.')
            sys.exit()

    # we are on the second file
    else:
        alt_2 = alt
        lat_2 = lat
        lon_2 = lon
        LST_2 = LST
        split_2 = find_split(LST)
        print("Second file split occurred at:", split_2, '\n')

        if y_name == 'Density':
            if whatplot == 'HE':
                dens_2 = He_dens
            elif whatplot == 'O':
                dens_2 = O_dens
            elif whatplot == 'N2':
                dens_2 = N2_dens
            elif whatplot == 'N':
                dens_2 = N_dens
            elif whatplot == 'Ar':
                dens_2 = Ar_dens
            else:
                print('ERROR: Invalid gas constituent.')
                sys.exit()
        else:
            print('ERROR: Invalid y axis.')
            sys.exit()

# end j in range (0, numfiles)

# ----------- POST PROCESSING + PLOTTING ---------------------------------------------------
#  -----------------------------------------------------------------------------------------

# now find the difference between the two density values
x_values, y_values, LT_name = dens_difference(dens_1, dens_2, lat_1, lat_2, lon_1, lon_2, LST_1, LST_2, split_1, split_2)

if compare_lon:
    check_lon(lon_1, lat_1, lon_2, lat_2)

if average:
    x_values_avg = avg(x_values, npts)
    y_values_avg = avg(y_values, npts)

plotData(x_values, y_values, filenames, LT_name)