from CAREM.Poinki import DirectEvInterface
from CAREM.Citvap import CitvapSourceModel
from PyCAR.PyCIT.FT import MeshFlux
from PyCAR.PyCIT.lector_mest_posta import AddableDict, Geometry, LoadCDP
import re
import numpy as np
import os
import time
import subprocess
from abc import ABC

class TWIGL_MAP(ABC):
    def MapMaterial(self, meshmaterial):
        if meshmaterial in [1, 2, 3]:
            attr = '_reg' + str(meshmaterial)
            return getattr(self, attr)[0][0.0]
        else:
            attr = '_NULL'
        return getattr(self, attr)

class Nu_Fission_Map(TWIGL_MAP):
    wdir = 'C:\\TWINGL\\'

    def __init__(self, **kwargs):
        if 'wdir' in kwargs:
            setattr(self, 'wdir', kwargs['wdir'])

        wdir = getattr(self, 'wdir')
        self._reg1 = LoadCDP(wdir + 'REGION1.cdp')
        self._reg2 = LoadCDP(wdir + 'REGION2.cdp')
        self._reg3 = LoadCDP(wdir + 'REGION3.cdp')

        self._NULL = {'XS': np.zeros(self._reg3[0][0.0]['XS'].shape),
                      'SM': np.zeros(self._reg3[0][0.0]['SM'].shape)}

class Kinetic_Map(TWIGL_MAP):
    def __init__(self):
        v1 = 10 ** 7
        v2 = 2 * (10 ** 5)
        self._reg1 = self._reg2 = self._reg3 = {0: {0.0: {'Beta': np.array([0.0075]),
                                                          'Decays': np.array([0.08]),
                                                          'Neutron Velocity': np.array([1 / v1, 1 / v2])}}}

        self._NULL = {'Beta': [0],
                      'Decays': [0],
                      'Neutron Velocity': [0, 0]}

class DirectEvolutionModel(DirectEvInterface):

    def run_transient(self, Convergence_Criterion):
        try:
            self._CV.run(executable='cittrs.exe', epi_1=Convergence_Criterion)
        except AssertionError:
            time.sleep(2.0)
            self._CV.run(executable='cittrs.exe', epi_1=Convergence_Criterion)
        subprocess.run(['caremdb', '-opt:export', '-val:meshflux', self.__database])
        return

    def evolve(self, dt, *args, **kwargs):
        if self.Equilibrium:
            self.equilibrium_precrs()
            self._Ct = self._C0.copy()
            self._Ct_1 = self._C0.copy()
        else:
            self._Ct_1 = self._Ct.copy()
            self.calculate_precrs(dt)
            self.t += dt

        if self._UseDerivative:
            self.dt = dt

        self.calculate_source(dt)
        self.source_to_file()
        self._send_to_lu_17__()

        try:
            self._CV.run(executable='pre_cit.exe')
        except AssertionError:
            time.sleep(2.0)
            self._CV.run(executable='pre_cit.exe')

        self._xsu_mod__()
        self.run_transient(self.FluxConvergence)
        self._get_power__()

        if self.Equilibrium:
            self.Equilibrium = False

        self.Flux_t_1 = self.Flux_t.copy()
        self.Flux_t = MeshFlux(self.__database+'.meshflux', self.Nx, self.Ny, self.Nz, self._NOG)
        return

    def _init_flux(self, groups):
        self._NOG = groups
        self.Flux_t = self.Flux_t_1 = \
            MeshFlux(self.__database.replace('S.cdb', '_eq.cdb.meshflux')
                                               , *self.Geom.Cantidad_de_Nodos(), Ng=self._NOG)
        self.Nx, self.Ny, self.Nz = self.Flux_t.shape[1:4]
        return

    def _init_citvap_model(self, file, seccion_4, seccion_5, materiales,
                           black_absorber_ID, GeometryType, seccion_26):
        self._CV = CitvapSourceModel(
            file=file,
            seccion_4=seccion_4,
            seccion_5=seccion_5,
            materiales=materiales,
            seccion_26=seccion_26,
            GeometricType=GeometryType,
            black_absorber_ID=black_absorber_ID)

        self._CV.PrintMeshMap()
        self._CV.Calculate()
        return

    def _init_mesh_parameters(self):
        self.Geom = Geometry(self._file.replace('S.cii', '0.ci@'))  # HARDCODEADO el *S.cii
        self.Vmesh = self.Geom.Vmesh()
        return

    def _init_parameters(self, _NuFissionMap, _KineticMap):
        self.NuFis = _NuFissionMap()
        self.KM = _KineticMap()
        self.BetaM = np.empty((1, self.Nx, self.Ny, self.Nz, self.nprc))
        self.LambdaM = np.empty((1, self.Nx, self.Ny, self.Nz, self.nprc))
        self.NuFisM = np.empty((1, self.Nx, self.Ny, self.Nz, self._NOG))
        self.VelocityM = np.empty((1, self.Nx, self.Ny, self.Nz, self._NOG))
        self.NuFissionRates = np.empty((1, self.Nx, self.Ny, self.Nz, self._NOG))

        for _x in range(self.Nx):
            for _y in range(self.Ny):
                for _z in range(self.Nz):
                    meshmaterial = self.Geom.sc5[_x][_y][_z]
                    KinParam = self.KM.MapMaterial(meshmaterial)
                    self.BetaM[self.state][_x][_y][_z][:] = KinParam['Beta']
                    self.LambdaM[self.state][_x][_y][_z][:] = KinParam['Decays']
                    self.VelocityM[self.state][_x][_y][_z][:] = KinParam['Neutron Velocity']
                    self.NuFisM[self.state][_x][_y][_z][:] = self.NuFis.MapMaterial(meshmaterial)['XS'][:, 3]

        self.react = self.__R0 = -9251.1E-5
        self.keff = 1 / (1 - self.__R0)
        return 0

    def __init__(self, file, NuFissionMap, KineticMap,
                 WorkingDirectory,
                 seccion_26, materiales, seccion_5, seccion_4,
                 state=0, nprc=1, groups=1,
                 Equilibrium=True,
                 UseDerivative=False,
                 GeometricType='TZ',
                 black_absorber_ID=10,
                 FluxConvergence=None, *args, **kwargs):
        """

        :param file:
        :param NuFissionMap:
        :param KineticMap:
        :param WorkingDirectory:
        :param seccion_26:
        :param materiales:
        :param seccion_5:
        :param seccion_4:
        :param state:
        :param nprc:
        :param groups:
        :param Equilibrium:
        :param UseDerivative:
        :param GeometricType:
        :param black_absorber_ID:
        """
        if FluxConvergence is not None:
            self.FluxConvergence = FluxConvergence
        else:
            self.FluxConvergence = 1.0E-6
        self._geo_type = GeometricType
        self._sec26 = seccion_26
        self._mat = materiales
        self._sec5 = seccion_5
        self._sec4 = seccion_4
        self._BA = black_absorber_ID
        self.t = 0.0
        self.dt = 0.0
        assert isinstance(state, int)
        self.state = state
        self.chi_g = [1.0, 0.0]
        self._file = file
        self.Equilibrium = Equilibrium
        self.nprc = nprc
        self.WorkingDirectory = WorkingDirectory

        self._rootfile = self._file.replace('.cii', '')
        self.__database = self._file.replace('.cii', '.cdb')
        self.__p_parser = re.compile(r'POWER\(WATTS\) +([0-9]\.[0-9]{5}E\+[0-9]{2})')
        self.Pt = 1
        self._Q = {}

        self._C0 = self._Ct = self._Ct_1 = {self.state: {}}

        self._UseDerivative = UseDerivative
        self.__datfile = r'source.dat'

        self._init_mesh_parameters()
        self._init_flux(groups)
        self._init_citvap_model(self._file, self._sec4, self._sec5, self._mat, self._BA, self._geo_type, self._sec26)
        self._init_parameters(NuFissionMap, KineticMap)
        return

    @property
    def Ct_1(self):
        return AddableDict(self._Ct_1).as_array()

    @property
    def C0(self):
        return AddableDict(self._C0).as_array()

    @property
    def Ct(self):
        return AddableDict(self._Ct).as_array()

    @property
    def Q(self):
        return AddableDict(self._Q).as_array()

    def equilibrium_precrs(self, *args, **kwargs):
        if not self._C0[self.state]:
            for nx in range(self.Flux_t.shape[1]):
                self._C0[self.state][nx] = {}
                for ny in range(self.Flux_t.shape[2]):
                    self._C0[self.state][nx][ny] = {}
                    for nz in range(self.Flux_t.shape[3]):
                        FluxL = [self.Flux_t[self.state, nx, ny, nz, group]
                                 for group in range(self.Flux_t.shape[-1])]
                        NuFisL = [self.NuFisM[self.state][nx][ny][nz][group] / self.keff
                                  for group in range(self.Flux_t.shape[-1])]
                        Nu_FluxM = [NuFisL[group] * FluxL[group] * self.Geom.Vmesh()
                                    for group in range(self.Flux_t.shape[-1])]
                        Bet_k = self.BetaM[self.state][nx][ny][nz]
                        Lamb_k = self.LambdaM[self.state][nx][ny][nz]

                        self._C0[self.state][nx][ny][nz] = [Bet_k[prec] / Lamb_k[prec] * sum(Nu_FluxM)
                                                            if Lamb_k[prec] != 0 else 0.0 for prec in range(self.nprc)]
        return

    def calculate_precrs(self, dt):
        for nx in range(self.Nx):
            # self._Ct[self.state][nx] = {}
            for ny in range(self.Ny):
                # self._Ct[self.state][nx][ny] = {}
                for nz in range(self.Nz):
                    FluxL = [self.Flux_t[self.state, nx, ny, nz, group] for group in range(self._NOG)]
                    NuFisL = [self.NuFisM[self.state][nx][ny][nz][group] for group in range(self._NOG)]

                    Bet_k = self.BetaM[self.state][nx][ny][nz]
                    Lamb_k = self.LambdaM[self.state][nx][ny][nz]
                    _c_t_1 = self._Ct_1[self.state][nx][ny][nz]

                    Nu_Flux = [NuFisL[group] * FluxL[group] * self.Geom.Vmesh()
                               for group in range(self._NOG)]

                    self._Ct[self.state][nx][ny][nz] = \
                        [_c_t_1[prec] * np.exp(-Lamb_k[prec] * dt) +
                         Bet_k[prec] / Lamb_k[prec] * sum(Nu_Flux) * (1 - np.exp(-Lamb_k[prec] * dt))
                         if Lamb_k[prec] != 0 else 0.0 for prec in range(self.nprc)]
        return

    def calculate_source(self, dt):
        for group in range(self.Flux_t.shape[-1]):
            self._Q[group] = {self.state: {}}
            for nx in range(self.Flux_t.shape[1]):
                self._Q[group][self.state][nx] = {}
                for ny in range(self.Flux_t.shape[2]):
                    self._Q[group][self.state][nx][ny] = {}
                    for nz in range(self.Flux_t.shape[3]):
                        _Lmk = self.LambdaM[self.state][nx][ny][nz]
                        _C = self._Ct[self.state][nx][ny][nz]

                        inv_V = self.VelocityM[self.state, nx, ny, nz, group]

                        self._Q[group][self.state][nx][ny][nz] = \
                            self.chi_g[group] * sum([_Lmk[prc] * _C[prc] for prc in range(self.nprc)])
                        if self._UseDerivative:
                            self._Q[group][self.state][nx][ny][nz] +=\
                                inv_V / dt * self.Flux_t_1[self.state, nx, ny, nz, group] * self.Vmesh
        return

    def source_to_file(self):
        with open(self.__datfile, 'w') as fod:
            for group in range(self._NOG):
                for nz in range(self.Nz):
                    for ny in range(self.Ny):
                        for nx in range(self.Nx):
                            fod.write('{:15.7E}'.format(self._Q[group][self.state][nx][ny][nz]))
                fod.write('\n')
        return

    def _send_to_lu_17__(self):
        subprocess.run(['copy', '/Y', self.__datfile, 'Source_To_LU17'], shell=True)
        os.chdir(self.WorkingDirectory+'Source_To_LU17')
        msg = subprocess.run(['main.exe', *map(str, [self._NOG, self.Nx, self.Ny, self.Nz])]
                             , shell=True, capture_output=True)
        msg2 = subprocess.run(['copy', '/Y', 'fort.17', '..\\' + self._rootfile + '.src'],
                              shell=True, capture_output=True)
        os.chdir(self.WorkingDirectory)
        return msg

    def _xsu_mod__(self, NoDerivative=False, OutsideFile=None):

        if not OutsideFile:
            XSU_FILE = '{}'.format(self._CV.RootFile + '.XSU')
        else:
            XSU_FILE = OutsideFile
        subprocess.run(['move', XSU_FILE, 'XSU_MOD\\'], shell=True)
        os.chdir(self.WorkingDirectory + 'XSU_MOD')

        if NoDerivative:
            time_step = 0.0
        elif self._UseDerivative:
            time_step = self.dt
        else:
            time_step = 0.0

        msg = subprocess.run(['neutyp', '{}'.format(XSU_FILE), str(self.Equilibrium),
                              *map(lambda a: '{:12.5E}'.format(round(a, ndigits=5)), [time_step, self.t])],
                             shell=True, capture_output=True)
        assert msg.stderr in ['', b''], 'Error en la sobreescritura del archivo ROOT.XSU\n' \
                                        '{}'.format(msg.stderr.decode('utf-8'))
        os.chdir(self.WorkingDirectory)
        msg2 = subprocess.run(['move', '/Y', self.WorkingDirectory+'\\XSU_MOD\\{}'.format(XSU_FILE), XSU_FILE],
                              shell=True, capture_output=True)
        assert msg2.stderr in ['', b''], 'Error en el movimiento del archivo ROOT.XSU\n' \
                                         '{}'.format(msg2.stderr.decode('utf-8'))
        return msg

    def _get_power__(self, file=None):
        if not file:
            fid = open(self._rootfile + '.cio', 'r')
        else:
            fid = open(file, 'r')
        self.Pt = float(self.__p_parser.findall(fid.read())[0])
        fid.close()
        return


schema = {
    "type": "object",
    "properties": {
        "title": {
            "type": "string"
        },
        "n_workers": {
            "type": "integer"
        },
        "random_seed": {
            "type": "number"
        },
        "n_sims": {
            "type": "integer"
        },
        "threshold": {
            "type": "number"
        }
    },
    "required": [
        "title",
        "n_workers",
        "random_seed",
        "n_sims"
    ]
}


#Создайте класс принимающий следующие параметры – title, name, surname.
#Параметры описывают название кинофильма, имя и фамилию одного из главных
#актёров. Создайте 5 экземпляров класса, в каждом из атрибутов название фильма
#должно состоять из одного-двух слов, и сами фильмы должны быть иностранного
#производства.
#Используя наследование, создайте второй класс, где будет тоже самое
#отечественного производства. Всего должно получиться 10 фильмов.
#Выведите всё из обоих классов в столбик, пример:
#Хищник - в главной роли Арнольд Шварценеггер
#Кавказская пленница - в главной роли Александр Демьяненко

class ForeignMovies:

    def __init__(self, title, name, surname):
        self.title = title
        self.name = name
        self.surname = surname
        print(f'{title} - в главной роли {name} {surname}.')

class RussianMovies(ForeignMovies):
    def __init__(self, title, name, surname):
        super().__init__(title, name, surname)

Predator = ForeignMovies('Predator', 'Arnold', 'Schwarzenegger')
Titanic = ForeignMovies('Titanic', 'Leonardo', 'Di Caprio')
TheUntouchables = ForeignMovies('The Untouchables', 'Kevin', 'Costner')
TheGodfather = ForeignMovies('The Godfather', 'Marlon', 'Brando')
StarWars = ForeignMovies('Star Wars', 'Dart', 'Wader')
KavkazskayaPlennitsa = RussianMovies('Кавказская пленница', 'Александр', 'Демьяненко')
BrilliantovayaRuka = RussianMovies('Бриллиантовая рука', 'Юрий', 'Никулин')
IroniyaSudbi = RussianMovies('Ирония судьбы', 'Андрей', 'Мягков')
Brat = RussianMovies('Брат', 'Сергей', 'Бодров')
Voyna = RussianMovies('Война', 'Алексей', 'Чадов')


from django.contrib import admin
from django.urls import path, include, re_path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from client_API.views import *
from weather_API.views import *
from daemon_API.views import *

schema_view = get_schema_view(
   openapi.Info(
      title="Client Weather and other API",
      default_version='v1',
      description="Тестовое задание",
      terms_of_service="https://www.google.com/policies/terms/",
      contact=openapi.Contact(email="mainsample@yandex.ru"),
      license=openapi.License(name="BSD License"),
   ),
   public=True,
   permission_classes=[permissions.AllowAny],
)

urlpatterns = [
    path('', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    path('admin/', admin.site.urls),

    path('api/client/', ClientView.as_view()),
    path('api/client/register/', ClientRegisterView.as_view()),
    path('api/weather/', WeatherView.as_view()),
    path('api/daemon/', DaemonView.as_view()),

    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    #path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))

    re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
    re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    re_path(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]


from __future__ import division
from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import range
import argparse
import os
os.environ['OPL_NUM_THREADS'] = "1"
os.environ['MKL_NUM_THREADS'] = "1"
os.environ['NUMEXPR_NUM_THREADS'] = "1"
import numpy as np
import pandas as pd
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
import os.path as osp
import sys

from .simulate import get_parameters
from . import util as ut
from . import newick
from collections import OrderedDict, defaultdict

def get_hatch(paramtype):
    hatch = None
    if paramtype == 'bottleneck':
        hatch = 'o'
    elif paramtype == 'rate':
        hatch = '\\'
    return hatch

def get_parameter_names(filename):
    with open(filename) as fin:
        tree_str = fin.read().strip()
    branch_names = []
    leaf_names = []
    tree = newick.loads(tree_str, look_for_multiplier = True,
                       length_parser = ut.length_parser_str)[0]
    vset = set([])
    for node in tree.walk('postorder'):
        if node == tree:
            continue
        vset.add(node.varname)
    vnames = sorted(list(vset))
    param_names = ['loglike']
    for vname in vnames:
        param_names.append(vname + '_l')
    for vname in vnames:
        param_names.append(vname + '_m')
    param_names.append('root')
    param_names.append('ppoly')
    return param_names

def get_chains(dat, num_walkers):
    chains = []
    for i in range(num_walkers):
        chains.append(dat.iloc[i::num_walkers,:].copy())
    return chains

def get_acceptance(chain):
    assert 3/2 == 1.5
    rows0 = chain.iloc[:-1,:].values
    rows1 = chain.iloc[1:,:].values
    num_changes = np.sum(np.all(~np.equal(rows0, rows1), axis = 1))
    num_comparisons = rows0.shape[0]
    return num_changes / num_comparisons

def get_len_limits(trees, ages_file, lower_drift, upper_drift,
        lower_bottleneck = 2, upper_bottleneck = 500):
    is_bottleneck = {}
    ages_dat = pd.read_csv(ages_file, sep = '\t')

    # first, get min and max multipliers
    mins = defaultdict(lambda: np.inf)
    maxes = defaultdict(lambda: -np.inf)
    for tree in trees:
        for node in tree.walk():
            varname = node.varname
            is_bottleneck[varname] = node.is_bottleneck
            mult = node.multipliername
            if mult is None:
                mins[varname] = 1.0
                maxes[varname] = 1.0
            else:
                minv, maxv = ages_dat[mult].min(), ages_dat[mult].max()
                if varname in mins:
                    mins[varname] = min(mins[varname], minv)
                else:
                    mins[varname] = minv
                if varname in maxes:
                    maxes[varname] = max(maxes[varname], maxv)
                else:
                    maxes[varname] = maxv
    limits = {}

    # so far, it has just been the multipliers, now need to make them limits
    assert set(mins.keys()) == set(maxes.keys())
    for varname in list(mins.keys()):
        minv = mins[varname]
        maxv = maxes[varname]
        ld = np.array([minv, maxv])
        #mults = limits[varname]
        if is_bottleneck[varname]:
            ld, ud = lower_bottleneck, upper_bottleneck
        else:
            ld, ud = lower_drift, upper_drift
        limits[varname] = [np.log10(ld / minv),
                np.log10(ud / maxv)]
    return limits

def make_figures(
        results, tree_files, num_walkers, ages_file, prefix = None,
        img_format = 'png', dpi = 300, colors_and_types = None,
        length_prior_limits = (-6,0.477), mutation_prior_limits = (-8,-1),
        true_parameters = None, which_plot = 'histograms',
        trace_burnin_steps = 500, posterior_burnin_frac = 0.3,
        add_title = True, log_uniform_drift_priors = False):

    do_histograms = False
    if which_plot in ('histograms', 'both'):
        do_histograms = True

    do_traces = False
    if which_plot in ('traces', 'both'):
        do_traces = True

    do_corner = False
    if which_plot == 'corner':
        do_corner = True

    validparamtypes = ('fixed', 'rate', 'bottleneck')

    ###################
    # output filenames
    ###################

    if prefix is None:
        path, filename = osp.split(results)
        prefix = filename.split('.')[0]

    trace_file = 'traces_' + prefix + '.' + img_format
    posterior_histograms_file = ('posterior_histograms_' + prefix + '.' +
            img_format)
    corner_file = ('corner_' + prefix + '.' + img_format)

    # loading colors and (drift) parameter types
    if do_histograms:
        if colors_and_types is not None:
            colors = OrderedDict()
            types = OrderedDict()
            #priortypes = OrderedDict()
            with open(colors_and_types) as inp:
                for line in inp:
                    spline = line.split()
                    param, color, paramtype = spline[:3]
                    if paramtype not in validparamtypes:
                        raise ValueError('invalid parameter type: must be "fixed", '
                                         '"rate", or "bottleneck"')
                    colors[param] = color
                    types[param] = paramtype

    ###################
    # loading the data
    ###################
    all_varnames = []
    trees = []
    with open(tree_files) as trees_in:
        for line in trees_in:
            fn = line.strip()
            with open(fn) as fin:
                tree_str = fin.read().strip()
                tree = newick.loads(tree_str,
                                    look_for_multiplier = True,
                                    length_parser = ut.length_parser_str)[0]
            trees.append(tree)
            cols = get_parameter_names(fn)
            varnames = [col[:-2] for col in cols if col.endswith('_l')]
            all_varnames += varnames
    try:
        # first try no header
        dat_m1 = pd.read_csv(results,
                sep = '\t', header = None, names = cols, dtype = np.float64,
                comment = '#')
    except ValueError:
        # then with header
        dat_m1 = pd.read_csv(results,
                sep = '\t', header = 0, names = cols, dtype = np.float64,
                comment = '#')
    # drift parameters are output in natural scale
    #dat_m1.loc[:,dat_m1.columns.str.contains('_l')] = np.log10(np.abs(
    #        dat_m1.loc[:,dat_m1.columns.str.contains('_l')]))
    # mutation parameters are given in log-10 scale
    dat_m1.loc[:,dat_m1.columns.str.contains('_m')] = -np.abs(
            dat_m1.loc[:,dat_m1.columns.str.contains('_m')])
    dat_m1.loc[:,'root'] = -np.abs(dat_m1.loc[:,'root'])
    dat_m1.loc[:,'ppoly'] = -np.abs(dat_m1.loc[:,'ppoly'])
    num_walkers = num_walkers
    burnin_steps = trace_burnin_steps
    chains = get_chains(dat_m1.iloc[burnin_steps*num_walkers:,:].copy(),
            num_walkers)

    # getting (optional) length prior limits
    if do_histograms:
        if length_prior_limits is not None:
            lower_l, upper_l = length_prior_limits
            lower_l = 10**float(lower_l)
            upper_l = 10**float(upper_l)
            length_limits = get_len_limits(trees, ages_file, lower_l, upper_l)

    # getting true parameters
    if true_parameters is not None:
        true_bls, true_mrs, true_ab, true_ppoly = get_parameters(
                true_parameters, tree)
        true_params = [true_bls[v] for v in varnames]
        true_params += [true_mrs[v] for v in varnames]
        true_params += [true_ab, true_ppoly]

    if do_traces:
        f, axes = plt.subplots(
                dat_m1.shape[1], 1, figsize = (10, 2.5*dat_m1.shape[1]))
        for i in range(dat_m1.shape[1]):
            col = list(dat_m1.columns)[i]
            ax = axes[i]
            if not np.any(dat_m1[col] <= 0.0):
                for chain in chains:
                    ax.plot(np.arange(chain.shape[0]),np.log10(chain.iloc[:,i]),
                            lw = 0.02)
                plot_log = True
            else:
                for chain in chains:
                    ax.plot(np.arange(chain.shape[0]),(chain.iloc[:,i]), lw = 0.02)
                plot_log = False
            if true_parameters is not None and i > 0:
                true_p = true_params[i-1]
                if plot_log:
                    ax.axhline(np.log10(true_p))
                else:
                    # now all the parameters are log10 transformed
                    ax.axhline(np.log10(true_p))
            ax.set_title(dat_m1.columns[i])
        f.tight_layout()
        plt.savefig(trace_file)

    # posterior histograms
    if do_histograms:
        if colors_and_types is None:
            final = False  # not a 'final', publication plot
            num_plots = dat_m1.shape[1]
            nrows = 4
            ncols = int(np.ceil(num_plots / nrows))
            f, axes = plt.subplots(nrows,ncols, figsize = (12,12*nrows/ncols))

        else:
            final = True   # a 'final', publication plot
            nrows = 2
            ncols = len(varnames)
            num_plots = (dat_m1.shape[1]-3)*2  # (no loglike, or the two root params)
            f, axes = plt.subplots(nrows,ncols, figsize = (2*ncols,4))
            mpl.rcParams.update({'font.size': 5})
            mpl.rcParams.update({'axes.labelsize': 'small'})

        burnin = int(np.floor(dat_m1.shape[0] * posterior_burnin_frac))
        dat_burn = dat_m1.iloc[burnin:,:]
        plotted = np.tile([False], nrows*ncols)
        plotted.shape = (nrows,ncols)

        if final:
            if not add_title:
                mpl.rc('text', usetex = False)
            #mpl.rcParams.update({'font.family': 'Arial'})
            colornames = list(colors.keys())
            for counter, var in enumerate(colornames):
                if true_parameters is not None:
                    true_l = np.log10(true_bls[var])
                    true_m = np.log10(true_mrs[var])
                # length
                if colors_and_types is not None:
                    try:
                        c = colors[var]
                        t = types[var]
                    except KeyError:
                        c = 'gray'
                        t = 'fixed'
                else:
                    c = 'gray'
                    t = 'fixed'
                hatch = get_hatch(t)

                ax = axes[0][counter]
                col = var + '_l'
                if length_prior_limits is not None:
                    minv, maxv = length_limits[var]
                else:
                    minv, maxv = dat_burn[col].min(), dat_burn[col].max()

                lower = np.floor(minv)
                upper = np.ceil(maxv)
                bins = np.linspace(lower, upper, 50)
                ticks = np.arange(lower, upper+1).astype(np.int)
                if ticks.shape[0] > 5:
                    ticks = np.arange(lower, upper+1, 2).astype(np.int)

                if length_prior_limits is not None:
                    x = np.linspace(minv, maxv, 1000)
                    if log_uniform_drift_priors:
                        y = 1.0/(maxv-minv)
                    else:
                        y = 10**x * np.log(10) / (10**maxv - 10**minv)
                    ax.fill_between(x, 0, y, alpha = 0.3, color = 'gray')

                ax.hist(dat_burn[col], bins, color = c, hatch = hatch,
                        normed = True)
                ax.set_xticks(ticks)
                #ax.set_xscale('log')
                ax.get_yaxis().set_visible(False)
                if true_parameters is not None:
                    ax.axvline(true_l, ls = '--', c = 'black', lw = 2)
                if add_title:
                    ax.set_title(col)

                ax = axes[1][counter]
                col = var + '_m'
                if mutation_prior_limits is None:
                    minv, maxv = dat_burn[col].min(), dat_burn[col].max()
                else:
                    minv, maxv = mutation_prior_limits
                    minv = float(minv)
                    maxv = float(maxv)
                lower = np.floor(minv)
                upper = np.ceil(maxv)
                ticks = np.arange(lower, upper+1).astype(np.int)
                if ticks.shape[0] > 5:
                    ticks = np.arange(lower, upper+1, 2).astype(np.int)
                bins = np.linspace(lower, upper, 50)

                if mutation_prior_limits is not None:
                    x = np.linspace(minv, maxv, 1000)
                    y = 1.0/(maxv-minv)
                    ax.fill_between(x, 0, y, alpha = 0.3, color = 'gray')

                ax.hist(dat_burn[col], bins, color = c, hatch = hatch,
                        normed = True)
                ax.set_xticks(ticks)
                ax.get_yaxis().set_visible(False)
                if true_parameters is not None:
                    ax.axvline(true_m, ls = '--', c = 'black', lw = 2)
                if add_title:
                    ax.set_title(col)

        else:  # not final
            counter = 0
            for row_idx in range(nrows):
                for col_idx in range(ncols):
                    ax = axes[row_idx][col_idx]
                    try:
                        col = cols[counter]
                    except IndexError:
                        break
                    if true_parameters is not None and counter > 0:
                        true_p = true_params[counter-1]
                    minv, maxv = dat_burn[col].min(), dat_burn[col].max()
                    if colors_and_types is not None:
                        var = col[:-2]
                        try:
                            c = colors[var]
                            t = types[var]
                        except KeyError:
                            c = 'gray'
                            t = 'fixed'
                    else:
                        c = 'gray'
                        t = 'fixed'
                    hatch = get_hatch(t)
                    if minv >= 0:
                        lower = np.floor(np.log10(minv))
                        upper = np.ceil(np.log10(maxv))
                        bins = np.logspace(lower, upper, 50)
                        ax.hist(dat_burn[col], bins, color = c, hatch = hatch)
                        ax.set_xscale('log')
                    else:
                        if col != 'loglike':
                            lower = np.floor(minv)
                            upper = np.ceil(maxv)
                            bins = np.logspace(lower, upper, 50)
                            datp = 10**dat_burn[col]
                            ax.hist(datp, bins, color = 'gray')
                            ax.set_xscale('log')
                        else:
                            lower = np.floor(minv)
                            upper = np.ceil(maxv)
                            bins = np.linspace(lower, upper, 50)
                            ax.hist(dat_burn[col], bins, color = 'gray')
                    if true_parameters is not None and counter > 0:
                        ax.axvline(true_p)
                    ax.set_title(col)
                    ax.get_yaxis().set_visible(False)
                    counter += 1
                    plotted[row_idx][col_idx] = True

            for row_idx in range(nrows):
                for col_idx in range(ncols):
                    if not plotted[row_idx][col_idx]:
                        ax = axes[row_idx][col_idx]
                        f.delaxes(ax)

        f.tight_layout()
        plt.savefig(posterior_histograms_file, dpi = dpi)

    if do_corner:
        try:
            import corner
        except ImportError:
            err = 'plotting corner plots requires installing corner'
            raise ImportError(err)

        try:
            import numpy.random as npr
        except ImportError:
            err = 'corner plots require numpy'
            raise ImportError(err)

        burnin = int(posterior_burnin_frac * dat_m1.shape[0] + 0.5)
        dat_burn = dat_m1.iloc[burnin:,:]
        sample_size = min(500000, dat_burn.shape[0])
        idxs = np.sort(npr.choice(dat_burn.shape[0], size = sample_size,
            replace = False))
        dat_samp = dat_burn.iloc[idxs,:]
        corner.corner(dat_samp)
        plt.savefig(corner_file)

def _run_make_figures(args):

    make_figures(
            results = args.results,
            tree_files = args.tree,
            num_walkers = args.num_walkers,
            ages_file = args.ages_file,
            prefix = args.prefix,
            img_format = args.format,
            dpi = args.dpi,
            colors_and_types = args.colors_and_types,
            length_prior_limits = args.length_prior_limits,
            mutation_prior_limits = args.mutation_prior_limits,
            true_parameters = args.true_parameters,
            which_plot = args.plot,
            trace_burnin_steps = args.trace_burnin_steps,
            posterior_burnin_frac = args.posterior_burnin_frac,
            add_title = args.add_title,
            log_uniform_drift_priors = not args.uniform_drift_priors)


import os

import docker
import dotenv
import mlflow
import requests
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel

dotenv.load_dotenv()
app = FastAPI()
webhook_url = os.environ["webhook_url"]
thread_id = "990434052538507294"
webhook_url += f"?thread_id={thread_id}"

class Parameters(BaseModel):
    experiment_name: str
    git_uri: str
    MLproject_location: str = ""
    commit_hash: str = "main"
    dataset_repo: str = "not_used"
    base_model: str = "None"

@app.get("/")
async def root():
    return {"message": "Hello, please go to /docs."}

@app.post("/retrain")
async def retrain(parameters: Parameters):
    # content used for notification that has detail req body.
    # so user know which retrain is running.
    content = "MLflow retrain running\n```\n{\n"
    for k, v in parameters:
        content += f'\t"{k}": "{v}",\n'
    content += "}\n```"
    requests.post(webhook_url, json={"content": content})
    uri = parameters.git_uri
    params = {"base_model": parameters.base_model, "dataset_repo": parameters.dataset_repo} # pharse argument
    if parameters.MLproject_location != "":
        uri += f"#{parameters.MLproject_location}"
    try:
        local_submitted_run = mlflow.projects.run(
            uri=uri,
            docker_args={"gpus": "device=0"},
            experiment_name=parameters.experiment_name,
            version=parameters.commit_hash,
            parameters=params
        )
    except Exception as e:
        requests.post(
            webhook_url, json={"content": f"MLflow retrain failed : {str(e)}"}
        )
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
    # Auto remove image that has been used for retrain, so it will not be spamming.
    mlflow_client = mlflow.tracking.MlflowClient()
    run = mlflow_client.get_run(local_submitted_run.run_id)
    docker_image_name = run.data.tags["mlflow.docker.image.uri"]
    docker_client = docker.from_env()
    try:
        docker_client.images.remove(docker_image_name)
    except docker.errors.ImageNotFound as e:
        print(
            f"Can not remove image '{docker_image_name}' because image '{docker_image_name}' not found"
        )
    run_url = f"http://192.168.103.67:5007/#/experiments/{run.info.experiment_id}/runs/{local_submitted_run.run_id}"
    content = f"MLflow retrain succeeded, for more detail see\n{run_url}"
    requests.post(webhook_url, json={"content": content})
    return {"message": "Success"}


#----------------------------------------------------------------------------#
# Imports
#----------------------------------------------------------------------------#

import json
import dateutil.parser
import babel
from flask import Flask, render_template, request, Response, flash, redirect, url_for, abort, jsonify
from flask_moment import Moment
from werkzeug.utils import secure_filename
from flask_sqlalchemy import SQLAlchemy
import logging
import os
from logging import Formatter, FileHandler
from flask_wtf import Form
from flask_wtf.csrf import CSRFProtect
from flask_migrate import Migrate
from forms import *
#----------------------------------------------------------------------------#
# App Config.
#----------------------------------------------------------------------------#

app = Flask(__name__)
moment = Moment(app)
app.config.from_object('config')
db = SQLAlchemy(app)
migrate = Migrate(app, db)
csrf = CSRFProtect(app)
ALLOWED_EXTENSIONS = {'jpeg', 'jpg', 'png'}

# TODO: connect to a local postgresql database (Completed)

#----------------------------------------------------------------------------#
# Models.
#----------------------------------------------------------------------------#

# Venue has many Show
class Venue(db.Model):
    __tablename__ = 'Venue'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    address = db.Column(db.String(120))
    phone = db.Column(db.String(120))
    image_link = db.Column(db.String(500))
    facebook_link = db.Column(db.String(120))
    website = db.Column(db.String(120))
    seeking_talent = db.Column(db.Boolean, default=False)
    seeking_description = db.Column(db.String(120))
    shows = db.relationship('Show', backref='venue', lazy=True, cascade='all, delete-orphan')
    genres = db.Column(db.String)
    city_state_id = db.Column(db.Integer, db.ForeignKey('CityState.id', ondelete='CASCADE'), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.today())

class CityState(db.Model):
    __tablename__ = 'CityState'
    id = db.Column(db.Integer, primary_key=True)
    city = db.Column(db.String(120), nullable=False, unique=True)
    venues = db.relationship('Venue', backref='city_state', lazy=True, cascade='all, delete-orphan')
    state = db.Column(db.String(120), nullable=False, unique=True)
    artists = db.relationship('Artist', backref='city_state', lazy=True, cascade='all, delete-orphan')

# TODO: implement any missing fields, as a database migration using Flask-Migrate (Completed)
# Artist has many Shows
class Artist(db.Model):
    __tablename__ = 'Artist'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    phone = db.Column(db.String(120))
    genres = db.Column(db.String(120))
    image_link = db.Column(db.String(500))
    facebook_link = db.Column(db.String(120))
    website = db.Column(db.String(120))
    seeking_venue = db.Column(db.Boolean, default=False)
    seeking_description = db.Column(db.String(120))
    shows = db.relationship('Show', backref='artist', lazy=True, cascade='all, delete-orphan')
    city_state_id = db.Column(db.Integer, db.ForeignKey('CityState.id', ondelete='CASCADE'), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.now())
    available_time = db.Column(db.String)

# TODO: implement any missing fields, as a database migration using Flask-Migrate (Completed)

# TODO (Completed): Implement Show and Artist models, and complete all model relationships and properties, as a database migration.
class Show(db.Model):
    __tablename__ = 'Show'
    id = db.Column(db.Integer, primary_key=True)
    venue_id = db.Column(db.Integer, db.ForeignKey('Venue.id', ondelete='CASCADE'), nullable=False)
    artist_id = db.Column(db.Integer, db.ForeignKey('Artist.id', ondelete='CASCADE'), nullable=False)
    start_time = db.Column(db.DateTime)

#----------------------------------------------------------------------------#
# Filters.
#----------------------------------------------------------------------------#

def format_datetime(value, format='medium'):
  date = dateutil.parser.parse(value)
  if format == 'full':
      format="EEEE MMMM, d, y 'at' h:mma"
  elif format == 'medium':
      format="EE MM, dd, y h:mma"
  return babel.dates.format_datetime(date, format)

app.jinja_env.filters['datetime'] = format_datetime

#----------------------------------------------------------------------------#
# Controllers.
#----------------------------------------------------------------------------#

@app.route('/')
def index():
  artists = Artist.query.order_by(db.desc(Artist.created_at)).limit(10)
  venues = Venue.query.order_by(db.desc(Venue.created_at)).limit(10)
  return render_template('pages/home.html', artists=artists, venues=venues)

#  Venues
#  ----------------------------------------------------------------

@app.route('/venues')
def venues():
  # TODO: replace with real venues data. (Completed)
  city_state = CityState.query.all()

  data = []
  for c in city_state:
      if c.venues:
          ven_list = [{
            "id": venue.id,
            "name": venue.name
          } for venue in c.venues]
          data.append({
            "city": c.city.title(),
            "state": c.state.upper(),
            "venues": ven_list
          })

  return render_template('pages/venues.html', areas=data);

@app.route('/venues/search', methods=['POST'])
def search_venues():
  # TODO: (Completed) implement search on artists with partial string search. Ensure it is case-insensitive.
  search_term = request.form.get('search_term', '')
  venues = Venue.query.filter(Venue.genres.ilike('%{}%'.format(search_term))).all()
  print(venues)
  response = {
    "count": len(venues),
    "data": [{
        "id": venue.id,
        "name": venue.name
    } for venue in venues]
  }
  return render_template('pages/search_venues.html', results=response, search_term=search_term)

@app.route('/venues/search_by_city', methods=['POST'])
def search_venues_by_city():
    search_term = request.form.get('search_term', '')
    city_state = CityState.query.filter(CityState.city.ilike('%{}%'.format(search_term))).all()
    venues = []
    for c in city_state:
        for venue in c.venues:
            venues.append(venue)

    print(venues)
    response = {
      "count": len(venues),
      "data": [{
          "id": venue.id,
          "name": venue.name
      } for venue in venues]
    }
    return render_template('pages/search_venues.html', results=response, search_term=search_term)

@app.route('/venues/<int:venue_id>')
def show_venue(venue_id):
  # TODO: replace with real venue data from the venues table, using venue_id (Completed)
  venue = Venue.query.get(venue_id)
  past_shows, upcoming_shows = get_past_upcom_shows(venue.shows, 0)
  genres = venue.genres.split(',')[:-1]
  city_state = CityState.query.get(venue.city_state_id)
  data = {
    "id": venue.id,
    "name": venue.name,
    "city": city_state.city,
    "state": city_state.state,
    "address": venue.address,
    "phone": venue.phone,
    "genres": genres,
    "facebook_link": venue.facebook_link,
    "seeking_talent": venue.seeking_talent,
    "seeking_description": venue.seeking_description,
    "website": venue.website,
    "image_link": venue.image_link,
    "past_shows": past_shows,
    "upcoming_shows": upcoming_shows,
    "past_shows_count": len(past_shows),
    "upcoming_shows_count": len(upcoming_shows)
  }
  return render_template('pages/show_venue.html', venue=data)

# Helper function to get Past_Shows and Upcoming_Shows | return Tuple(p_shows, u_shows)
def get_past_upcom_shows(shows, category): # Category:  0 => Venue | 1 => Artist
    past_shows = []
    upcoming_shows = []
    if category: # Get shows for Artist
        for show in shows:
            venue = Venue.query.get(show.venue_id)
            if show.start_time < datetime.today():
                past_shows.append({
                    "venue_id": show.venue_id,
                    "venue_name": venue.name,
                    "venue_image_link": venue.image_link,
                    "start_time": show.start_time.strftime("%m/%d/%Y, %H:%M:%S")
                })
            else:
                upcoming_shows.append({
                    "venue_id": show.venue_id,
                    "venue_name": venue.name,
                    "venue_image_link": venue.image_link,
                    "start_time": show.start_time.strftime("%m/%d/%Y, %H:%M:%S")
                })
    else: # Get shows for Venue
        for show in shows:
            artist = Artist.query.get(show.artist_id)
            if show.start_time < datetime.today():
                past_shows.append({
                    "artist_id": show.artist_id,
                    "artist_name": artist.name,
                    "artist_image_link": artist.image_link,
                    "start_time": show.start_time.strftime("%m/%d/%Y, %H:%M:%S")
                })
            else:
                upcoming_shows.append({
                    "artist_id": show.artist_id,
                    "artist_name": Artist.query.get(show.artist_id).name,
                    "artist_image_link": Artist.query.get(show.artist_id).image_link,
                    "start_time": show.start_time.strftime("%m/%d/%Y, %H:%M:%S")
                })
    return (past_shows, upcoming_shows)

#  Create Venue
#  ----------------------------------------------------------------

@app.route('/venues/create', methods=['GET'])
def create_venue_form():
  form = VenueForm()
  return render_template('forms/new_venue.html', form=form)

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def create_setup(request, form): # return Tuple(file, genres_list, cityState obj)
    error=False
    genres_list = ''
    filepath = ''
    city_state = None
    try:
        # Get image_link
        file = request.files['image_link']
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
            file.save(filepath)
            for g in form.genres.data:
                genres_list += '{},'.format(g)
            # Check if City and State Already exists
            city = form.city.data.strip().title()
            state = form.state.data
            city_state = CityState.query.filter_by(state=state).first()
            if not city_state:
                city_state = CityState(
                  city=city,
                  state=state
                )
    except:
        error=True
        form.errors['image_link'] = ['Invalid image extension']
    finally:
        return (filepath, genres_list, city_state) if not error else None

@app.route('/venues/create', methods=['POST'])
def create_venue_submission():
  # TODO: insert form data as a new Venue record in the db, instead (Completed)
  # TODO: on unsuccessful db insert, flash an error instead. (Completed)
  # TODO: modify data to be the data object returned from db insertion (Completed)
  form = VenueForm()
  error = False
  genres_list = ''
  if form.validate():
      try:
          # Get image_link
          filepath, genres_list, city_state = create_setup(request, form)
          venue = Venue(
            name=form.name.data.strip(),
            address=form.address.data.strip(),
            phone=form.phone.data.strip(),
            genres=genres_list,
            image_link=filepath,
            seeking_talent=form.seeking_talent.data,
            seeking_description=form.seeking_description.data.strip(),
            website=form.website.data.strip(),
            facebook_link=form.facebook_link.data.strip())
          city_state.venues.append(venue)
          db.session.add(venue)
          db.session.commit()
          print('File : {}'.format(filepath))
      except Exception as e:
          db.session.rollback()
          error = True
          print(e)
      finally:
          db.session.close()
      if not error:
          flash('Venue "' + form.name.data + '" was successfully listed!')
          return redirect(url_for('index', errors=form.errors))
      else:
          print(form.errors)
          flash('Error: Venue "' + form.name.data + '" Cannot be created')
          return render_template('forms/new_venue.html', form=form, errors=form.errors)

  print(form.errors)
  return render_template('forms/new_venue.html', form=form, errors=form.errors)

@app.route('/venues/<venue_id>', methods=['POST'])
def delete_venue(venue_id):
  # TODO: Complete this endpoint for taking a venue_id, and using (Compeleted)
  error=False
  try:
      venue = Venue.query.get(venue_id)
      db.session.delete(venue)
      db.session.commit()
  except Exception as e:
      error=True
      db.session.rollback()
      print(e)
  finally:
      db.session.close()
      if not error:
          flash('Venue ID: "' + venue_id + '" Deleted successfully')
          print('Venue with ID: {} | Deleted successfully'.format(venue_id))
      else:
          flash('ERROR: Venue ID: "' + venue_id + '" Cannot be deleted')
          print('ERROR: Venue with ID: {} | Colud not be deleted'.format(venue_id))
          return render_template('pages/show_venue.html', venue=venue)

  # BONUS CHALLENGE: Implement a button to delete a Venue on a Venue Page, have it so that
  # clicking that button delete it from the db then redirect the user to the homepage
  return redirect(url_for('index'))

#  Artists
#  ----------------------------------------------------------------
@app.route('/artists')
def artists():
  # TODO: replace with real data returned from querying the database (Completed)
  artists = Artist.query.all()
  data = [{
    "id": artist.id,
    "name": artist.name,
  } for artist in artists]
  return render_template('pages/artists.html', artists=data)

@app.route('/artists/search', methods=['POST'])
def search_artists():
  # TODO: (Completed) implement search on artists with partial string search. Ensure it is case-insensitive.
  search_term = request.form.get('search_term', '')
  artists = Artist.query.filter(Artist.name.ilike('%{}%'.format(search_term))).all()
  response={
    "count": len(artists),
    "data": [{
        "id": artist.id,
        "name": artist.name,
        "num_upcoming_shows": filter(lambda show: show.start_time >= datetime.today(), artist.shows)
    } for artist in artists]
  }
  return render_template('pages/search_artists.html', results=response, search_term=search_term)

@app.route('/artists/search_by_city', methods=['POST'])
def search_artists_by_city():
    search_term = request.form.get('search_term', '')
    city_state = CityState.query.filter(CityState.city.ilike('%{}%'.format(search_term))).all()
    artists = []
    for c in city_state:
        for artist in c.artists:
            artists.append(artist)

    print(artists)
    response = {
      "count": len(artists),
      "data": [{
          "id": artist.id,
          "name": artist.name
      } for artist in artists]
    }
    return render_template('pages/search_venues.html', results=response, search_term=search_term)

@app.route('/artists/<int:artist_id>')
def show_artist(artist_id):
  # TODO: (Completed) replace with real artist data from the artists table, using artist_id
  artist = Artist.query.get(artist_id)
  genres = artist.genres.split(',')[:-1]
  city_state = CityState.query.get(artist.city_state_id)
  past_shows, upcoming_shows = get_past_upcom_shows(artist.shows, 1)

  available_time_list = artist.available_time.split(',')[:] if artist.available_time else []
  data={
    "id": artist.id,
    "name": artist.name,
    "genres": genres,
    "facebook_link": artist.facebook_link,
    "website": artist.website,
    "city": city_state.city,
    "state": city_state.state,
    "phone": artist.phone,
    "available_time": available_time_list,
    "seeking_venue": artist.seeking_venue,
    "seeking_description": artist.seeking_description,
    "image_link": artist.image_link,
    "past_shows": past_shows,
    "upcoming_shows": upcoming_shows,
    "past_shows_count": len(past_shows),
    "upcoming_shows_count": len(upcoming_shows),
  }
  print(data)
  return render_template('pages/show_artist.html', artist=data)

#  Update
#  ----------------------------------------------------------------
@app.route('/artists/<int:artist_id>/edit', methods=['GET'])
def edit_artist(artist_id):
  artist = Artist.query.get(artist_id)
  city_state = CityState.query.get(artist.city_state_id)
  form = ArtistForm(seeking_venue = 1 if artist.seeking_venue else 0, genres=artist.genres.split(',')[:-1], state=city_state.state)
  artist={
    "id": artist.id,
    "name": artist.name,
    "city": city_state.city,
    "state": city_state.state,
    "phone": artist.phone,
    "website": artist.website,
    "available_time": artist.available_time,
    "facebook_link": artist.facebook_link,
    "seeking_venue": artist.seeking_venue,
    "seeking_description": artist.seeking_description,
    "image_link": artist.image_link
  }
  # TODO: populate form with fields from artist with ID <artist_id> (Completed)
  return render_template('forms/edit_artist.html', form=form, artist=artist)

@app.route('/artists/<int:artist_id>/edit', methods=['POST'])
def edit_artist_submission(artist_id):
  # TODO: take values from the form submitted, and update existing (Completed)
  form = ArtistForm()
  error=False
  is_new_city_state = False
  artist = {}
  genres_list = ''
  filepath = ''
  try:
      # Get image_link
      file = request.files['image_link']
      if file and allowed_file(file.filename):
          filename = secure_filename(file.filename)
          filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
          file.save(filepath)
          artist = Artist.query.get(artist_id)
          city_state_old = CityState.query.get(artist.city_state_id)
          city_state_new = CityState.query.filter_by(state=form.state.data).first()
          if not city_state_new:
              city_state_new = CityState(city=form.city.data.strip(), state=form.data.state)
              db.session.add(city_state_new)
              city_state_new.artists.append(artist)
          else:
              if city_state_old.state != city_state_new.state:
                  is_new_city_state=True

          for g in form.genres.data:
              genres_list += '{},'.format(g)
          artist.name = form.name.data.strip()
          artist.phone = form.phone.data.strip()
          artist.genres = genres_list
          artist.website = form.website.data.strip()
          artist.facebook_link = form.facebook_link.data.strip()
          artist.seeking_venue = form.seeking_venue.data
          artist.available_time = form.available_time.data.strip()
          artist.seeking_description = form.seeking_description.data.strip()
          artist.image_link = filepath
          if is_new_city_state:
              db.session.add(artist)
              city_state_new.artists.append(artist)
              # city_state_old.artists.remove(artist)

          db.session.commit()
      else:
          artist = {
            'name': form.name.data,
            'phone': form.phone.data,
            'genres': form.genres.data,
            'city': form.city.data,
            'state': form.state.data,
            'website': form.website.data,
            'available_time': form.available_time,
            'facebook_link': form.facebook_link.data,
            'seeking_venue': form.seeking_venue.data,
            'seeking_description': form.seeking_description.data,
          }
          print(artist)
          error=True
          form.errors['image_link'] = ['Invalid image']
  except Exception as e:
      print(e)
      artist = {
        'name': form.name.data,
        'phone': form.phone.data,
        'genres': form.genres.data,
        'city': form.city.data,
        'state': form.state.data,
        'website': form.website.data,
        'available_time': form.available_time,
        'facebook_link': form.facebook_link.data,
        'seeking_venue': form.seeking_venue.data,
        'seeking_description': form.seeking_description.data,
      }
      error=True
      db.session.rollback()
  finally:
      db.session.close()
      if not error:
          flash('Artist "' + form.name.data + '" Update Successfully')
          return redirect(url_for('show_artist', artist_id=artist_id))
      else:
          flash('ERROR: Artist "' + form.name.data + '" Cannot be updated')
          return render_template('forms/edit_artist.html', form=form, artist=artist, errors=form.errors)

@app.route('/venues/<int:venue_id>/edit', methods=['GET'])
def edit_venue(venue_id):
  venue = Venue.query.get(venue_id)
  city_state = CityState.query.get(venue.city_state_id)
  form = VenueForm(seeking_talent = 1 if venue.seeking_talent else 0, genres=venue.genres.split(',')[:-1], state=city_state.state)
  venue={
    "id": venue.id,
    "name": venue.name,
    "city": city_state.city,
    "state": city_state.state,
    "phone": venue.phone,
    "address": venue.address,
    "website": venue.website,
    "facebook_link": venue.facebook_link,
    "seeking_talent": venue.seeking_talent,
    "seeking_description": venue.seeking_description,
    "image_link": venue.image_link
  }
  # TODO: populate form with values from venue with ID <venue_id> (Completed)
  return render_template('forms/edit_venue.html', form=form, venue=venue)

@app.route('/venues/<int:venue_id>/edit', methods=['POST'])
def edit_venue_submission(venue_id):
  # TODO: take values from the form submitted, and update existing (Completed)
  form = VenueForm()
  error=False
  is_new_city_state = False
  venue = {}
  genres_list = ''
  filepath = ''
  try:
      # Get image_link
      file = request.files['image_link']
      if file and allowed_file(file.filename):
          filename = secure_filename(file.filename)
          filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
          file.save(filepath)
          venue = Venue.query.get(venue_id)
          city_state_old = CityState.query.get(venue.city_state_id)
          city_state_new = CityState.query.filter_by(state=form.state.data).first()
          if not city_state_new:
              city_state_new = CityState(city=form.city.data.strip(), state=form.state.data)
              db.session.add(city_state_new)
              city_state_new.venues.append(venue)
          else:
              if city_state_old.state != city_state_new.state:
                  is_new_city_state=True

          for g in form.genres.data:
              genres_list += '{},'.format(g)
          venue.name = form.name.data.strip()
          venue.phone = form.phone.data.strip()
          venue.genres = genres_list
          venue.address = form.address.data.strip()
          venue.website = form.website.data.strip()
          venue.facebook_link = form.facebook_link.data.strip()
          venue.seeking_talent = form.seeking_talent.data
          venue.seeking_description = form.seeking_description.data.strip()
          venue.image_link = filepath
          if is_new_city_state:
              db.session.add(venue)
              city_state_new.venues.append(venue)

          db.session.commit()
      else:
          venue = {
            'name': form.name.data,
            'phone': form.phone.data,
            'genres': form.genres.data,
            'city': form.city.data,
            'address': form.address.data,
            'state': form.state.data,
            'website': form.website.data,
            'facebook_link': form.facebook_link.data,
            'seeking_talent': form.seeking_talent.data,
            'seeking_description': form.seeking_description.data,
          }
          print(venue)
          error=True
          form.errors['image_link'] = ['Invalid image']
  except Exception as e:
      print(e)
      venue = {
        'name': form.name.data,
        'phone': form.phone.data,
        'genres': form.genres.data,
        'city': form.city.data,
        'address': form.address.data,
        'state': form.state.data,
        'website': form.website.data,
        'facebook_link': form.facebook_link.data,
        'seeking_talent': form.seeking_talent.data,
        'seeking_description': form.seeking_description.data,
      }
      error=True
      db.session.rollback()
  finally:
      db.session.close()
      if not error:
          flash('Venue "' + form.name.data + '" Update Successfully')
          return redirect(url_for('show_venue', venue_id=venue_id))
      else:
          flash('ERROR: Venue "' + form.name.data + '" Cannot be updated')
          return render_template('forms/edit_venue.html', form=form, errors=form.errors, venue=venue)

#  Create Artist
#  ----------------------------------------------------------------

@app.route('/artists/create', methods=['GET'])
def create_artist_form():
  form = ArtistForm()
  return render_template('forms/new_artist.html', form=form)

@app.route('/artists/create', methods=['POST'])
def create_artist_submission():
  # TODO: insert form data as a new Venue record in the db, instead (Completed)
  # TODO: on unsuccessful db insert, flash an error instead. (Completed)
  # TODO: modify data to be the data object returned from db insertion (Completed)
  error = False
  form = ArtistForm()
  genres_list = ''
  if form.validate():
      try:
          filepath, genres_list, city_state = create_setup(request, form)
          artist = Artist(
            name = form.name.data.strip(),
            phone = form.phone.data.strip(),
            genres = genres_list,
            image_link = filepath,
            seeking_venue = form.seeking_venue.data,
            seeking_description = form.seeking_description.data.strip(),
            website = form.website.data.strip(),
            facebook_link = form.facebook_link.data.strip())
          city_state.artists.append(artist)
          db.session.add(artist)
          db.session.commit()
      except Exception as e:
          print(e)
          error=True
          db.session.rollback()
      finally:
          db.session.close()
          if not error:
              flash('Artist "' + request.form['name'] + '" Added Successfully')
              return redirect(url_for('index', errors=form.errors))
          else:
              flash('ERROR: Artist "' + request.form['name'] + '" cannot be added')
              return render_template('forms/new_artist.html', form=form, errors=form.errors)

  print(form.errors)
  return render_template('forms/new_artist.html', form=form, errors=form.errors)

#  Shows
#  ----------------------------------------------------------------

@app.route('/shows')
def shows():
  # TODO: replace with real venues data. (Completed)
  shows = Show.query.all()
  data = []
  for show in shows:
      artist = Artist.query.get(show.artist_id)
      data.append({
          "venue_id": show.venue_id,
          "venue_name": Venue.query.get(show.venue_id).name,
          "artist_id": show.artist_id,
          "artist_name": artist.name,
          "artist_image_link": artist.image_link,
          "start_time": show.start_time.strftime('%m/%d/%Y, %H:%M:%S')
      })
  return render_template('pages/shows.html', shows=data)

@app.route('/shows/create')
def create_shows():
  form = ShowForm()
  return render_template('forms/new_show.html', form=form)

def get_available_time(available_time):
    available_time_list = []
    for avatime in available_time.split(',')[:]:
        print('available_time: {}'.format(avatime))
        available_time_list.append(avatime)
    return available_time_list

@app.route('/shows/create', methods=['POST'])
def create_show_submission():
  # TODO: insert form data as a new Show record in the db, instead (Completed)
  # TODO: on unsuccessful db insert, flash an error instead. (Completed)
  error=False
  form = ShowForm(request.form)
  if form.validate():
      try:
          artist = Artist.query.get(form.artist_id.data)
          venue_id = form.venue_id.data
          artist_id = form.artist_id.data
          start_time = form.start_time.data
          if start_time.strftime('%Y-%m-%d %H:%M:%S') not in get_available_time(artist.available_time):
              show = Show(
                venue_id = venue_id,
                artist_id = artist_id,
                start_time = start_time
              )
              db.session.add(show)
              db.session.commit()
          else:
              error= True
              form.errors['Show Time'] = ['The Artist is not Available at this time']
      except Exception as e:
          db.session.rollback()
          print(e)
          error=True
      finally:
          db.session.close()
          if not error:
              flash('Show was successfully created')
              return redirect(url_for('index', errors=form.errors))
          else:
              flash('There is an Error creating show')
              return render_template('forms/new_show.html', form=form, errors=form.errors)
  else:
      return render_template('forms/new_show.html', form=form, errors=form.errors)

@app.errorhandler(404)
def not_found_error(error):
    return render_template('errors/404.html'), 404

@app.errorhandler(500)
def server_error(error):
    return render_template('errors/500.html'), 500

if not app.debug:
    file_handler = FileHandler('error.log')
    file_handler.setFormatter(
        Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]')
    )
    app.logger.setLevel(logging.INFO)
    file_handler.setLevel(logging.INFO)
    app.logger.addHandler(file_handler)
    app.logger.info('errors')

#----------------------------------------------------------------------------#
# Launch.
#----------------------------------------------------------------------------#

# Default port:
if __name__ == '__main__':
    app.run()

# Or specify port manually:
'''
if __name__ == '__main__':
    port = int(os.environ.get('PORT', 5000))
    app.run(host='0.0.0.0', port=port)
'''


from django.template import RequestContext
from django.template.loader import render_to_string
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.paginator import Paginator
from django.shortcuts import redirect

from datetime import datetime, timedelta

from timebank.utils import ViewClass, login_required, send_mail, I18nString
from user.models import Profile
from tasks.models import Task
from serv.models import Transfer, Service
from tbmessages.models import Message

class SendEmailUpdates(ViewClass):
    '''
    If emails should be sent or not it's something will be checked every
    <update_period>. Note that the period needs to be at least the same
    as the lesser "every" period, and it's best to make them multiples of each
    other.
    '''
    update_period = timedelta(hours=6)

    '''
    Email periods: List of tuples with periods. For example:
        * first day, send an email each day
        * first week, send an email
        ..
    '''
    email_periods = [
        {"period": timedelta(days=1), 'every': timedelta(days=1)},
        {"period": timedelta(weeks=1), 'every': timedelta(weeks=1)},
        {"period": timedelta(days=30), 'every': timedelta(days=15)},
        {"period": timedelta(days=365), 'every': timedelta(days=30)}
    ]

    def GET(self):
        # if the task exists, retrieve it, or create it
        task_list = Task.objects.filter(name=self.__class__.__name__)
        if task_list:
            task = task_list[0]
        else:
            task = Task()
            task.name = self.__class__.__name__
            # ensure that the task will be executed
            self.flash(_(u"Update task postponed"))

        if not task.last_update or\
            datetime.now() - task.last_update > self.update_period:

            self.__execute(task)
            task.last_update = datetime.now()
            task.save()
            self.flash(_(u"Update task done"))
        else:
            self.flash(_(u"Update task postponed"))
        return redirect("/")

    def __execute(self, task):
        '''
        Sends an email update when needed. We will send for each user only one
        update email each time, with all the the user update info. Updates will
        be sent in periods.
        '''
        for user in Profile.objects.all():
            if not user.email_updates:
                continue
            unread_messages = Message.objects.inbox_for(user).filter(read_at__isnull=True)
            unread_comments = Message.objects.public_inbox_for(user).\
                filter(read_at__isnull=True)
            transfers_pending = user.transfers_pending()
            send_update_data = [
                {"data": unread_messages, "date_field": "sent_at"},
                {"data": unread_comments, "date_field": "sent_at"},
                {"data": transfers_pending, "date_field": "request_date"},
            ]

            if self.__should_send_update(task, send_update_data):
                subject = I18nString(_(u"Updates from %s"), settings.SITE_NAME)
                default_protocol = getattr(settings, 'DEFAULT_HTTP_PROTOCOL', 'http')
                new_services = Service.objects.filter(is_active=True)
                paginator = Paginator(new_services, 5)
                new_services = paginator.page(1)

                message = I18nString("tasks/email_update.html", dict(
                    site_url='%s://%s' % (default_protocol, Site.objects.get_current().domain),
                    site_name=settings.SITE_NAME,
                    unread_messages=unread_messages,
                    unread_comments=unread_comments,
                    transfers_pending=transfers_pending,
                    new_services=new_services,
                    recipient=user,
                    context_instance=RequestContext(self.request)
                ), True)
                send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
                    [user], fail_silently=True)
                return

    def __should_send_update(self, task, data):
        '''
        Given an array of arrays of objects, each object having field with a
        date to be checked, returns if it's needed to send an email.
        '''
        current_date = datetime.now()
        for dict_item in data:
            for obj in dict_item["data"]:
                start_date = getattr(obj, dict_item["date_field"])
                email_period_date = start_date

                # For each object, find the email period we are in
                for email_period in self.email_periods:
                    max_period_date = email_period_date + email_period["period"]
                    if max_period_date < current_date:
                        # we're not in this period, maybe in the following one
                        email_period_date = max_period_date
                        continue
                    # we found the period we are in! if task.last_update is
                    # from an older period, then we should send the email update
                    if not task.last_update or task.last_update < email_period_date:
                        return True
        return False

send_email_updates = SendEmailUpdates()


# -*- coding:utf-8 -*-
import argparse
import threading
import time
from Modules import ReadyWork
from Modules import HttpServer
from Modules import SecureHttpServer
from Modules import StartAP_airbase

def main():
    parser = argparse.ArgumentParser(description="一款简单的伪造AP钓鱼工具")
    parser.add_argument("--route_page",default='original',help="指定登录验证页面类型")
    parser.add_argument("--wifiname",default="Iphone",help="指定伪造WIFI名称")
    args = parser.parse_args()

    routepage_dict={'original':"Modules/access-point-pages/minimal", \
                'mianliao':'Modules/access-point-pages/mianliao'}

    route_page=routepage_dict[args.route_page]

    ReadyWork.Start(IP="127.0.0.1",PORT=8080,SSL_IP="127.0.0.1",SSL_PORT=443)

    ts=[]
    ts.append(threading.Thread(target=HttpServer.Start, args=('127.0.0.1',8080,route_page,)))
    ts.append(threading.Thread(target=SecureHttpServer.Start, args=("127.0.0.1",443,'127.0.0.1',8080,)))
    for t in ts:
        t.setDaemon(True)
        t.start()

    time.sleep(3) #等待上面线程完成准备工作，否则会出现显示混乱
    AP=StartAP_airbase.StartAP(a_iface='eth0',ap_iface="wlan0",wifiname=args.wifiname,channel=1)
    AP.run()

main()


########################################
# Slot Machine Program

# Lucas Balog
# Creation date: Oct. 21, 2020
# Modified date:

# SlotMachineP3.py
# other files: none

# The program

# Documented Errors
#

# Variable List
# not_done_game         boolean variable used to determin wearther to run game or end program
# not_done_spin         boolean variable used to determin wearther to spin slots or end loop
# slot1                 holds the number between 1 and 10 that has been randomly generated
# slot2                 holds the number between 1 and 10 that has been randomly generated
# slot3                 holds the number between 1 and 10 that has been randomly generated
# bet_num               holds the number the user bet on
# bet_amount            holds the amount the user bet
# bank                  holds the amount of virtual money the user has
# winnings              holds the amount the user won each round
# high_scores           list used to remeber the high scores of the user
# window                variable used to control the turtle window
# run_screen            boolean variable used to determin weather to run the intro_screen() loop or exit
# run_ask_load          boolean variable used to determin weather to run the check_save() loop or exit
# bool_to_change        used by end_section() function to change the bool of this name to False
# FONT_SIZE             constant variable used to set the standard font size
# FONT_TYPE             constant variable used to set the standard font type
# FONT_COLOUR           constant variable used to set the standard font colour
# FONT_COLOUR_ALT       constant variable used to set the standard font alternet colour
# BG_COLOUR             constant variable used to set the back gorund colour
########################################

import random
import time
import tkinter
import turtle

########################################
# TODO
# The get_info() function has the user input the names and ages.
# ages is check to make sure if is an int
# Local Variable List
# not_done          boolean variable used by while loop to determine if input loop needs to run again due to an error
########################################

def initialize_game():
    global slot1, slot2, slot3, bet_num, bet_amount, bank, winnings, high_scores,\
        window, FONT_SIZE, FONT_TYPE, FONT_COLOUR, FONT_COLOUR_ALT, BG_COLOUR

    slot1 = slot2 = slot3 = bet_num = winnings = 0
    bet_amount = 5
    bank = 200
    high_scores = []
    bool_to_change = ""

    FONT_SIZE = "46"
    FONT_TYPE = "Arial"
    FONT_COLOUR = "gold4"
    FONT_COLOUR_ALT = "gray72"
    BG_COLOUR = "black"

    window = turtle.Screen()
    window.bgcolor(BG_COLOUR)
    turtle.setup(1000, 1000, 0, 0)
    turtle.setheading(270)
    turtle.hideturtle()
    turtle.up()

########################################
# TODO
# The get_info() function has the user input the names and ages.
# ages is check to make sure if is an int
# Local Variable List
# not_done          boolean variable used by while loop to determine if input loop needs to run again due to an error
########################################

def check_save():
    global  window, bool_to_change, run_ask_load

    save_file = open("SaveSlotGame.dat", "r")
    saveed_bank = save_file.readline()
    save_file.close()

    if saveed_bank != "":

        bool_to_change = "run_ask_load"
        turtle.onkey(end_section, "n")
        turtle.onkey(load_game, "y")
        turtle.listen()

        run_ask_load = True
        while run_ask_load:
            turtle.color(FONT_COLOUR)
            turtle.write("Saved game detected\nwould you like to load? y/n", font=(FONT_TYPE, FONT_SIZE), align="center")
            time.sleep(0.5)
            turtle.color(FONT_COLOUR_ALT)
            turtle.write("Saved game detected\nwould you like to load? y/n", font=(FONT_TYPE, FONT_SIZE), align="center")
            time.sleep(0.5)

        turtle.clear()
        print(bank)

########################################
# TODO
# The end_intro_screen() function changes the run_screen variable to False to end the intro_screen loop
########################################

def end_section():
    globals()[bool_to_change] = False

########################################
# TODO
# The end_intro_screen() function changes the run_screen variable to False to end the intro_screen loop
########################################

def load_game():
    global run_ask_load
    save_file = open("SaveSlotgame.dat","r")
    bank = str(save_file.readline())
    run_ask_load = False

########################################
# The intro_screen() function displays instruction for the game and continues when enter is pressed
########################################

# TODO write instructions
# TODO make look better
def intro_screen():
    global run_screen, bool_to_change
    run_screen = True

    bool_to_change = "run_screen"
    window.onkey(end_section, "Return")
    window.listen()
    while run_screen:
        turtle.color(FONT_COLOUR)
        turtle.write("Press enter to begin", font=(FONT_TYPE, FONT_SIZE), align="center")
        time.sleep(0.5)
        turtle.color(FONT_COLOUR_ALT)
        turtle.write("Press enter to begin", font=(FONT_TYPE, FONT_SIZE), align="center")
        time.sleep(0.5)
    turtle.clear()
    # turtle.bye()

########################################
# TODO
# The get_bets() function has the user input the number they are betting on (bet_num) and
# the amount they bet(bet_amount) in a multiple of 5. The inputs are error checked an
########################################

def get_bet_num():
    global bet_num, bool_to_change, bet_turtle

    not_done_gbn= True

    while not_done_gbn:
        bet_num = tkinter.simpledialog.askinteger("Bet", "Please enter your bet")
        if bet_num <= 10 and bet_num >= 1:
            not_done_gbn = False
        else:
            tkinter.messagebox.showwarning("Number out of range", "Number must be between 1 and 10")

    turtle.clear()

########################################
# TODO
# The get_bets() function has the user input the number they are betting on (bet_num) and
# the amount they bet(bet_amount) in a multiple of 5. The inputs are error checked an
########################################

def bet_up():
    global bet_amount, bet_turtle

    bet_amount = bet_amount + 5
    bet_turtle.clear()
    bet_turtle.setpos(0, 0)
    bet_turtle.write(str(bet_amount), font=(FONT_TYPE, FONT_SIZE), align="center")

########################################
# TODO
# The bet_down() function has the user input the number they are betting on (bet_num) and
# the amount they bet(bet_amount) in a multiple of 5. The inputs are error checked an
########################################

def bet_down():
    global bet_amount, bet_turtle

    if bet_amount > 5:
        bet_amount = bet_amount - 5
        bet_turtle.clear()
        bet_turtle.setpos(0, 0)
        bet_turtle.write(str(bet_amount), font=(FONT_TYPE, FONT_SIZE), align="center")

########################################
# The get_bet_amount() function has the user input the number they are betting on (bet_num) and
# the amount they bet(bet_amount) in a multiple of 5. The inputs are error checked and the user
# is asked to re enter if nessisarry
########################################

def get_bet_amount():
    global bet_amount, bool_to_change, bet_turtle, not_done_gba

    # set up bet turtle
    bet_turtle = turtle.Turtle()
    bet_turtle.up()
    bet_turtle.color(FONT_COLOUR)
    bet_turtle.hideturtle()

    # set up instructions turtle
    instructions_turtle = turtle.Turtle()
    instructions_turtle.up()
    instructions_turtle.hideturtle()

    #set up onkney() functions
    bool_to_change = "not_done_gba"
    turtle.onkey(end_section, "Return")
    turtle.onkey(bet_up, "Up")
    turtle.onkey(bet_down, "Down")
    turtle.listen()

    # set up canvas
    bet_turtle.clear()
    bet_turtle.setpos(0, 0)
    bet_turtle.write(str(bet_amount), font=(FONT_TYPE, FONT_SIZE), align="center")

    # write instructions to entert bet
    instructions_turtle.setpos(0, -200)

    not_done_gba = True
    while not_done_gba:
        instructions_turtle.color(FONT_COLOUR)
        instructions_turtle.write("Press enter to select", font=(FONT_TYPE, FONT_SIZE), align="center")
        time.sleep(0.5)
        instructions_turtle.color(FONT_COLOUR_ALT)
        instructions_turtle.write("Press enter to select", font=(FONT_TYPE, FONT_SIZE), align="center")
        time.sleep(0.5)

    bet_turtle.clear()
    instructions_turtle.clear()

########################################
# The stop_spin() function changes the not_done_spin variable to False to end the spin_slot loop
########################################

def stop_spin():
    global not_done_spin
    not_done_spin = False

########################################
# TODO
# The spin_slots() function randomly generates three numbers and siplays them to the screen (slot1, slot2, slot3)
# all number are between 1 and 10.

# Local Variable List
# TODO document special turtle variables
########################################

def spin_slots():
    global slot1, slot2, slot3, not_done_spin
    not_done_spin = True
    slot1_turtle = turtle.Turtle()
    slot2_turtle = turtle.Turtle()
    slot3_turtle = turtle.Turtle()
    turtle.clear()

    slot1_turtle.color(FONT_COLOUR)
    slot2_turtle.color(FONT_COLOUR)
    slot3_turtle.color(FONT_COLOUR)

    slot1_turtle.hideturtle()
    slot2_turtle.hideturtle()
    slot3_turtle.hideturtle()

    slot1_turtle.up()
    slot2_turtle.up()
    slot3_turtle.up()

    turtle.onkey(stop_spin, "space")
    turtle.listen()

    while not_done_spin:
        slot1_turtle.setpos(-100, 0)
        slot1_turtle.clear()
        slot1 = random.randint(1, 10)
        slot1_turtle.write(str(slot1), font=(FONT_TYPE, FONT_SIZE), align="center")
        time.sleep(0.1)

        slot2_turtle.clear()
        slot2_turtle.setpos(0, 0)
        slot2 = random.randint(1, 10)
        slot2_turtle.write(str(slot2), font=(FONT_TYPE, FONT_SIZE), align="center")
        time.sleep(0.1)

        slot3_turtle.setpos(100, 0)
        slot3_turtle.clear()
        slot3 = random.randint(1, 10)
        slot3_turtle.write(str(slot3), font=(FONT_TYPE, FONT_SIZE), align="center")
        time.sleep(0.1)

    slot1_turtle.clear()
    slot2_turtle.clear()
    slot3_turtle.clear()

########################################
# TODO
# The calculate_winnings() function calculates how much the user won in a roudn based on thier bed and
# if there are any winning sequances
########################################

def calculate_winnings():
    global slot1, slot2, slot3, bet_num, bet_amount, winnings, bank

    slot1_and_2_match = slot1 == bet_num and slot2 == bet_num
    slot2_and_3_match = slot2 == bet_num and slot3 == bet_num
    slot1_and_3_match = slot1 == bet_num and slot3 == bet_num
    all_match = slot1 == bet_num and slot2 == bet_num and slot3 == bet_num

    turtle.setpos(0,50)
    if slot1 == bet_num or slot2 == bet_num or slot3 == bet_num:
        if slot1_and_2_match or slot2_and_3_match:
            turtle.write("side by side match", font=(FONT_TYPE, FONT_SIZE), align="center")
            winnings = bet_amount*2
        elif slot1_and_3_match:
            turtle.write("outside match", font=(FONT_TYPE, FONT_SIZE), align="center")
            winnings = bet_amount * 3
        elif all_match:
            turtle.write("Three of a kind", font=(FONT_TYPE, FONT_SIZE), align="center")
            winnings = bet_amount * 10
        elif slot2 == bet_num + 1 and slot3 == bet_num + 2:
            turtle.write("straight slot 1", font=(FONT_TYPE, FONT_SIZE), align="center")
            winnings = bet_amount * 5
        elif slot1 == bet_num - 1 and slot3 == bet_num + 1:
            turtle.write("straight slot 2", font=(FONT_TYPE, FONT_SIZE), align="center")
            winnings = bet_amount * 5
        elif slot1 == bet_num - 2 and slot2 == bet_num - 1:
            turtle.write("straight slot 3", font=(FONT_TYPE, FONT_SIZE), align="center")
            winnings = bet_amount * 5
        else:
            turtle.write("number in slot but no match", font=(FONT_TYPE, FONT_SIZE), align="center")
            winnings = -bet_amount
    else:
        turtle.write("not match", font=(FONT_TYPE, FONT_SIZE), align="center")
        winnings = -bet_amount

    bank = bank + winnings

########################################
# The display_results() function wites the values of the three slots to the turtle screen
########################################

def display_results():
    global slot1, slot2, slot3, bank

    turtle.setpos(-250, 200)
    turtle.write("Slot 1: " + str(slot1), font=(FONT_TYPE, FONT_SIZE), align="center")
    turtle.setpos(0, 200)
    turtle.write("Slot 2:" + str(slot2), font=(FONT_TYPE, FONT_SIZE), align="center")
    turtle.setpos(250, 200)
    turtle.write("Slot 3:" + str(slot3), font=(FONT_TYPE, FONT_SIZE), align="center")
    turtle.setpos(0,0)
    turtle.write('Bank: ' + str(bank), font=(FONT_TYPE, FONT_SIZE), align="center")

########################################
# TODO
# The continue_game() changes the variable not_done_check to exit the while loop in
# # check_end_game and the variable not_done_main in the main program to exit the program
########################################

def end_game():
    global not_done_game, not_done_check
    not_done_game = False
    not_done_check = False

########################################
# The continue_game() changes the variable not_done_scheck to exit thw while loop in
# check_end_game to play again
########################################

def continue_game():
    global not_done_check
    not_done_check = False

########################################
# TODO
# The check_end_game() function checks if the user wishes to end th game or continue
########################################

def check_end_game():
    global not_done_check
    if bank > 0:

        turtle.onkey(end_game, "q")
        turtle.onkey(continue_game, "Return")
        turtle.listen()
        turtle.setpos(0,-200)

        not_done_check = True
        while not_done_check:
            turtle.color(FONT_COLOUR)
            turtle.write("Press q to quit,\n Enter to play again", font=(FONT_TYPE, FONT_SIZE), align="center")
            time.sleep(0.25)
            turtle.color(FONT_COLOUR_ALT)
            turtle.write("Press q to quit,\n Enter to play again", font=(FONT_TYPE, FONT_SIZE), align="center")
            time.sleep(0.25)
    turtle.clear()

########################################
# The save_game() function save te surrent bank ballance to SaveSlotGame.dat
########################################

def save_game():
    global not_done_check
    not_done_check = False
    save_file = open("SaveSlotGame.dat", "w")
    save_file.write(str(bank) + "\n")

########################################
# TODO
# check_save_game() functon asl the user if they would like to sace their game. if so it calls save_game()
########################################

def check_save_game():
    global not_done_check
    not_done_check = True
    print("in check save game")

    turtle.onkey(save_game, "s")
    turtle.onkey(continue_game, "Return")
    turtle.listen()
    turtle.setpos(0,0)
    while not_done_check:
        turtle.color(FONT_COLOUR)
        turtle.write("Press s to save game,\n enter to exit without saving", font=(FONT_TYPE, FONT_SIZE), align="center")
        time.sleep(0.25)
        turtle.color(FONT_COLOUR_ALT)
        turtle.write("Press s to save game,\n enter to exit without saving", font=(FONT_TYPE, FONT_SIZE), align="center")
        time.sleep(0.25)

    turtle.clear()
########################################
# The calculate_high_score() function sort the top ten socres and adds them to the list
########################################

def calculate_high_score():
    global high_scores
    my_file = open("SlotHighScores.dat", 'r')

    for i in range(10):
        high_scores.append(int(my_file.readline()))

    print(high_scores)

    not_done_sort = True
    position = 9

    # find posision where new_number is lower and select the posision before knowing new_number is higher
    while not_done_sort:
        if winnings >= high_scores[position]:
            if position != 0:  # check next number
                position = position - 1
            else:  # positions <= any score on the list
                not_done_sort = False

        else:  # not higher then current position
            not_done_sort = False
            position = position + 1

    # if new_number shoudld be on list add it and remove lowest
    if position < 9:
        scores.insert(position, new_score)
        names.insert(position, new_name)
        del (scores[9])
        del (names[9])

    print(high_scores)

########################################
# TODO
# The display_results() function wites the values of the three slots to the screen
########################################

def display_bank_amount():
    global bank
    turtle.setpos(0,400)
    turtle.write('Bank: ' + str(bank), font=(FONT_TYPE, str((int(FONT_SIZE)-20))), align="center")

# main  program

initialize_game()
check_save()
intro_screen()

not_done_game = True

while not_done_game:
    get_bet_num()
    get_bet_amount()
    spin_slots()
    calculate_winnings()
    display_results()
    check_end_game()
    if not_done_game == False:
        display_bank_amount()
        check_save_game()

display_bank_amount()
calculate_high_score()
turtle.exitonclick()

U7 - Slot Machine/SlotMachineP2BACKUPOCT24.py


import requests
from cherrypy.lib import static
import cherrypy
import os
from jinja2 import Environment, FileSystemLoader
from requests.auth import HTTPDigestAuth
import ast
import json

localDir = os.path.dirname(__file__)
absDir = os.path.join(os.getcwd(), localDir)

env = Environment(loader=FileSystemLoader('html'))

class Landing_Page(object):
    @cherrypy.expose
    def index(self):
        tmpl = env.get_template('index.html')
        return tmpl.render(name="Bitcoin")

    @cherrypy.expose
    def newTransaction(self):
        tmpl = env.get_template('db.html')
        return tmpl.render()
        #raise cherrypy.HTTPRedirect("https://simulator-api.db.com/gw/oidc/authorize?response_type=token&redirect_uri=localhost:5000&client_id=49dff74c-4c3d-43dc-bbe5-cf2d8c9bf4e9")
        #return "https://simulator-api.db.com/gw/oidc/authorize?response_type=token&redirect_uri=localhost:5000&client_id=49dff74c-4c3d-43dc-bbe5-cf2d8c9bf4e9"
        #print(cherrypy.url())

    @cherrypy.expose
    def win(self):
        tmpl = env.get_template('win.html')
        headers = {'Accept': 'application/json'}
        url = "https://api.coindesk.com/v1/bpi/currentprice.json"
        r = requests.get(url, headers=headers)
        response_data = r.json()
        price = "$" + "{0:.2f}".format(float(str(response_data["bpi"]["USD"]["rate"]).replace(',',''))+2.5)
        return tmpl.render(name=price)

    @cherrypy.expose
    def some(self, code):
        r = requests.post("https://simulator-api.db.com/gw/oidc/token", data={"grant_type":"authorization_code",
                                                                          "code":code,
                                                                          "redirect_uri":"http://localhost:5000/some"},
                                                                           auth=('9afba87e-d902-450d-8942-79d7a5e0b0a3','ANQLk7veOPIrOUlec4uBUA8--6d6R9V-DM4zD95NnikzjJPs3prjJZXIldpW-KwZ-w-x8I1ghQVWJj3jgx884QQ'))

        access_token = ast.literal_eval(r.content.decode())["access_token"]
        def parse_code(parsed):
#        ind = parse_token.index("access_token")
#        parsed = parse_token.split("&")[0][ind+13:]
            response =  requests.get("https://simulator-api.db.com/gw/dbapi/v1/ageCertificate?certificationMethod=LEGAL_AGE", headers = {"Authorization":"Bearer "+parsed})
            if(response.ok):
                bracket =  str(response.content).index("}")
            if(str(response.content)[15:bracket] == 'true'):
                return True
            elif(str(response.content)[15:bracket] == 'false'):
                return False
            else:
                print("connection broken")

        legal = (parse_code(access_token))
        if(legal):
            # The user is legal he can gamble
            tmpl = env.get_template('testing.html')
            headers = {'Accept': 'application/json'}
            url = "https://api.coindesk.com/v1/bpi/currentprice.json"
            r = requests.get(url, headers=headers)
            response_data = r.json()
            price = "$" + "{0:.2f}".format(float(str(response_data["bpi"]["USD"]["rate"]).replace(',','')))
            return tmpl.render(name=price)
        else:
            return "You are too young to bet. Sorry!"
            # Please reach your leagal age

    @cherrypy.expose
    def testing(self):
        tmpl = env.get_template('testing.html')

        headers = {'Accept': 'application/json'}
        url = "https://api.coindesk.com/v1/bpi/currentprice.json"
        r = requests.get(url, headers=headers)
        response_data = r.json()
        price = "$" + "{0:.2f}".format(float(str(response_data["bpi"]["USD"]["rate"]).replace(',','')))
        return tmpl.render(name=price)

config = {
    'global': {
        'server.socket_host': '0.0.0.0',
        'server.socket_port': int(os.environ.get('PORT', 5000)),
    },
    '/assets': {
        'tools.staticdir.root': os.path.dirname(os.path.abspath(__file__)),
        'tools.staticdir.on': True,
        'tools.staticdir.dir': 'assets',
    }
}

cherrypy.quickstart(Landing_Page(), '/', config=config)


#!/usr/bin/python3

'''This is a module for pascal_triangle function'''

def pascal_triangle(n):
    '''This function returns a list of lists of integers'''

    if n <= 0:
        return []
    triangle = [[1]]
    for i in range(1, n):
        triangle.append([1])
        for j in range(1, i):
            triangle[i].append(triangle[i - 1][j - 1] + triangle[i - 1][j])
        triangle[i].append(1)
    return triangle