#!/usr/bin/env python
# coding: utf-8

# In[185]:

#serve deosAgPage.py --port 5000 --allow-websocket-origin=dev-web.demac.udel.edu:5000
#steps to recreate envirionment...
### 1. Create new conda environment
### 2. conda install -c conda-forge bokeh geoviews holoviews
### 3. conda install -c conda-forge hvplot

from cartopy import crs as ccrs
import cartopy.io.shapereader as shpreader
import matplotlib as mpl
from datetime import date, datetime, timedelta
import pandas as pd
import panel as pn #version 0.9.5
import xarray as xr #version 0.11.3
import hvplot.xarray #version 0.5.2
import hvplot.pandas #version 0.5.2
import geoviews as gv #version 1.7.0
from io import BytesIO
import calendar
import numpy as np

gv.extension('bokeh')

# Declare bounds of the data
bounds=(-76.2,38.3,-74.85, 40.3)

# read in the refET dataset
dsRefET = xr.open_dataset("http://basin.ceoe.udel.edu/thredds/dodsC/DEOSAG.nc")
dsRefET = dsRefET.sel(latitude=slice(bounds[3], bounds[1]), longitude=slice(bounds[0],bounds[2]))

climo = xr.open_dataset("http://basin.ceoe.udel.edu/thredds/dodsC/deos_doy_climatology.nc")
climo = climo.sel(latitude=slice(bounds[3], bounds[1]), longitude=slice(bounds[0],bounds[2]))

# In[ ]:

# In[186]:

# read in the ncep stage IV precip dataset
dsPrec = xr.open_dataset("http://thredds.demac.udel.edu/thredds/dodsC/NCEPIVQC.nc")
dsPrec = dsPrec.sel(lat=slice(bounds[1], bounds[3]),
                    lon=slice(bounds[0],bounds[2]),
                    time=slice(datetime.strptime("2014-01-01", "%Y-%m-%d"),
                              date.today()))

dsPrec = dsPrec.reindex(lat=list(reversed(dsPrec.lat)))
dsPrec = dsPrec.rename(name_dict= {'lat' : 'latitude'})
dsPrec = dsPrec.rename(name_dict= {'lon' : 'longitude'})
dsPrec = dsPrec.drop('crs')

# In[297]:

startcolor = '#8B4513'
midcolor = '#FFFFFF'
endcolor = '#008000'
own_cmap1 = mpl.colors.LinearSegmentedColormap.from_list( 'own2', [startcolor, midcolor, endcolor] )

# create lists for select widget to pull from
sum_dict = dict(zip(['Heating Degree Days', 'Cooling Degree Days','DEOS Precip',  'Energy Density',
                     'Reference Evapotranspiration', 'Growing Degree Days'],
                    ['HDD', 'CDD',  'dailyprecip', 'energyDens','refET', 'GDD']))
mean_dict = dict(zip(['Mean Temperature', 'Max Temperature', 'Min Temperature', 'Mean Wind Speed', 'Mean Dew Point',
                      'Mean Relative Humidity', 'Max Relative Humidity', 'Min Relative Humidity',
                      'Mean Soil Temperature', 'Max Soil Temperature', 'Min Soil Temperature',
                      'Mean Volumetric Water Content','Max Volumetric Water Content', 'Min Volumetric Water Content',
                      'Mean Solar', 'Mean Wind Direction', 'Wind Gust', 'Min Wind Chill'],
                     ['meanTemp', 'maxTemp', 'minTemp', 'meanWS','meanDP','meanRH', 'maxRH', 'minRH',
                      'meanST', 'maxST', 'minST','meanVWC', 'maxVWC', 'minVWC','meanSolar', 'meanWD',
                      'dailyGust', 'dailyMinWC']))

datasets = list(sum_dict.keys()) + list(mean_dict.keys()) + ['NCEP Stage IV Precip', 'NCEP Stage IV Precip - DEOS RefET']
dataset = pn.widgets.Select(name='Dataset', options=datasets, value=datasets[10])
cmap_keys = ['Drought', 'Jet', 'Viridis','Red-Yellow-Green', 'Red-Yellow-Blue', 'Cool-Warm', 'Spectral']
cmap_values = [own_cmap1, 'jet', 'viridis', 'RdYlGn', 'RdYlBu' , 'coolwarm', 'Spectral']
cmap_dict = dict(zip(cmap_keys,cmap_values))

dataset = pn.widgets.Select(name='Dataset', options=datasets, value=datasets[6])
cmap = pn.widgets.Select(name='Color Ramp', options=cmap_keys, value=cmap_keys[5])
start_date = pn.widgets.DatePicker(name='Start Date', value=(date.today() + timedelta(days=-3)))
end_date = pn.widgets.DatePicker(name='End Date', value=(date.today() + timedelta(days=-1)))
#clb_min = pn.widgets.Spinner(name="Colorbar Minimum (mm day-1)", value=0, step=1, start=-5000, end=5000, width=100)
#clb_max = pn.widgets.Spinner(name="Colorbar Maximum (mm day-1)", value=1, step=1, start=-5000, end=5000, width=100)

# information for quadmesh plot
ylim=(38.3, 40.3)
xlim=(-76.2,-74.85)
width=800
height=1000

# load in the shapefiles
deos = shpreader.Reader("/home/james/agriculture/shapefolder/cb_2018_us_state_500k/" + 'cb_2018_us_state_500kclipped.shp')
coastline = gv.Shape.from_records(deos.records())
shp = gv.Polygons(coastline).opts('Polygons', line_color='black',fill_color=None)

deos = shpreader.Reader("/home/james/agriculture/shapefolder/mapLayers/" + 'deoscounties.shp')
coastline = gv.Shape.from_records(deos.records())
shp1 = gv.Polygons(coastline).opts('Polygons', line_color='black',fill_color=None)

### second dataset lists for the departure maps
datasets2 = list(sum_dict.keys()) + list(mean_dict.keys()) + ['NCEP Stage IV Precip']
dataset2 = pn.widgets.Select(name='Dataset', options=datasets2, value=datasets2[6])
cmap2 = pn.widgets.Select(name='Color Ramp', options=cmap_keys, value=cmap_keys[5])

# In[298]:

# only use the @pn.depends if we are making an interactive plot call WITHOUT a button!
#@pn.depends(dataset.param.value, start_date.param.value, end_date.param.value, clb_min.param.value, clb_max.param.value, cmap.param.value)
def make_plot1(dataset, start_date, end_date,cmap):              # clb_min, clb_max,
     # Load and format the data
    sDate = datetime(start_date.year, start_date.month, start_date.day )
    eDate = datetime(end_date.year, end_date.month, end_date.day)

    quad_title = str(str(dataset) + "    Start Date: " +
                    datetime.strftime(sDate, "%Y-%m-%d") + " 00:00 - End Date: " +
                    datetime.strftime(eDate, "%Y-%m-%d") + " 23:59")
    if any(dataset in s for s in mean_dict.keys()):
        df = dsRefET[mean_dict[dataset]]
        opLabel = 'Avg ' + df.units
        df = df.sel(time=slice(sDate,eDate + timedelta(days=1)))
        df = df.mean('time')

    if any(dataset in s for s in sum_dict.keys()):
        df = dsRefET[sum_dict[dataset]]
        opLabel = 'Total ' + df.units
        df = df.sel(time=slice(sDate,eDate + timedelta(days=1)))
        df = df.sum('time')
    if dataset == 'NCEP Stage IV Precip':
        df = dsPrec
        opLabel = 'Total mm'
        df = df.sel(time=slice(sDate,eDate))
        df = df.sum('time')
        dwnldName = str("ncepStageIV.nc")
    if dataset == 'NCEP Stage IV Precip - DEOS RefET':
        dfref = dsRefET['refET']
        df1 = dfref.sel(time=slice(sDate,eDate + timedelta(days=1)))
        df1 = df1.sum('time')
        df2 = dsPrec.sel(time=slice(sDate,eDate + timedelta(days=1)))
        df2 = df2.sum('time')
        df = df2 - df1.values
        df['Precip - ET'] = df.Precipitation_Flux
        df = df.drop('Precipitation_Flux')
        opLabel = 'Total mm'

    x = 'longitude'
    y = 'latitude'
     # create the Altair chart object
    chart = df.hvplot.quadmesh(width=width, height=height, x=x, y=y, cmap=cmap_dict[cmap],
            project=True, geo=True,title=quad_title,xlim=xlim,ylim=ylim,label=opLabel, #clim=(vmin,vmax)
            rasterize=True, dynamic=False) * shp * shp1
    return chart, df

# create update plot window button
def update(event):
    plotwindow1[1].object = make_plot1(dataset.value, start_date.value, end_date.value,cmap.value)[0] # clb_min, clb_max,

generate_button = pn.widgets.Button(name='Plot', button_type='primary')
generate_button.on_click(update)

############################################################
def download_cb():
    bout = make_plot1(dataset.value, start_date.value, end_date.value,cmap.value)[1].to_netcdf()
    bio = BytesIO()
    bio.write(bout)
    bio.seek(0)
    return bio

fd = pn.widgets.FileDownload(
    callback=download_cb,
    filename='AgWx.nc')

# set the widget box for the widgets to be placed into
sel_box = pn.WidgetBox(dataset, start_date, end_date, cmap, generate_button,
                       pn.layout.Spacer(height=30),fd)

title1       = '
CEMA Agriculture Dashboard
'
instruction = '
Select a dataset, set your parameters, and click plot
'
oggm_logo   = ''
pn_logo     = ''
cema_logo = '/home/james/agriculture/shapefolder/cema2logo.png'

header = pn.Row(pn.panel(cema_logo, width=170),  pn.layout.Spacer(width=10),
                pn.Column(pn.Pane(title1, width=1000), pn.Pane(instruction, width=1000)))
plotwindow1 = pn.Row(sel_box, make_plot1(dataset.value, start_date.value, end_date.value, cmap.value)[0]) #clbmin clbmax

# In[299]:

# only use the @pn.depends if we are making an interactive plot call WITHOUT a button!
#@pn.depends(dataset.param.value, start_date.param.value, end_date.param.value, clb_min.param.value, clb_max.param.value, cmap.param.value)
def make_plot2(dataset2, start_date, end_date,cmap2):              # clb_min, clb_max,
     # Load and format the data
    sDate = datetime(start_date.year, start_date.month, start_date.day )
    eDate = datetime(end_date.year, end_date.month, end_date.day)

    quad_title = str(str(dataset2) + "    Start Date: " +
                    datetime.strftime(sDate, "%Y-%m-%d") + " 00:00 - End Date: " +
                    datetime.strftime(eDate, "%Y-%m-%d") + " 23:59")
    # special clauses for climatology file which has a dayofyear index instead of time
    if calendar.isleap(sDate.timetuple().tm_year) == False:
        redim_climo = climo.drop([60], dim='dayofyear')
    else:
        redim_climo = climo
    datetime_list = pd.date_range(start='1/1/' + str(sDate.timetuple().tm_year) , end='12/31/' + str(sDate.timetuple().tm_year))
    redim_climo = redim_climo.assign_coords(time=("time", datetime_list))
    redim_climo = redim_climo.reset_index(['dayofyear'], drop = True)
    for var in redim_climo.data_vars:
        redim_climo[var]= redim_climo[var].rename({'dayofyear': 'time'})

    if sDate.timetuple().tm_year < eDate.timetuple().tm_year:
        y_mult = eDate.timetuple().tm_year - sDate.timetuple().tm_year
        redim_list = []
        for di in range(sDate.timetuple().tm_year, eDate.timetuple().tm_year + 1):
            if calendar.isleap(di) == False:
                redim_list.append(climo.drop([60], dim='dayofyear'))
            else:
                redim_list.append(climo)
        redim_climo = xr.concat(redim_list, dim='dayofyear')
        datetime_list = pd.date_range(start='1/1/' + str(sDate.timetuple().tm_year) , end='12/31/' + str(eDate.timetuple().tm_year))
        redim_climo = redim_climo.assign_coords(time=("time", datetime_list))
        redim_climo = redim_climo.reset_index(['dayofyear'], drop = True)
        for var in redim_climo.data_vars:
            redim_climo[var]= redim_climo[var].rename({'dayofyear': 'time'})

    if any(dataset2 in s for s in mean_dict.keys()):
        df2 = dsRefET[mean_dict[dataset2]]
        opLabel = "Avg Diff. (" + df2.units + ")"
        df2 = df2.sel(time=slice(sDate,eDate + timedelta(days=1)))
        df2 = df2.mean('time')
        cf = redim_climo[mean_dict[dataset2]]
        cf = cf.sel(time=slice(sDate,eDate + timedelta(days=1)))
        cf = cf.mean('time')
        df2 = df2 - cf
        tem_vmin = np.min(df2.values)
        tem_vmax = np.max(df2.values)
        if abs(tem_vmin) >= abs(tem_vmax):
            vmin = tem_vmin
            vmax = (-1)*tem_vmin
        if abs(tem_vmax) > abs(tem_vmin):
            vmax = tem_vmax
            vmin = (-1)*tem_vmax

    if any(dataset2 in s for s in sum_dict.keys()):
        df = dsRefET[sum_dict[dataset2]]
        opLabel = "Total Diff. (" + df2.units + ")"
        df = df.sel(time=slice(sDate,eDate + timedelta(days=1)))
        df = df.sum('time')
        cf = redim_climo[sum_dict[dataset2]]
        cf = cf.sel(time=slice(sDate,eDate + timedelta(days=1)))
        cf = cf.sum('time')
        df2 = df - cf
        tem_vmin = np.min(df2.values)
        tem_vmax = np.max(df2.values)
        if abs(tem_vmin) >= abs(tem_vmax):
            vmin = tem_vmin
            vmax = (-1)*tem_vmin
        if abs(tem_vmax) > abs(tem_vmin):
            vmax = tem_vmax
            vmin = (-1)*tem_vmax
    if dataset2 == 'NCEP Stage IV Precip':
        df = dsPrec
        opLabel = "Total Diff. (mm)"
        df = df.sel(time=slice(sDate,eDate + timedelta(days=1)))
        df = df.sum('time')
        cf = redim_climo['NCEPstageIVPrecip']
        cf = cf.sel(time=slice(sDate,eDate + timedelta(days=1)))
        cf = cf.sum('time')
        df2 = df - cf.values
        dwnldName = str("ncepStageIV.nc")
        tem_vmin = np.min(df2.Precipitation_Flux.values)
        tem_vmax = np.max(df2.Precipitation_Flux.values)
        if abs(tem_vmin) >= abs(tem_vmax):
            vmin = tem_vmin
            vmax = (-1)*tem_vmin
        if abs(tem_vmax) > abs(tem_vmin):
            vmax = tem_vmax
            vmin = (-1)*tem_vmax

    x = 'longitude'
    y = 'latitude'

     # create the Altair chart object
    chart2 = df2.hvplot.quadmesh(width=width, height=height, x=x, y=y, cmap=cmap_dict[cmap2],
            project=True, geo=True,title=quad_title,xlim=xlim,ylim=ylim,label=opLabel, clim=(vmin,vmax),
            rasterize=True, dynamic=False) * shp * shp1
    return chart2, df2

# create update plot window button
def update2(event):
    plotwindow2[1].object = make_plot2(dataset2.value, start_date.value, end_date.value,cmap2.value)[0] # clb_min, clb_max,

generate_button2 = pn.widgets.Button(name='Plot', button_type='primary')
generate_button2.on_click(update2)

############################################################
def download_cb2():
    bout = make_plot2(dataset2.value, start_date.value, end_date.value,cmap2.value)[1].to_netcdf()
    bio = BytesIO()
    bio.write(bout)
    bio.seek(0)
    return bio

fd2 = pn.widgets.FileDownload(
    callback=download_cb2,
    filename='Departure_AgWx.nc')

# set the widget box for the widgets to be placed into
sel_box2 = pn.WidgetBox(dataset2, start_date, end_date, cmap2, generate_button2,
                       pn.layout.Spacer(height=30),fd2)

title2       = '
CEMA Agricultural Departure Dashboard
'
instruction2 = '
Select a dataset, set your parameters, and click plot
'
oggm_logo   = ''
pn_logo     = ''
cema_logo = '/home/james/agriculture/shapefolder/cema2logo.png'

header2 = pn.Row(pn.panel(cema_logo, width=170),  pn.layout.Spacer(width=10),
                pn.Column(pn.Pane(title2, width=1000), pn.Pane(instruction2, width=1000)))

plotwindow2 = pn.Row(sel_box2, make_plot2(dataset2.value, start_date.value, end_date.value, cmap2.value)[0]) #clbmin clbmax

# In[300]:

# now add in another option for the line plots
years_list = list(range(2010, date.today().timetuple().tm_year + 1))
start_year = pn.widgets.Select(name='Start Date', options=years_list, value=years_list[-1])
end_year = pn.widgets.Select(name='Start Date', options=years_list, value=years_list[-1])

county_list = ['Chester County', 'New Castle County', 'Kent County', 'Sussex County']
county = pn.widgets.Select(name='County', options=county_list, value=county_list[1])
datasets3 = list(sum_dict.keys()) + list(mean_dict.keys()) + ['NCEP Stage IV Precip']
dataset3 = pn.widgets.Select(name='Variable', options=datasets3, value=datasets3[6])

def make_plot3(dataset3, start_year, end_year, county):              # clb_min, clb_max,
     # Load and format the data
    if start_year > end_year:
        start_year = end_year
    sDate = datetime(start_year,1, 1)
    eDate = datetime(end_year, 12, 31)
    quad_title = str(county + " " + str(dataset3) + " (" + str(start_year) + " to " +  str(end_year) + ") vs. Climatology")

    if county == 'Chester County':
        county_df = xr.open_dataset("http://basin.ceoe.udel.edu/thredds/dodsC/chester_agwx.nc")
        clim_df = xr.open_dataset("http://basin.ceoe.udel.edu/thredds/dodsC/chester_agwx_climatology.nc")
    if county == 'New Castle County':
        county_df = xr.open_dataset("http://basin.ceoe.udel.edu/thredds/dodsC/ncc_agwx.nc")
        clim_df = xr.open_dataset("http://basin.ceoe.udel.edu/thredds/dodsC/ncc_agwx_climatology.nc")
    if county == "Kent County":
        county_df = xr.open_dataset("http://basin.ceoe.udel.edu/thredds/dodsC/kentc_agwx.nc")
        clim_df = xr.open_dataset("http://basin.ceoe.udel.edu/thredds/dodsC/kent_agwx_climatology.nc")
    if county == "Sussex County":
        county_df = xr.open_dataset("http://basin.ceoe.udel.edu/thredds/dodsC/sussex_agwx.nc")
        clim_df = xr.open_dataset("http://basin.ceoe.udel.edu/thredds/dodsC/sussex_agwx_climatology.nc")

    # special clauses for climatology file which has a dayofyear index instead of time
    if sDate.timetuple().tm_year == eDate.timetuple().tm_year:
        if calendar.isleap(sDate.timetuple().tm_year) == False:
            clim_df = clim_df.drop([60], dim='dayofyear')
        else:
            clim_df = clim_df
        datetime_list = pd.date_range(start='1/1/' + str(sDate.timetuple().tm_year) , end='12/31/' + str(sDate.timetuple().tm_year))
        clim_df = clim_df.assign_coords(time=("time", datetime_list))
        clim_df = clim_df.reset_index(['dayofyear'], drop = True)
        for var in clim_df.data_vars:
            clim_df[var]= clim_df[var].rename({'dayofyear': 'time'})
    if sDate.timetuple().tm_year < eDate.timetuple().tm_year:
        y_mult = eDate.timetuple().tm_year - sDate.timetuple().tm_year
        redim_list = []
        for di in range(sDate.timetuple().tm_year, eDate.timetuple().tm_year + 1):
            if calendar.isleap(di) == False:
                redim_list.append(clim_df.drop([60], dim='dayofyear'))
            else:
                redim_list.append(clim_df)
        clim_df = xr.concat(redim_list, dim='dayofyear')
        datetime_list = pd.date_range(start='1/1/' + str(sDate.timetuple().tm_year) , end='12/31/' + str(eDate.timetuple().tm_year))
        clim_df = clim_df.assign_coords(time=("time", datetime_list))
        clim_df = clim_df.reset_index(['dayofyear'], drop = True)
        for var in clim_df.data_vars:
            clim_df[var]= clim_df[var].rename({'dayofyear': 'time'})

    if any(dataset3 in s for s in mean_dict.keys()):
        df3 = county_df[mean_dict[dataset3]]
        yval = mean_dict[dataset3]
        regLabel = "Avg (" + df3.units + ")"
        df3 = df3.sel(time=slice(sDate,eDate))
        cf3 = clim_df[mean_dict[dataset3]]
        cf3 = cf3.sel(time=slice(sDate,eDate))
        climLabel = "Climatology"
        # create the Altair chart object
        chart3 = cf3.hvplot(height=800, width=1200, x="time", y=yval, label = climLabel, grid=True, title=quad_title, legend = 'left', line_width=3, color='midnightblue')  * df3.hvplot(x="time",
                        y=yval, label = regLabel, legend = 'left', line_width=3, color='seagreen')

    if any(dataset3 in s for s in sum_dict.keys()):
        sDate = datetime(start_year,1, 1)
        eDate = datetime(end_year, 12, 31)
        df3 = county_df[sum_dict[dataset3]]
        yval = sum_dict[dataset3]
        regLabel = "Observed - Cum. Sum (" + df3.units + ")"
        df3 = df3.sel(time=slice(sDate,eDate))
        ddf3 = df3.sel(time=slice(sDate,eDate))
        ddf3 = ddf3.cumsum(skipna=True)
        cf3 = clim_df[sum_dict[dataset3]]
        cf3 = cf3.sel(time=slice(sDate,eDate))
        ccf3 = cf3.sel(time=slice(sDate,eDate))
        ccf3.values = ccf3.values.cumsum()
        climLabel = "Climatology - Cum. Sum (mm)"
        chart3 = ccf3.hvplot(height=800, width=1200, x="time", y=yval, label = climLabel, title=quad_title, grid=True,legend = 'left', line_width=3, color='midnightblue')  * ddf3.hvplot(x="time",
                        y=yval, label = regLabel, legend = 'left', line_width=3, color='seagreen') * cf3.hvplot(x="time", y=yval, label= "Climatology (mm/day)", line_width=2, color='midnightblue') * df3.hvplot(x="time",
                        y=yval, label = "Obsereved (mm/day)", line_width=2, color='seagreen')

    if dataset3 == 'NCEP Stage IV Precip':
        df3 = county_df['NCEPstageIVPrecip']
        yval = 'NCEPstageIVPrecip'
        regLabel = "Observed - Cum. Sum (mm)"
        df3 = df3.sel(time=slice(sDate,eDate))
        ddf3 = df3.sel(time=slice(sDate,eDate))
        ddf3.values = ddf3.values.cumsum()
        cf3 = clim_df['NCEPstageIVPrecip']
        cf3 = cf3.sel(time=slice(sDate,eDate))
        ccf3 = cf3.sel(time=slice(sDate,eDate))
        ccf3.values = ccf3.values.cumsum()
        climLabel = "Climatology - Cum. Sum (mm)"
        chart3 = ccf3.hvplot(height=800, width=1200, x="time", y=yval, label = climLabel, title=quad_title, grid=True,legend = 'left', line_width=3, color='midnightblue')  * ddf3.hvplot(x="time",
                        y=yval, label = regLabel, legend = 'left', line_width=3, color='seagreen') * cf3.hvplot(x="time", y=yval, label= "Climatology (mm/day)", line_width=2, color='midnightblue') * df3.hvplot(x="time",
                        y=yval, label = "Obsereved (mm/day)", line_width=2, color='seagreen')

    return chart3, df3

##### where i left off

# need to update labels to add county, and then adjust the stuff below
# create update plot window button
def update3(event):
    plotwindow3[1].object = make_plot3(dataset3.value, start_year.value, end_year.value, county.value)[0] # clb_min, clb_max,

generate_button3 = pn.widgets.Button(name='Plot', button_type='primary')
generate_button3.on_click(update3)

############################################################
def download_cb3():
    bout = make_plot3(dataset3.value, start_year.value, end_year.value, county.value)[1].to_netcdf()
    bio = BytesIO()
    bio.write(bout)
    bio.seek(0)
    return bio

fd3 = pn.widgets.FileDownload(
    callback=download_cb3,
    filename='County_AgWx.nc')

# set the widget box for the widgets to be placed into
sel_box3 = pn.WidgetBox(dataset3, start_year, end_year, county, generate_button3,
                       pn.layout.Spacer(height=30),fd3)

title3       = '
CEMA County Averaged Agriculture Dashboard
'
instruction3 = '
Select a dataset, set your parameters, and click plot
'
oggm_logo   = ''
pn_logo     = ''
cema_logo = '/home/james/agriculture/shapefolder/cema2logo.png'
header3 = pn.Row(pn.panel(cema_logo, width=170),  pn.layout.Spacer(width=10),
                pn.Column(pn.Pane(title3, width=1000), pn.Pane(instruction3, width=1000)))

plotwindow3 = pn.Row(sel_box3, make_plot3(dataset3.value, start_year.value, end_year.value, county.value)[0]) #clbmin clbmax

# In[301]:

dashboard = pn.Column(header,pn.layout.Spacer(height=50), plotwindow1, pn.layout.Spacer(height=50),header2,
                      pn.layout.Spacer(height=50), plotwindow2,
                      pn.layout.Spacer(height=50),header3, pn.layout.Spacer(height=50), plotwindow3)
dashboard.servable()


from json import loads
from os import getenv
from urllib import request
from datetime import datetime, timedelta
from time import strftime

from discord import Embed
from discord.ext import commands

class Weather(commands.Cog):

    def __init__(self, bot):
        self.bot = bot
        self.api_calls = 0
        self.last_update = datetime.now() - timedelta(minutes=10)

    def generateQuery(self):
        # define query parameters
        api_url = 'https://api.darksky.net/forecast/'
        key = getenv('DARK_SKY_SECRET')
        latitude = '28.4196'
        longitude = '-81.2937'
        exclude = 'minutely,hourly,daily,flags'
        language = 'en'

        # generate query string
        query = f'{api_url}{key}/{latitude},{longitude}?exclude={exclude}&lang={language}'

        return query

    def rateLimit(self):
        delta = datetime.now() - self.last_update
        interval = timedelta(minutes=10)

        if self.api_calls >= 200:
            return False
        if delta < interval:
            return False

        return True

    def getResponse(self, update: bool, query: str):
        response = request.urlopen(query)
        self.api_calls = int(response.getheader('X-Forecast-API-Calls'))
        return response

    def parseResponse(self, response):
        json_data = loads(response.read())

        # f = open('logs.json', 'w')
        # print(json_data, file=f)
        # f.close()

        self.last_update = datetime.fromtimestamp(json_data['currently']['time'])

        properties = ['summary', 'icon', 'apparentTemperature', 'temperature', 'precipProbability']
        data = []

        for x in properties:
            data.append(json_data['currently'][x])

        data.append(self.last_update.strftime(r'%b %d, %I:%M %p'))

        return data

    def colorizer(self, icon: str):
        switch = {
            'clear-day': 0xFFD600,
            'clear-night': 0x90A4AE,
            'rain': 0x2962FF,
            'snow': 0xFFFFFF,
            'sleet': 0xB3E5FC,
            'wind': 0xBDBDBD,
            'fog': 0x9E9E9E,
            'cloudy': 0x757575,
            'partly-cloudy-day': 0xb29400,
            'partly-cloudy-night': 0x647e8b,
            'hail': 0x00B0FF,
            'thunderstorm': 0xFF8F00,
            'tornado': 0x42424
        }

        return switch.get(icon, 0x2ecc71)

    def generateEmbed(self, data):
        summary = data[0]
        color = self.colorizer(data[1])

        weather_embed = Embed(title='Orlando', description=summary, color=color)
        # weather_embed.set_thumbnail(url="...")

        names = ['Feels Like', 'Actual', 'Precipitation Probability']
        fields = list(zip(names, data[2:-1]))

        for n, v in fields:
            weather_embed.add_field(name=n, value=round(v))

        weather_embed.set_footer(text=f"Powered by Dark Sky ◉ Updated: {data[-1]}")

        return weather_embed

    @commands.command()
    async def weather(self, ctx):
        query = self.generateQuery()
        update = self.rateLimit()

        if update:
            response = self.getResponse(update, query)
            self.cache = self.parseResponse(response)

        weather_embed = self.generateEmbed(self.cache)

        await ctx.send(embed=weather_embed)


import json
import requests
from requests.api import get, head

def get_octopus_resource(uri, headers, skip_count = 0):
    items = []
    skip_querystring = ""

    if '?' in uri:
        skip_querystring = '&skip='
    else:
        skip_querystring = '?skip='

    response = requests.get((uri + skip_querystring + str(skip_count)), headers=headers)
    response.raise_for_status()

    # Get results of API call
    results = json.loads(response.content.decode('utf-8'))

    # Store results
    if 'Items' in results.keys():
        items += results['Items']

        # Check to see if there are more results
        if (len(results['Items']) > 0) and (len(results['Items']) == results['ItemsPerPage']):
            skip_count += results['ItemsPerPage']
            items += get_octopus_resource(uri, headers, skip_count)

    else:
        return results

    # return results
    return items

# Define Octopus server variables
octopus_server_uri = 'https://YourURL/api'
octopus_api_key = 'API-YourAPIKey'
headers = {'X-Octopus-ApiKey': octopus_api_key}
project_name = "MyProject"
environment_name_list = ['Environment', 'List']
tenant_tag = 'TENANT TAG TO FILTER ON'  #Format = [Tenant Tag Set Name]/[Tenant Tag] "Tenant Type/Customer"
max_number_tenants = 1
tenants_updated = 0
space_name = 'Default'
what_if = False

# Get space
uri = '{0}/spaces'.format(octopus_server_uri)
spaces = get_octopus_resource(uri, headers)
space = next((x for x in spaces if x['Name'] == space_name), None)

# Get project
uri = '{0}/{1}/projects'.format(octopus_server_uri, space['Id'])
projects = get_octopus_resource(uri, headers)
project = next((x for x in projects if x['Name'] == project_name), None)

# Get environments
environments = []
uri = '{0}/{1}/environments'.format(octopus_server_uri, space['Id'])
all_environments = get_octopus_resource(uri, headers)
for environment_name in environment_name_list:
    environment = next((x for x in all_environments if x['Name'] == environment_name), None)
    environments.append(environment['Id'])

# Get tenants by tag
uri = '{0}/{1}/tenants?tags={2}'.format(octopus_server_uri, space['Id'], tenant_tag)
tenants = get_octopus_resource(uri, headers)

# Loop through tenants
for tenant in tenants:
    tenant_updated = False

    if tenant['ProjectEnvironments'] == None or len(tenant['ProjectEnvironments']) == 0:
        projectEnvironments = {
            project['Id']: environments
        }

        # Attach to tenant
        tenant['ProjectEnvironments'] = projectEnvironments
        tenant_updated = True
    else:
        # Get current project environments
        projectEnvironments = tenant['ProjectEnvironments']

        # Loop through environments
        for environment in environments:
            #print (projectEnvironments[project['Id']])
            if environment not in projectEnvironments[project['Id']]:
                projectEnvironments[project['Id']].append(environment)

        tenant['ProjectEnvironments'] = projectEnvironments
        tenant_updated = True

    if tenant_updated:
        if what_if:
            print(tenant)
        else:
            uri = '{0}/{1}/tenants/{2}'.format(octopus_server_uri, space['Id'], tenant['Id'])
            response = requests.put(uri, headers=headers, json=tenant)
            response.raise_for_status

        tenants_updated = tenants_updated + 1

    if tenants_updated == max_number_tenants:
        break


"""Initializer helpers for HomematicIP fake server."""
from asynctest import MagicMock, Mock, patch
from homematicip.aio.auth import AsyncAuth
from homematicip.aio.connection import AsyncConnection
from homematicip.aio.home import AsyncHome
import pytest

from homeassistant import config_entries
from homeassistant.components.homematicip_cloud import (
    DOMAIN as HMIPC_DOMAIN,
    async_setup as hmip_async_setup,
    const as hmipc,
    hap as hmip_hap,
)
from homeassistant.helpers.typing import ConfigType, HomeAssistantType

from .helper import AUTH_TOKEN, HAPID, HAPPIN, HomeTemplate

from tests.common import MockConfigEntry, mock_coro

@pytest.fixture(name="mock_connection")
def mock_connection_fixture() -> AsyncConnection:
    """Return a mocked connection."""
    connection = MagicMock(spec=AsyncConnection)

    def _rest_call_side_effect(path, body=None):
        return path, body

    connection._restCall.side_effect = (  # pylint: disable=protected-access
        _rest_call_side_effect
    )
    connection.api_call.return_value = mock_coro(True)
    connection.init.side_effect = mock_coro(True)

    return connection

@pytest.fixture(name="hmip_config_entry")
def hmip_config_entry_fixture() -> config_entries.ConfigEntry:
    """Create a mock config entriy for homematic ip cloud."""
    entry_data = {
        hmipc.HMIPC_HAPID: HAPID,
        hmipc.HMIPC_AUTHTOKEN: AUTH_TOKEN,
        hmipc.HMIPC_NAME: "",
        hmipc.HMIPC_PIN: HAPPIN,
    }
    config_entry = MockConfigEntry(
        version=1,
        domain=HMIPC_DOMAIN,
        title=HAPID,
        data=entry_data,
        source="import",
        connection_class=config_entries.CONN_CLASS_CLOUD_PUSH,
        system_options={"disable_new_entities": False},
    )

    return config_entry

@pytest.fixture(name="default_mock_home")
def default_mock_home_fixture(mock_connection) -> AsyncHome:
    """Create a fake homematic async home."""
    return HomeTemplate(connection=mock_connection).init_home().get_async_home_mock()

@pytest.fixture(name="default_mock_hap")
async def default_mock_hap_fixture(
    hass: HomeAssistantType, mock_connection, hmip_config_entry
) -> hmip_hap.HomematicipHAP:
    """Create a mocked homematic access point."""
    return await get_mock_hap(hass, mock_connection, hmip_config_entry)

async def get_mock_hap(
    hass: HomeAssistantType,
    mock_connection,
    hmip_config_entry: config_entries.ConfigEntry,
) -> hmip_hap.HomematicipHAP:
    """Create a mocked homematic access point."""
    hass.config.components.add(HMIPC_DOMAIN)
    hap = hmip_hap.HomematicipHAP(hass, hmip_config_entry)
    home_name = hmip_config_entry.data["name"]
    mock_home = (
        HomeTemplate(connection=mock_connection, home_name=home_name)
        .init_home()
        .get_async_home_mock()
    )
    with patch.object(hap, "get_hap", return_value=mock_coro(mock_home)):
        assert await hap.async_setup()
    mock_home.on_update(hap.async_update)
    mock_home.on_create(hap.async_create_entity)

    hass.data[HMIPC_DOMAIN] = {HAPID: hap}

    await hass.async_block_till_done()

    return hap

@pytest.fixture(name="hmip_config")
def hmip_config_fixture() -> ConfigType:
    """Create a config for homematic ip cloud."""

    entry_data = {
        hmipc.HMIPC_HAPID: HAPID,
        hmipc.HMIPC_AUTHTOKEN: AUTH_TOKEN,
        hmipc.HMIPC_NAME: "",
        hmipc.HMIPC_PIN: HAPPIN,
    }

    return {HMIPC_DOMAIN: [entry_data]}

@pytest.fixture(name="dummy_config")
def dummy_config_fixture() -> ConfigType:
    """Create a dummy config."""
    return {"blabla": None}

@pytest.fixture(name="mock_hap_with_service")
async def mock_hap_with_service_fixture(
    hass: HomeAssistantType, default_mock_hap, dummy_config
) -> hmip_hap.HomematicipHAP:
    """Create a fake homematic access point with hass services."""
    await hmip_async_setup(hass, dummy_config)
    await hass.async_block_till_done()
    hass.data[HMIPC_DOMAIN] = {HAPID: default_mock_hap}
    return default_mock_hap

@pytest.fixture(name="simple_mock_home")
def simple_mock_home_fixture() -> AsyncHome:
    """Return a simple AsyncHome Mock."""
    return Mock(
        spec=AsyncHome,
        devices=[],
        groups=[],
        location=Mock(),
        weather=Mock(create=True),
        id=42,
        dutyCycle=88,
        connected=True,
    )

@pytest.fixture(name="simple_mock_auth")
def simple_mock_auth_fixture() -> AsyncAuth:
    """Return a simple AsyncAuth Mock."""
    return Mock(spec=AsyncAuth, pin=HAPPIN, create=True)


from typing import Sequence

from tacotron2.callbacks.reduce_lr_on_plateau_callback import ReduceLROnPlateauCallback

def test_reduce_on_plateau_callback():
    class Learner:
        class _Optimizer:
            param_groups = [{'lr': 1}]

        def __init__(self, strategy: Sequence[float]):
            self.valid_dl = None
            self.strategy = strategy
            self.optimizer = self._Optimizer()
            self.cur_step = -1

        def eval(self, valid_dl):
            self.cur_step += 1
            return self.strategy[self.cur_step]

    learner = Learner(strategy=[9, 8, 7, 6, 5, 6, 4, 3, 4, 5, 3, 2, 3, 3])
    clb = ReduceLROnPlateauCallback(patience=2, reduce_factor=0.5)

    for _ in learner.strategy:
        clb.on_epoch_end(learner=learner)
        print(learner.optimizer.param_groups)

test_reduce_on_plateau_callback()


import cv2
import numpy as np
import video
from serial import Serial
import time

ser = Serial('/dev/ttyUSB0', baudrate=9600, timeout=1) # подключаемся к Arduino

edge = 35 # рамки условного центра изо6ражения
panAngle = 90 # начальное положение сервопривода 1
tiltAngle = 90 # начальное положение сервопривода 2

def ObjectPosition (x, y): # функция вывода в консоль координат центра цели
    print ("[INFO] Object Center coordenates at X0 = {0} and Y0 =  {1}".format(x, y))

def ServoPosition (x, y): # функция управления сервоприводами
    global panAngle
    global tiltAngle
    if (x < width/2 - edge):
        panAngle += 1
        if panAngle > 170:
            panAngle = 170
        ser.write(bytearray([85, panAngle,180 - tiltAngle]))

    if (x > width/2 + edge):
        panAngle -= 1
        if panAngle < 10:
            panAngle = 10
        ser.write(bytearray([85, panAngle,180 - tiltAngle]))

    if (y < height/2 - edge):
        tiltAngle += 1
        if tiltAngle > 170:
            tiltAngle = 170
        ser.write(bytearray([85, panAngle, 180 - tiltAngle]))

    if (y > height/2 + edge):
        tiltAngle -= 1
        if tiltAngle < 10:
            tiltAngle = 10
        ser.write(bytearray([85, panAngle, 180 - tiltAngle]))

cv2.namedWindow( "result" )

cap = video.create_capture(0) # ловим камеру

hsv_min = np.array((59, 49, 56), np.uint8) # нижняя граница зеленого в HSV
hsv_max = np.array((95, 255, 255), np.uint8) # верxняя граница зеленого в HSV

while True:

    flag, img = cap.read() # считываем изо6ражение
    height, width = img.shape[:2] # размеры изо6ражения
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV ) # прео6разуем RGB к HSV
    # накладываем маску и применяем ее
    thresh = cv2.inRange(hsv, hsv_min, hsv_max)
    thresh = cv2.erode(thresh, None, iterations=2)
    thresh = cv2.dilate(thresh, None, iterations=2)
    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
		cv2.CHAIN_APPROX_SIMPLE)[-2]
    center = None

    if len(cnts) > 0:
		# поиск наи6ольIIIего контура
        c = max(cnts, key = cv2.contourArea)
        ((x, y), radius) = cv2.minEnclosingCircle(c)
        M = cv2.moments(c)
        center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))

        if radius > 50:
			# рисуем окружность в центре фигуры
            cv2.circle(img, (int(x), int(y)), int(radius),
				(0, 255, 255), 2)
            cv2.circle(img, center, 5, (0, 0, 255), -1)

            ObjectPosition(int(x), int(y))
            ServoPosition (int(x), int(y))

    cv2.imshow('result', img)

    ch = cv2.waitKey(5) # выxод через escp
    if ch == 27:
        break
    time.sleep(0.02) # временная задержка, что6ы из6авиться от осцилляции

cap.release()
cv2.destroyAllWindows()


# check if grank or other scripts work correctly
# 运行本程序进行测试 请直接 python check.py 没有太大必要用 shell_plus
from bs4 import BeautifulSoup
import os, re

current_path = os.path.dirname(os.path.realpath(__file__))
raw_html = os.path.join(current_path, 'check.html')
with open(raw_html) as f:
    raw_html = f.read()
soup = BeautifulSoup(raw_html, 'html.parser')

star_review_pattr = re.compile(r'[\n\r]+')
star_pattr = re.compile(r'([\d\.]+).*([\d\.]+)')
review_pattr = re.compile(r'(\d+)')
remove_script_tag = re.compile(r'<script.*?</script>')

def getStarAndReview(soup):
    soup = soup.find(id="averageCustomerReviews")
    while soup.script:
        soup.script.decompose()
    try:
        _star, _review = star_review_pattr.split(soup.text.strip())
    except ValueError: # not enough values to unpack (expected 2, got 1)
        return (0, 0) # 没有评分 或者没有抓到
    _pure_star = star_pattr.findall(_star)[0]
    star = min(_pure_star, key=lambda x:float(x))
    review = review_pattr.findall(_review)[0]
    return (float(star), int(review))

getStarAndReview(soup)


#!/usr/bin/env python3
# 病歷查詢 2014.09.22
#coding: utf-8

from PyQt5 import QtWidgets, QtGui
from PyQt5.QtCore import QSettings, QSize, QPoint

from classes import table_widget
from libs import ui_utils
from libs import system_utils
from libs import string_utils
from libs import db_utils

# 動態輸入主訴
class DialogDiagnosticPicker(QtWidgets.QDialog):
    # 初始化
    def __init__(self, parent=None, *args):
        super(DialogDiagnosticPicker, self).__init__(parent)
        self.parent = parent
        self.database = args[0]
        self.system_settings = args[1]
        self.text_edit = args[2]
        self.clinic_type = args[3]
        self.input_code = args[4]

        self.settings = QSettings('__settings.ini', QSettings.IniFormat)
        self.ui = None
        self.clinic_name = None

        self._set_ui()
        self._set_signal()
        self._read_data()

    # 解構
    def __del__(self):
        self.close_all()

    # 關閉
    def close_all(self):
        pass

    def closeEvent(self, a0: QtGui.QCloseEvent):
        self.settings.setValue("dialog_diagnostic_picker_size", self.size())
        self.settings.setValue("dialog_diagnostic_picker_pos", self.pos())

    # 設定GUI
    def _set_ui(self):
        self.ui = ui_utils.load_ui_file(ui_utils.UI_DIALOG_DIAGNOSTIC_PICKER, self)
        self.setFixedSize(self.size())  # non resizable dialog
        system_utils.set_css(self, self.system_settings)

        self.ui.resize(self.settings.value("dialog_diagnostic_picker_size", QSize(861, 479)))
        self.ui.move(self.settings.value("dialog_diagnostic_picker_pos", QPoint(846, 215)))

        self.ui.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText('確定')
        self.ui.buttonBox.button(QtWidgets.QDialogButtonBox.Cancel).setText('取消')
        self.table_widget_diagnostic = table_widget.TableWidget(
            self.ui.tableWidget_diagnostic, self.database)
        self._set_table_width()

    def _set_table_width(self):
        width = [200, 200, 200, 200, 200]
        self.table_widget_diagnostic.set_table_heading_width(width)

    # 設定信號
    def _set_signal(self):
        self.ui.buttonBox.accepted.connect(self.accepted_button_clicked)
        self.ui.tableWidget_diagnostic.clicked.connect(self._table_item_clicked)
        self.ui.tableWidget_diagnostic.doubleClicked.connect(self.accepted_button_clicked)

    def _table_item_clicked(self):
        # database.ui.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).animateClick()
        clinic_name = self._get_clinic_name() + ' '
        self.parent.insert_text(self.text_edit, clinic_name, self.input_code)
        self.input_code = ''

        self._update_diagnosis_hit_rate()

    def accepted_button_clicked(self):
        self.clinic_name = self._get_clinic_name()
        self._update_diagnosis_hit_rate()

        self.close()

    def _update_diagnosis_hit_rate(self):
        clinic_key = self._get_clinic_key()
        if clinic_key is not None:
            db_utils.increment_hit_rate(self.database, 'clinic', 'ClinicKey', clinic_key)

    def _get_clinic_name(self):
        item = self.ui.tableWidget_diagnostic.item(
            self.ui.tableWidget_diagnostic.currentRow(),
            self.ui.tableWidget_diagnostic.currentColumn(),
        )

        if item is not None:
            clinic_name = item.text()
        else:
            clinic_name = ''

        return clinic_name

    def _read_data(self):
        # database.table_widget_diagnostic.set_db_data(database.sql, database._set_wait_data)
        order_type = '''
            ORDER BY LENGTH(ClinicName), CAST(CONVERT(`ClinicName` using big5) AS BINARY)
        '''
        if self.system_settings.field('診察詞庫排序') == '點擊率':
            order_type = 'ORDER BY HitRate DESC'

        sql = '''
            SELECT * FROM clinic
            WHERE
                ClinicType = "{clinic_type}" AND
                InputCode LIKE "{input_code}%"
            GROUP BY ClinicName
            {order_type}
        '''.format(
            clinic_type=self.clinic_type,
            input_code=self.input_code,
            order_type=order_type,
        )
        self.rows = self.database.select_record(sql)
        self.table_widget_diagnostic.set_db_data_without_heading(sql, 'ClinicName')

    def _set_table_data(self, row_no, row):
        diagnostic_row = [
            string_utils.xstr(row['ClinicKey']),
            string_utils.xstr(row['ClinicName']),
        ]

        for column in range(len(diagnostic_row)):
            self.ui.tableWidget_diagnostic.setItem(
                row_no, column,
                QtWidgets.QTableWidgetItem(diagnostic_row[column])
            )

    def _get_clinic_key(self):
        row_no = self.ui.tableWidget_diagnostic.currentRow()
        col_no = self.ui.tableWidget_diagnostic.currentColumn()
        column_count = self.ui.tableWidget_diagnostic.columnCount()

        index = (row_no * column_count) + col_no
        if index >= len(self.rows):
            return None

        return self.rows[index]['ClinicKey']


"""converginblockchain URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.conf.urls import url,include

from .views import home,trnxmngrloginaction,trnxviewtransaction,trnxviewpurchase
from .views import patient,patientregister,doctor,doctorregistration,adminlogin,txmanagerlogin,adminloginaction
from .views import viewadminpatientspage,viewadmindoctorspage,viewadmintransactionspage,logout,activatepatients,activatedoctors
from patients.urls import urlpatterns as patients_urlpattern
from patients.views import patientlogincheck,patientsendsymptoms,patientsymtomsanalysis,patientsymptomsview,checkandpay,transactionmanagement,patientpurchaseblkmodel
from doctors.views import doctorlogincheck,doctoranalyzesysmptoms,DoctorsSendPriscription,DoctorPriscription,purchaseviewbydoctor,doctorviewtransaction

urlpatterns = [
    #url(r'^patients/',include((patients_urlpattern,'patients'),namespace='patients'),name='patients'),
    url(r'^admin/', admin.site.urls),
    url(r'^$',home, name="home"),
    url(r'^patient/', patient, name="patient"),
    url(r'^home/',home, name="home"),
    url(r'^patientregister/',patientregister, name="patientregister"),
    url(r'^doctor/',doctor, name="doctor"),
    url(r'^doctorregistration/',doctorregistration, name="doctorregistration"),
    url(r'^adminlogin/',adminlogin, name="adminlogin"),
    url(r'^txmanagerlogin/',txmanagerlogin, name="txmanagerlogin"),
    url(r'^adminloginaction/',adminloginaction,name="adminloginaction"),
    url(r'^viewadminpatientspage/',viewadminpatientspage,name="viewadminpatientspage"),
    url(r'^viewadmindoctorspage/',viewadmindoctorspage,name="viewadmindoctorspage"),
    url(r'^viewadmintransactionspage/',viewadmintransactionspage,name="viewadmintransactionspage"),
    url(r'^logout/',logout,name="logout"),
    #url(r'^activatepatients/',activatepatients,name="activatepatients")
    url(r'^activatepatients/$', activatepatients, name="activatepatients"),
    url(r'^activatedoctors/$', activatedoctors, name="activatedoctors"),
    url(r'^trnxmngrloginaction/',trnxmngrloginaction,name="trnxmngrloginaction"),
    url(r'^trnxviewtransaction/',trnxviewtransaction,name="trnxviewtransaction"),
    url(r'^trnxviewpurchase/',trnxviewpurchase,name="trnxviewpurchase"),

    url(r'^patientlogincheck/', patientlogincheck, name="patientlogincheck"),
    url(r'^patientsendsymptoms/', patientsendsymptoms, name="patientsendsymptoms"),
    url(r'^patientsymtomsanalysis/', patientsymtomsanalysis, name="patientsymtomsanalysis"),
    url(r'^patientsymptomsview/',patientsymptomsview, name="patientsymptomsview"),
    url(r'^checkandpay/',checkandpay, name="checkandpay"),
    url(r'^transactionmanagement/', transactionmanagement, name="transactionmanagement"),
    url(r'patientpurchaseblkmodel/',patientpurchaseblkmodel, name="patientpurchaseblkmodel"),

    url(r'^doctorlogincheck/',doctorlogincheck, name="doctorlogincheck"),
    url(r'^doctoranalyzesysmptoms/',doctoranalyzesysmptoms,name="doctoranalyzesysmptoms"),
    url(r'^DoctorsSendPriscription/',DoctorsSendPriscription, name="DoctorsSendPriscription"),
    url(r'^DoctorPriscription/',DoctorPriscription,name="DoctorPriscription"),
    url(r'^purchaseviewbydoctor/',purchaseviewbydoctor,name="purchaseviewbydoctor"),
    url(r'^doctorviewtransaction/',doctorviewtransaction,name="doctorviewtransaction"),

]


import pygame
# 将图像当作rect（矩形来处理）
from pygame.sprite import Sprite

class Ship(Sprite):
    """管理飞船的类"""
    def __init__(self, ai_game):    # ai_game就是ai
        """初始化飞船并设置其初始位置"""
        super().__init__()
        self.screen = ai_game.screen    # 获取主程序中的屏幕大小（ai.screen）
        self.settings = ai_game.settings
        # get_rect()是一个处理矩形图像的方法，返回值包含矩形的居中属性，用点表示面location1
        self.screen_rect = ai_game.screen.get_rect() # 获取主程序中屏幕的中心位置即x轴，目的是保证飞船中心和屏幕中心一致

        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')   # 上传飞船图片
        self.rect = self.image.get_rect()   # 获取飞船的中心位置即x,y轴，用点表示面location2
        # location1 要和location2 一致 飞船屏幕中心左右一致
        # 对于每艘新飞船，都将其放在屏幕底部的中央
        self.rect.midbottom = self.screen_rect.midbottom    # 把屏幕底部位置给飞船

        # 在飞船的属性x中存储小数值
        self.x = float(self.rect.x)
        self.y = float(self.rect.y)

        self.moving_right = False
        self.moving_left = False
        self.moving_up = False
        self.moving_down = False
    # 实现持续移动
    def update_move(self):
        """根据移动标志持续调整飞船位置向右"""
        # 更新飞船而不是rect对象的x值
        if self.moving_right and self.rect.right < self.screen_rect.right:  # 限定飞船左右移动范围
            self.x += self.settings.ship_speed
        """根据移动标志持续调整飞船位置向左"""
        if self.moving_left and self.rect.left > self.screen_rect.left:
            self.x -= self.settings.ship_speed
        """根据移动标志持续调整飞船位置向上"""
        if self.moving_up and self.rect.top > self.screen_rect.top:
            self.y -= self.settings.ship_speed
        """根据移动标志持续调整飞船位置向下"""
        if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
            self.y += self.settings.ship_speed
        self.rect.x = self.x
        self.rect.y = self.y

    def blitme(self):
        """在指定位置绘制飞船"""
        # 使用图片，位置来绘制飞船self.rect矩形对象，self.rect.midbottom 矩形位置
        self.screen.blit(self.image, self.rect)

    def center_ship(self):
        """让飞船处于屏幕低端中央"""
        self.rect.midbottom = self.screen_rect.midbottom
        # 更新了飞船的位置 需要重新指定 飞船x轴y轴 为了其可以继续进行 满足小数位 移动
        self.x = float(self.rect.x)
        # 重置用于跟踪飞船确切位置的 x，y
        self.y = float(self.rect.y)


# -*- coding: utf-8 -*-

from numpy import *
import numpy as np
import math
import modules.var as v

def area_elem():

    # calculate area of elements and relate nodes to element numbers
    # calculate area of each element
    # loop for elements
    x = np.zeros(3, dtype=np.float64)
    y = np.zeros(3, dtype=np.float64)
    v.totalArea = 0.0

    for l in range(v.nElem):
        # calculate area of element
        x[0] = v.xCo[v.inn[l][0]]
        y[0] = v.yCo[v.inn[l][0]]
        x[1] = v.xCo[v.inn[l][1]]
        y[1] = v.yCo[v.inn[l][1]]
        x[2] = v.xCo[v.inn[l][2]]
        y[2] = v.yCo[v.inn[l][2]]
        v.area[l] = ((y[0]+y[1])*(x[0]-x[1])+(y[1]+y[2])*(x[1]-x[2])+(y[2]+y[0])*(x[2]-x[0]))*0.5
        v.area[l] = abs(v.area[l])
        v.totalArea = v.totalArea+v.area[l]

def isostasy():
    # calculates isostatic compensation after sediment load
    # AIRY model see example given by Eisbacher 1991, p. 219

    subiso = np.zeros(v.nNode, dtype=np.float64)
    for node in range(v.nNode):
        dummy = 0.0
        dumdens = 0.0
        dumpor = 0.0
    # dummy is the total amount deposited in jti time step
    # calculate sediment density
    # average density of all sedim. types
        for m in range(v.nTotalMat):
            for t in range(v.jti):
                dummy = dummy+v.grid[node][t][m]
                dumdens = dumdens+v.grid[node][t][m]*v.density[m]
                dumpor = dumpor+v.grid[node][t][m]*v.porosity[node][t][m]

        if dummy > 0.00005:
            dumdens = dumdens/dummy
            dumpor = dumpor/dummy
        # average with water
            densitySed = dumpor*v.density[v.nTotalMat+1]+(1-dumpor)*dumdens
            para = (v.density[v.nTotalMat+1]-v.density[v.nTotalMat])/(v.density[v.nTotalMat+1]-densitySed)
            subiso[node] = dummy-(dummy/para)
            newBasem = v.readBasem[node]-subiso[node]-v.subsidence[node]*v.totalTime/1000
            diffe = v.basem[node]-newBasem
            v.basem[node] = newBasem
            v.surface[node] = v.readBasem[node]+dummy
            for t in range(v.jti-1):
                v.surf[t][node] = v.surf[t][node]-diffe

def subsidence():

    # calculate subsidence for a defined timestep dt
    # subsidence comes in m/1000 y
    # downward movement is positive
    # upward movement is negative

    temps = v.dt/1000.0
    for node in range(v.nNode):
        for j in range(v.jti-1):
            v.surf[j][node] = v.surf[j][node]-(v.subsidence[node]*temps)

    v.surface = v.surface-v.subsidence*temps
    v.basem = v.basem-v.subsidence*temps

def slope(te):
    punt = np.zeros((3, 3), dtype=np.float64)
    u = np.zeros(3, dtype=np.float64)
    b = np.zeros(3, dtype=np.float64)
    n = np.zeros((v.nElem, 3), dtype=np.float64)
    medNormal = np.zeros(3, dtype=np.float64)
    # this calculates the slope of each element and node
    for i in range(v.nElem):
        # coordenates x,y,z of punt 1
        punt[0][0] = v.xCo[v.inn[i][0]]
        punt[0][1] = v.yCo[v.inn[i][0]]
        punt[0][2] = v.surface[v.inn[i][0]]
        # coordenates x,y,z of punt 2
        punt[1][0] = v.xCo[v.inn[i][1]]
        punt[1][1] = v.yCo[v.inn[i][1]]
        punt[1][2] = v.surface[v.inn[i][1]]
        # coordenates x,y,z of punt 3
        punt[2][0] = v.xCo[v.inn[i][2]]
        punt[2][1] = v.yCo[v.inn[i][2]]
        punt[2][2] = v.surface[v.inn[i][2]]
        # calcula el vector del 'punt 1' al 'punt 2'
        # (x2-x1,y2-y1,z2-z1)
        u[0] = punt[1][0]-punt[0][0]
        u[1] = punt[1][1]-punt[0][1]
        u[2] = punt[1][2]-punt[0][2]
        # calcula el vector del 'punt 1' al 'punt 3'
        # (x3-x1,y3-y1,z3-z1)
        b[0] = punt[2][0]-punt[0][0]
        b[1] = punt[2][1]-punt[0][1]
        b[2] = punt[2][2]-punt[0][2]
        # calcula el vector normal al pla format pels vectors 'u' i 'b'
        # n=u x b (producte escalar de dos vectors)
        n[i][0] = (u[1]*b[2])-(u[2]*b[1])
        n[i][1] = -((u[0]*b[2])-(u[2]*b[0]))
        n[i][2] = (u[0]*b[1])-(u[1]*b[0])
        if n[i][2] < 0.0:
            n[i][0] = -n[i][0]
            n[i][1] = -n[i][1]
            n[i][2] = -n[i][2]
        v.slope[te][i] = (math.acos(n[i][2]/(math.sqrt((n[i][0]**2.0)+(n[i][1]**2.0)+(n[i][2]**2.0))))) * \
                                                                                57.29577951308232  # 180/PI

    for i in range(v.nNode):
        if v.nodElems[i][6] == 1:
            medNormal[0] = n[v.nodElems[i][0]][0]
            medNormal[1] = n[v.nodElems[i][0]][1]
            medNormal[2] = n[v.nodElems[i][0]][2]
        elif v.nodElems[i][6] == 2:
            medNormal[0] = (n[v.nodElems[i][0]][0]+n[v.nodElems[i][1]][0])/2
            medNormal[1] = (n[v.nodElems[i][0]][1]+n[v.nodElems[i][1]][1])/2
            medNormal[2] = (n[v.nodElems[i][0]][2]+n[v.nodElems[i][1]][2])/2
        elif v.nodElems[i][6] == 3:
            medNormal[0] = (n[v.nodElems[i][0]][0] +
                           n[v.nodElems[i][1]][0] +
                           n[v.nodElems[i][2]][0])/3
            medNormal[1] = (n[v.nodElems[i][0]][1] +
                           n[v.nodElems[i][1]][1] +
                           n[v.nodElems[i][2]][1])/3
            medNormal[2] = (n[v.nodElems[i][0]][2] +
                           n[v.nodElems[i][1]][2] +
                           n[v.nodElems[i][2]][2])/3
        elif v.nodElems[i][6] == 6:
            medNormal[0] = (n[v.nodElems[i][0]][0] +
                          n[v.nodElems[i][1]][0] +
                          n[v.nodElems[i][2]][0] +
                          n[v.nodElems[i][3]][0] +
                          n[v.nodElems[i][4]][0] +
                          n[v.nodElems[i][5]][0])/6
            medNormal[1] = (n[v.nodElems[i][0]][1] +
                          n[v.nodElems[i][1]][1] +
                          n[v.nodElems[i][2]][1] +
                          n[v.nodElems[i][3]][1] +
                          n[v.nodElems[i][4]][1] +
                          n[v.nodElems[i][5]][1])/6
            medNormal[2] = (n[v.nodElems[i][0]][2] +
                          n[v.nodElems[i][1]][2] +
                          n[v.nodElems[i][2]][2] +
                          n[v.nodElems[i][3]][2] +
                          n[v.nodElems[i][4]][2] +
                          n[v.nodElems[i][5]][2])/6

        v.slopeNode[te][i] = (math.acos(medNormal[2]/(math.sqrt((medNormal[0]**2.0)+(medNormal[1]**2.0) +
                                                                (medNormal[2]**2.0)))))*57.29577951308232  # 180/PI

def sea_level():
    v.seaLevel = np.interp(v.totalTime, v.sl[0][:], v.sl[1][:])

def waderdepth():
    v.wDepth[:] = v.head[:]+v.seaLevel-v.surface[:]
    mask = v.wDepth < 0.0
    v.wDepth[mask] = v.wDepth[mask]*(-0.0)
    v.wDepthE = np.zeros(v.nElem, dtype=float64)
    for i in range(v.nElem):
        w = np.zeros(3, float)
        w[0] = v.wDepth[v.inn[i][0]]
        w[1] = v.wDepth[v.inn[i][1]]
        w[2] = v.wDepth[v.inn[i][2]]
        if min(w) > 0.0:
            v.wDepthE[i] = (w[0]+w[1]+w[2])/3.0

def boundary_conditions():
    import random
    # It checks the boun#  print 'cTime:',v.cTime,'dt:',v.dt, 'totalTime:',v.totalTime, 'stepTime',v.stepTimedary
    # conditions to move them when sealevel changes

    nCLE = np.zeros(v.nElem, int)
    proxNod = np.zeros(2, int)
    nLAN = np.zeros(v.nElem, int)
    dista = np.zeros(v.nNode, dtype=np.float64)
    distaT = np.zeros(v.nNode, dtype=np.float64)
    headd = np.zeros(v.nNode, dtype=np.float64)

    # Add sealevel change to initial head boundary condition
    headd = np.copy(v.head)
    # for i in range(v.nNode):
    # if v.typBC[i]==0:
    # v.head[i]=v.headBC[i]
    mask = v.typBC == 0
    v.head[mask] = v.headBC[mask]
    # Copy boundary conditions to local for move boundary

    v.typBC = np.copy(v.readtypBC)
    v.q = np.copy(v.readQ)
    v.clastQ = np.copy(v.readClastQ)
    v.conClastBC = np.copy(v.readConClastBC)

    someElem = 0
    dista = 10000000000.0
    proxNod = 0
    someLand = 0

    # look for coast line and land elements

    for el in range(v.nElem):
        wa = np.zeros(3, float)
        wa[0] = v.wDepth[v.inn[el][0]]-v.head[v.inn[el][0]]
        wa[1] = v.wDepth[v.inn[el][1]]-v.head[v.inn[el][1]]
        wa[2] = v.wDepth[v.inn[el][2]]-v.head[v.inn[el][2]]
        if max(wa) > 0.0 and min(wa) <= 0.0:
            nCLE[el] = el
            someElem += 1

        if (wa[0] <= 0.0) and (wa[1] <= 0.0) and (wa[2] <= 0.0):
            nLAN[el] = el
            someLand += 1

    if someElem > 0:
        for node in range(v.nNode):
            # if a node is over sea level and have BC look for the nearest nodes in the coastline
            if v.wDepth[node] <= 0.0:
                tempc = 0
                for j in range(v.nClsMat):
                    if v.clastQ[node][j] > 0.0:
                        tempc = tempc+1
                if v.q[node] > 0.0 or v.typBC[node] < 1 or tempc > 0:
                    dista = np.zeros(v.nNode, float)
                    # search nodes near input node over sea level
                    for el in range(v.nElem):
                        if nCLE[el] > 0:
                            for i in range(3):
                                if (v.wDepth[v.inn[el][i]]-v.head[v.inn[el][i]] > 0.0) and (v.dist[node][v.inn[el][i]]) > 0.0:
                                    dista[v.inn[el][i]] = v.dist[node][v.inn[el][i]]
                    for i in range(v.nNode):
                        if dista[i] > 0.0:
                            canvi = 0
                            for ii in range(6):
                                if v.connectNodes[i][ii] == 0.0:
                                    canvi = canvi+1
                                else:
                                    if v.wDepth[v.connectNodes[i][ii]]-v.head[v.connectNodes[i][ii]] <= 0.0:
                                        canvi = canvi+1
                            if canvi >= 3:
                                dista[i] = 0.0
                    for i in range(v.nNode):
                        if dista[i] <= 0.0:
                            dista[i] = 1000000000.0
                    aixxx1 = np.argmin(dista)
                    distaT = dista
                    distaT[aixxx1] = 1000000000.0
                    aixxx2 = np.argmin(distaT)

                    if dista[aixxx1] == distaT[aixxx2]:
                        rannum = random.random()
                        if rannum<0.5:
                            aixxx1 = aixxx2

                    v.q[aixxx1] = v.q[node]+v.q[aixxx1]
                    v.q[node] = 0.0
                    v.typBC[aixxx1] = v.typBC[node]
                    for j in range(v.nClsMat):
                        v.clastQ[aixxx1][j] = v.clastQ[node][j]+v.clastQ[aixxx1][j]
                        v.conClastBC[aixxx1][j] = v.conClastBC[node][j]+v.conClastBC[aixxx1][j]
                        v.clastQ[node][j] = 0.0
                        v.conClastBC[node][j] = 0.0

    if someLand > 0:
        for el in range(v.nElem):
            if nLAN[el] > 0:
                v.head[v.inn[el][0]] = 0.0
                v.head[v.inn[el][1]] = 0.0
                v.head[v.inn[el][2]] = 0.0
                for mat in range(v.nClsMat):
                    v.conClast[v.inn[el][0]][mat] = 0.0
                    v.conClast[v.inn[el][1]][mat] = 0.0
                    v.conClast[v.inn[el][2]][mat] = 0.0

def mass_balance(material, cc):

    # function to correct mass balance due to hyperbolic dominance in
    # transport equation from advective transport term and eulerian approach
    # in the transport algorithm (non moving grid)

  j = np.ones(v.nElem, dtype=np.float64)
  j *= -1.0
  elemTag = np.zeros(v.nElem, int)

  multiplicationFactor = 1
  additionFactor = 0
  j = -1.0
  newMassTotalCc = 0.0
  oldMassTotalConc = 0.0
  sedInFlow = 0.0
  sediOutFlow = 0.0
  sediOutFlow_2 = 0.0
  volTotal = 0.0

#  inflow mass
  for node in range(v.nNode):
    sedInFlow = sedInFlow+(v.clastQ[node][material]*v.dt)

  for elem in range(v.nElem):
    # mass in suspension before transport
    oldMassTotalConc = oldMassTotalConc+((v.conClast[v.inn[elem][0]][material]+
                     v.conClast[v.inn[elem][1]][material]+v.conClast[v.inn[elem][2]][material])/
                     3.0)*(v.area[elem]*v.wDepthE[elem])

#      new mass in suspension after trasnport
    newMassTotalCc = newMassTotalCc+((cc[v.inn[elem][0]]+cc[v.inn[elem][1]]+cc[v.inn[elem][2]])/
                                    3.0)*(v.area[elem]*v.wDepthE[elem])

#       Calculation of outflow mass in a dt

  for node in range(v.nNode):
    if v.typBC[node] == 1:
      for numElems in range(v.nodElems[node][6]):
        if elemTag[v.nodElems[node][numElems]] != 1:
          for k in range(3):
            alter = v.inn[v.nodElems[node][numElems]][k]
            if alter != node:
              if v.typBC[alter] == 1:
                elemTag[v.nodElems[node][numElems]] = 1
                x1 = v.xCo[node]
                x2 = v.xCo[alter]
                y1 = v.yCo[node]
                y2 = v.yCo[alter]
                z1 = v.wDepth[node]
                z2 = v.wDepth[alter]
                vx = x2-x1
                vy = y2-y1
                beta = math.atan2(vy,vx)*180.0/3.1415927
                dist12 = math.sqrt(vx*vx+vy*vy)
                gamma = v.velDi3[v.nodElems[node][numElems]]-beta
                gamma = gamma*3.1415927/180.0
                r1 = math.sin(gamma)
                dista = r1*dist12
                if dista < 0.0:
                    dista = -dista
                crossection = dista*(z1+z2)/2.0
                qq = crossection*v.ve3[v.nodElems[node][numElems]]
                sediOutFlow = sediOutFlow+qq*v.dt*((cc[node]+cc[alter])/2.0)

  if sediOutFlow < 0.0:
        sediOutFlow = -sediOutFlow

  mBalance = sedInFlow-sediOutFlow-newMassTotalCc+oldMassTotalConc

  if newMassTotalCc == 0.0 and sediOutFlow == 0.0:
      factor = 0.0
  else:
      factor = (sedInFlow+oldMassTotalConc)/(newMassTotalCc+sediOutFlow)

  cc *= factor

# Tornem a calcular la massa total del sistema per comprovar que el balan\8D de massa ha funcionat per un dt. Aquest calcul es nom\8Es de control, es pot el.liminar
  newMassTotalCc = 0.0;
  for elem in range(v.nElem):
#       calculated mass in suspension
    newMassTotalCc = newMassTotalCc+((cc[v.inn[elem][0]]+cc[v.inn[elem][1]]+cc[v.inn[elem][2]])/
                                     3.0)*(v.area[elem]*v.wDepthE[elem])

# We have to re-calculated the new mass that flows out of the model
# Calculation of outflow mass
  elemTag = np.zeros(v.nElem, int)
  sediOutFlow = 0.0
  for node in range(v.nNode):
    if v.typBC[node] == 1:
      for numElems in range(v.nodElems[node][6]):
        if elemTag[v.nodElems[node][numElems]] != 1:
          for k in range(3):
            alter = v.inn[v.nodElems[node][numElems]][k]
            if alter != node:
              if v.typBC[alter] == 1:
                elemTag[v.nodElems[node][numElems]] = 1
                x1 = v.xCo[node]
                x2 = v.xCo[alter]
                y1 = v.yCo[node]
                y2 = v.yCo[alter]
                z1 = v.wDepth[node]
                z2 = v.wDepth[alter]
                vx = x2-x1
                vy = y2-y1
                beta = math.atan2(vy,vx)*180.0/3.1415927
                dist12 = math.sqrt(vx*vx+vy*vy)
                gamma = v.velDi3[v.nodElems[node][numElems]]-beta
                gamma = gamma*3.1415927/180.0
                r1 = math.sin(gamma)
                dista = r1*dist12
                if dista < 0.0:
                    dista = -dista
                crossection = dista*(z1+z2)/2.0
                qq = crossection*v.ve3[v.nodElems[node][numElems]]
                sediOutFlow = sediOutFlow+qq*v.dt*((cc[node]+cc[alter])/2.0)

# New loop to calculate in a diferent way the material that flows out of the model
# The material that can flow out of the model is the material located in the element that has nodes with ouflow of sediment

  sediOutFlow_2 = 0.0;
  for elem in range(v.nElem):
    if (((v.typBC[v.inn[elem][0]] == 1)and(v.typBC[v.inn[elem][1]] == 1))or\
        ((v.typBC[v.inn[elem][0]] == 1)and(v.typBC[v.inn[elem][2]] == 1))or\
        ((v.typBC[v.inn[elem][1]] == 1)and(v.typBC[v.inn[elem][2]] == 1))):

      sediOutFlow_2 = sediOutFlow_2+((cc[v.inn[elem][0]]+cc[v.inn[elem][1]]+cc[v.inn[elem][2]])/3.0)*(v.area[elem]*v.wDepthE[elem])

  if sediOutFlow < 0.0:
        sediOutFlow = -sediOutFlow
  v.totalVolSedOut = v.totalVolSedOut+sediOutFlow/v.density[material]
  v.totalVolSedOut_2 = v.totalVolSedOut_2+sediOutFlow_2/v.density[material]


import inspect
import pathlib
from typing import Callable, Tuple

import numpy as np
import pandas as pd
import tensorflow as tf

from models.transfer import build_transfer_model
from utils.logging import logger

def decode_image(
    image_file: pathlib.Path, image_height: int, image_width: int
):
    """
    Read image path and decode as tensor
    :param image_file: image path object
    :param image_height: image height
    :param image_width: image width
    :return: resized image array
    """
    image_data = tf.image.decode_png(image_file, channels=3)
    return tf.image.resize(image_data, [image_height, image_width])

def process_image(
    image_height: int, image_width: int, preprocessing_function: Callable
):
    """
    Function to process image from file path
    :param image_height: image height
    :param image_width: image width
    :param preprocessing_function: preprocessing function
    :return: function thunk to pass to custom data pipeline
    """

    def process_path(file_path: str):
        """
        Thunk function to process image path
        :param file_path: file path in string format
        :return: processed image array
        """
        image_file = tf.io.read_file(file_path)
        image_data = decode_image(
            image_file, image_height=image_height, image_width=image_width
        )
        image_data = preprocessing_function(image_data)
        return tf.expand_dims(image_data, 0)

    return process_path

def generate_test_dataset(
    test_data_dir: pathlib.Path,
    preprocessing_function: Callable = lambda image: image / 255.0,
    image_height: int = 224,
    image_width: int = 224,
):
    """
    Generate testing dataset
    :param test_data_dir: test data directory path object
    :param preprocessing_function: preprocessing function default as rescaling to [0,1]
    :param image_height: image height
    :param image_width: image width
    :return: path and testing dataset object
    """
    image_path = tf.data.Dataset.list_files(
        str(test_data_dir / "*.png"), shuffle=False
    )
    return image_path, image_path.map(
        process_image(
            image_height=image_height,
            image_width=image_width,
            preprocessing_function=preprocessing_function,
        ),
        num_parallel_calls=tf.data.experimental.AUTOTUNE,
    )

def generate_predictions(
    model_path: pathlib.Path,
    base_model: tf.keras.Model,
    image_path: tf.data.Dataset,
    testing_dataset: tf.data.Dataset,
    activation_function: str = "softmax",
):
    base_model.load_weights(model_path)
    activation_layer = dict(inspect.getmembers(tf.nn))[activation_function]
    score = activation_layer(base_model.predict(testing_dataset))
    prediction = np.argmax(score, axis=1)

    series = []
    for path, prediction in zip(
        list(image_path.as_numpy_iterator()), prediction
    ):
        series.append([pathlib.Path(path.decode("utf-8")).stem, prediction])

    predictions = (
        pd.DataFrame(data=series, columns=["ID", "Label"])
        .astype(int)
        .sort_values("ID")
    )
    predictions.to_csv(model_path.parent / "predictions.csv", index=False)
    logger.info(
        f"Predictions saved as {model_path.parent / 'predictions.csv'}"
    )
    return predictions

def load_transfer_model(
    input_shape: Tuple[int, int, int],
    num_classes: int,
    base_model_name: str,
    global_average_pooling: bool,
):
    """
    Helper function to load transfer learning model
    :param input_shape: input shape tuple
    :param num_classes: number of classes
    :param base_model_name: base model name
    :param global_average_pooling: boolean flag of using global average pooling
    :return:
    """
    model_package = None
    base_model = None
    if base_model_name == "vgg16":
        model_package = tf.keras.applications.vgg16
        base_model = model_package.VGG16
    elif base_model_name == "vgg19":
        model_package = tf.keras.applications.vgg19
        base_model = model_package.VGG19
    elif base_model_name == "resnet50":
        model_package = tf.keras.applications.resnet50
        base_model = model_package.ResNet50
    elif base_model_name == "inception":
        model_package = tf.keras.applications.inception_v3
        base_model = model_package.InceptionV3
    else:
        raise ValueError("Model not supported")
    model, base_model = build_transfer_model(
        input_shape, num_classes, base_model, global_average_pooling
    )
    return model, model_package.preprocess_input

if __name__ == "__main__":
    from utils.configuration import (
        MODEL_CHECKPOINT_PATH,
        IMAGE_SHAPE,
        IMAGE_SIZE,
        TEST_DATA_DIR,
    )
    import tensorflow as tf

    activation = "softmax"
    path = (
        MODEL_CHECKPOINT_PATH
        / "transfer_vgg16_o_block4/10311903_cgavg_fc1512_fc2256_fc3128_d30_i512_bn_0.96575/ft-epoch-88-loss-0.0985"
    )
    built_model, preprocessing = load_transfer_model(
        input_shape=IMAGE_SHAPE,
        num_classes=3,
        base_model_name="vgg16",
        global_average_pooling=True,
    )

    test_path, test_dataset = generate_test_dataset(
        test_data_dir=TEST_DATA_DIR,
        preprocessing_function=preprocessing,
        image_height=IMAGE_SIZE[0],
        image_width=IMAGE_SIZE[1],
    )

    generate_predictions(
        model_path=path,
        base_model=built_model,
        image_path=test_path,
        testing_dataset=test_dataset,
        activation_function=activation,
    )


import sys
sys.stdin = open("input.txt","r")
from collections import deque

def bfs(node):
    q = deque()
    q.append(node)
    check[node] = 0
    while q:
        node = q.popleft()
        for i in range(1, 7):
            n = node + i
            if n > 100:
                continue
            # 다음으로 넘어갈 부분의 인덱스 값
            t = graph[n]
            # 안들린 노드이면 들리고 다시 q에 넣는다.
            if check[t] == -1:
                q.append(t)
                check[t] = check[node] + 1

N, M = map(int, input().split())
graph = [i for i in range(101)]
check = [-1] * 101
for _ in range(N):
    u, v = map(int, input().split())
    graph[u] = v
for _ in range(M):
    u, v = map(int, input().split())
    graph[u] = v

bfs(1)
print(check[100])


"""Test for AsyncRainbirdController."""

import datetime
from collections.abc import Awaitable, Callable

import aiohttp
import pytest

from pyrainbird import (
    RAINBIRD_COMMANDS,
    AvailableStations,
    ModelAndVersion,
    WaterBudget,
)
from pyrainbird.async_client import (
    AsyncRainbirdClient,
    AsyncRainbirdController,
    RainbirdApiException,
)
from pyrainbird.encryption import encrypt

from .conftest import LENGTH, PASSWORD, REQUEST, RESPONSE, RESULT_DATA, ResponseResult

async def test_request(
    rainbird_client: Callable[[], Awaitable[AsyncRainbirdClient]],
    response: ResponseResult,
) -> None:
    """Test of basic request/response handling."""
    response(aiohttp.web.Response(body=RESPONSE))
    client = await rainbird_client()
    resp = await client.request(REQUEST, LENGTH)
    assert resp == RESULT_DATA

async def test_request_failure(
    rainbird_client: Callable[[], Awaitable[AsyncRainbirdClient]],
    response: ResponseResult,
) -> None:
    """Test a basic request failure handling."""

    response(aiohttp.web.Response(status=500))
    client = await rainbird_client()
    with pytest.raises(RainbirdApiException):
        await client.request(REQUEST, LENGTH)

@pytest.fixture(name="api_response")
def mock_api_response(response: ResponseResult) -> Callable[[...], Awaitable[None]]:
    """Fixture to construct a fake API response."""

    def _put_result(command: str, **kvargs) -> None:
        resp = RAINBIRD_COMMANDS["ControllerResponses"][command]
        data = command + ("00" * (resp["length"] - 1))
        for k in resp:
            if k in ["type", "length"]:
                continue
            param_template = "%%0%dX" % (resp[k]["length"])
            start_ = resp[k]["position"]
            end_ = start_ + resp[k]["length"]
            data = "%s%s%s" % (
                data[:start_],
                (param_template % kvargs[k]),
                data[end_:],
            )

        body = encrypt(
            ('{"jsonrpc": "2.0", "result": {"data":"%s"}, "id": 1} ' % data),
            PASSWORD,
        )
        response(aiohttp.web.Response(body=body))

    return _put_result

async def test_get_model_and_version(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("82", modelID=16, protocolRevisionMajor=1, protocolRevisionMinor=3)
    assert await controller.get_model_and_version() == ModelAndVersion(16, 1, 3)

async def test_get_available_stations(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("83", pageNumber=1, setStations=0x7F000000)
    assert await controller.get_available_stations() == AvailableStations("7f000000", 1)

async def test_get_serial_number(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("85", serialNumber=0x12635436566)
    assert await controller.get_serial_number() == 0x12635436566

async def test_get_current_time(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    time = datetime.time()
    api_response("90", hour=time.hour, minute=time.minute, second=time.second)
    assert await controller.get_current_time() == time

async def test_get_current_date(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    date = datetime.date.today()
    api_response("92", year=date.year, month=date.month, day=date.day)
    assert await controller.get_current_date() == date

async def test_get_water_budget(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("B0", programCode=1, seasonalAdjust=65)
    assert await controller.water_budget(5) == WaterBudget(1, 65)

@pytest.mark.parametrize(
    "state,expected",
    [(1, True), (0, False)],
)
async def test_get_rain_sensor(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
    state: int,
    expected: bool,
) -> None:
    controller = await rainbird_controller()
    api_response("BE", sensorState=state)
    assert await controller.get_rain_sensor_state() == expected

async def test_get_zone_state(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    for i in range(1, 9):
        for j in range(1, 9):
            mask = (1 << (i - 1)) * 0x1000000
            api_response("BF", pageNumber=0, activeStations=mask)
            assert await controller.get_zone_state(j) == (i == j)

async def test_set_program(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("01", commandEcho=5)
    await controller.set_program(5)

async def test_irrigate_zone(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("01", pageNumber=0, commandEcho=6)
    api_response("BF", pageNumber=0, activeStations=0b10000000000000000000000000000)
    await controller.irrigate_zone(5, 30)

async def test_test_zone(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("01", commandEcho=6)
    await controller.test_zone(6)

async def test_stop_irrigation(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("01", pageNumber=0, commandEcho=6)
    api_response("BF", pageNumber=0, activeStations=0b0)
    await controller.stop_irrigation()

async def test_get_rain_delay(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("B6", delaySetting=16)
    await controller.get_rain_delay() == 16

async def test_set_rain_delay(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("01", pageNumber=0, commandEcho=6)
    await controller.set_rain_delay(3)

async def test_advance_zone(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("01", commandEcho=3)
    await controller.advance_zone(3)

@pytest.mark.parametrize(
    "state,expected",
    [
        (1, True),
        (0, False),
    ],
)
async def test_get_current_irrigation(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
    state: int,
    expected: bool,
) -> None:
    controller = await rainbird_controller()
    api_response("C8", irrigationState=state)
    assert await controller.get_current_irrigation() == expected

async def test_not_acknowledge_response(
    rainbird_controller: Callable[[], Awaitable[AsyncRainbirdController]],
    api_response: Callable[[...], Awaitable[None]],
) -> None:
    controller = await rainbird_controller()
    api_response("00", commandEcho=17, NAKCode=28)
    with pytest.raises(RainbirdApiException):
        await controller.irrigate_zone(1, 30)


import math
import os
import cv2
import pygame
import numpy as np
from pygame.locals import *

FPS = 60
ANIMATION_SPEED = 0.18
WIN_WIDTH = 223 * 2
WIN_HEIGHT = 382

# define reward
REWARD_SURVIVE = 1.0
REWARD_PASS = 0.1
REWARD_DEAD = 0.0

# define actions
ACTION_DO_NOTHING = 0
ACTION_FLAP = 1

# define state type
STATE_TYPE_NUMBERS = 0
STATE_TYPE_IMAGE = 1

def msec_to_frames(milliseconds, fps=FPS):
   return fps * milliseconds / 1000.0

def frame_to_msec(frames, fps=FPS):
	return 1000.0*frames/fps

# hidden bird, to generate input for model
class H_Bird:
	WIDTH = HEIGHT = 32
	SINK_SPEED = 0.05
	CLIMB_SPEED = 0.2
	CLIMB_DURATION = 333.3

	def __init__(self, x, y, msec_to_climb, raw_images):
		self.x, self.y = x, y
		self.msec_to_climb = msec_to_climb
		self._img_wingup, self._img_wingdown = raw_images
		self._mask_wingup = self._img_wingup[:,:,3:4]/255
		self._mask_wingdown = self._img_wingdown[:,:,3:4]/255
		self._img_wingup = self._img_wingup[:,:,0:3]
		self._img_wingdown = self._img_wingdown[:,:,0:3]
		self.speed = H_Bird.SINK_SPEED

	def flap(self):
		self.msec_to_climb = H_Bird.CLIMB_DURATION

	def update(self, delta_frames=1):
		delta_time = frame_to_msec(delta_frames)
		if self.msec_to_climb>0:
			frac_climb_done = 1 -  self.msec_to_climb/H_Bird.CLIMB_DURATION
			self.speed = -H_Bird.CLIMB_SPEED * (1 - math.cos(frac_climb_done * math.pi))
			self.y +=  self.speed * delta_time
			self.msec_to_climb -= delta_time
		else:
			self.speed = H_Bird.SINK_SPEED
			self.y += self.speed * delta_time

	def hit_edges(self):
		if self.y<0 or self.y + H_Bird.HEIGHT>WIN_HEIGHT:
			return True
		return False

	def get_image(self, clock_tick):
		if clock_tick%500>=250:
			return self._img_wingup
		else:
			return self._img_wingdown

	def get_mask(self, clock_tick):
		if clock_tick%500>=250:
			return self._mask_wingup
		else:
			return self._mask_wingdown
			image

	def draw(self, paper, clock_tick):
		start_x = int(self.x)
		end_x = start_x + H_Bird.WIDTH
		start_y = int(self.y)
		end_y = start_y + H_Bird.HEIGHT

		if start_y<0:
			mask = self.get_mask(clock_tick)[int(-start_y):H_Bird.HEIGHT]
			image = self.get_image(clock_tick)[int(-start_y):H_Bird.HEIGHT]
			end_y = int(end_y)
			start_y = 0
		elif end_y > WIN_HEIGHT:
			mask = self.get_mask(clock_tick)[0: int(WIN_HEIGHT-start_y)]
			image = self.get_image(clock_tick)[0: int(WIN_HEIGHT-start_y)]
			end_y = WIN_HEIGHT
			start_y = int(start_y)
		else:
			mask = self.get_mask(clock_tick)
			image = self.get_image(clock_tick)
			start_y = int(start_y)
			end_y = int(end_y)

		bird_img = image * mask + paper[start_y: end_y, start_x: end_x] * (1 - mask)
		paper[start_y: end_y, start_x: end_x] = bird_img

class H_PipePair:
	WIDTH = 80
	PIECE_HEIGHT = 32
	ADD_INTERVAL = 2500
	TOTAL_PIPE_BODY_PIECES = int(
		(WIN_HEIGHT -                  # fill window from top to bottom
		3 * H_Bird.HEIGHT -             # make room for bird to fit through
		3 * PIECE_HEIGHT) /  # 2 end pieces + 1 body piece
		PIECE_HEIGHT)

	def __init__(self, pipe_end_img, pipe_body_img, bottom_pieces=np.random.randint(1, TOTAL_PIPE_BODY_PIECES)):
		self.x = float(WIN_WIDTH - 1)
		self.score_counted = False

		self.image = np.zeros([WIN_HEIGHT, H_PipePair.WIDTH, 4], dtype=np.float32)
		self.bottom_pieces = bottom_pieces
		self.top_pieces = H_PipePair.TOTAL_PIPE_BODY_PIECES - self.bottom_pieces

		start_x = 0
		end_x = H_PipePair.WIDTH

		# bottom pieces
		for i in range(1, self.bottom_pieces+1):
			start_y = WIN_HEIGHT - i * H_PipePair.PIECE_HEIGHT
			end_y = start_y + H_PipePair.PIECE_HEIGHT
			self.image[start_y: end_y, start_x: end_x] = pipe_body_img

		start_y = WIN_HEIGHT - self.bottom_pieces * H_PipePair.PIECE_HEIGHT - H_PipePair.PIECE_HEIGHT
		end_y = start_y + H_PipePair.PIECE_HEIGHT
		self.image[start_y: end_y, start_x: end_x] = pipe_end_img

		# top pipe
		for i in range(self.top_pieces):
			start_y = i * H_PipePair.PIECE_HEIGHT
			end_y = start_y + H_PipePair.PIECE_HEIGHT
			self.image[start_y:end_y, start_x:end_x] = pipe_body_img
		top_pipe_end_y = self.top_pieces * PipePair.PIECE_HEIGHT
		start_y = self.top_pieces * H_PipePair.PIECE_HEIGHT
		end_y = start_y + H_PipePair.PIECE_HEIGHT
		self.image[start_y: end_y, start_x: end_x] = pipe_end_img

		# compensate for added end pieces
		self.top_pieces += 1
		self.bottom_pieces += 1

		self.mask = self.image[:,:,3:4]/255
		self.image = self.image[:,:,0:3]

	def collides_with(self, bird):
		bird_upper_left_corner = [bird.x, bird.y]
		if self.x<bird_upper_left_corner[0]<self.x + H_PipePair.WIDTH and bird_upper_left_corner[1]<self.top_pieces*H_PipePair.PIECE_HEIGHT:
			return True

		bird_upper_right_corner = [bird.x + H_Bird.WIDTH, bird.y]
		if self.x<bird_upper_right_corner[0]<self.x + H_PipePair.WIDTH and bird_upper_right_corner[1] < self.top_pieces*H_PipePair.PIECE_HEIGHT:
			return True

		bird_lower_left_corner = [bird.x, bird.y + H_Bird.HEIGHT]
		if self.x<bird_lower_left_corner[0]<self.x + H_PipePair.WIDTH and bird_lower_left_corner[1]>WIN_HEIGHT - self.bottom_pieces*H_PipePair.PIECE_HEIGHT:
			return True

		bird_lower_right_corner = [bird.x + H_Bird.WIDTH, bird.y + H_Bird.HEIGHT]
		if self.x<bird_lower_right_corner[0]<self.x + H_PipePair.WIDTH and bird_lower_right_corner[1]>WIN_HEIGHT - self.bottom_pieces*H_PipePair.PIECE_HEIGHT:
			return True

		return False

	def is_visible(self):
		return -H_PipePair.WIDTH< self.x < WIN_WIDTH

	def update(self, delta_frames=1):
		self.x -=ANIMATION_SPEED * frame_to_msec(delta_frames)

	def draw(self, paper):
		start_x = self.x
		end_x = start_x + H_PipePair.WIDTH
		if start_x<0:
			mask = self.mask[:, int(-start_x): H_PipePair.WIDTH]
			image = self.image[:, int(-start_x): H_PipePair.WIDTH]
			end_x = int(end_x)
			start_x = 0
		elif end_x>WIN_WIDTH:
			mask = self.mask[:, 0: int(WIN_WIDTH-start_x)]
			image = self.image[:, 0: int(WIN_WIDTH-start_x)]
			end_x = WIN_WIDTH
			start_x = int(start_x)
		else:
			mask = self.mask
			image = self.image
			start_x = int(start_x)
			end_x = int(end_x)
		pipe_img = image * mask + paper[:, start_x: end_x] * (1 - mask)
		paper[:, start_x: end_x] = pipe_img

# create environment without clock ticking
class QuickEnvironment:

	SCREENSHOT_INTERVAL = 100
	MAX_DURATION = 100000 # 100 secs

	def __init__(self, image_folder):
		self.images = self.load_raw_images(image_folder=image_folder)
		self.bird = H_Bird(50, int((WIN_HEIGHT - H_Bird.HEIGHT)/2), 2, [self.images['bird-wingup'], self.images['bird-wingdown']])
		self.clock_tick = 0
		self.frame_count = 0
		self.pipes = []
		self.background = np.concatenate([self.images['background'], self.images['background']], axis=1)
		self.background = self.background[:,:,0:3]

	def load_raw_images(self, image_folder='./cimages/'):
		def load_raw_image(img_file_name):
			img = np.float32(cv2.imread(image_folder + img_file_name, cv2.IMREAD_UNCHANGED))
			if img.shape[2]==3:
				img = np.concatenate([img, np.ones([img.shape[0], img.shape[1], 1], dtype=np.float32)*255], axis=2)
			return img

		return {
			'background':  load_raw_image('background.png'),
			'pipe-end': load_raw_image('pipe_end.png'),
			'pipe-body': load_raw_image('pipe_body.png'),
			'bird-wingup': load_raw_image('bird_wing_up.png'),
			'bird-wingdown': load_raw_image('bird_wing_down.png')}

	def reset(self, state_type=STATE_TYPE_NUMBERS):
		self.clock_tick = 0
		self.frame_count = 0
		self.bird.y = np.random.randint(low=WIN_HEIGHT/4, high=3*WIN_HEIGHT/4-H_Bird.HEIGHT)
		self.bird.msec_to_climb = 2
		self.pipes.clear()
		return self.get_state(state_type)

	def screenshot(self):
		paper = self.background.copy()
		self.bird.draw(paper, self.clock_tick)
		for pipe in self.pipes:
			pipe.draw(paper)
		return paper

	def get_duration(self):
		return frame_to_msec(self.frame_count)/1000

	def get_next_pipe_pos(self):
		pipe_pos = WIN_WIDTH
		for pipe in self.pipes:
			if pipe.x + H_PipePair.WIDTH > self.bird.x:
				pipe_pos = pipe.x
				break
		return pipe_pos

	def update(self, action=ACTION_DO_NOTHING):
		# increase clock tick
		self.clock_tick+=16.667 # 60 fps => clock tick += 1000/60
		t_action = action
		# init reward, done
		reward = REWARD_SURVIVE
		done = False

		n_frames = int(np.round(msec_to_frames(QuickEnvironment.SCREENSHOT_INTERVAL)))
		for i in range(n_frames):
			# after every 3000 ms add a piple
			if self.frame_count%msec_to_frames(PipePair.ADD_INTERVAL)==0:
				self.pipes.append(H_PipePair(self.images['pipe-end'], self.images['pipe-body'], np.random.randint(1, H_PipePair.TOTAL_PIPE_BODY_PIECES)))

			# update bird
			if t_action==ACTION_FLAP:
				self.bird.flap()
				t_action = ACTION_DO_NOTHING
			self.bird.update()

			# check bird state
			if self.bird.hit_edges():
				reward = REWARD_DEAD
				done = True
				return None, reward, done

			# update pipe
			for pipe in self.pipes:
				pipe.update()
				if pipe.collides_with(self.bird):
					reward = REWARD_DEAD
					done = True
					return None, reward, done

			# check if episode is too long
			if self.frame_count>msec_to_frames(QuickEnvironment.MAX_DURATION):
				reward = REWARD_SURVIVE
				done = True
				return None, reward, done

			if not self.pipes[0].is_visible():
				self.pipes.pop(0)

			# increase frame count
			self.frame_count += 1
		return self.get_state(), reward, done

	def get_state(self, state_type=STATE_TYPE_NUMBERS):
		if state_type==STATE_TYPE_NUMBERS:
			return [self.bird.y/191 - 1, self.bird.speed/H_Bird.SINK_SPEED, self.get_next_pipe_pos()/223 - 1]
		else: # this state type does not contain speed information
			return self.screenshot()

class Bird(pygame.sprite.Sprite):

	WIDTH = H_Bird.WIDTH
	HEIGHT = H_Bird.HEIGHT
	SINK_SPEED = H_Bird.SINK_SPEED
	CLIMB_SPEED = H_Bird.CLIMB_SPEED
	CLIMB_DURATION = H_Bird.CLIMB_DURATION

	def __init__(self, x, y, msec_to_climb, images):
		super(Bird, self).__init__()
		self.x, self.y = x, y
		self.msec_to_climb = msec_to_climb
		self._img_wingup, self._img_wingdown = images
		self._mask_wingup = pygame.mask.from_surface(self._img_wingup)
		self._mask_wingdown = pygame.mask.from_surface(self._img_wingdown)
		self.speed = Bird.SINK_SPEED

	def update(self, delta_frames=1):
		delta_time = frame_to_msec(delta_frames)
		if self.msec_to_climb>0:
			frac_climb_done = 1 -  self.msec_to_climb/Bird.CLIMB_DURATION
			self.speed = -Bird.CLIMB_SPEED * (1 - math.cos(frac_climb_done * math.pi))
			self.y += self.speed * delta_time
			self.msec_to_climb -= delta_time
		else:
			self.speed = Bird.SINK_SPEED
			self.y += self.speed * delta_time

	def flap(self):
		self.msec_to_climb = Bird.CLIMB_DURATION-1

	@property
	def image(self):
		if pygame.time.get_ticks()%500>=250:
			return self._img_wingup
		else:
			return self._img_wingdown

	@property
	def mask(self):
		if pygame.time.get_ticks()%500>=250:
			return self._mask_wingup
		else:
			return self._mask_wingdown

	@property
	def rect(self):
		return Rect(self.x, self.y, Bird.WIDTH, Bird.HEIGHT)

class PipePair(pygame.sprite.Sprite):
	WIDTH = H_PipePair.WIDTH
	PIECE_HEIGHT = H_PipePair.PIECE_HEIGHT
	ADD_INTERVAL = H_PipePair.ADD_INTERVAL
	TOTAL_PIPE_BODY_PIECES = H_PipePair.TOTAL_PIPE_BODY_PIECES
	def __init__(self, pipe_end_img, pipe_body_img, bottom_pieces=np.random.randint(0, H_PipePair.TOTAL_PIPE_BODY_PIECES)):
		self.x = float(WIN_WIDTH - 1)
		self.score_counted = False

		self.image = pygame.Surface((PipePair.WIDTH, WIN_HEIGHT), SRCALPHA)
		self.image.convert()
		self.image.fill((0,0,0,0))

		total_pipe_body_pieces = int(
			(WIN_HEIGHT -                  # fill window from top to bottom
			 3 * Bird.HEIGHT -             # make room for bird to fit through
			 3 * PipePair.PIECE_HEIGHT) /  # 2 end pieces + 1 body piece
			PipePair.PIECE_HEIGHT          # to get number of pipe pieces
		)
		self.bottom_pieces = bottom_pieces
		self.top_pieces = total_pipe_body_pieces - self.bottom_pieces
		# bottom pieces
		for i in range(1, self.bottom_pieces+1):
			piece_pos = (0, WIN_HEIGHT - i*PipePair.PIECE_HEIGHT)
			self.image.blit(pipe_body_img, piece_pos)
		bottom_pipe_end_y = WIN_HEIGHT - self.bottom_pieces*PipePair.PIECE_HEIGHT
		bottom_end_piece_pos = (0, bottom_pipe_end_y - PipePair.PIECE_HEIGHT)
		self.image.blit(pipe_end_img, bottom_end_piece_pos)

		# top pipe
		for i in range(self.top_pieces):
			self.image.blit(pipe_body_img, (0, i * PipePair.PIECE_HEIGHT))
		top_pipe_end_y = self.top_pieces * PipePair.PIECE_HEIGHT
		self.image.blit(pipe_end_img, (0, top_pipe_end_y))

		# compensate for added end pieces
		self.top_pieces += 1
		self.bottom_pieces += 1

		# for collision detection
		self.mask = pygame.mask.from_surface(self.image)

	def is_visible(self):
		return -PipePair.WIDTH< self.x < WIN_WIDTH

	@property
	def rect(self):
		return Rect(self.x, 0, PipePair.WIDTH, PipePair.PIECE_HEIGHT)

	def update(self, delta_frames=1):
		self.x -=ANIMATION_SPEED * frame_to_msec(delta_frames)

	def collides_with(self, bird):
		bird_upper_left_corner = [bird.x, bird.y]

		if self.x<bird_upper_left_corner[0]<self.x + PipePair.WIDTH and bird_upper_left_corner[1]<self.top_pieces*PipePair.PIECE_HEIGHT:
			return True

		bird_upper_right_corner = [bird.x + Bird.WIDTH, bird.y]
		if self.x<bird_upper_right_corner[0]<self.x + PipePair.WIDTH and bird_upper_right_corner[1] < self.top_pieces*PipePair.PIECE_HEIGHT:
			return True

		bird_lower_left_corner = [bird.x, bird.y + Bird.HEIGHT]
		if self.x<bird_lower_left_corner[0]<self.x + PipePair.WIDTH and bird_lower_left_corner[1]>WIN_HEIGHT - self.bottom_pieces*PipePair.PIECE_HEIGHT:
			return True

		bird_lower_right_corner = [bird.x + Bird.WIDTH, bird.y + Bird.HEIGHT]
		if self.x<bird_lower_right_corner[0]<self.x + PipePair.WIDTH and bird_lower_right_corner[1]>WIN_HEIGHT - self.bottom_pieces*PipePair.PIECE_HEIGHT:
			return True

		return False

class Environment:

	SCREENSHOT_INTERVAL = QuickEnvironment.SCREENSHOT_INTERVAL

	def __init__(self, image_folder='./images/'):
		pygame.init()
		self.display_surface = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
		pygame.display.set_caption('Reinforcement Environment')
		self.images = self.load_images(image_folder=image_folder)
		self.clock = pygame.time.Clock()
		self.bird = Bird(
			50,
			int((WIN_HEIGHT - Bird.HEIGHT)/2),
			2,
			[self.images['bird-wingup'], self.images['bird-wingdown']])
		self.pipes = []

	def load_images(self, image_folder='./images/'):
		def load_image(img_file_name):
			img = pygame.image.load(image_folder + img_file_name)
			img.convert()
			return img

		return {
			'background':  load_image('background.png'),
			'pipe-end': load_image('pipe_end.png'),
			'pipe-body': load_image('pipe_body.png'),
			'bird-wingup': load_image('bird_wing_up.png'),
			'bird-wingdown': load_image('bird_wing_down.png')}

	def should_screenshot(self):
		if self.frame_count%msec_to_frames(Environment.SCREENSHOT_INTERVAL)==0:
			return True
		return False

	def screenshot(self):
		img = pygame.surfarray.array3d(self.display_surface)
		img = img.swapaxes(0,1)
		return img

	def reset(self):
		self.clock_tick = 0
		self.frame_count = 0
		self.bird.y = int((WIN_HEIGHT - H_Bird.HEIGHT)/2)
		self.bird.msec_to_climb = 2
		self.pipes.clear()

	def run(self):
		done = False
		paused = False
		frame_clock = 0
		while not done:
			self.clock.tick(FPS)
			clock_tick = pygame.time.get_ticks()

			# add pipe after every 3 secs
			if not (paused or frame_clock%msec_to_frames(PipePair.ADD_INTERVAL)):
				self.pipes.append(PipePair(self.images['pipe-end'], self.images['pipe-body'], np.random.randint(1, PipePair.TOTAL_PIPE_BODY_PIECES)))

			# handle events
			for e in pygame.event.get():
				if e.type==KEYUP:
					if e.key==K_ESCAPE:
						done=True
						break
					elif e.key==K_UP:
						self.bird.flap()
					elif e.key==K_p:
						paused = not paused
				elif e.type==QUIT:
					done=True
					break

			# do not do anything if paused
			if paused:
				continue

			if self.bird.y<=0 or self.bird.y>=WIN_HEIGHT-Bird.HEIGHT:
				done = True

			for x in [0, WIN_WIDTH/2]:
				self.display_surface.blit(self.images['background'], (x, 0))

			if len(self.pipes)>0 and not self.pipes[0].is_visible():
				self.pipes.pop(0)

			for pipe in self.pipes:
				pipe.update()
				self.display_surface.blit(pipe.image, pipe.rect)
				if pipe.collides_with(self.bird):
					done = True
					break

			self.bird.update()
			self.display_surface.blit(self.bird.image, self.bird.rect)

			# screenshot
			'''
			if not frame_clock%msec_to_frames(Environment.SCREENSHOT_INTERVAL):
				cv2.imwrite('test{:06d}.jpg'.format(frame_clock), self.screenshot())
			'''

			pygame.display.update()
			frame_clock+=1

		pygame.quit()

'''
env = Environment('./images/')
env.run()

env = QuickEnvironment('./images/')
env.reset()
done = False
count = 0
while not done:
	reward, done = env.update(action=np.random.randint(2))
	img = env.screenshot()
	cv2.imwrite('test{:06d}.jpg'.format(count), img)
	count += 1
	if count>1000:
		break
'''