import numpy as np
from numpy import ma
import xarray as xr
from netCDF4 import Dataset
import struct
import sys
import os
import datetime as dt
import glob

"""
This module contains functions for reading external data
to use with LPT.

The data_read_function is called at various points in other LPT functions.

To add a new data set, do the following:
1) Write a read function similar to read_generic_netcdf below.
2) Add an "elif" option that calls that function in readdata
"""

################################################################################

def readdata(datetime_to_read, dataset_options_dict, verbose=None):
    """
    Main data read function. Get data at datetime datetime_to_read.
    Based on the oprions in dataset_options_dict, it will look in the data directory
    and use the rain function specified below.

    To add a dataset type, add an elif block to this function.

    The function is expected to return a dictionary with keys 'lon', 'lat', and 'data'

    Verbose option (new 05/2023):
    - If set to None (default), it will use the verbose option from dataset_options_dict.
    - Otherwise, the value will be used *instead of* dataset_options_dict.
      This allows a function call to override the setting in dataset_options_dict.
    """

    ## Manage verbose
    if verbose is None:
        verbose_actual = dataset_options_dict['verbose']
    else:
        verbose_actual = verbose

    if dataset_options_dict['raw_data_format'] == 'generic_netcdf':
        variable_names = (dataset_options_dict['longitude_variable_name']
                , dataset_options_dict['latitude_variable_name']
                , dataset_options_dict['field_variable_name'])

        DATA = read_generic_netcdf_at_datetime(datetime_to_read
                , variable_names = variable_names
                , data_dir = dataset_options_dict['raw_data_parent_dir']
                , fmt = dataset_options_dict['file_name_format']
                , verbose = verbose_actual)

    elif dataset_options_dict['raw_data_format'] == 'generic_netcdf_with_multiple_times':
        variable_names = (dataset_options_dict['longitude_variable_name']
                , dataset_options_dict['latitude_variable_name']
                , dataset_options_dict['time_variable_name']
                , dataset_options_dict['field_variable_name'])

        DATA = read_generic_netcdf_at_datetime(datetime_to_read
                , variable_names = variable_names
                , dt_to_use = datetime_to_read
                , data_dir = dataset_options_dict['raw_data_parent_dir']
                , fmt = dataset_options_dict['file_name_format']
                , verbose = verbose_actual)

    elif dataset_options_dict['raw_data_format'] == 'cmorph':
        DATA = read_cmorph_at_datetime(datetime_to_read
                , area = dataset_options_dict['area']
                , data_dir = dataset_options_dict['raw_data_parent_dir']
                , fmt = dataset_options_dict['file_name_format']
                , verbose = verbose_actual)

    elif dataset_options_dict['raw_data_format'] == 'imerg_hdf5':
        DATA = read_imerg_hdf5_at_datetime(datetime_to_read
                , area = dataset_options_dict['area']
                , data_dir = dataset_options_dict['raw_data_parent_dir']
                , fmt = dataset_options_dict['file_name_format']
                , verbose = verbose_actual)

    elif dataset_options_dict['raw_data_format'] == 'cfs_forecast':
        fcst_hour = int((datetime_to_read - dataset_options_dict['datetime_init']).total_seconds()/3600)
        fcst_resolution_hours = dataset_options_dict['data_time_interval']
        if fcst_hour < 1: # There is no data in the file for fcst = 0. Use 6h fcst values.
            records = [1,]
        else:
            records = [int(fcst_hour/fcst_resolution_hours),]

        DATA = read_cfs_rt_at_datetime(dataset_options_dict['datetime_init'] # datetime_to_read
                , data_dir = dataset_options_dict['raw_data_parent_dir']
                , fmt = dataset_options_dict['file_name_format']
                , records = records
                , verbose = verbose_actual)
        DATA['data'] = ma.masked_array(DATA['precip'][0])

    ## -- Add an elif block here for new datasets. --

    else:
        print(('ERROR! '+dataset_options_dict['raw_data_format'] + ' is not a valid raw_data_format!'), flush=True)
        DATA = None

    return DATA

################################################################################
## Read functions for generic NetCDF data.
################################################################################

def read_generic_netcdf(fn, variable_names=('lon','lat','rain'), dt_to_use=None):
    """
    DATA = read_generic_netcdf(fn)

    output is like this:
    list(DATA)
    Out[12]: ['lon', 'lat', 'precip']
    In [21]: DATA['lon'].shape
    Out[21]: (1440,)
    In [22]: DATA['lat'].shape
    Out[22]: (400,)
    In [23]: DATA['precip'].shape
    Out[23]: (400, 1440)
    """

    DATA = {}
    with xr.open_dataset(fn) as DS:
        DATA['lon'] = DS[variable_names[0]].values
        DATA['lat'] = DS[variable_names[1]].values
        ## If no time variable, just retrieve the 2-D data as it is.
        if not dt_to_use is None: #'time' in list(DS.variables):
            DATA['data'] = DS.sel({variable_names[2]:str(dt_to_use)},method='nearest')[variable_names[3]].values
        else:
            DATA['data'] = DS[variable_names[2]].values

    DATA['data'] = np.ma.masked_array(DATA['data'], mask=np.isnan(DATA['data']))

    ## Need to get from (-180, 180) to (0, 360) longitude.
    lon_lt_0, = np.where(DATA['lon'] < -0.0001)
    lon_ge_0, = np.where(DATA['lon'] > -0.0001)
    if len(lon_lt_0) > 0:
        DATA['lon'][lon_lt_0] += 360.0
        DATA['lon'] = np.concatenate((DATA['lon'][lon_ge_0], DATA['lon'][lon_lt_0]))
        DATA['data'] = np.concatenate((DATA['data'][:,lon_ge_0], DATA['data'][:,lon_lt_0]), axis=1)

    return DATA

def read_generic_netcdf_at_datetime(dt, data_dir='.'
        , variable_names=('lon','lat','rain'), dt_to_use=None, fmt='gridded_rain_rates_%Y%m%d%H.nc'
        , verbose=False):

    fn = (data_dir + '/' + dt.strftime(fmt))
    DATA=None

    if not os.path.exists(fn):
        print('File not found: ', fn)
    else:
        if verbose:
            print(fn)
        DATA=read_generic_netcdf(fn,
            variable_names = variable_names,
            dt_to_use = dt_to_use)

    return DATA

################################################################################
## Read functions for specific datasets.
################################################################################

"""
CMORPH reading functions.
"""
def read_cmorph_rt_bin(fn, area=[0,360,-90,90]):

    """
    DATA = read_cmorph_rt_bin(fn)
    DATA is a dict with keys lon, lat, and precip.

    CMORPH RT files are binary.
    The GrADS control file below is used as the basis for this function:

    DSET ^../%y4/%y4%m2/CMORPH_V0.x_RT_8km-30min_%y4%m2%d2%h2
    OPTIONS little_endian template
    UNDEF -999.0
    TITLE CMORPH Rain Rate (Real-Time Version)
    XDEF  4948 LINEAR   0.0363783345 0.072756669
    YDEF  1649 LINEAR -59.963614312  0.072771376
    ZDEF     1 LEVELS   1
    TDEF 99999 LINEAR 00:00z01Jan2017 30mn
    VARS 1
    cmorph  1  99  CMORPH Rain Rate [mm/hr]
    ENDVARS
    """

    dtype=np.dtype([('field1', '<i2')])
    DATA={}
    DATA['lon'] = np.arange(0.0363783345, 360.0, 0.072756669)
    DATA['lat'] = np.arange(-59.963614312, 60.0, 0.072771376)
    fid = open(fn,'rb')

    ## GrADS uses FORTRAN REAL values, which is np.float32 for Python.
    DATA['data'] = np.fromfile(fid, dtype=np.float32, count=2*4948*1649)
    if sys.byteorder == 'big': # Data is little endian.
        DATA['data'] = DATA['data'].byteswap()

    ## Shape and scale the data.
    DATA['data'] = np.reshape(np.double(DATA['data']), [2, 1649, 4948])
    DATA['data'][DATA['data'] < -0.001] = 0.0 # Usually, missing high latitude data.
    fid.close()

    ## Cut out area.
    keep_lon, = np.where(np.logical_and(DATA['lon'] > area[0], DATA['lon'] < area[1]))
    keep_lat, = np.where(np.logical_and(DATA['lat'] > area[2], DATA['lat'] < area[3]))

    DATA['lon'] = DATA['lon'][keep_lon[0]:keep_lon[-1]+1]
    DATA['lat'] = DATA['lat'][keep_lat[0]:keep_lat[-1]+1]
    DATA['data'] = DATA['data'][:, keep_lat[0]:keep_lat[-1]+1, keep_lon[0]:keep_lon[-1]+1]
    DATA['data'] = 0.5*(DATA['data'][0,:,:] + DATA['data'][1,:,:])

    return DATA

def read_cmorph_at_datetime(dt_this, force_rt=False, data_dir='.'
        , fmt='CMORPH_V0.x_RT_8km-30min_%Y%m%d%H'
        , verbose=False, area=[0,360,-90,90]):

    """
    DATA = read_cmorph_at_datetime(dt, force_rt=False, verbose=False)

    DATA is a dict with keys lon, lat, and precip.

    Based on the provided datetime dt, read in the CMORPH data.

    By default, it will first check for the research product,
    and use the realtime product if the research product was not found.
    However, if force_rt = True, it just uses the realtime product.
    """

    ## First try research product
    fn = (data_dir + '/' + dt_this.strftime(fmt))
    if verbose:
        print(fn)
    DATA = read_cmorph_rt_bin(fn, area=area)

    DATA['data'] = ma.masked_array(DATA['data'])
    return DATA

def read_imerg_hdf5_at_datetime(dt_this, force_rt=False, data_dir='.'
        , fmt='%Y/%m/%d/3B-HHR.MS.MRG.3IMERG.%Y%m%d-S%H*.HDF5'
        , verbose=False, area=[0,360,-90,90]):

    """
    DATA = read_imerg_hdf5_at_datetime(dt_this, force_rt=False, data_dir='.'
        , fmt='%Y/%m/%d/3B-HHR.MS.MRG.3IMERG.%Y%m%d-S%H*.HDF5'
        , verbose=False, area=[0,360,-90,90])

    DATA is a dict with keys lon, lat, and precip.

    Based on the provided datetime dt, read in the IMERG HDF data.

    By default, it will first check for the final product,
    and use the "late" realtime product if the final product was not found.
    However, if force_rt = True, it just uses the "late" realtime product.

    (It will search for a filename with modified fmt to check for "late" product
    - append 'late/' to the front of the directory path.
    - replace '3B-HHR' with '3B-HHR-L').
    """

    fn_list = sorted(glob.glob(data_dir + '/' + dt_this.strftime(fmt)))
    if len(fn_list) < 1:
        if not force_rt:
            ## Try "late" realtime data.
            print('Final data version not found. Trying to use late realtime data instead.')
            fmt_rt = 'late/' + fmt.replace('3B-HHR','3B-HHR-L')
            fn_list = sorted(glob.glob(data_dir + '/' + dt_this.strftime(fmt_rt)))

    if len(fn_list) < 1:
        print('WARNING: No input data found.')

    fn = fn_list[0]
    if verbose:
        print(fn)

    with Dataset(fn) as DS:
        lon_rain = DS['Grid']['lon'][:]
        lat_rain = DS['Grid']['lat'][:]
        rain = DS['Grid']['precipitationCal'][:][0].T

    if len(fn_list) > 1:
        fn = fn_list[1]
        if verbose:
            print(fn)

        with Dataset(fn) as DS:
            rain30 = DS['Grid']['precipitationCal'][:][0].T

        rain = 0.5 * (rain + rain30)

    ## lon -180:0 --> 180:360
    idx_neg_lon = [x for x in range(len(lon_rain)) if lon_rain[x] < -0.0001]
    idx_pos_lon = [x for x in range(len(lon_rain)) if lon_rain[x] > -0.0001]

    lon_rain = np.append(lon_rain[idx_pos_lon[0]:idx_pos_lon[-1]+1], 360.0 + lon_rain[idx_neg_lon[0]:idx_neg_lon[-1]+1], axis=0)
    rain = np.append(rain[:,idx_pos_lon[0]:idx_pos_lon[-1]+1], rain[:,idx_neg_lon[0]:idx_neg_lon[-1]+1], axis=1)

    DATA={}
    DATA['lon'] = lon_rain
    DATA['lat'] = lat_rain
    DATA['data'] = ma.masked_array(rain)

    ## Cut out area.
    keep_lon, = np.where(np.logical_and(DATA['lon'] > area[0], DATA['lon'] < area[1]))
    keep_lat, = np.where(np.logical_and(DATA['lat'] > area[2], DATA['lat'] < area[3]))

    DATA['lon'] = DATA['lon'][keep_lon[0]:keep_lon[-1]+1]
    DATA['lat'] = DATA['lat'][keep_lat[0]:keep_lat[-1]+1]
    DATA['data'] = DATA['data'][keep_lat[0]:keep_lat[-1]+1, keep_lon[0]:keep_lon[-1]+1]

    return DATA

################################################################################
################################################################################
################################################################################

"""
CFS Grib2 reading function
"""

def read_cfs_rt_at_datetime(dt_this, data_dir = './'
                , fmt = 'cfs.%Y%m%d/%H/time_grib_01/prate.01.%Y%m%d%H.daily.grb2'
                , records=range(1,45*4+1), verbose=False):

    fn = (data_dir + '/' + dt_this.strftime(fmt))
    if verbose:
        print(fn, flush=True)

    return read_cfs_rt_grib2(fn, records=records, verbose=verbose)

def read_cfs_rt_grib2(fn, records=range(1,45*4+1), verbose=False):
    """
    RT = read_cfs_rt_grib2(fn, records=N)

    N is the list of records to get.
    By default, get the first 45 days, 6 hourly intervals.

    example output:
    In [23]: RT['lon'].shape
    Out[23]: (384,)

    In [24]: RT['lat'].shape
    Out[24]: (190,)

    In [25]: RT['precip'].shape
    Out[25]: (180, 190, 384)
    """

    import gdal  # Import gdal if dealing with grib data.

    DS = gdal.Open(fn, gdal.GA_ReadOnly)
    width = DS.RasterXSize
    height = DS.RasterYSize
    lon =  np.arange(0.0,359.062 + 0.5,0.938)
    ## grid file with Gaussian latitude was obtained from wgrib2 like this:
    ## wgrib2 -d 1 -gridout grid.txt /home/orca/data/model_fcst_grib/cfs/cfs.20190508/00/time_grib_01/prate.01.2019050800.daily.grb2
    ## awk -F, '{print $3}' grid.txt | uniq | tr "\n" ", "
    lat = np.flip(np.array([-89.277, -88.340, -87.397, -86.454, -85.509
                , -84.565, -83.620, -82.676, -81.731, -80.786
                , -79.841, -78.897, -77.952, -77.007, -76.062
                , -75.117, -74.173, -73.228, -72.283, -71.338
                , -70.393, -69.448, -68.503, -67.559, -66.614
                , -65.669, -64.724, -63.779, -62.834, -61.889
                , -60.945, -60.000, -59.055, -58.110, -57.165
                , -56.220, -55.275, -54.330, -53.386, -52.441
                , -51.496, -50.551, -49.606, -48.661, -47.716
                , -46.771, -45.827, -44.882, -43.937, -42.992
                , -42.047, -41.102, -40.157, -39.212, -38.268
                , -37.323, -36.378, -35.433, -34.488, -33.543
                , -32.598, -31.653, -30.709, -29.764, -28.819
                , -27.874, -26.929, -25.984, -25.039, -24.094
                , -23.150, -22.205, -21.260, -20.315, -19.370
                , -18.425, -17.480, -16.535, -15.590, -14.646
                , -13.701, -12.756, -11.811, -10.866, -9.921
                , -8.976, -8.031, -7.087, -6.142, -5.197
                , -4.252, -3.307, -2.362, -1.417, -0.472
                , 0.472, 1.417, 2.362, 3.307, 4.252
                , 5.197, 6.142, 7.087, 8.031, 8.976
                , 9.921, 10.866, 11.811, 12.756, 13.701
                , 14.646, 15.590, 16.535, 17.480, 18.425
                , 19.370, 20.315, 21.260, 22.205, 23.150
                , 24.094, 25.039, 25.984, 26.929, 27.874
                , 28.819, 29.764, 30.709, 31.653, 32.598
                , 33.543, 34.488, 35.433, 36.378, 37.323
                , 38.268, 39.212, 40.157, 41.102, 42.047
                , 42.992, 43.937, 44.882, 45.827, 46.771
                , 47.716, 48.661, 49.606, 50.551, 51.496
                , 52.441, 53.386, 54.330, 55.275, 56.220
                , 57.165, 58.110, 59.055, 60.000, 60.945
                , 61.889, 62.834, 63.779, 64.724, 65.669
                , 66.614, 67.559, 68.503, 69.448, 70.393
                , 71.338, 72.283, 73.228, 74.173, 75.117
                , 76.062, 77.007, 77.952, 78.897, 79.841
                , 80.786, 81.731, 82.676, 83.620, 84.565
                , 85.509, 86.454, 87.397, 88.340, 89.277]), axis=0)

    num_list = []
    for band in records:
        if verbose:
            print('Record #' + str(band), flush=True)
        data_array = DS.GetRasterBand(band).ReadAsArray()
        for row in data_array:
            for value in row:
                num_list.append(value*3600.0) # kg/m2/sec --> mm/h

    DS = None # Close the file.

    precip = np.array(num_list).reshape([len(records), len(lat), len(lon)])

    DATA={}
    DATA['lon'] = lon
    DATA['lat'] = lat
    DATA['precip'] = precip

    return DATA

def read_cfsr_grib2(fn, band_list=None, verbose=False):
    """
    RT = read_cfsr_grib2(fn)

    example output:
    In [23]: RT['lon'].shape
    Out[23]: (384,)

    In [24]: RT['lat'].shape
    Out[24]: (190,)

    In [25]: RT['precip'].shape
    Out[25]: (180, 190, 384)
    """

    DS = gdal.Open(fn, gdal.GA_ReadOnly)
    width = DS.RasterXSize
    height = DS.RasterYSize
    lon =  np.arange(0.0,359.51,0.5)
    lat =  np.arange(90.0,-90.01,-0.5)
    n_records = DS.RasterCount

    num_list = []

    if band_list is None:
        band_list = range(1, n_records+1)

    for band in band_list:
        if verbose:
            print((str(band) + ' of ' + str(n_records)))
        data_array = DS.GetRasterBand(band).ReadAsArray()
        for row in data_array:
            for value in row:
                num_list.append(value)

    DS = None # Close the file.

    precip = np.array(num_list).reshape([int(len(band_list)/6), 6, len(lat), len(lon)])
    #precip /= 1e6  # Values in file are multiplied by 1e6.
                   # kg/m2 in 1h is equivalent to mm/h.

    DATA={}
    DATA['lon'] = lon
    DATA['lat'] = lat
    DATA['precip'] = precip

    return DATA

def get_cfsr_6h_rain(dt_ending, verbose=False):

    """
    Read in the rainfall using read_cfs_historical_grib2(fn)
    Then calculate the 6 hourly rain rate (mm/h) and return it.

    CFSR rain is stored in monthly files. It it initialized every 6 h,
    and the data provide hourly accumulations (in kg/m^2, equivalent to mm) like this:

    1:0:d=2011120100:APCP:surface:0-1 hour acc fcst:
    2:94325:d=2011120100:APCP:surface:1-2 hour acc fcst:
    3:193206:d=2011120100:APCP:surface:2-3 hour acc fcst:
    4:309596:d=2011120100:APCP:surface:3-4 hour acc fcst:
    5:421187:d=2011120100:APCP:surface:4-5 hour acc fcst:
    6:537704:d=2011120100:APCP:surface:5-6 hour acc fcst:

    To get the 6 hourly accumulation, all 6 of these need to be added.
    Then take the mean (e.g., divide by 6h) to get mm/h.
    """

    dt_beginning = dt_ending - dt.timedelta(hours=6)

    if dt_beginning < dt.datetime(2011,3,31,23,59,0):
        fn_beginning = ('/home/orca/data/model_anal/cfsr/rain_accum/' + dt_beginning.strftime('%Y')
            + '/apcp.gdas.' + dt_beginning.strftime('%Y%m') + '.grb2')
    else:
        fn_beginning = ('/home/orca/data/model_anal/cfsr/rain_accum/' + dt_beginning.strftime('%Y')
            + '/apcp.cdas1.' + dt_beginning.strftime('%Y%m') + '.grb2')

    if verbose:
        print(fn_beginning, flush=True)

    rec_num = 1 + int((dt_beginning - dt.datetime(dt_beginning.year, dt_beginning.month,1,0,0,0)).total_seconds()/3600.0)
    F = read_cfsr_grib2(fn_beginning, band_list=range(rec_num,rec_num+6,1), verbose=verbose)

    precip6hr = np.nanmean(F['precip'], axis=1)[0]

    DATA={}
    DATA['lon'] = F['lon']
    DATA['lat'] = F['lat']
    DATA['precip'] = precip6hr

    return DATA


import numpy as np

def fitness(f, x):
    """
    Supplied function f(x) returns a value for fitness so long as f(x) has a range >= 0
    :param f:
    :param x:
    :return:
    """
    # return np.exp(f(x))
    # e^y made table unreadable from extremely small numbers
    # return f(x) if (int(x,2) > 0 and int(x,2) < 15) else 1
    return f(x) if f(x) > 0 else 1

def generate_population(size):
    pop = ['{0:04b}'.format(i) for i in range(size)]
    return pop

def choose(population, size, relative=None):
    if relative is None:
        return np.random.choice(population, size)
    return np.random.choice(population, relative, size)

def choose_parents(group, relative):
    x1, x2 = np.random.choice(a=group, p=relative, size=2)
    return x1, x2

def crossover(a, b):
    pivot = np.random.randint(0, max(len(a), len(b)), size=1)[0]
    c1 = a[0:pivot] + b[pivot:len(b)]
    c2 = b[0:pivot] + a[pivot:len(a)]
    return c1, c2

def mutate(child):
    pivot = np.random.randint(0, len(child))

    if child[pivot] == '0':
        r = '1'
    else:
        r = '0'

    child = child[:pivot] + r + child[pivot+1:]
    return child

def ga(func, pop_size=15, elite_size=6, generations=500, pc=0.7, pm=0.1):
    mostfit = -1
    fitnesses = None
    relative = None
    avgfits = []

    population = generate_population (pop_size)

    for epoch in range(1, generations):
        print("===========================")
        print("Generation", epoch)
        print("---------------------------")
        elites = choose(population, elite_size)

        while elites.size < pop_size:
            fitnesses = [fitness(func, elite) for elite in elites]
            relative = [(x/sum(fitnesses)) for x in fitnesses]
            x1, x2 = choose(elites, relative, 2)

            if np.random.random() <= pc:
                c1, c2 = crossover(x1, x2)
                if np.random.random() <= pm:
                    c1 = mutate(c1)
                    fitnesses.append(fitness(func, c1))
                    elites = np.append(elites, c1)
                if np.random.random() <= pm:
                    c2 = mutate(c2)
                    fitnesses.append(fitness(func, c1))
                    elites = np.append(elites, c2)

        avgfits.append(np.mean(fitnesses))
        population = elites
        print("Average Fitness:", np.around(np.mean(fitnesses), decimals=3))
        print("{:>2}{:>10}{:>10}{:>15}{:>15}".format("N", "bin", "int", "fitness", "relative"))
        for i in range(len(elites)):
            print("{:>2}{:>10}{:>10}{:>15.4g}{:>15.2g}".format(
                i, elites[i], int(elites[i], 2), np.around(fitness(func, elites[i]), decimals=4),
                fitnesses[i]/sum(fitnesses)))
        print("===========================")
    return population, avgfits


"""
Problem:
    1. Two Sum
Difficulty:
    Easy
URL:
    https://leetcode.com/problems/two-sum
Tags:
    Array, Hash Table
Date:
    2022-05-10T14:00:29.877163+08:00
"""
from typing import List

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i, num in enumerate(nums):
            if target - num in nums[i + 1:]:
                return [i, nums.index(target - num, i + 1)]

tests = [
    (
        ([2, 7, 11, 15], 9,
         ),
        [0, 1],
    ),
    (
        ([3, 2, 4], 6,
         ),
        [1, 2],
    ),
    (
        ([3, 3], 6,
         ),
        [0, 1],
    ),
]


import json
import requests  # see http://python-requests.org

def url_for(endpoint):
    return 'http://localhost:5000/{}/'.format(endpoint)

def delete_all_people():
    r = requests.delete(url_for('people'))
    print("'people' deleted, server response:", r.status_code)

def post_people():
    data = [
        {'firstname': 'John', 'lastname': 'Doe'},
        {'firstname': 'Mike', 'lastname': 'Green'},
    ]

    response = requests.post(
        url_for('people'),
        json.dumps(data),
        headers={'Content-Type': 'application/json'}
    )
    print("'people' posted, server response:", response.status_code)

def get_people():
    r = requests.get(url_for('people'))
    print('people downloaded, server response:', r.status_code)

    if r.status_code == 200:
        people = r.json()['_items']
        print('{} people:'.format(len(people)))
        for person in people:
            print('{}, {}'.format(person['firstname'], person['_id']))

def main():
    delete_all_people()
    post_people()
    get_people()

if __name__ == '__main__':
    main()


import connexion

from openapi_server import orm
from openapi_server.db import db
from openapi_server.models.error import Error  # noqa: E501
from openapi_server.models.qc_result import QcResult  # noqa: E501

def samples_id_qc_result_delete(id):  # noqa: E501
    """samples_id_qc_result_delete

    Delete the QC result associated with a sample with {id}. # noqa: E501

    :param id:
    :type id: str

    :rtype: None
    """

    sample = orm.Sample.query.get(id)
    if not sample or not sample.qc_result:
        return Error(404, 'Not found'), 404

    db.session.delete(sample.qc_result)
    db.session.commit()

    return '', 204

def samples_id_qc_result_get(id):  # noqa: E501
    """samples_id_qc_result_get

    Return the QC result associated with a sample. # noqa: E501

    :param id:
    :type id: str

    :rtype: QcResult
    """

    sample = orm.Sample.query.get(id)
    if not sample or not sample.qc_result:
        return Error(404, 'Not found'), 404

    return sample.qc_result.to_model(), 200

def samples_id_qc_result_put(id, qc_result=None):  # noqa: E501
    """samples_id_qc_result_put

    Add or replace new QC result associated with a sample. # noqa: E501

    :param id:
    :type id: str
    :param qc_result: QC result to be added
    :type qc_result: dict | bytes

    :rtype: QcResult
    """
    if connexion.request.is_json:
        qc_result = QcResult.from_dict(connexion.request.get_json())  # noqa: E501

    sample = orm.Sample.query.get(id)
    if not sample:
        return Error(404, 'Not found'), 404

    inst = orm.QcResult.from_model(qc_result)
    inst.sample_id = sample.id

    if sample.qc_result:
        sample.qc_result = inst
    else:
        db.session.add(inst)
    db.session.commit()

    return inst.to_model(), 200, {'location': ''}


import logging
import random
from typing import Set, Generator, Optional

from .location import Location
from .move import Move
from .piece import Color, Piece, Rank
from .board import Board

class MoveSet:
    _brd = None  # type: Board

    @staticmethod
    def set_board(brd: Board) -> None:
        r""" Sets the board for the entire class """
        MoveSet._brd = brd

    r""" Moves available to a player """
    def __init__(self, color: Color):
        r"""
        :param color: Color that is making the moves
        """
        self._avail = dict()  # Available moves
        self._color = color

    @property
    def avail(self) -> dict:
        r""" Accessor for the available moves """
        return self._avail

    @staticmethod
    def build(pieces: Set[Piece], locs: dict, other_locs: dict) -> 'MoveSet':
        r"""
        Factory method used to construct an initial move set.

        :param pieces: All of the players pieces
        :param locs: Location of the player pieces
        :param other_locs: Location of pieces of other player
        :return: Constructed move set
        """
        assert MoveSet._brd is not None, "Board information be present"
        assert pieces, "Piece set can never be empty"
        color = next(iter(pieces)).color
        ms = MoveSet(color)
        for p in pieces:
            ms.add_piece(p, locs, other_locs)
        return ms

    def add_piece(self, piece: Piece, locs: dict, other_locs: dict):
        r"""
        Add a piece's moves to the MoveSet
        :param piece: Piece whose moves (if any) will be added
        :param locs: Location of the player pieces
        :param other_locs: Location of pieces of other player
        """
        self._process_piece(piece, locs, other_locs, add=True)

    def del_piece(self, piece: Piece, locs: dict, other_locs: dict):
        r"""
        Add a piece's moves to the MoveSet
        :param piece: Piece whose moves (if any) will be added
        :param locs: Location of the player pieces
        :param other_locs: Location of pieces of other player
        """
        self._process_piece(piece, locs, other_locs, add=False)

    def _process_piece(self, piece: Piece, locs: dict, other_locs: dict, add: bool):
        r"""
        Standardizes adding/removing a piece since same algorithm with minor change.

        :param piece: Piece to process
        :param locs: Location for pieces of same color as \p Piece
        :param other_locs: Location for other player's pieces
        :param add: If True, add the piece, otherwise remove the piece
        """
        # Verify color is same for all pieces
        assert piece.color == self._color, "Piece set has pieces of different colors"

        # Standard function for either adding or deleting a move
        def _process_func(_p: Piece, _loc: Location):
            if add:
                try: self._add_move(_p, _loc, other_locs[_loc])
                except KeyError: self._add_move(_p, _loc)
            else: self._del_move(_p, _loc)

        # Bombs and flags can be ignored
        if piece.is_immobile(): return
        # Check ordinary pieces
        if piece.rank != Rank.scout():
            for loc in piece.loc.neighbors():
                # Ignore pieces not allowed by board or where piece of same color
                if not self._brd.is_inside(loc) or loc in locs: continue
                _process_func(piece, loc)
        # Check scout pieces specially
        else:
            for direction_list in self._brd.to_edge_lists(piece.loc):
                for loc in direction_list:
                    # If scout blocked by board location or same color, immediately stop
                    if not self._brd.is_inside(loc) or loc in locs: break
                    _process_func(piece, loc)
                    if loc in other_locs: break

    def _add_move(self, p: Piece, other: Location, attacked: Optional[Piece] = None) -> None:
        r""" Add \p piece's move to \p other to the \p MoveSet """
        assert p.is_scout() or p.loc.is_adjacent(other)

        key = self._make_move_key(p.loc, other)
        # assert key not in self._avail
        self._avail[key] = Move(p, p.loc, other, attacked)

    def _del_move(self, p: Piece, other: Location) -> None:
        r"""
        Delete the corresponding move from the \p MoveSet

        :param p: Piece whose move will be deleted
        :param other: Location where \p p will be moved
        """
        assert p.is_scout() or p.loc.is_adjacent(other)

        key = self._make_move_key(p.loc, other)
        del self._avail[key]

    def has_move(self, p: Piece, new_loc: Location) -> bool:
        r""" Returns True if the \p Piece has an availble move to the specified \p Location """
        key = self._make_move_key(p.loc, new_loc)
        return key in self._avail

    def get_move(self, p: Piece, new_loc: Location) -> Optional[Move]:
        r"""
        Gets the move corresponding to the \p Piece and \p Location.  If the corresponding \p Move
        is not found, \p None is returned.
        """
        key = self._make_move_key(p.loc, new_loc)
        try: return self._avail[key]
        except KeyError: return None

    def __len__(self) -> int:
        r""" Return number of moves in the \p MoveSet """
        return len(self._avail)

    def __contains__(self, item: Move) -> bool:
        r""" Adds support for the "in" operator """
        if item.piece is None: return False
        return self.has_move(item.piece, item.new)

    def remove_moves_after_add(self, loc: Location, plyr_locs: dict, other_locs: dict) -> None:
        r"""
        Process the adding of a piece at Location \p loc

        :param loc: Location of added piece
        :param plyr_locs: Location of pieces for same color as \p MoveSet
        :param other_locs: Location of pieces of other \p Player
        """
        self._handle_loc_change(loc, plyr_locs, other_locs, False)

    def add_moves_after_delete(self, loc: Location, plyr_locs: dict, other_locs: dict) -> None:
        r"""
        Process the deletion of a piece that was at Location \p loc

        :param loc: Location of deleted piece
        :param plyr_locs: Location of pieces for same color as \p MoveSet
        :param other_locs: Location of pieces of other \p Player
        """
        self._handle_loc_change(loc, plyr_locs, other_locs, True)

    def _handle_loc_change(self, loc: Location, plyr_locs: dict, other_locs: dict, add: bool):
        r"""
        Process a \p Location's state change by either removing or add moves to the MoveSet.

        :param loc: Location whose state is being changed
        :param plyr_locs: Locations of the implicit player's pieces
        :param other_locs: Location dictionary for the other player
        :param add: If True, add moves to the MoveSet.  Otherwise, remove those locations.
        """
        el = self._brd.to_edge_lists(loc)
        el_groups = [(el.right, el.left), (el.left, el.right), (el.up, el.down), (el.down, el.up)]

        def _add_func(_p: Piece, _loc: Location):
            try: self._add_move(_p, _loc, other_locs[_loc])
            except KeyError: self._add_move(_p, _loc)

        for search, opp in el_groups:
            # Find first piece in search direction (if any)
            p = None
            for srch in search:
                if srch in plyr_locs: p = plyr_locs[srch]
                elif srch in other_locs: p = other_locs[srch]
                if p is not None: break
            # If no piece in search direction
            if p is None or p.is_immobile(): continue
            # Ignore pieces of other color since will be handled in separate function call
            if p.color != self._color: continue
            # If found p is not a scout and not adjacent, move on
            if not p.is_scout() and not p.loc.is_adjacent(loc): continue

            # Delete first since may need to add in next step
            if not add: self._del_move(p, loc)
            # In an add, always add the move.  In a delete, may need to add back if the moved
            # piece is of the other player's color
            if add or loc in other_locs:
                _add_func(p, loc)

            if p.is_scout():
                for srch in opp:
                    if srch in plyr_locs: break

                    if add: _add_func(p, srch)
                    else: self._del_move(p, srch)

                    # Perform second since could still attack
                    if srch in other_locs: break

    @staticmethod
    def _make_move_key(orig: Location, new: Location):
        return orig, new

    def is_empty(self, cyclic_moves: Set[Move] = None) -> bool:
        r""" Returns \p True if the \p MoveSet is empty """
        if cyclic_moves is not None and cyclic_moves:
            avail = set(self.avail.values())
            # If available larger than cyclic, definitely not empty move set
            if len(avail) > len(cyclic_moves): return False
            # Check if each available moves in cyclic.  If any not in there, not empty move set
            for a_m in avail:
                for c_m in cyclic_moves:
                    if Move.is_identical(a_m, c_m):
                        break
                else:
                    return False
            return True
        return not bool(self.avail)

    def __iter__(self):
        return iter(self.avail.values())

class Player:
    r""" Represents one of the two players """
    def __init__(self, color: Color):
        r"""
        :param color: Color of the player
        """
        self._color = color

        # noinspection PyTypeChecker
        self._move_set = None  # type: MoveSet
        self._locs = dict()
        self._pieces = set()

    @property
    def color(self) -> Color:
        r""" Accessor for the \p Player's \p Color. """
        return self._color

    @property
    def num_pieces(self) -> int:
        r""" Accessor for number of pieces the player has """
        return len(self._pieces)

    @property
    def move_set(self) -> MoveSet:
        r""" Accessor for the \p Player's \p MoveSet"""
        return self._move_set

    def add_piece(self, piece: Piece, other: 'Player' = None) -> None:
        r""" Add \p piece to \p Player's set of pieces """
        assert piece not in self._pieces, "Duplicate piece"
        assert piece.loc not in self._locs, "Two pieces in same location"

        self._pieces.add(piece)
        self._locs[piece.loc] = piece

        if other is not None:
            assert self._color != other.color
            self.move_set.add_piece(piece, self._locs, other._locs)

    def delete_piece_info(self, piece: Piece, other: 'Player') -> None:
        r""" Remove \p piece from the \p Player's set of pieces """
        self._pieces.remove(piece)
        del self._locs[piece.loc]
        self.move_set.del_piece(piece, self._locs, other._locs)

    def delete_moveset_info(self, loc: Location, other: 'Player') -> None:
        r""" Update the MoveSet information after deleting a piece at Location \p loc """
        assert self._color != other.color
        self.move_set.add_moves_after_delete(loc, self._locs, other._locs)

    def update_moveset_after_add(self, loc: Location, other: 'Player') -> None:
        r"""
        When adding a piece (i.e., moving it and placing it back down), some previously valid moves
        become blocked.  This method updates \p MoveSet to accomodate that.
        :param loc: \p Location where piece was placed
        :param other: Other player
        """
        assert self._color != other.color
        # pylint: disable=protected-access
        self.move_set.remove_moves_after_add(loc, self._locs, other._locs)

    def has_flag(self) -> bool:
        r""" Returns True if the player has a flag """
        flag = Rank.flag()
        return any(p.rank == flag for p in self._pieces)

    def get_piece_at_loc(self, loc: Location) -> Optional[Piece]:
        r""" Returns the piece at the specified location. If no piece is there, returns None """
        try: return self._locs[loc]
        except KeyError: return None

    def has_move(self, piece: Piece, new_loc: Location) -> bool:
        r""" Returns \p True if the player has a move for the piece ot the specified \p Location """
        assert piece is not None
        return self.move_set.has_move(piece, new_loc)

    def is_valid_next(self, m: Move) -> bool:
        r"""
        Checks whether move \m is in the player's \p MoveSet

        :param m: \p Move to check
        :return: True if \p m is a valid next move.
        """
        return m in self.move_set

    def get_move(self, piece: Piece, new_loc: Location) -> Optional[Move]:
        r""" Returns \p True if the player has a move for the piece ot the specified \p Location """
        assert piece is not None
        return self.move_set.get_move(piece, new_loc)

    def piece_locations(self) -> Set[Location]:
        r""" Location of all of the \p Player's pieces """
        set_locs = set(self._locs.keys())
        assert len(set_locs) == len(self._pieces)
        return set_locs

    def pieces(self) -> Generator[Piece, None, None]:
        r""" Generator that yields the Player's pieces """
        for p in self._pieces:
            yield p

    def build_move_set(self, other: 'Player'):
        r""" Construct the move set of the """
        assert self._color != other.color
        self._move_set = MoveSet.build(self._pieces, self._locs, other._locs)

    def verify_piece_set(self, piece_set: Board.PieceSet) -> bool:
        r"""
        Verify that the player piece information is compliance with the \p Board \p PieceSet
        :param piece_set: Piece set maximum counts
        :return: True if the player's piece set information is in compliance
        """
        pieces_by_rank = dict()
        # Count the number of pieces for each rank
        for p in self._pieces:
            try: pieces_by_rank[p.rank] += 1
            except KeyError: pieces_by_rank[p.rank] = 1
        res = True
        for r in Rank.get_all():
            if r in pieces_by_rank and pieces_by_rank[r] > piece_set.get_rank_count(r):
                logging.warning("Color %s has too many pieces of rank: \"%s\"", self._color.name, r)
                res = False
        return res

    def get_random_move(self) -> Move:
        r"""
        Selects a piece to move uniformly at random.  Then select the move from that piece's
        available moves uniformly at random.
        :return: Randomly selected move
        """
        move_dict = dict()
        keys = []
        for m in self.move_set.avail.values():
            try:
                move_dict[m.piece].append(m)
            except KeyError:
                keys.append(m.piece)
                move_dict[m.piece] = [m]

        key = random.choice(keys)
        return random.choice(move_dict[key])


import mindspore
from mindspore import Tensor, nn
from mindspore.common.initializer import Uniform, VarianceScaling
from mindspore.ops import composite as C
from mindspore.ops import operations as P

from mindspore_rl.agent.actor import Actor
from mindspore_rl.agent.learner import Learner
from mindspore_rl.utils import OUNoise, SoftUpdate

class HuberLoss(nn.Cell):
    """Huber Loss"""

    def __init__(self, delta=1.0):
        super().__init__()
        self.delta = Tensor(delta, mindspore.float32)
        self.abs = P.Abs()
        self.square = P.Square()
        self.select = P.Select()
        self.reduce_sum = P.ReduceSum()

    def construct(self, predict, label):
        abs_error = self.abs(predict - label)
        cond = abs_error <= self.delta
        loss = self.select(
            cond,
            0.5 * self.square(abs_error),
            self.delta * abs_error - 0.5 * self.square(self.delta),
        )
        return self.reduce_sum(loss)

class DDPGPolicy:
    """This is DDPGPolicy class. You should define your networks (DDPGActorNet and DDPGCriticNet here)
    which you prepare to use in the algorithm. Moreover, you should also define you loss function
    (DDPGLossCell here) which calculates the loss between policy and your ground truth value.
    """

    class DDPGActorNet(nn.Cell):
        """DDPGActorNet is the actor network of DDPG algorithm. It takes a set of state as input
        and outputs miu, sigma of a normal distribution"""

        def __init__(
            self,
            input_size,
            hidden_size1,
            hidden_size2,
            output_size,
            compute_type=mindspore.float32,
        ):
            super(DDPGPolicy.DDPGActorNet, self).__init__()
            weight_init = VarianceScaling(
                scale=1.0 / 3, mode="fan_in", distribution="uniform"
            )
            self.dense1 = nn.Dense(
                input_size, hidden_size1, weight_init=weight_init
            ).to_float(compute_type)
            self.dense2 = nn.Dense(
                hidden_size1, hidden_size2, weight_init=weight_init
            ).to_float(compute_type)
            last_weight_init = Uniform(scale=0.003)
            self.dense3 = nn.Dense(
                hidden_size2, output_size, weight_init=last_weight_init
            ).to_float(compute_type)
            self.tanh = P.Tanh()
            self.relu = P.ReLU()

        def construct(self, x):
            """calculate"""
            x = self.relu(self.dense1(x))
            x = self.relu(self.dense2(x))
            x = self.tanh(self.dense3(x))
            return x

    class DDPGCriticNet(nn.Cell):
        """DDPGCriticNet is the critic network of DDPG algorithm. It takes a set of states as input
        and outputs the value of input state"""

        def __init__(
            self,
            obs_size,
            action_size,
            hidden_size1,
            hidden_size2,
            output_size,
            compute_type=mindspore.float32,
        ):
            super(DDPGPolicy.DDPGCriticNet, self).__init__()
            weight_init = VarianceScaling(
                scale=1.0 / 3, mode="fan_in", distribution="uniform"
            )
            self.dense1 = nn.Dense(
                obs_size, hidden_size1, weight_init=weight_init
            ).to_float(compute_type)
            self.dense2 = nn.Dense(
                hidden_size1 + action_size, hidden_size2, weight_init=weight_init
            ).to_float(compute_type)
            last_weight_init = Uniform(scale=0.003)
            self.dense3 = nn.Dense(
                hidden_size2, output_size, weight_init=last_weight_init
            ).to_float(compute_type)
            self.concat = P.Concat(axis=-1)
            self.relu = P.ReLU()
            self.cast = P.Cast()

        def construct(self, observation, action):
            """predict value"""
            x = self.relu(self.dense1(observation))
            action = self.cast(action, x.dtype)
            x = self.concat((x, action))
            x = self.relu(self.dense2(x))
            x = self.dense3(x)
            return x

    def __init__(self, params):
        # nn.Cell do not support clone or deepcopy. Create target network manually.
        self.actor_net = self.DDPGActorNet(
            params["state_space_dim"],
            params["hidden_size1"],
            params["hidden_size2"],
            params["action_space_dim"],
            params["compute_type"],
        )
        self.target_actor_net = self.DDPGActorNet(
            params["state_space_dim"],
            params["hidden_size1"],
            params["hidden_size2"],
            params["action_space_dim"],
            params["compute_type"],
        )

        self.critic_net = self.DDPGCriticNet(
            params["state_space_dim"],
            params["action_space_dim"],
            params["hidden_size1"],
            params["hidden_size2"],
            1,
            params["compute_type"],
        )
        self.target_critic_net = self.DDPGCriticNet(
            params["state_space_dim"],
            params["action_space_dim"],
            params["hidden_size1"],
            params["hidden_size2"],
            1,
            params["compute_type"],
        )

class DDPGActor(Actor):
    """This is an actor class of DDPG algorithm, which is used to interact with environment, and
    generate/insert experience (data)"""

    def __init__(self, params=None):
        super().__init__()
        self.actor_net = params["actor_net"]
        self.env = params["collect_environment"]
        self.expand_dims = P.ExpandDims()
        self.squeeze = P.Squeeze()
        low, high = self.env.action_space.boundary
        self.clip_value_min = Tensor(low)
        self.clip_value_max = Tensor(high)
        self.noise = OUNoise(
            params["stddev"], params["damping"], self.env.action_space.shape
        )

    def act(self, phase, params):
        """collect experience and insert to replay buffer (used during training)"""
        actions = self.get_action(phase, params)
        next_obs, rewards, done = self.env.step(actions)
        rewards = self.expand_dims(rewards, 0)
        done = self.expand_dims(done, 0)
        return next_obs, actions, rewards, done

    def get_action(self, phase, params):
        """get action"""
        obs = self.expand_dims(params, 0)
        actions = self.actor_net(obs)
        actions = self.squeeze(actions)
        if phase != 3:
            actions = self.noise(actions)
            actions = C.clip_by_value(actions, self.clip_value_min, self.clip_value_max)
        return actions

class DDPGLearner(Learner):
    """This is the learner class of DDPG algorithm, which is used to update the policy net"""

    class CriticLossCell(nn.Cell):
        """DDPGLossCell calculates the loss of DDPG algorithm"""

        def __init__(self, gamma, target_actor_net, target_critic_net, critic_net):
            super(DDPGLearner.CriticLossCell, self).__init__(auto_prefix=True)
            self.gamma = gamma
            self.target_actor_net = target_actor_net
            self.target_critic_net = target_critic_net
            self.critic_net = critic_net
            self.huber_loss = HuberLoss()

        def construct(self, obs, actions, rewards, next_obs, done):
            """calculate the total loss"""
            # critic Loss
            target_actions = self.target_actor_net(next_obs)
            target_q_values = self.target_critic_net(next_obs, target_actions)
            # One step td error.
            td_targets = rewards + self.gamma * (1.0 - done) * target_q_values
            q_values = self.critic_net(obs, actions)
            critic_loss = self.huber_loss(td_targets, q_values)
            return critic_loss

    class ActorLossCell(nn.Cell):
        """ActorLossCell calculates the loss of DDPG algorithm"""

        def __init__(self, actor_net, critic_net):
            super(DDPGLearner.ActorLossCell, self).__init__(auto_prefix=True)
            self.actor_net = actor_net
            self.critic_net = critic_net
            self.reduce_mean = P.ReduceMean()

        def construct(self, obs):
            """calculate the total loss"""
            actions = self.actor_net(obs)
            q_values = self.critic_net(obs, actions)
            actor_loss = -self.reduce_mean(q_values)
            return actor_loss

    def __init__(self, params):
        super().__init__()
        gamma = params["gamma"]
        self.critic_net = params["critic_net"]
        self.actor_net = params["actor_net"]

        # optimizer network
        critic_optimizer = nn.Adam(
            self.critic_net.trainable_params(), learning_rate=params["critic_lr"]
        )
        actor_optimizer = nn.Adam(
            self.actor_net.trainable_params(), learning_rate=params["actor_lr"]
        )

        # loss network
        self.target_actor_net = params["target_actor_net"]
        self.target_critic_net = params["target_critic_net"]
        critic_loss_cell = self.CriticLossCell(
            gamma, self.target_actor_net, self.target_critic_net, self.critic_net
        )
        critic_loss_cell = self.CriticLossCell(
            gamma, self.target_actor_net, self.target_critic_net, self.critic_net
        )
        actor_loss_cell = self.ActorLossCell(self.actor_net, self.critic_net)

        self.critic_train = nn.TrainOneStepCell(critic_loss_cell, critic_optimizer)
        self.actor_train = nn.TrainOneStepCell(actor_loss_cell, actor_optimizer)
        self.critic_train.set_train(mode=True)
        self.actor_train.set_train(mode=True)

        # soft update network
        factor, interval = params["update_factor"], params["update_interval"]
        params = self.actor_net.trainable_params() + self.critic_net.trainable_params()
        target_params = (
            self.target_actor_net.trainable_params()
            + self.target_critic_net.trainable_params()
        )
        self.soft_updater = SoftUpdate(factor, interval, params, target_params)

    def learn(self, experience):
        """DDPG learners"""
        obs, actions, rewards, next_obs, done = experience
        critic_loss = self.critic_train(obs, actions, rewards, next_obs, done)
        actor_loss = self.actor_train(obs)

        # update target network parameters.
        self.soft_updater()
        return critic_loss + actor_loss


import numpy as np
import statistics
from scipy import stats

dataset= [5,6,7,5,6,5,7,4,5,5,5,5,7,5,6,6,7,6,6,7,7,7,6,5,6]

#mean value
mean= np.mean(dataset)

#median value
median = np.median(dataset)

#mode value
mode= stats.mode(dataset)

#standard Deviation
Std = statistics.stdev(dataset)

#Variance
Var = statistics.variance(dataset)

print("Mean: ", mean)
print("Median: ", median)
print("Mode: ", mode)
print("Std", Std)
print("Var", Var)


# Busca Local Múltiplos Inicios
# Local Search Multiple Starts
import sys
import time

sys.path.insert(1, '../stage_01')
sys.path.insert(1, '../')
from utils import corrent_solution_size, objetive_function, read_instance, viable_solution
from local_search import local_search
from semi_greedy import semi_greedy
import config

# Busca local múltiplos inícios + semi-guloso alpha + busca local primeira melhora
def multiple_starts_local_search(alpha, timeout):
    desks, tests, empty = config.desks, config.tests, config.empty
    desk_count = len(desks)
    test_count = len(tests)
    s_ = viable_solution(desk_count, desk_count, test_count)
    value_ = objetive_function(s_)

    initial_time = time.time()
    current_time = time.time()
    execution_time = current_time - initial_time
    while execution_time < timeout:
        s, _ = semi_greedy(alpha, False)
        s, value = local_search(s, 0, False) # 1 = Primeira  melhora

        if value < value_:
            s_ = s
            value_ = value

        current_time = time.time()
        execution_time = current_time - initial_time

    s_, value_ = corrent_solution_size(s_, empty)
    return s_, value_

if __name__ == '__main__':
    file_name = sys.argv[1]
    timeout = int(sys.argv[2])
    alpha = float(sys.argv[3])
    config.set_timeout(timeout)
    read_instance(file_name)
    s_, value_ = multiple_starts_local_search(alpha, timeout)

    print(s_)
    print(value_)


from src.main.python.Solution import Solution
from src.main.python.datastructures.Interval import Interval

# Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).
#
# You may assume that the intervals were initially sorted according to their start times.
#
# Example 1:
#   Given intervals [1,3],[6,9], insert and merge [2,5] in as [1,5],[6,9].
#
# Example 2:
#   Given [1,2],[3,5],[6,7],[8,10],[12,16], insert and merge [4,9] in as [1,2],[3,10],[12,16].
#
#   This is because the new interval [4,9] overlaps with [3,5],[6,7],[8,10].

class Q057(Solution):
    def insert(self, intervals, newInterval):
        """
        :type intervals: List[Interval]
        :type newInterval: Interval
        :rtype: List[Interval]
        """
        ans = []
        if newInterval:
            copy = newInterval
            for interval in intervals:
                if copy.start <= interval.end and interval.start <= copy.end:
                    copy.start = min(copy.start, interval.start)
                    copy.end = max(copy.end, interval.end)
                elif copy.end < interval.start:
                    ans.append(copy)
                    copy = interval
                else:
                    ans.append(interval)
            ans.append(copy)
        return ans


from fastapi import APIRouter, Body, Depends, Request, status
from fastapi.responses import JSONResponse
from jarvis.db.database import DataBase, get_database
from jarvis.core import config, utils
from jarvis.lib import TwilioHelper
from typing import Dict
from twilio.rest import Client
import jarvis.crud as crud
import jarvis.models as model
import jarvis.core.text_responses as text

router = APIRouter()
twilio_helper = TwilioHelper()
client = Client(config.TWILIO_ACCOUNT_SID, config.TWILIO_ACCOUNT_AUTH_TOKEN)

@router.post("/add")
async def add_item_to_cart(request: Request, db: DataBase = Depends(get_database)):
    async with db.pool.acquire() as conn:
        body = await request.form()
        parsed_body = dict(body)
        cart_item = model.CartItem(**parsed_body)
        normalized_cart_item = await utils.normalize_cart_item_model(conn, cart_item)
        cart_item_name = normalized_cart_item.get("name")
        item_quantity = normalized_cart_item.get("quantity")

        success_message = text.add_item_success(cart_item_name, item_quantity)
        shopping_cart_message = text.shopping_cart_info(1)
        msg = "".join([success_message, shopping_cart_message])
        return twilio_helper.compose_mesage(msg)
        # Make potentially a new helper class that has add item
        # because you have to then convert this to a message after etc
        # shopping_cart = model.ShoppingCart(**payload)

        # return None

@router.get("/menu/{item_type}")
async def get_menu(
    item_type: str, db: DataBase = Depends(get_database),
):
    async with db.pool.acquire() as conn:
        try:
            items = await crud.get_all_item_by_type(conn, item_type)
            message_list = [utils.item_model_to_message(item) for item in items]
            message = "\n".join(message_list)
            twilio_message = twilio_helper.compose_mesage(message)
            return twilio_message

        except UserWarning as warning:
            return JSONResponse(
                status_code=status.HTTP_202_ACCEPTED, content=str(warning)
            )

@router.post("/checkout")
async def checkout_cart(
    payload: Dict = Body(...), db: DataBase = Depends(get_database)
):
    pass

@router.post("/sms")
async def get_twilio_text():
    resp = ":)"
    return utils.create_text_response(resp)

@router.get("/test")
async def twilio_test(payload: Dict = Body(...)):

    message = client.messages.create(
        body="Jarvis test",
        messaging_service_sid=config.TWILIO_ACCOUNT_MESSAGING_SID,
        to=config.TO_PHONE_NUMBER,
    )

    return message.sid


from datetime import timedelta, datetime
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session
from jose import jwt, JWTError

from app import database, models
from app.schemas import TokenData
from app.config import settings

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

SECRET_KEY = settings.secret_key
ALGORITHM = settings.algorithm
ACCESS_TOKEN_EXPIRE_MINUTES = settings.access_token_expire_minutes

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

def verify_access_token(token: str, credentials_exception):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        user_id: str = payload.get("user_id")
        if user_id is None:
            raise credentials_exception
        token_data = TokenData(id=user_id)
    except JWTError:
        raise credentials_exception
    return token_data

def get_current_user(
    token: str = Depends(oauth2_scheme), db: Session = Depends(database.get_db)
):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    token = verify_access_token(token, credentials_exception)
    user = db.query(models.User).filter(models.User.id == token.id).first()
    return user


import requests
from bs4 import BeautifulSoup
import pandas as pd
from os import listdir, remove
import datetime as dt
from time import sleep

from MainMethods import getInfo, showDays
from conf import INT, INF, URL, LOC, NINF, LOC2,\
    chosenF, errorsF, doneF

"""
The information for saved days is checked
and old files deleted.
"""
oldflags = [f for f in listdir(LOC2) if f[0]== "F"]
if oldflags:
    for f in oldflags:
        remove(f"{LOC2}\{f}")

saved= listdir(LOC)
if saved:
    saved = [f.split(".csv")[0] for f in saved]
    ints = [int(f.split("y")[-1]) for f in saved]
    for i, f in enumerate(saved):
        if ints[i] < dt.datetime.today().day and max(ints) -ints[i] <9:
            remove(f"{LOC}\{f}.csv")
            saved.remove(f)

def flagIt():
    now = dt.datetime.timestamp(dt.datetime.now())
    name = fr"{LOC2}\F{str(int(now))}.txt"
    with open(name, "w") as F:
        pass
    sleep(2)
    return name

def unflagIt(name):
    remove(name)

def checkWait():
    flag = [f for f in listdir(LOC2) if f[0] == "T"]
    if flag:
        while flag[0] in listdir(LOC2):
            print("Wait, Sign Up in process....")
            sleep(5)

checkWait()

flag = flagIt()
Chosen = pd.read_csv(chosenF)
Done = pd.read_csv(doneF)[NINF[-2]].to_list()
if Done:
    print(f"These SignUps are done and should be confirmed by email:\n"
          f"{Chosen[Chosen[NINF[-2]].isin(Done)][[NINF[0], NINF[1], NINF[2]]].to_string(index=False)}\n\n"
          f"-------------------------------------------")
    Chosen.drop(Chosen[Chosen[NINF[-2]].isin(Done)].index, inplace=True)
    pd.DataFrame(columns= NINF).to_csv(doneF, index= False)

Errors = pd.read_csv(errorsF)[NINF[-2]].to_list()
if Errors:
    print(f"The sign up for these classes failed:\n"
          f"{Errors.iloc[:,:3]}\n"
          f"Please check manually if you are still interested and "
          f"allow them to be deleted from the program.")
    conf = "n"
    while conf.lower() != "y":
        conf = input("Allow? (y/n):")
        if conf.lower() == "y":
            Errors = pd.DataFrame(columns= NINF)
            Errors.to_csv(errorsF, index = False)
            Chosen.drop(Chosen[Chosen[NINF[-2]].isin(Errors)].index, inplace=True)
        else:
            conf = input("There is no benefit in keeping them !\n"
                         "Are you sure "
                         "you don't want to let them go?\n"
                         "(y/n):")

Chosen.to_csv(chosenF, index= False)

"""
This uses requests and beautiful soup to setup
the iterators.
"""
r = requests.get(URL)
soup = BeautifulSoup(r.text, "lxml")
classes = soup.find(id= "classes")
days = classes.find_all(class_= "scheduleDay")[:8]

"""
The following loop gets the basic info from the websites
and keeps it in the dictionary DFs as DataFrames
"""
DFs = {}
for day in days:
    date= day["id"]
    if date in saved:
        continue
    DFs[date] = pd.DataFrame(columns= INF)

    # iterate over each class in the day
    dayclss = day.find_all("div")
    for clss in dayclss:
        #then within each class I select the link in "schedSignup"
        if any(x in clss["class"] for x in INT):
            link = clss.find(class_= "schedSignup").a["href"]
            inf = getInfo(link)
            DFs[date] = DFs[date].append(pd.Series(inf, index= INF), ignore_index=True)

"""
This condition runs the showDays loop to check
each new day's classes for availability and presents the options
"""
num = 0
NewDF = pd.DataFrame(columns= NINF)
if DFs:
    result = showDays(DFs, num, NewDF)
    NewDF, num  = result[0], result[1]

#############

"""
Here, the requests waiting in the 'chosen' csv file
are presented and offered for cancellation
"""

# this just sets up the sig and UId variables for
sigs= [f for f in listdir(LOC2) if f[:3]== "Sig"]
if sigs:
    with open(f"{LOC2}\{sigs[0]}", "r") as s:
        UId = int(s.read())

                        ##### Cancel
if Chosen.shape[0]:
    print(f"\n============== OPTIONS TO CANCEL ======================\n"
          f"These are signups that are waiting to be executed:\n\n"
          f"{Chosen.iloc[:,:3]}\n\n"
          f"Type in the row number on the left if you want to cancel it, seperate with commas\n"
          f"Otherwise, just hit enter and confirm\n")

    confirm = "n"
    while confirm.lower() != "y":
        inp = input("CANCEL:")
        if inp:
            try:
                inp = list(map(int, inp.split(",")))
                print(f"cancel these:\n"
                      f"{Chosen.loc[inp, [NINF[0], NINF[1], NINF[2]]]}")
                confirm = input("Confirm (y/n):")
                if confirm.lower() == "y":
                    Chosen.drop(inp, inplace=True)
            except:
                print(f"There seems to be a mistake in your input,\n"
                      f"please don't type any unnecessary commas, spaces or words.")
        else:
            confirm = input("Keep all (y/n):")

"""
If there are newly available classes:
the following while loop will get requests and
add the newly chosen ones to the 'chosen' csv file

It will also give Unique IDs to each class based on
the UId variable retrieved from the Signal File (SigA or SigB)
"""

                        ##### Choose
if num:
    print(f"=====================================\n"
          f"The column on the RIGHT of each list contains the code to choose the class\n"
          f"please type in your choice(s)"
          f"(seperate codes with commas if you want multiple, hit enter if you want none.)\n")
    confirm = "n"
    while confirm.lower() != "y":
        choice = input("Choice:")
        if choice:
            try:
                choice = list(map(int,choice.split(",")))
                chosen = NewDF[NewDF[NINF[-1]].isin(choice)].copy()
                if max(choice) <= NewDF[NINF[-1]].max():
                    print(f"These are your new choices:\n"
                          f"{chosen.iloc[:,:3].to_string(index= False)}\n")

                    if Chosen.shape[0]:
                        print(f"These are still waiting to be executed:\n"
                              f"{Chosen.iloc[:, :3].to_string(index=False)}\n")
                    else:
                        print(f"There are no signups waiting.")
                    confirm = input("Confirm (y/n):")

                else:
                    print(f"You may have forgotten a comma or got the wrong number,\n"
                          f"please try again")

            except:
                print(f"There seems to be a mistake in your input,\n"
                      f"please don't type any unnecessary commas, spaces or words.")

        else:
            print(f"You chose none.")
            chosen = pd.DataFrame()
            if Chosen.shape[0]:
                print(f"These are still waiting to be executed:\n"
                      f"{Chosen.iloc[:, :3].to_string(index= False)}\n")
            else:
                print(f"There are no signups waiting.")
            confirm = input("Confirm (y/n):")

    if chosen.shape[0]:
        chosen[NINF[-2]] = [UId +i for i in range(1, chosen.shape[0]+1)]
        UId = chosen[NINF[-2]].max()
        Chosen = Chosen.append(chosen, ignore_index=True)

# The days and requestes are saved
Chosen.to_csv(chosenF, index= False)
unflagIt(flag)

for d in DFs:
    DFs[d].to_csv(fr"{LOC}\{d}.csv", index = False)

# The SigFile is updated
if sigs:
    nxtSig = int(sigs[0].split(".")[0][3:])+1
    remove(fr"{LOC2}\{sigs[0]}")

    with open(fr"{LOC2}\Sig{nxtSig}.txt", "w") as s:
        s.write(str(UId))


#!/usr/bin/env python
# Code property of Matteo Scanavino - matteo.svanavino@gmail.it
# Minor changes by Iris David Du Mutel
import rospy
# from std_msgs.msg import Float32MultiArray
from myrobot.msg import vect_msg
from geometry_msgs.msg import Twist
from nav_msgs.msg import Odometry
import cv2
# import cv2.cv
import os
import math
import numpy as np
#import pyrealsense2 as rs
import message_filters
from sensor_msgs.msg import Image, CameraInfo
from cv_bridge import CvBridge, CvBridgeError
import imutils #collection of OpenCV and Python convenience functions
from collections import deque
from scipy.spatial.transform import Rotation as R

def green_ball():
    rospy.init_node('realsense_behaviour', anonymous=True)
    pub = rospy.Publisher('gb_vect', vect_msg, queue_size=10)
    color_sub = message_filters.Subscriber('camera/color/image_raw',Image)
    # depth_sub = message_filters.Subscriber('camera/depth/image_raw',Image)
    x_sub = message_filters.Subscriber('/odom',Odometry)
    ts = message_filters.ApproximateTimeSynchronizer([color_sub, x_sub], queue_size=10,slop=0.1)
    ts.registerCallback(callback,pub)
    rospy.spin()

def callback(color_raw, x_sub,pub):
    vect = [0, 0]
    msg = vect_msg()
    bridge = CvBridge()
    greenLower = (29, 86, 6)
    greenUpper = (64, 255, 255)
    # realsense min and max distance
    try:
            color_image = bridge.imgmsg_to_cv2(color_raw, "bgr8")
    except CvBridgeError as e:
            print(e)
    Xest = x_sub
    # # Variable assignation:

    [yaw, pitch, roll] = get_rotation(Xest)
    psi_est = yaw*180/math.pi
    frame = imutils.resize(color_image, width=600)
    blurred = cv2.GaussianBlur(frame, (11, 11), 0)
    hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
    # construct a mask for the color "green", then perform
	# a series of dilations and erosions to remove any small
    # blobs left in the mask
    mask = cv2.inRange(hsv, greenLower, greenUpper)
    mask = cv2.erode(mask, None, iterations=2)
    mask = cv2.dilate(mask, None, iterations=2)
    # find contours in the mask and initialize the current
    # (x, y) center of the ball
    cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    center = None
    if len(cnts) > 0:
        # find the largest contour in the mask, then use
        # it to compute the minimum enclosing circle and
        # centroid
        c = max(cnts, key=cv2.contourArea)
        ((x, y), radius) = cv2.minEnclosingCircle(c)
        if x<280:
            vect[0]=90
            vect[1]=0
        elif x>305:
            vect[0]=-90
            vect[1]=0
        else:
            if radius<100:
                vect[0]=psi_est
                vect[1]=0.8
            else:
                vect[0]= psi_est
                vect[1]=0
        M = cv2.moments(c)
        center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
        print(center)
        # only proceed if the radius meets a minimum size
        if radius > 10:
            # draw the circle and centroid on the frame,
            # then update the list of tracked points
            cv2.circle(frame, (int(x), int(y)), int(radius),
                (0, 255, 255), 2)
            print('radius=', radius)
            cv2.circle(frame, center, 5, (0, 0, 255), -1)
    else:
        print('out of frame')
    # show the frame to our screen
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1) & 0xFF
    # Send data
    msg.header.stamp = rospy.Time.now()
    msg.angle = vect[0]
    msg.value = vect[1]
    rospy.loginfo('Realsense vector data sent')
    pub.publish(msg)

def get_rotation(Xest):
    orientation_q = Xest.pose.pose.orientation
    orientation_list = [orientation_q.x, orientation_q.y, orientation_q.z, orientation_q.w]
    r = R.from_quat(orientation_list)
    EuAn = r.as_euler('zyx', degrees=False)
    return EuAn
if __name__ == '__main__':
    try:
        green_ball()
    except rospy.ROSInterruptException:
        pass


import threading
import socket
def server_echo (sock):
    while True:
        conn, addr = sock.accept()
        while True:
            data = conn.recv(1024)
            if not data: break
            if data == b"close":
                #            print ("Close connection")
                break
            conn.send(data)
#            print (data)
        conn.close()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#s.bind(('127.0.0.1', 2222))
s.bind(('0.0.0.0', 2222))
s.listen(10)

for i in range (10):
    pserver = threading.Thread(target=server_echo, name="srv"+str (i+1), args=[s])
    pserver.start()


import unittest
from main import *

class TestEveryMethodInMain(unittest.TestCase):
    def test_GetContainer(self):
        self.assertIsNotNone(getPricesContainer())
        self.assertIsNotNone(returnListedPricesToServer())
        self.assertIsNotNone(returnInOrderedPricesToServer())

    #checkPriceDiffWithDatabase
    #getSearchedItems

    def test_takeMinPriceFromRange(self):
        _input = [{"CRL Eastern [Dominus]":{"Normal":{"price":[200,300]}}},{"Alchemist":{"Black":{"price":[2300,2500]}}},{"Overgrowth":{"Titanium White":{"price":[350,450]}},"Dominus":{"Lime":{"price":[4900,5300]}}}]
        _output = [{"CRL Eastern [Dominus]":{"Normal":{"price":200}}},{"Alchemist":{"Black":{"price":2300}}},{"Overgrowth":{"Titanium White":{"price":350}},"Dominus":{"Lime":{"price":4900}}}]
        self.assertEqual(takeMinPriceFromRange(_input), _output)
        self.assertEqual(takeMinPriceFromRange([{"Item": {"paint": {"price":[21,37]}}}]), [{"Item": {"paint": {"price":21}}}])

    def test_calculateQuickSellPrice(self):
        _input = [{"CRL Eastern [Dominus]":{"Normal":{"price":200}}},{"Alchemist":{"Black":{"price":2300}}},{"Overgrowth":{"Titanium White":{"price":350}},"Dominus":{"Lime":{"price":4900}}}]
        _output = [{"CRL Eastern [Dominus]":{"Normal":{"price":100}}},{"Alchemist":{"Black":{"price":2200}}},{"Overgrowth":{"Titanium White":{"price":250}},"Dominus":{"Lime":{"price":4800}}}]
        self.assertEqual(calculateQuickSellPrice(_input, 100), _output)

    # tests = [{input: [], expected: []},{items: [], expected: []}]
    # for test in tests:
    # assert(test.expected, takeMinPriceFromRange(test.items))

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


# May 10, 1981 00:31
# 정보 취합
month, day, year, time = input().split()
day = int(day[:-1])
year = int(year)
hour, minute = map(int, time.split(':'))
# 윤년 여부에 따라 2월 날짜 변경
month_name_lst = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
month_days_lst = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if (year%400 == 0) or (year%4 == 0 and year%100 != 0):
    month_days_lst[1] += 1
# 1년 총 시간 (단위는 분으로)
total_time = sum(month_days_lst) * 24 * 60
# 현재 총 시간
current_month_idx = month_name_lst.index(month)
current_time = (sum(month_days_lst[:current_month_idx]) + day-1)*24*60 + hour*60 + minute
print(current_time/total_time*100)


import os

while True:
    info = input().split("-")

    if info[0] == 'Create':
        file = open(f"files/{info[1]}", "w")
        file.close()

    elif info[0] == 'Add':
        with open(f"files/{info[1]}", "a") as file:
            file.write(f"{info[2]}\n")

    elif info[0] == 'Replace':
        try:
            with open(f"files/{info[1]}", "r+") as file:
                text = file.readlines()

            file = open(f"files/{info[1]}", "w")

            for i in range(len(text)):
                text[i] = text[i]. replace(info[2], info[3])

            file.write("".join(text))
            file.close()
        except FileNotFoundError:
            print("An error occurred")

    elif info[0] == 'Delete':
        try:
            os.remove(f"files/{info[1]}")
        except FileNotFoundError:
            print("An error occurred")
    else:
        break


# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#

import sublime, sublime_plugin
from os import path

class GoToPackageFileCommand(sublime_plugin.TextCommand):
  def run(self, edit):
    f = self.view.file_name()
    curFolder = path.dirname(f)
    print("Current folder: {0}".format(curFolder))
    packagejs = self.find_package_js(curFolder)
    if packagejs:
      print("Found {0}".format(packagejs))
      sublime.active_window().open_file(packagejs)
    else:
        sublime.status_message("package.js not found in hierarchy of {0}".format(curFolder))

  def find_package_js(self, f):
    print("Checking '{0}'".format(f))
    packagejs = path.join(f, "package.js")
    if path.isfile(packagejs):
      return packagejs
    else:
        newF = path.dirname(f)
        if newF != f:
          return self.find_package_js(newF)