import torch
from torch import optim
from torch.nn import Parameter
import torch.nn.functional as F

class Encoder(torch.nn.Module):
	def __init__(self, input_size, hidden_size, num_layers, dropout):
		super(Encoder, self).__init__()

		self.input_size = input_size
		self.hidden_size = hidden_size
		self.num_layers = num_layers

		self.fc1 = torch.nn.Linear(input_size, hidden_size*num_layers*2)
		self.fc2 = torch.nn.Linear(hidden_size*num_layers*2, hidden_size*num_layers)
		self.fc3 = torch.nn.Linear(hidden_size*num_layers, hidden_size*num_layers)

		self.bn1 = torch.nn.BatchNorm1d(hidden_size*num_layers*2)
		self.bn2 = torch.nn.BatchNorm1d(hidden_size*num_layers)

		self.dropout1 = torch.nn.Dropout(dropout)
		self.dropout2 = torch.nn.Dropout(dropout)

	def forward(self, inp):
		out = self.fc1(inp)
		out = F.relu(out)
		out = self.bn1(out)
		out = self.dropout1(out)
		out = self.fc2(out)
		out = F.relu(out)
		out = self.bn2(out)
		out = self.dropout2(out)
		out = self.fc3(out)
		out = out.view(-1, self.num_layers, self.hidden_size)
		out = out.permute(1, 0, 2)
		return out

class Decoder(torch.nn.Module):
	def __init__(self, input_size, hidden_size, output_size, num_layers, dropout):
		super(Decoder, self).__init__()

		self.input_size = input_size
		self.hidden_size = hidden_size
		self.output_size = output_size
		self.num_layers = num_layers

		self.lstm = torch.nn.LSTM(input_size=input_size,
								  hidden_size=hidden_size,
								  num_layers=num_layers,
								  batch_first=True,
								  dropout=dropout)
		self.fc = torch.nn.Linear(hidden_size, output_size)

	def forward(self, inp, h0, c0):
		out, (hn, cn) = self.lstm(inp, (h0, c0))
		batch_size = inp.size(0)
		out = out.contiguous()
		out = out.view(-1, out.size(2))
		out = self.fc(out)
		out = out.view(batch_size, -1, out.size(1))
		out = F.log_softmax(out, dim=2)
		return out, (hn, cn)


from bs4 import BeautifulSoup
import requests
import pickle

r  = requests.get("https://www.fifa.com/fifa-world-ranking/associations/index.html")
data = r.text
soup = BeautifulSoup(data)
mydivs = soup.find("div", {"class": "ranking-teamlist"})
team_links = []
links = mydivs.find_all("a")

country_history_rankings = {}
for link in links:
    print(link)
    country_name = link.find("img").get("alt")
    r  = requests.get("https://www.fifa.com"+link.get("href"))
    data = r.text
    soup = BeautifulSoup(data)
    ranking_table = soup.find_all("table",{"class":"table tbl-ranking table-striped"})
    ranking_table = ranking_table[1]
    ranks = ranking_table.find_all("td",{"class":"tbl-rank"})
    years = ranking_table.select("td.tbl-deliveryyear > span")
    match_played = ranking_table.select("td.tbl-matchplayed > span")
    wins = ranking_table.select("td.tbl-matchplayed-win > span")

    match_pl = {}
    for i,y in enumerate(years):
      y = y.get_text()
      mp = int(match_played[i].get_text())
      win = int(wins[i].get_text())
      if y not in match_pl:
         match_pl[y] = {"mp":mp,"win":win}
      else:
         obj = match_pl[y]
         obj["mp"] = obj["mp"]+mp
         obj["win"] = obj["win"]+win

    rank_year = {}
    for i in range(0,len(ranks)):
        rank = ranks[i].find("span").get_text()
        year = years[i].get_text()
        rank_year[year] = rank

    country_history_rankings[country_name] = {"rankings":rank_year,"wins":match_pl}

pickle.dump(country_history_rankings,open("fifa_rankings_history.pkl","wb"))
print(country_history_rankings)


import numpy as np

# to plot the data
import matplotlib.pyplot as plt

# for neural network
import torch
import torch.nn as nn
import torch.nn.functional as F
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# to save parameters
import copy

# for evaluating results
from sklearn.metrics import classification_report

# to track progress
from tqdm.notebook import tqdm

# train network
def trainNet(net,criterion,optimizer,scheduler,train_loader,val_loader,epochs,print_every=None,earlyStopping=None,approach="defailt"):

  print("training network")
  # early stopping parameter indicates how long to wait after last time validation loss improved.

  if not print_every:
      print_every = int(epochs / 10)

  stopCounter = 0
  avg_trainLosses = []
  avg_valLosses = []

  for epoch in tqdm(range(epochs)):  # loop over the dataset multiple times

    train_loss = []
    val_loss = []

    net.train()
    for i, (inputBatch,labelBatch) in enumerate(train_loader):

        if approach == "fusion":
          inputBatch = [ip.to(device).float() for ip in inputBatch]
        else:
          inputBatch = inputBatch.to(device).float()

        labelBatch = labelBatch.to(device)

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputBatch = net(inputBatch)
        loss = criterion(outputBatch, labelBatch)
        loss.backward()
        optimizer.step()

        # print statistics
        train_loss.append(loss.item())

    net.eval()
    for i, (inputBatch,labelBatch) in enumerate(val_loader):
      with torch.no_grad():

        if approach == "fusion":
          inputBatch = [ip.to(device).float() for ip in inputBatch]
        else:
          inputBatch = inputBatch.to(device).float()

        labelBatch = labelBatch.to(device)

        # forward + backward + optimize
        outputBatch = net(inputBatch)
        loss = criterion(outputBatch, labelBatch)
        val_loss.append(loss.item())

    avg_trainLoss = sum(train_loss) / len(train_loss)
    avg_valLoss = sum(val_loss) / len(val_loss)
    avg_trainLosses.append(avg_trainLoss)

    if (epoch > 0) and (avg_valLoss < min(avg_valLosses)):
        best_params = copy.deepcopy(net.state_dict())
        best_epoch, best_loss = epoch, avg_valLoss
        stopCounter = 0
    else:
      stopCounter += 1
      if stopCounter == earlyStopping:
        break
    avg_valLosses.append(avg_valLoss)

    # print statistics
    if epoch % print_every == print_every - 1:
      print('epoch: %d, train loss: %.3f, val loss: %.3f' % (epoch + 1, avg_trainLoss, avg_valLoss))

    scheduler.step(avg_valLoss)

  print('Finished Training')
  plt.plot(avg_trainLosses, label='train loss')
  plt.plot(avg_valLosses, label='val loss')
  plt.plot([best_loss]*epoch, linestyle='dashed')
  plt.plot(best_epoch, best_loss, 'o')
  plt.legend()
  plt.show()

  return best_params

# evaluate and print
def evaluate(net,input1,output_true,classes=None, approach="default"):

  output_pred = netOutput(net,input1,outType="class", approach=approach)

  if classes is not None:
    print(classification_report(output_true, output_pred, target_names=classes, labels=range(len(classes)) ,digits=4))
  else:
    print(classification_report(output_true, output_pred, digits=4))

def netOutput(net, inputs, outType="default", approach="default"):
  net.eval()
  outputs = []

  with torch.no_grad():
    for input1 in tqdm(inputs):
      if outType == "latent":
        output = net.latent(torch.from_numpy(input1).unsqueeze(0).float().to(device)).cpu().numpy()

      elif outType == "class":
        if approach == "fusion":
          input1 = [torch.from_numpy(ip).unsqueeze(0).float() for ip in input1]
          output = net(input1).cpu().numpy()
        else:
          output = net(torch.from_numpy(input1).unsqueeze(0).float().to(device)).cpu().numpy()
        output = np.argmax(output)

      else:
        output = net(torch.from_numpy(input1).unsqueeze(0).float().to(device)).cpu().numpy()

      outputs.append(output)

  return np.array(outputs)


"""
python3.4 construct_matrices_sdss.py -0.1 0.05 X_AGN
"""
import sys
import numpy as n
from scipy.interpolate import interp1d
import os
import astropy.io.fits as fits
import pickle
import glob

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as p

version = 'v5'

# parameters of the run
zmin = 1. # float(sys.argv[1])
zmax = 2. # float(sys.argv[2])
gal_type = 'cosmosagn' # sys.argv[3]
# ELG, LRG, X_AGN, QSO

Nspec_max = 3000

name = "deimos_"+gal_type+"_zmin_"+str(int(10*zmin)).zfill(2)+"_zmax_"+str(int(10*zmax)).zfill(2)+"_Nlt_"+str(int(Nspec_max))

SN_threshold = 0.1#0.5
wlmin = 6000.
wlmax = 9500.
wlmin_rf = int(wlmin/(1+zmax))
wlmax_rf = int(wlmax/(1+zmin))
d_lambda = 0.2
masterwave = n.arange(wlmin_rf, wlmax_rf, d_lambda) #0.5)

cat = fits.open(os.path.join(os.environ['OBS_REPO'], 'COSMOS', 'DEIMOS_catalog_27MAR2018.fits'))[1].data

ok=(cat['z']>zmin)&(cat['z']<=zmax)
print(len(cat['z'][ok]))
print(n.histogram(cat['Z'][ok],bins=n.arange(0,3.1,0.5)))
nGal = len(ok.nonzero()[0])

#cat['Mask']
#cat['Slt']
#cat['Name']

get_path_to_spectrum = lambda slit, name : glob.glob(os.path.join( os.environ['OBS_REPO'], 'COSMOS', 'spec1d.*-*.' +str(slit).zfill(3) +"."+name+"_acal.fits" ))[0]

def get_spec(path_to_spectrum):
	hdulist = fits.open(path_to_spectrum)
	wavelength = hdulist[1].data['LAMBDA'][0]
	flux = hdulist[1].data['FLUX'][0]
	ivar = hdulist[1].data['IVAR'][0]
	sel = (flux>0)&(ivar>0)
	return wavelength[sel], flux[sel], ivar[sel] #, selection

allflux = n.zeros( ( nGal, len(masterwave)) )
allivar = n.zeros( ( nGal, len(masterwave)) )

print("gets the spectra to construct the matrix")
for index in range(nGal) :# = 0
	try :
		print(index, cat['Mask'][ok][index], cat['Slt'][ok][index], cat['Name'][ok][index])
		path = get_path_to_spectrum(cat['Slt'][ok][index], cat['Name'][ok][index])
		print( path )
		redshift = cat['z'][ok][index]
		wl, fl, iv = get_spec(path)
		p.figure(0,(10,5))
		p.plot(wl,fl)
		p.savefig(path+".png")
		p.clf()
		w_new = masterwave*(1+redshift)
		itp_fl = interp1d( n.hstack((100.,wl[0]-1.,wl,wl[-1]+1,80000.)), n.hstack((0.,0.,wl*fl,0.,0.)) )
		itp_iv = interp1d( n.hstack((100.,wl[0]-1.,wl,wl[-1]+1,80000.)), n.hstack((0.,0.,iv,0.,0.)) )
		f_new = itp_fl(w_new)/w_new
		iv_new = itp_iv(w_new)
		allflux[index] = f_new
		allivar[index] = iv_new
	except(ValueError, IndexError):
		print('error', index, cat['Slt'][ok][index], cat['Name'][ok][index])

out_dir = os.path.join( os.environ['OBS_REPO'], 'archetypes', version, gal_type )
if os.path.isdir(out_dir)==False:
  os.system('mkdir -p '+out_dir)

n.savetxt(os.path.join(out_dir, "allflux_"+name+".txt")   , allflux)
n.savetxt(os.path.join(out_dir, "allivar_"+name+".txt")   , allivar)
n.savetxt(os.path.join(out_dir, "masterwave_"+name+".txt"), masterwave)

#class ObjIds:
    #def __init__(self, plate, mjd, fiberid):
        #self.plate = plate
        #self.mjd = mjd
        #self.fiberid = fiberid

#f = open(os.path.join(out_dir, "specObj_"+name+".pkl"), 'wb')
#obj = ObjIds(cat['PLATE'][ok], cat['MJD'][ok], cat['FIBERID'][ok])
#pickle.dump(obj, f)
#f.close()


from kivy.lang.builder import Builder
from kivy.uix.label import Label

Builder.load_string('''
<Divider>:
    height: '1sp'
    size_hint_y: None
    color: (0.5, 0.5, 0.5, 1)
    canvas.before:
        Color:
            rgba: self.color
        Rectangle:
            pos: self.pos
            size: self.size
''')

class Divider(Label):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import random
import time
import torch
from torch.utils.data import DataLoader
import numpy as np
from hparam import hparam as hp
from data_load import SpeakerDatasetTIMIT, SpeakerDatasetTIMITPreprocessed
from speech_embedder_net import SpeechEmbedder, GE2ELoss, get_centroids, get_cossim

def test(model_path):
    sequence = []
    if hp.data.data_preprocessed:
        test_dataset = SpeakerDatasetTIMITPreprocessed()
    else:
        test_dataset = SpeakerDatasetTIMIT()
    test_loader = DataLoader(test_dataset, batch_size=1, shuffle=True, num_workers=hp.test.num_workers,
                             drop_last=True)

    embedder_net = SpeechEmbedder()
    embedder_net.load_state_dict(torch.load(model_path))
    embedder_net.eval()

    device = torch.device(hp.device)
    count = 0
    embeddings = []
    devector = []
    for e in range(hp.test.epochs):
        print("hp.test.epochs",hp.test.epochs)
        for batch_id, mel_db_batch in enumerate(test_loader):
            #print("mel_db_batch.shape",batch_id,mel_db_batch.shape)   #(1,10,160,40)
            assert hp.test.M % 2 == 0
            test_batch = mel_db_batch
            test_batch = torch.reshape(test_batch, (
            hp.test.N * hp.test.M, test_batch.size(2), test_batch.size(3)))
            #print("test_batch.shape",test_batch.shape)    #(10,160,40)

            enrollment_embeddings = embedder_net(test_batch)
            #print("enrollment_embeddings.shape", enrollment_embeddings.shape)  # (10,256)
            # enrollment_embeddings = torch.reshape(enrollment_embeddings,(hp.test.N, hp.test.M, enrollment_embeddings.size(1)))

            embedding = enrollment_embeddings.detach().numpy()
            embeddings.append(embedding)
            #print('embedding.shape', type(embedding), embedding.shape)  # (10,256)

            devector = np.concatenate(embeddings, axis=0)
            count = count + 1
    np.save('/run/media/rice/DATA/speakerdvector.npy', devector)

if __name__ == "__main__":
    test(hp.model.model_path)


import matplotlib.pyplot as plt
import numpy as np
from typing import List

def plot_training_results(rewards_history: List,
                          running_rewards_history: List,
                          steps_history: List,
                          loss_history: List,
                          wallclock_history: List,
                          save_dir: str = None):

    fig, ((ax1, ax2, ax3), (ax4, ax5, ax6), (ax7, ax8, ax9)) = plt.subplots(nrows=3, ncols=3, figsize=(16, 12))
    fig.suptitle("Training Results", fontsize=16)

    # EPISODES
    num_episodes = len(rewards_history)

    # Episodes vs rewards
    ax1.plot(np.arange(num_episodes), rewards_history, color='grey', alpha=0.3, linestyle='-', label="episode reward")
    ax1.plot(np.arange(num_episodes), running_rewards_history, color='green', label="moving average reward")
    ax1.set_xlabel("Number of episodes")
    ax1.set_ylabel("Episode reward")
    ax1.legend()
    ax1.grid()

    # Episodes vs loss
    ax4.plot(np.arange(num_episodes), loss_history, color='green')
    ax4.set_xlabel("Number of episodes")
    ax4.set_ylabel("Episode loss")
    ax4.grid()

    # Epochs vs steps per epoch
    ax7.plot(np.arange(num_episodes), steps_history, color='green')
    ax7.set_xlabel("Number of episodes")
    ax7.set_ylabel("Episode duration (steps)")
    ax7.grid()

    # STEPS
    cumulative_steps = list(np.cumsum(steps_history))

    # Steps vs rewards
    ax2.plot(cumulative_steps, rewards_history, color='grey', alpha=0.3, linestyle='-', label="episode reward")
    ax2.plot(cumulative_steps, running_rewards_history, color='green', label="moving average reward")
    ax2.set_xlabel("Number of steps")
    ax2.set_ylabel("Episode reward")
    ax2.legend()
    ax2.grid()

    # Steps vs loss
    ax5.plot(cumulative_steps, loss_history, color='green')
    ax5.set_xlabel("Number of steps")
    ax5.set_ylabel("Episode loss")
    ax5.grid()

    # TIME

    # Wallclock vs rewards
    ax3.plot(wallclock_history, rewards_history, color='grey', alpha=0.3, linestyle='-', label="episode reward")
    ax3.plot(wallclock_history, running_rewards_history, color='green', label="moving average reward")
    ax3.set_xlabel("Wallclock time (seconds)")
    ax3.set_ylabel("Episode reward")
    ax3.legend()
    ax3.grid()

    # Wallclock vs loss
    ax6.plot(wallclock_history, loss_history, color='green')
    ax6.set_xlabel("Wallclock time (seconds)")
    ax6.set_ylabel("Episode loss")
    ax6.grid()

    # Turn off unused plots
    ax8.set_axis_off()
    ax9.set_axis_off()

    # Save
    if save_dir:
        plt.savefig(save_dir)

    # Show plots
    plt.tight_layout()
    plt.show()


from django.shortcuts import render ,HttpResponseRedirect

from .serializers import *
from .models import *
from django.core.files.storage import FileSystemStorage

def test(request):
    if request.method == 'POST':

        post = Brand()
        post.brand_title = request.POST.get('brand_title')
        # post.image = request.POST.get('image')
        image = request.FILES['image']
        fss = FileSystemStorage()
        file_image = fss.save(image.name, image)
        file_image_url = fss.url(file_image)
        post.image = file_image_url
        post.status = request.POST.get('status')
        post.save()

        return render(request, 'brand/index.html')

    else:

        return render(request, 'brand/index.html')

def list(request):
    lists = Brand.objects.all()
    args = {'lists': lists}
    return render(request, 'brand/list.html', args)

def delete_data(request, id):
    if request.method == 'POST':
        pi = Brand.objects.get(pk=id)
        pi.delete()
        return HttpResponseRedirect('/home/brand-list')


import numpy
from numpy.linalg import inv, slogdet
from scipy.special import logsumexp

class GMM:

    def __init__(self, components, X, log_prior, prior_weight, small_number = 1e-6, log_likelihood_tolerance = 1e-3, max_iterations = 1000):
        self.__components = components  # number of components
        self.__X = X  # list of samples, each sample is a #bins by #features matrix
        self.__log_prior = log_prior  # log probability
        self.__prior_weight = prior_weight
        self.__small_number = small_number
        self.__log_likelihood_tolerance = log_likelihood_tolerance
        self.__max_iterations = max_iterations

    def __get_x(self):
        '''
        self.__x: samples * features
        '''
        self.__x = numpy.array([numpy.mean(X, axis = 0) for X in self.__X], dtype = numpy.float64)

    def __initialize_log_gaussian_density(self):
        self.__log_gaussian_density = numpy.empty(shape = (self.__samples, self.__components), dtype = numpy.float64)

    def __construct_mapping(self):
        self.__mapping = numpy.array([index for index, X in enumerate(self.__X) for _ in X], dtype = numpy.int64)

    def __flatten_X(self):
        self.__X = numpy.concatenate(self.__X, axis = 0)

    def __initialize_responsibility(self):
        self.__responsibility = numpy.copy(self.__prior)
        self.log_responsibility = numpy.copy(self.__log_prior)

    def __update_mu(self):
        weight = self.__responsibility[self.__mapping]
        self.__mu = weight.T @ self.__X / (numpy.sum(weight, axis = 0, keepdims = True).T)

    def __update_sigma(self):
        self.__sigma = numpy.empty(shape = (self.__components, self.__features, self.__features), dtype = numpy.float64)
        weight = self.__responsibility[self.__mapping]
        for component in range(self.__components):
            temp = self.__X - self.__mu[component]
            self.__sigma[component] = temp.T * weight[:, component] @ temp / numpy.sum(weight[ : , component])
            self.__sigma[component][numpy.diag_indices(self.__features)] += self.__small_number

    def __update_pi(self):
        self.__log_pi = numpy.log(self.__responsibility + self.__prior_weight * self.__prior) - numpy.log(1.0 + self.__prior_weight)
        self.__pi = numpy.exp(self.__log_pi)

    def __calculate_log_gaussian_density(self):
        for component in range(self.__components):
            temp = self.__x - self.__mu[component]
            self.__log_gaussian_density[:, component] = (
                -0.5 * (
                    numpy.sum(temp @ inv(self.__sigma[component]) * temp, axis = 1)
                    + self.__features * numpy.log(2.0 * numpy.pi)
                    + slogdet(self.__sigma[component])[1]
                )
            )

    def __update_responsibility(self):
        self.__calculate_log_gaussian_density()
        # log(pi) + log(gaussian density)
        self.log_responsibility = self.__log_pi + self.__log_gaussian_density
        # log(posterior)
        self.log_responsibility -= logsumexp(self.log_responsibility, axis = 1, keepdims = True)
        # posterior
        self.__responsibility = numpy.exp(self.log_responsibility)
        self.__responsibility += numpy.finfo(numpy.float64).eps
        self.__responsibility /= numpy.sum(self.__responsibility, axis = 1, keepdims = True)

    def __calculate_log_likelihood(self):
        log_likelihood = (
            numpy.sum(logsumexp(self.__log_pi[self.__mapping] + self.__log_gaussian_density[self.__mapping], axis = 1))
            - self.__prior_weight * numpy.sum(self.__prior[self.__mapping] * (self.__log_prior[self.__mapping] - self.__log_pi[self.__mapping]))
        )
        return log_likelihood

    def main(self):
        self.__prior = numpy.exp(self.__log_prior)
        self.__get_x()  # self.__x
        self.__samples, self.__features = self.__x.shape
        self.__initialize_log_gaussian_density()  # self.__log_gaussian_density
        self.__construct_mapping()  # self.__mapping
        self.__flatten_X()
        self.__initialize_responsibility()  # self.__responsibility self.log_responsibility
        lower_log_likelihood = -numpy.inf
        iterations = 0
        while iterations < self.__max_iterations:
            self.__update_mu()  # m step
            self.__update_sigma()  # m step
            self.__update_pi()  # m step
            self.__update_responsibility()  # e step, update posterior distribution
            log_likelihood = self.__calculate_log_likelihood()  # log likelihood
            iterations += 1
            if (log_likelihood - lower_log_likelihood) < self.__log_likelihood_tolerance:
                break
            lower_log_likelihood = log_likelihood


import time
import functools
from sklearn.ensemble import RandomForestClassifier
from sklearn import svm
from sklearn.model_selection import RandomizedSearchCV
from sklearn.model_selection import StratifiedKFold, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn import metrics
from sklearn.metrics import classification_report, confusion_matrix
import numpy as np

def timer(func):
    '''
    Decorator to log time taken by classifier
    '''
    # from https://realpython.com/primer-on-python-decorators/

    # print runtime of decorated function
    @functools.wraps(func)

    def wrapper_timer(*args, **kwargs):
        start_time = time.perf_counter()
        value = func(*args, **kwargs)
        end_time = time.perf_counter()
        run_time = end_time - start_time
        print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
        return value

    return wrapper_timer

@timer
def run_linear_svm_classification(X, y, X_test, y_test, scoring_measure, random_seed):

    '''This part of the code was partly inspired by Sandra Vieira, Rafael Garcia-Dias, Walter Hugo Lopez Pinaya - Chapter 19: A Step-By-Step Tutorial On How To Build A Machine Learning Model (https://github.com/MLMH-Lab/How-To-Build-A-Machine-Learning-Model/)'''

    # scaling
    scaler = StandardScaler()

    # X and X_test are images, convert to flat matrices
    X_flat = list(X)
    X_flat = np.nan_to_num(X_flat)
    scaler.fit(X_flat)
    X_flat = np.nan_to_num(scaler.transform(X_flat))

    X_test_flat = list(X_test)
    X_test_flat = np.nan_to_num(X_test_flat)
    X_test_flat = np.nan_to_num(scaler.transform(X_test_flat))

    y = y.astype('category').cat.codes.values
    y_test = y_test.astype('category').cat.codes.values

    clf = svm.LinearSVC(random_state=random_seed, max_iter= 1000000)

    param_grid = {'C': [2 ** -2, 2 ** -1, 2 ** 0, 2 ** 1, 10, 25, 50, 100], 'penalty' : ['l2']}

    # grid search
    internal_cv = StratifiedKFold(n_splits=2)
    grid_cv = GridSearchCV(estimator=clf,
                            param_grid=param_grid,
                            cv=internal_cv,
                            scoring=scoring_measure,
                            verbose=1)

    grid_result = grid_cv.fit(X_flat, y)

    print('Best: %f using %s' % (grid_result.best_score_, grid_result.best_params_))
    means = grid_result.cv_results_['mean_test_score']
    stds = grid_result.cv_results_['std_test_score']
    params = grid_result.cv_results_['params']

    for mean, stdev, param in zip(means, stds, params):
        print('%f (%f) with: %r' % (mean, stdev, param))

    print("Best estimator: ", grid_cv.best_estimator_)

    best_clf = grid_cv.best_estimator_

    prediction = best_clf.predict(X_test_flat)

    print('Confusion matrix')
    cm = confusion_matrix(y_test, prediction)
    print(cm)

    tn, fp, fn, tp = cm.ravel()

    bac_test = metrics.balanced_accuracy_score(y_test, prediction)
    sens_test = tp / (tp + fn)
    spec_test = tn / (tn + fp)

    print('Balanced accuracy: %.3f ' % bac_test)
    print('Sensitivity: %.3f ' % sens_test)
    print('Specificity: %.3f ' % spec_test)

    coefs = best_clf.coef_

    return prediction, bac_test, sens_test, spec_test, cm, coefs

@timer
def run_nonlinear_svm_classification(X, y, X_test, y_test, scoring_measure, random_seed):

    # scaling
    scaler = StandardScaler()

    # X and X_test are images, convert to flat matrices
    X_flat = list(X)
    X_flat = np.nan_to_num(X_flat)
    scaler.fit(X_flat)
    X_flat = np.nan_to_num(scaler.transform(X_flat))

    X_test_flat = list(X_test)
    X_test_flat = np.nan_to_num(X_test_flat)
    X_test_flat = np.nan_to_num(scaler.transform(X_test_flat))

    y = y.astype('category').cat.codes.values
    y_test = y_test.astype('category').cat.codes.values

    clf = svm.SVC(kernel = 'poly', random_state=random_seed, max_iter= 1000000)

    param_grid = {'C': [2 ** -6, 2 ** -5, 2 ** -4, 2 ** -3, 2 ** -2, 2 ** -1, 2 ** 0, 2 ** 1, 10, 25, 50, 100]}

    # grid search
    internal_cv = StratifiedKFold(n_splits=2)
    grid_cv = GridSearchCV(estimator=clf,
                            param_grid=param_grid,
                            cv=internal_cv,
                            scoring=scoring_measure,
                            verbose=1)

    grid_result = grid_cv.fit(X_flat, y)

    print('Best: %f using %s' % (grid_result.best_score_, grid_result.best_params_))
    means = grid_result.cv_results_['mean_test_score']
    stds = grid_result.cv_results_['std_test_score']
    params = grid_result.cv_results_['params']

    for mean, stdev, param in zip(means, stds, params):
        print('%f (%f) with: %r' % (mean, stdev, param))

    print("Best estimator: ", grid_cv.best_estimator_)

    best_clf = grid_cv.best_estimator_

    prediction = best_clf.predict(X_test_flat)

    print('Confusion matrix')
    cm = confusion_matrix(y_test, prediction)
    print(cm)

    tn, fp, fn, tp = cm.ravel()

    bac_test = metrics.balanced_accuracy_score(y_test, prediction)
    sens_test = tp / (tp + fn)
    spec_test = tn / (tn + fp)

    print('Balanced accuracy: %.3f ' % bac_test)
    print('Sensitivity: %.3f ' % sens_test)
    print('Specificity: %.3f ' % spec_test)

    coefs = best_clf.coef_

    return prediction, bac_test, sens_test, spec_test, cm, coefs

@timer
def run_rf_classification(X, y, X_test, y_test, scoring_measure, random_seed):

    ''' This part of the code was partly inspired by Ruben Otter - Unsupervised Gaze Event Discrimination (https://github.com/LEO-UMCG/Unsupervised-Gaze-Event-Discrimination/)'''

   # scaling to variance but keep sparsity structure of data
    scaler = StandardScaler()

    # X and X_test are images, convert to flat matrices
    X_flat = list(X)
    X_flat = np.nan_to_num(X_flat)
    scaler.fit(X_flat)
    X_flat = np.nan_to_num(scaler.transform(X_flat))

    X_test_flat = list(X_test)
    X_test_flat = np.nan_to_num(X_test_flat)
    X_test_flat = np.nan_to_num(scaler.transform(X_test_flat))

    y = y.astype('category').cat.codes.values
    y_test = y_test.astype('category').cat.codes.values

    # number of trees
    n_estimators = [int(x) for x in np.linspace(start = 40, stop = 2500, num = 40)]
    criterion = ['gini', 'entropy', 'log_loss']
    # number of features to take into account at every split
    max_features = ['log2', 'sqrt']
    # max number of levels in one tree
    max_depth = [int(x) for x in np.linspace(10, 110, num = 20)]
    max_depth.append(None)
    # min samples needed to split a node
    min_samples_split = [2, 3, 4, 5, 10]
    # min samples required at each leaf node
    min_samples_leaf = [1, 2, 3, 4]
    # method of selecting samples for training each tree
    bootstrap = [True, False]

    random_grid = {'n_estimators': n_estimators,
                'max_features': max_features,
                'max_depth': max_depth,
                'min_samples_split': min_samples_split,
                'min_samples_leaf': min_samples_leaf,
                'bootstrap': bootstrap,
                'criterion': criterion}
    print(random_grid)

    # random forest classifier using Scikit-learn package
    clf = RandomForestClassifier()
    rf_random = RandomizedSearchCV(estimator = clf, param_distributions = random_grid, n_iter = 100, cv = 3, verbose=2, random_state=random_seed, n_jobs = -1)
    rf_random.fit(X_flat, y)

    print("train arrays img", X_flat[:10])
    print("train lab", y)

    print(rf_random.best_estimator_)
    print(rf_random.best_params_)

    rf_classifier = rf_random.best_estimator_

    rf_classifier.fit(X_flat, y)

    y_pred = rf_classifier.predict(X_test_flat)

    print("accuracy:", metrics.accuracy_score(y_test, y_pred))
    print("balanced accuracy:", metrics.balanced_accuracy_score(y_test, y_pred))
    print("f1:", metrics.f1_score(y_test, y_pred))
    accuracy = metrics.balanced_accuracy_score(y_test, y_pred)
    sensitivity = metrics.recall_score(y_test, y_pred)
    conf_matrix = confusion_matrix(y_test, y_pred).ravel()
    tn, fp, fn, tp = conf_matrix
    specificity = tn / (tn+fp)

    print(classification_report(y_test, y_pred))

    coefs = rf_classifier.feature_importances_
    print(coefs)

    return rf_classifier, y_pred, accuracy, sensitivity, specificity, conf_matrix, coefs


#!/usr/bin/env python3
from collections import deque
lines = [list(l.rstrip()) for l in open("input.txt").readlines()]

def set_points(lines):
    start_points = []
    end_point = None
    for row in range(len(lines)):
        for col in range(len(lines[0])):
            if lines[row][col] == 'S':
                lines[row][col] = 'a'
                start_points.append((row, col))
            if lines[row][col] == 'a':
                start_points.append((row, col))
            elif lines[row][col] == 'E':
                lines[row][col] = 'z'
                end_point = (row, col)
    return (start_points, end_point)

def find_shortest_path(start_points, end_point, lines):
    positions = deque([(point, 0) for point in start_points])
    visited = set()

    while positions:
        curr_pos, steps = positions.popleft()
        row, col = curr_pos
        if curr_pos == end_point:
            return steps
        if curr_pos in visited:
            continue
        visited.add(curr_pos)
        for direction in [(1, 0), (0, 1), (-1, 0), (0, -1)]:
            new_row, new_col = row + direction[0], col + direction[1]
            if new_row < 0 or new_col < 0 or new_row >= len(lines) or new_col >= len(lines[0]):
                continue
            if ord(lines[new_row][new_col]) <= ord(lines[row][col]) + 1:
                positions.append(((new_row, new_col), steps + 1))
    return None

start_points, end_point = set_points(lines)
print(find_shortest_path(start_points, end_point, lines))


import types
import os
import stat
import asyncio
import time
import socket

from flask import Flask, render_template
from functools import wraps, partial
from threading import Thread

class App(Flask):
    def __init__(self, *args, **kwargs):
        print('Run inited App')
        super().__init__(*args, **kwargs)
        self.list_decorers = []

    def build_configs(self, debug=False):
        self.config['PREFERRED_URL_SCHEME'] = 'https'
        self.config['TEMPLATES_AUTO_RELOAD'] = debug
        self.config['SECRET_KEY'] = os.urandom(32).hex()

    def route(self, rule, **options):
        if options.get('endpoint', None) is None:
            options['endpoint'] = '__{}__'.format(rule)

        flask_route = Flask.route(self, rule, **options)
        wrapper = self.decorate_rout(flask_route)
        print('Rule "{}" is route'.format(rule))
        return wrapper

    def decorate_rout(self, flask_route):
        def wrap_func(func):
            for dec in self.list_decorers:
                func = dec(func)
            func = flask_route(func)
            return func
        return wrap_func

    def append_decorer(self, dec):
        self.list_decorers.append(dec)


from msrest.serialization import Model

class CreateReviewBodyItem(Model):
    """Schema items of the body.

    :param type: Type of the content. Possible values include: 'Image', 'Text'
    :type type: str or
     ~azure.cognitiveservices.vision.contentmoderator.models.enum
    :param content: Content to review.
    :type content: str
    :param content_id: Content Identifier.
    :type content_id: str
    :param callback_endpoint: Optional CallbackEndpoint.
    :type callback_endpoint: str
    :param metadata: Optional metadata details.
    :type metadata:
     list[~azure.cognitiveservices.vision.contentmoderator.models.CreateReviewBodyItemMetadataItem]
    """

    _validation = {
        'type': {'required': True},
        'content': {'required': True},
        'content_id': {'required': True},
    }

    _attribute_map = {
        'type': {'key': 'Type', 'type': 'str'},
        'content': {'key': 'Content', 'type': 'str'},
        'content_id': {'key': 'ContentId', 'type': 'str'},
        'callback_endpoint': {'key': 'CallbackEndpoint', 'type': 'str'},
        'metadata': {'key': 'Metadata', 'type': '[CreateReviewBodyItemMetadataItem]'},
    }

    def __init__(self, type, content, content_id, callback_endpoint=None, metadata=None):
        super(CreateReviewBodyItem, self).__init__()
        self.type = type
        self.content = content
        self.content_id = content_id
        self.callback_endpoint = callback_endpoint
        self.metadata = metadata


"""Creates VoiceText class"""
import json
import subprocess
# Disable linting error that package is not found locally
# pylint: disable=E0401
import speech_recognition as sr

class VoiceText:
    """ Handles all the functionality regarding voice to text methods """
    def __init__(self):
        with open("config.json", "r") as file:
            data = json.load(file)
        self.mic_name = data["microphone"]

    def get_device(self):
        """ Finds microphone device ID by looking for the name of the microphone """
        for i, microphone_name in enumerate(sr.Microphone.list_microphone_names()):
            if microphone_name == self.mic_name:
                device_id = i
                return device_id
        return False

    def voice_to_text(self):
        """ This function converts all audio with Google Speech Recognition into text """
        microphone_device = self.get_device()
        if microphone_device is not None:
            repeat = True
            while repeat:
                recogniser = sr.Recognizer()
                with sr.Microphone(device_index=microphone_device) as source:
                    subprocess.run("clear")
                    recogniser.adjust_for_ambient_noise(source)
                    print("Please say the book details to the microphone.")
                    try:
                        audio = recogniser.listen(source, timeout=1.5)
                    except sr.WaitTimeoutError:
                        print("Listening timed out whilst waiting for phrase to start")
                        quit()
                try:
                    print(
                        "Google Speech Recognition thinks you said '{}'".format(
                            recogniser.recognize_google(audio)
                        )
                    )
                    text = input("Is the speech to text correct? (yes/no): ")
                    while True:
                        if text == "yes":
                            repeat = False
                            return recogniser.recognize_google(audio)
                        if text == "no":
                            print("Sorry about that, we will repeat the process again.")
                            break
                        else:
                            print("Wrong input!")
                except sr.UnknownValueError:
                    print("Google Speech Recognition could not understand audio")
                except sr.RequestError as error:
                    print(
                        "Could not request results from Google Speech Recognition service; {0}"
                        .format(error)
                    )
        return False


import sys
from bitarray import bitarray

print('HEX | DEC | REV OCT | BINARY')
print('==============================')

for i in range(256):
    starting_byte = chr(i)
    original_bits = bitarray()
    original_bits.frombytes(starting_byte)

    reversed_bits = bitarray()
    reversed_bits.frombytes(starting_byte)
    reversed_bits.reverse()

    reversed_byte = reversed_bits.tobytes()[0]
    reversed_byte = ord(reversed_byte)

    sys.stdout.write(' {:02X} |'.format(reversed_byte))
    sys.stdout.write(' {:03d} |'.format(reversed_byte))
    sys.stdout.write('     {:03o} |'.format(i))
    print(' {}'.format(bin(reversed_byte)[2:].zfill(8)))


from selenium import webdriver
import csv
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time
from time import sleep
from selenium.webdriver.support import expected_conditions as EC
from datetime import date,timedelta
from urllib.parse import quote
from googletrans import Translator
until=date.today()
since= until-timedelta(days=1)
# nhập từ khóa muốn tìm kiếm trên twitter
keyword = input("keyword = ")

# chọn browser là Chrome
# Khởi tạo trình duyệt Chrome với tùy chọn
# chọn browser là Chrome
# Khởi tạo trình duyệt Chrome với tùy chọn
def web_driver():
    driver = webdriver.Chrome()
    return driver

browser = web_driver()
browser.get("https://twitter.com/login")

# Điền thông tin tài khoản và mật khẩu vào các trường đăng nhập
time.sleep(5)
# Chờ trang tải hoàn tất
browser.implicitly_wait(10)
browser.find_element(By.XPATH, "//input").send_keys("rtar1003@gmail.com")
next = browser.find_element(By.XPATH, "/html/body/div/div/div/div[1]/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div/div/div/div[6]/div")
next.click()
time.sleep(3)
browser.find_element(By.XPATH, "/html/body/div/div/div/div[1]/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[1]/div/div/div[3]/div/label/div/div[2]/div[1]/input").send_keys("duclam123")
login = browser.find_element(By.XPATH, "/html/body/div/div/div/div[1]/div/div/div/div/div/div/div[2]/div[2]/div/div/div[2]/div[2]/div[2]/div/div[1]/div/div/div/div")
login.click()
time.sleep(15)
f=open("C:/Users/TranDucLam/Downloads/Data/DataCrawl.csv",mode='w',encoding='utf-8')
f.close()
len_file = 0
tran=Translator()
# vòng lặp lưu data vào file
with open("C:/Users/TranDucLam/Downloads/Data/DataCrawl.csv",mode='a',encoding = 'utf-8') as out:
    writer=csv.writer(out,delimiter=',',lineterminator='\n')
    writer.writerow(['No','Context'])
    # lấy khoảng 50.000 tweet
    while(len_file<100):
        time.sleep(5)
        url = "https://twitter.com/search?q={}%20until%3A{}%20since%3A{}&src=typed_query&f=live".format(quote(keyword), until, since)
        browser.get(url)
        sleep(5)
        htmlElem = browser.find_element(by=By.TAG_NAME, value='html')
        # tạo list chứa 20 tweets ngay trước để so sánh, tránh lặp
        contented = []
        len_day = 0
        # vòng lặp lấy dữ liệu theo mỗi 2 ngày, mỗi lần lấy khoảng 500 data
        while(len_day<10):
            try:
                # tìm tweets trong trang hiện tại
                local_contents = browser.find_elements(by=By.CSS_SELECTOR,value='[data-testid="tweetText"]')
                # so sánh với data trước đó để tránh lặp dữ liệu
                local_contents = [i.text for i in local_contents if i.text not in contented]
                print(local_contents)
            except:
                browser.implicitly_wait(3)
                continue
            # Cập nhật list các tweet trước
            if len(contented) > 20:
                del contented[0:len(local_contents)]
            contented.extend(local_contents)

            for i in local_contents:
                len_file += 1
                len_day += 1
                # Dịch các tweet không phải tiếng Anh
                try:
                    translation = tran.translate(str(i)).text
                except Exception as e:
                    print("Lỗi khi dịch: ", e)
                    translation = "Lỗi khi dịch"

                writer.writerow([len_file, translation])
                print(len_file)
            # lệnh cuộn trang web xuống
            htmlElem.send_keys(Keys.PAGE_DOWN)
            htmlElem.send_keys(Keys.PAGE_DOWN)
            htmlElem.send_keys(Keys.PAGE_DOWN)
        #cập nhật 1 ngày trước đó
        until = until - timedelta(days=1)
        since = since - timedelta(days=1)


import discord, os, gettext, traceback
from discord.ext import commands
import logging
from logging.handlers import TimedRotatingFileHandler
from config import *
localedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locales')
translate = gettext.translation('messages', localedir, languages=[config['LANGUAGE']])
translate.install()
_ = translate.gettext

from data import *
from farm import *

logger = logging.getLogger('discord')
logger.setLevel(logging.DEBUG)
log_filename = os.path.join("..","logs",'mercure.log')
handler = logging.FileHandler(filename=log_filename, encoding='utf-8', mode='w')
handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s'))
logger.addHandler(handler)

fileHandler = TimedRotatingFileHandler(os.path.join("..","logs",'mercure.log'), when='midnight')
fileHandler.suffix = "%Y_%m_%d.log"
fileHandler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s: %(message)s'))
fileHandler.setLevel(logging.INFO)
logger.addHandler(fileHandler)

description = _('''LGC's BGS assistant''')
bot = commands.Bot(command_prefix=config['prefix'], description=description, pm_help=True, heartbeat_timeout=150)
bot.remove_command("help")

# Checks if the user is a server admin
def is_admin():
    def predicate(ctx):
        return ctx.message.author.id in admin_ids
    return commands.check(predicate)

# Checks if we are in the oracle channel, and if we are, that the user who
# wrote the command is in admin
def acces_oracle():
    def verifier_droits_oracle(ctx):
        if str(ctx.message.channel) == channel_for_daily_post_name:
            if ctx.message.author.id in admin_ids:
                return True
            else:
                return False
        else:
            return True
    return commands.check(verifier_droits_oracle)

@bot.event
async def on_ready():
    """
    Actions to do when the bot is connected successfully and ready to process commands
    """
    if config['DEBUG']:
        print(_("Logged in as"))
        print(bot.user.name)
        print(bot.user.id)
        print('------')

    # we load all modules
    for f in os.listdir("modules"+os.sep):
        if f.endswith('.py'):
            bot.load_extension(f"modules.{f[:-3]}")

    await bot.change_presence(activity=discord.Game(name=_('helping the cartographers')))

@bot.event
async def on_command(ctx):
    """Called when a command is called. Used to log commands on a file."""
    if config['DEBUG']: print(_("on_command event"))
    if isinstance(ctx.message.channel, discord.abc.PrivateChannel):
        destination = 'PM'
    else:
        destination = '#{0.channel.name} ({0.guild.name})'.format(ctx.message)
    logger.info('Command by {0} in {1}: {2}'.format(ctx.message.author.display_name, destination, ctx.message.content))

@bot.event
async def on_message(message):
    """Called every time a message is sent on a visible channel.
    This is used to make commands case insensitive.
    Also, we check if we're in the channel where only admins can use the bot
    """
    if config['DEBUG']: print(_("on_message function"))
    await bot.process_commands(message)

@bot.command()
async def reload(ctx):
    # we reload all modules
    # we load all modules
    for f in os.listdir("modules"+os.sep):
        if f.endswith('.py'):
            if config['DEBUG']: print(_("Module {module} reloaded").format(module=f[:-3]))
            bot.unload_extension(f"modules.{f[:-3]}")
            bot.load_extension(f"modules.{f[:-3]}")

@bot.command()
async def test_farm(ctx, arg):
    if config['DEBUG']: print(_("test_farm command"))
    for site in farm[str(arg).lower()]["sites"][_("bubble")]:
        await ctx.send(site)

@test_farm.error
async def test_farm_error(ctx, error):
    if config['DEBUG']: print(_("test_farm command"))
    message = _("Sorry, but that element is unknown. Here's a list : \n")
    for key in farm.keys():
        message += "- "+str(key).capitalize()+"\n"
    await ctx.send(message)

if __name__ == "__main__":
    try:
        bot.run(config["discord_token"])
    finally:
        exit()


#!/usr/bin/env python3

###
### Count blocks modified by minecraft player
###

# https://www.python.org/dev/peps/pep-0008/#comments

import sys
import fcntl
import yaml
import time
from os import path

from config import Config
from source_database import SourceDatabase
from destination_database import DestinationDatabase

def process_world():
	pass

###
###	compress raw data
###
def process_data(raw_data):

	# data storage
	pick = {};
	put = {};

	for item in raw_data.get_item():
		user = item["playerid"]
		replaced = item["replaced"]
		type = item["type"]

		if (replaced == 0):
			if not (user in put):
				put[user] = {}

			if not (type in put[user]):
				put[user][type] = 0

			put[user][type] += 1
		else:
			if not (user in pick):
				pick[user] = {}

			if not (replaced in pick[user]):
				pick[user][replaced] = 0

			pick[user][replaced] += 1

	# for user in pick:
	# 	for replaced in pick[user]:
	# 		print("player[{}] replace {} now {}x".format(user, replaced, pick[user][replaced]))

	return {"pick": pick, "put": put, "start_id": raw_data.get_start_id(), "end_id": raw_data.get_end_id()}

###
### main function
###
def main():

	config = Config("./../config/config.yml")

	# save time on start
	main_start = time.time()

	if config.is_debug_mode_on():
		print("Start blocklog_counter with debugging on.")

	# get configuration
	worlds = config.get_world_names()
	maximum_to_proccess = config.get_maximum_processed()

	# get database connection
	blocklog_counter_database = DestinationDatabase(config)
	blocklog_database = SourceDatabase(config)

	for world_name in worlds:

		while True:

			# select items from id
			select_start = blocklog_counter_database.get_position(world_name)

			source_data = blocklog_database.get_data(world_name, select_start, maximum_to_proccess)

			# if no data for world then try next world
			if source_data.get_row_count() == 0:
				break

			data = process_data(source_data)

			blocklog_counter_database.save_data(world_name, data)

			blocklog_counter_database.set_position(world_name, source_data.get_end_id())

			if config.is_agresive_process_on():
				# prevent endless loop consuming whole resources
				time.sleep(1)
			else:
				break

		if config.is_debug_mode_on():
			print(world_name + " done...")

	# calculate process runtime
	main_end = time.time()
	main_elapsed = main_end - main_start

	if config.is_debug_mode_on():
		print("Time main running: {}".format(main_elapsed))

if __name__ == "__main__":

	# open file for locking
	pid_file = "program.pid"
	fp = open(pid_file, 'w')

	# try lock file to see if only one instance is running
	try:
		fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
	except IOError:
		exit("ERROR: Another instance is running");

	# no other instance is running -> start main function
	main()


#pandas uses plot() method to create diagrams
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("data.csv")
df.plot()
plt.show()
#Scatter Plot: kind argument, needs x and y-axis

df.plot(kind = "scatter", x = "Duration", y ="Calories")
plt.show()

#Histogram: kind argument: kind = "hist"
#example: will use the "Duration" column to create the histogram
df["Duration"].plot(kind = "hist")
plt.show()


from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_mail import Mail
from .main import main as main_blueprint

# Initialize SQLAlchemy, Mail, and other extensions
db = SQLAlchemy()
mail = Mail()

def create_app():
    app = Flask(__name__)

    app.config['SECRET_KEY'] = 'secret-key-goes-here'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'

    # Flask-Mail configuration
    app.config['MAIL_SERVER'] = 'smtp.gmail.com'
    app.config['MAIL_PORT'] = 587
    app.config['MAIL_USE_TLS'] = True
    app.config['MAIL_USERNAME'] = 'your-email@gmail.com'
    app.config['MAIL_PASSWORD'] = 'your-password'
    app.config['MAIL_DEFAULT_SENDER'] = 'your-email@gmail.com'

    db.init_app(app)
    mail.init_app(app)

    # Import blueprints
    from .main import main as main_blueprint
    from .research import research as research_blueprint

    # Register blueprints
    app.register_blueprint(main_blueprint)
    app.register_blueprint(research_blueprint, url_prefix='/research')

    # Initialize LoginManager
    login_manager = LoginManager()
    login_manager.login_view = 'auth.login'
    login_manager.init_app(app)

    from .models import User

    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(int(user_id))

    # blueprint for auth routes in our app
    from .auth import auth as auth_blueprint
    app.register_blueprint(auth_blueprint)

    # Create database tables if they don't exist
    with app.app_context():
        db.create_all()

    return app


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

"""
stabilize images using sparse optical flow.
dump numpy files of 3D(x, y, z) image movement direction.

configure [STABILIZE] section in "config/config.ini".
    ANGLE_THRESH   : If the variance of the flows in a frame is under ANGLE_THRESH, features in the frame are thought to be undetected.
    DUMP_FILEPATH  : filepath to dump numpy files (fixDirection_arr) to this path.
    VIDEO_FILEPATH : filepath to output stabilized video to this path.
"""

import sys
import time
import numpy as np
import statistics as st
import cv2
import matplotlib.pyplot as plt
from configparser import ConfigParser
import ciputil

TIME_MAX = None
PAGE_MAX = None
OUTPUT_VIDEO = None

class FeatureError(Exception):
    def __init__(self, value):
        self.value = value

def calc_fix_direction(angleThresh):
    """
    calculate fix direction for each time point.
    fixDirection_arr[page][time] = fixDirectionX, fixDirectionY, fixDirectionZ
    (page: 1 ~ PAGE_MAX, time: 1 ~ TIME_MAX)
    """
    def get_feature(prevImg, nextImg, prevFeature):
        assert prevImg.shape == nextImg.shape
        #parameter of Lucas-Kanade method
        lk_params = dict(winSize = (20, 20),
                            maxLevel = 5,
                            criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
        if len(prevImg.shape) == 3:
            prevImg = cv2.cvtColor(prevImg, cv2.COLOR_BGR2GRAY)
            nextImg = cv2.cvtColor(nextImg, cv2.COLOR_BGR2GRAY)
            nextFeature, status, err = cv2.calcOpticalFlowPyrLK(prevImg, nextImg, prevFeature, None, **lk_params)
            prevFeatureFiltered = prevFeature[status == 1]
            nextFeatureFiltered = nextFeature[status == 1]
        return prevFeatureFiltered, nextFeatureFiltered

    def get_binarization(gray):
        """
        Threshold is determined by the Otsu algorithm
        """
        _, binaryImg = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
        return binaryImg

    def calc_sparseFlow(prevFeatureFiltered, nextFeatureFiltered):
        sparseFlow = np.zeros((nextFeatureFiltered.shape[0],3))
        for i, (prevPoint, nextPoint) in enumerate(zip(prevFeatureFiltered, nextFeatureFiltered)):
            prevX, prevY = prevPoint.ravel()
            nextX, nextY = nextPoint.ravel()
            sparseFlow[i][0] = nextX - prevX
            sparseFlow[i][1] = nextY - prevY
        return sparseFlow

    def calc_movement(sparseFlow):
        try:
            meanX, meanY, meanZ = np.mean(sparseFlow, axis=0)
        except ZeroDivisionError:
            meanX, meanY, meanZ = 0, 0, 0
        movement = np.array([meanX, meanY, meanZ])
        return movement

    def calc_angle_variance(sparseFlow):
        filterSparseFlow = sparseFlow.copy()
        filterSparseFlow[filterSparseFlow <= 5] = 0
        filterSparseFlowX = filterSparseFlow[:, 0]
        filterSparseFlowY = filterSparseFlow[:, 1]
        angle_arr = np.arctan2(filterSparseFlowX, filterSparseFlowY) * 180 / np.pi
        angleVar = np.var(angle_arr)
        return angleVar

    def normalized_variance(angleVar_arr):
        varMax = np.amax(angleVar_arr)
        if varMax != 0:
            normAngleVar_arr = angleVar_arr / varMax
        else:
            return angleVar_arr
        return normAngleVar_arr

    fixDirection_arr = np.zeros((PAGE_MAX + 1,TIME_MAX + 1, 3))  # +1 to adjust to 1 origin of time
    allPage_fixDirection_arr = np.zeros((TIME_MAX + 1, 3))
    latestMovement = np.array([0, 0, 0])
    angleVar_arr = np.zeros((PAGE_MAX+1, TIME_MAX+1))
    movement_arr = np.zeros((PAGE_MAX + 1, TIME_MAX + 1, 3))

    feature_params = dict(maxCorners = 200,
                            qualityLevel = 0.001,
                            minDistance = 10,
                            blockSize = 5)

    for time in range(2, TIME_MAX+1):
        for page in range(1, PAGE_MAX + 1):
            prevImg = ciputil.get_image(time=time-1, page=page)
            prevGray = cv2.cvtColor(prevImg, cv2.COLOR_BGR2GRAY)
            nextImg = ciputil.get_image(time=time, page=page)
            flowMask = get_binarization(prevGray)
            prevFeature = cv2.goodFeaturesToTrack(prevGray, mask=flowMask, **feature_params)
            try:
                prevFeatureFiltered, nextFeatureFiltered = get_feature(prevImg, nextImg, prevFeature)
                if prevFeatureFiltered.shape[0] <= 50:
                    raise FeatureError("Not detect feature")
                sparseFlow = calc_sparseFlow(prevFeatureFiltered, nextFeatureFiltered)
                prevFeature = nextFeatureFiltered.reshape(-1, 1, 2)
                angleVar = calc_angle_variance(sparseFlow)
                angleVar_arr[page][time] = angleVar
                movement_arr[page][time] = calc_movement(sparseFlow)
            except FeatureError:
                movement_arr[page][time] = movement_arr[page-1][time]

    normAngleVar_arr = normalized_variance(angleVar_arr)
    for time in range(2, TIME_MAX+1):
        for page in range(1, PAGE_MAX+1):
            if normAngleVar_arr[page][time] >= angleThresh:
                movement_arr[page][time] = movement_arr[page-1][time]
            for i in range(3):
                fixDirection_arr[page][time][i] = fixDirection_arr[page][time - 1][i] + movement_arr[page][time][i]
    return fixDirection_arr

def output_stabilized_video(fixDirection_arr, videoFilepath, configFilepath):
    """
    output stabilized video using fixDirection_arr
    """

    config = ConfigParser()
    config.read(configFilepath)
    if config["VIDEO"]["PAGE"] == "ALL":
        pageFirst = 1
        pageLast = PAGE_MAX
    else:
        pageFirst = int(config["VIDEO"]["PAGE"])
        pageLast = pageFirst
    print("START: output video to {}, pageFirst: {}, pageLast: {}".format(videoFilepath, pageFirst, pageLast))

    fourcc = int(cv2.VideoWriter_fourcc(*'avc1'))
    video = cv2.VideoWriter(videoFilepath, fourcc, 5.0, (960, 960))
    waitImg = np.zeros((960, 960, 3), np.uint8)  # image for waiting

    for page in range(pageFirst, pageLast + 1):
        for time in range(1, TIME_MAX + 1):
            img = ciputil.get_image(time=time, page=page)
            fixImg = ciputil.get_stabilized_image(img, fixDirection_arr[page][time])

            data = "[page: {0:03d} time: {1:03d}]".format(page, time)
            cv2.putText(fixImg, data, (160, 935),
                        cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255))

            video.write(fixImg)
        for _ in range(10):
            video.write(waitImg)
    video.release()

    print("DONE: output video to {}".format(videoFilepath))

def main(level):
    global TIME_MAX
    global PAGE_MAX
    global OUTPUT_VIDEO

    configFilepath = "./config/config.ini"
    TIME_MAX, PAGE_MAX, OUTPUT_VIDEO = ciputil.read_config(configFilepath, level)
    print("level = {}".format(level))
    angleThresh, dumpFilepath, videoFilepath = ciputil.read_config_stabilize(configFilepath)
    fixDirection_arr = calc_fix_direction(angleThresh)
    dumpFilepath =  dumpFilepath.replace("fixDir.npy", str(level) + "_fixDir.npy")
    np.save(dumpFilepath, fixDirection_arr)
    print("DONE:  calcurate fix direction")

    if OUTPUT_VIDEO:
        output_stabilized_video(fixDirection_arr, videoFilepath, configFilepath)

if __name__ == "__main__":
    start = time.time()
    main()
    elapse = time.time() - start
    print('\nelapse time: {} sec'.format(elapse))


"""
Zombie Dice is by Steve Jackson Games
http://zombiedice.sjgames.com/

Zombie Dice simulator by Al Sweigart (al@inventwithpython.com)
(I'm not affiliated with SJ Games. This is a hobby project.)

Note: A "turn" is a single player's turn. A "round" is every player having one turn.
Note: Since all variables are public in Python, it is trivial to have a bot that hacks the tournament code. Inspect the bot code before running it.

Instructions for making your own bot can be found here: http://inventwithpython.com/blog/2012/11/21/programming-ai-bots-for-zombie-dice/
"""

__version__ = '0.1.6'

EXCEPTIONS_LOSE_GAME = False # if True, errors in bot code won't stop the tournament code but instead result in the bot losing that game. Leave on False for debugging.
MAX_TURN_TIME = None # number of seconds bot can take per turn. Violating this results in the bot losing the game.

# TODO - I wish there was a way to pre-emptively cut off the bot's turn() call
# after MAX_TURN_TIME seconds have elapsed, but it seems like there's no
# simple way to share GAME_STATE state between the threads/processes.

import logging, random, sys, copy, platform, time, threading, webbrowser, os, re
from collections import namedtuple

# Import correct web server module
here = os.path.abspath(os.path.dirname(__file__))
os.chdir(here)

if platform.python_version().startswith('2.'):
    from SimpleHTTPServer import * # python 2 code
    from SocketServer import *
else:
    from http.server import HTTPServer, SimpleHTTPRequestHandler # python 3 code

logging.basicConfig(level=logging.WARNING, format='%(asctime)s - %(levelname)s - %(message)s')
logging.debug('Start of the Zombie Dice program.')

# constants, to keep a typo in a string from making weird errors
COLOR = 0
ICON = 1
RED = 'red'
GREEN = 'green'
YELLOW = 'yellow'
SHOTGUN = 'shotgun'
BRAINS = 'brains'
FOOTSTEPS = 'footsteps'

DieRoll = namedtuple('DieRoll', 'color icon')

TOURNAMENT_STATE = None

GAME_STATE = { # Just so that this dict is shareable for user's bot code, don't reassign this variable ever. (Poor design, but it's made for implementation simplicity for newbie programmers.)
    'CURRENT_ZOMBIE': None, # string of the zombie whose turn it is currently
    'CURRENT_CUP': None, # list of dice strings (i.e. 'red', 'yellow', 'green')
    'CURRENT_HAND': None, # list of dice being rolled (should always be length of three)
    'SHOTGUNS_ROLLED': None, # number of shotguns rolled this turn
    'BRAINS_ROLLED': None, # number of brains rolled this turn
    'ROLLED_BRAINS_DETAILS': None, # list of dice strings for each brain rolled, used in the rare event we run out of brain dice
    'TURN_START_TIME': None, # time since unix epoch that the current turn began
}

# Web GUI stuff:
WEB_SERVER_PORT = random.randint(49152, 61000)
SCORE_BAR_MAX_WIDTH = 350 # width in pixels in the web ui for the score bar
TOURNAMENT_RUNNING = False
WEB_GUI_NUM_GAMES = None
START_TIME = None

def assignUniqueZombieName(zombies):
    # Assign names to zombies missing names:
    for zombie in zombies:
        if not hasattr(zombie, 'name') or zombie.name is None:
            zombie.name = zombie.__class__.__name__
        elif not isinstance(zombie.name, str):
            zombie.name = str(zombie.name)

    # Assign names to zombies with duplicate names:
    for zombie in zombies:
        otherZombiesNames = [z.name for z in zombies]
        otherZombiesNames.remove(zombie.name)

        # Check if a duplicate name exists:
        if zombie.name in otherZombiesNames:
            i = 2
            while zombie.name + str(i) in otherZombiesNames:
                i += 1
            zombie.name = zombie.name + str(i)

def runWebGui(zombies, numGames):
    global BOTS, NUM_GAMES
    assignUniqueZombieName(zombies)
    BOTS = list(zombies)
    NUM_GAMES = numGames
    print('Zombie Dice Visualization is running. Open your browser to http://localhost:%s to view it.' % (WEB_SERVER_PORT))
    print('Press Ctrl-C to quit.')
    broswerOpenerThread = BrowserOpener()
    broswerOpenerThread.start()

    if platform.python_version().startswith('2.'):
        httpd = TCPServer(('localhost', WEB_SERVER_PORT), ZombieDiceHandler) # python 2 code
    else:
        httpd = HTTPServer(('localhost', WEB_SERVER_PORT), ZombieDiceHandler) # python 3 code
    try:
        httpd.serve_forever()
    except (KeyboardInterrupt, SystemExit):
        httpd.socket.close()
        sys.exit('Quitting...')

def runGame(zombies):
    """Runs a single game of zombie dice. zombies is a list of zombie dice bot objects."""
    global GAME_STATE
    assignUniqueZombieName(zombies)

    # create a new game state object
    playerScores = dict([(zombie.name, 0) for zombie in zombies])
    playerOrder = [zombie.name for zombie in zombies]
    logging.debug('Player order: ' + ', '.join(playerOrder))

    GAME_STATE['ORDER'] = playerOrder
    GAME_STATE['SCORES'] = playerScores
    GAME_STATE['ROUND'] = 0

    # validate zombie objects, return None to signify an aborted game
    if len(playerOrder) != len(set(playerOrder)): # set() will get rid of any duplicates
        logging.error('Zombies must have unique names.')
        return
    if len(playerOrder) < 2:
        logging.error('Need at least two zombies to play.')
        return
    for zombie in zombies:
        if 'turn' not in dir(zombie):
            logging.error('All zombies need a turn() method.')
        if 'name' not in dir(zombie):
            logging.error('All zombies need a name member.')

    # call every zombie's newGame() method, if it has one
    for zombie in zombies:
        if 'newGame' in dir(zombie):
            zombie.newGame()

    # set up for a new game
    lastRound = False # True when a player has reached 13 brains
    tieBreakingRound = False # True when the "last round" ended in a tie
    zombiesInPlay = copy.copy(zombies) # all zombies play
    crashedBots = [] # list of bots that had exceptions
    while True: # game loop
        GAME_STATE['ROUND'] += 1
        logging.debug('ROUND #%s, scores: %s' % (GAME_STATE['ROUND'], GAME_STATE['SCORES']))

        for zombie in zombiesInPlay:
            if zombie in crashedBots:
                continue
            GAME_STATE['CURRENT_ZOMBIE'] = zombie.name
            logging.debug("%s's turn." % (GAME_STATE['CURRENT_ZOMBIE']))

            # set up for a new turn
            GAME_STATE['CURRENT_CUP'] = [RED] * 3 + [YELLOW] * 4 + [GREEN] * 6
            random.shuffle(GAME_STATE['CURRENT_CUP'])
            GAME_STATE['CURRENT_HAND'] = []
            GAME_STATE['SHOTGUNS_ROLLED'] = 0
            GAME_STATE['BRAINS_ROLLED'] = 0
            GAME_STATE['ROLLED_BRAINS_DETAILS'] = [] # list of dice colors, in case of "ran out of dice"

            # run the turn
            try:
                stateCopy = copy.deepcopy(GAME_STATE)
                GAME_STATE['TURN_START_TIME'] = time.time()
                zombie.turn(stateCopy) # (don't pass the original GAME_STATE)
            except Exception:
                crashedBots.append(zombie)
                if EXCEPTIONS_LOSE_GAME:
                    # if the bot code has an unhandled exception, it
                    # automatically loses this game
                    GAME_STATE['SCORES'][zombie.name] = -1
                    logging.warn('%s has lost the game due to a raised exception.' % (GAME_STATE['CURRENT_ZOMBIE']))
                else:
                    raise # crash the tournament program (good for debugging)
            if GAME_STATE['SHOTGUNS_ROLLED'] < 3:
                logging.debug('%s stops.' % (GAME_STATE['CURRENT_ZOMBIE']))
            else:
                logging.debug('%s is shotgunned. Lose all brains.' % (GAME_STATE['CURRENT_ZOMBIE']))

            # add brains to the score
            if GAME_STATE['SHOTGUNS_ROLLED'] < 3:
                GAME_STATE['SCORES'][zombie.name] += GAME_STATE['BRAINS_ROLLED']

            if GAME_STATE['SCORES'][zombie.name] >= 13:
                # once a player reaches 13 brains, it becomes the last round
                lastRound = True
                logging.debug('Last round. (%s has reached 13 brains.)' % (zombie.name))

        if tieBreakingRound:
            break # there is only one tie-breaking round, so after it end the game

        if lastRound:
            # only zombies tied with the highest score go on to the tie-breaking round (if there is one)
            zombiesInPlay = []
            highestScore = max(GAME_STATE['SCORES'].values()) # used for tie breaking round
            # zombiesInPlay will now only have the zombies tied with the highest score:
            zombiesInPlay = [zombie for zombie in zombies if GAME_STATE['SCORES'][zombie.name] == highestScore]

            if len(zombiesInPlay) == 1:
                # only one winner, so end the game
                break
            else:
                # multiple winners, so go on to the tie-breaking round.
                logging.debug('Tie breaking round with %s' % (', '.join([zombie.name for zombie in zombiesInPlay])))
                tieBreakingRound = True

    # call every zombie's endGame() method, if it has one
    for zombie in zombies:
        if 'endGame' in dir(zombie):
            zombie.endGame(copy.deepcopy(GAME_STATE))

    # rank bots by score
    ranking = sorted(GAME_STATE['SCORES'].items(), key=lambda x: x[1], reverse=True)
    highestScore = ranking[0][1]
    logging.debug('Final Scores: %s' % (', '.join(['%s %s' % (x[0], x[1]) for x in ranking])))     #(', '.join(['%s %s' % (name, score) for name, score in ranking.items()])))

    # winners are the bot(s) with the highest score
    winners = [x[0] for x in ranking if x[1] == highestScore]
    logging.debug('Winner%s: %s' % ((len(winners) != 1 and 's' or ''), ', '.join(winners)))

    return GAME_STATE

def runTournament(zombies, numGames):
    """A tournament is one or more games of Zombie Dice. The bots are re-used between games, so they can remember previous games.
    zombies is a list of zombie bot objects. numGames is an int of how many games to run."""
    global TOURNAMENT_STATE
    zombies = list(zombies)

    TOURNAMENT_STATE = {'GAME_NUMBER': 0,
                        'WINS': dict([(zombie.name, 0) for zombie in zombies]),
                        'TIES': dict([(zombie.name, 0) for zombie in zombies])}

    print('Tournament of %s games started...' % (numGames))

    for TOURNAMENT_STATE['GAME_NUMBER'] in range(numGames):
        random.shuffle(zombies) # randomize the order of the bots
        endState = runGame(zombies) # use the same zombie objects so they can remember previous games.

        if endState is None:
            sys.exit('Error when running game.')

        # Sort out the scores and find the winner.
        ranking = sorted(endState['SCORES'].items(), key=lambda x: x[1], reverse=True)
        highestScore = ranking[0][1]
        winners = [x[0] for x in ranking if x[1] == highestScore]
        if len(winners) == 1:
            TOURNAMENT_STATE['WINS'][ranking[0][0]] += 1
        elif len(winners) > 1:
            for score in endState['SCORES'].items():
                if score[1] == highestScore:
                    TOURNAMENT_STATE['TIES'][score[0]] += 1

    TOURNAMENT_STATE['GAME_NUMBER'] += 1 # increment to show all games are finished

    # print out the tournament results in neatly-formatted columns.
    print('Tournament results:')
    maxNameLength = max([len(zombie.name) for zombie in zombies])

    winsRanking = sorted(TOURNAMENT_STATE['WINS'].items(), key=lambda x: x[1], reverse=True)
    print('Wins:')
    for winnerName, winnerScore in winsRanking:
        print('    %s %s' % (winnerName.rjust(maxNameLength), str(winnerScore).rjust(len(str(numGames)))))

    tiesRanking = sorted(TOURNAMENT_STATE['TIES'].items(), key=lambda x: x[1], reverse=True)
    print('Ties:')
    for tiedName, tiedScore in tiesRanking:
        print('    %s %s' % (tiedName.rjust(maxNameLength), str(tiedScore).rjust(len(str(numGames)))))

def roll():
    """This global function is called by a zombie bot object to indicate that they wish to roll the dice.
    The state of the game and previous rolls are held in global variables."""
    global GAME_STATE

    if MAX_TURN_TIME is not None and (time.time() - GAME_STATE['TURN_START_TIME']) > MAX_TURN_TIME:
        # if the bot code has taken too long, it
        # automatically loses this game
        #GAME_STATE['SCORES'][zombie.name] = -1
        logging.warn('%s has lost the game due to taking too long.' % (GAME_STATE['CURRENT_ZOMBIE']))
        raise Exception('Exceeded max turn time.')

    # make sure zombie can actually roll
    if GAME_STATE['SHOTGUNS_ROLLED'] >= 3:
        return None

    logging.debug(GAME_STATE['CURRENT_ZOMBIE'] + ' rolls. (brains: %s, shotguns: %s)' % (GAME_STATE['BRAINS_ROLLED'], GAME_STATE['SHOTGUNS_ROLLED']))

    # "ran out of dice", so put the rolled brains back into the cup
    if 3 - len(GAME_STATE['CURRENT_HAND']) > len(GAME_STATE['CURRENT_CUP']):
        logging.debug('Out of dice! Putting rolled brains back into cup.')
        GAME_STATE['CURRENT_CUP'].extend(GAME_STATE['ROLLED_BRAINS_DETAILS'])
        GAME_STATE['ROLLED_BRAINS_DETAILS'] = []

    # add new dice to hand from cup until there are 3 dice in the hand
    while len(GAME_STATE['CURRENT_HAND']) < 3:
        newDie = random.choice(GAME_STATE['CURRENT_CUP'])
        logging.debug('%s die added to hand from cup.' % (newDie))
        GAME_STATE['CURRENT_CUP'].remove(newDie)
        GAME_STATE['CURRENT_HAND'].append(newDie)

    # roll the dice
    logging.debug('Hand is %s' % (', '.join(GAME_STATE['CURRENT_HAND'])))
    logging.debug('Cup has %s: %s' % (len(GAME_STATE['CURRENT_CUP']), ', '.join(GAME_STATE['CURRENT_CUP'])))
    diceRollResults = {SHOTGUN: 0, FOOTSTEPS: 0, BRAINS: 0, 'rolls': []}
    for die in GAME_STATE['CURRENT_HAND']:
        dieRollResult = rollDie(die)
        diceRollResults['rolls'].append(dieRollResult)
        diceRollResults[dieRollResult[ICON]] += 1 # increase the shotgun/brain/footstep count

    logging.debug('%s rolled %s' % (GAME_STATE['CURRENT_ZOMBIE'], diceRollResults))

    # count the shotguns and remove them from the hand
    for dieRollResult in diceRollResults['rolls']:
        if dieRollResult[ICON] == SHOTGUN:
            GAME_STATE['SHOTGUNS_ROLLED'] += 1
            logging.debug('Removing ' + dieRollResult[COLOR] + ' from hand for shotgun.')
            GAME_STATE['CURRENT_HAND'].remove(dieRollResult[COLOR])

    # count the brains and remove them from the hand
    for dieRollResult in diceRollResults['rolls']:
        if dieRollResult[ICON] == BRAINS:
            GAME_STATE['ROLLED_BRAINS_DETAILS'].append(dieRollResult[COLOR])
            GAME_STATE['BRAINS_ROLLED'] += 1
            logging.debug('Removing ' + dieRollResult[COLOR] + ' from hand for brains.')
            GAME_STATE['CURRENT_HAND'].remove(dieRollResult[COLOR])

    return diceRollResults

def rollDie(die):
    """Returns the result of a single die roll as a DieRoll namedtuple.
    Index 0 of the tuple is the color of the die (i.e. 'green', 'yellow', 'red').
    Index 1 is the icon: 'shotgun', 'footsteps', 'brains'."""
    roll = random.randint(1, 6)
    if die == RED:
        if roll in (1, 2, 3):
            return DieRoll(RED, SHOTGUN)
        elif roll in (4, 5):
            return DieRoll(RED, FOOTSTEPS)
        elif roll in (6,):
            return DieRoll(RED, BRAINS)
    elif die == YELLOW:
        if roll in (1, 2):
            return DieRoll(YELLOW, SHOTGUN)
        elif roll in (3, 4):
            return DieRoll(YELLOW, FOOTSTEPS)
        elif roll in (5, 6):
            return DieRoll(YELLOW, BRAINS)
    elif die == GREEN:
        if roll in (1,):
            return DieRoll(GREEN, SHOTGUN)
        elif roll in (2, 3):
            return DieRoll(GREEN, FOOTSTEPS)
        elif roll in (4, 5, 6):
            return DieRoll(GREEN, BRAINS)

# ========================================================================
# Web Gui Code:
# ========================================================================

class ZombieDiceHandler(SimpleHTTPRequestHandler):
    def log_message(self, format, *args):
        pass # comment out this entire method if you want to see the original HTTP log messages.

    def output(self, msg):
        self.send_header('Content-type','text/html')
        self.end_headers()
        self.wfile.write(msg.encode('ascii'))

    def moreoutput(self, msg):
        self.wfile.write(msg.encode('ascii'))

    def do_GET(self):
        global WEB_GUI_NUM_GAMES, START_TIME, TOURNAMENT_RUNNING
        self.send_response(200)
        reqPath = os.path.join(os.getcwd(), os.path.normpath(self.path[1:]))

        if os.path.isfile(reqPath):
            self.serveFile(reqPath)

        elif self.path == '/mainstatus':
            self.renderStatus()

        elif self.path == '/score':
            self.renderScoreJavascript()

        elif self.path.startswith('/start'):
            # "/start/<NUM GAMES>" is visited when the player clicks the "Begin Tournament" button. Check the path for the number of games to run.
            self.beginTournamentButtonPressed()

        elif self.path == '/':
            self.renderMainPage()

    def serveFile(self, reqPath):
        mimeTypeMapping = {'.js': 'application/x-javascript',
                           '.html': 'text/html',
                           '.css': 'text/css',
                           '.png': 'image/png',
                           '.gif': 'image/gif',
                           '.jpg': 'image/jpeg'}
        ending = reqPath[reqPath.rfind('.'):]
        if ending in mimeTypeMapping:
            self.send_header('Content-type',mimeTypeMapping[ending])
        else:
            self.send_header('Content-type','text/plain')
        self.end_headers()
        fp = open(reqPath, 'rb')
        self.wfile.write(fp.read())

    def renderStatus(self):
        if not TOURNAMENT_RUNNING:
            # display the "Begin Tournament" button.
            self.output("""

                  Run <input type="text" size="4" id="numGamesToRun" value="%s"> simulated games.

                  <input type="button" value="Begin Tournament" onclick="startTournament(); return false;" />

                """ % (NUM_GAMES))
        else:
            # display the current status of the tournament simulation that is in progress
            self.output("""

                %s / %s Games Run
                Estimate Time Remaining: %s
                """ % (TOURNAMENT_STATE['GAME_NUMBER'], WEB_GUI_NUM_GAMES, estTimeRemaining(START_TIME, TOURNAMENT_STATE['GAME_NUMBER'], WEB_GUI_NUM_GAMES)))

            if TOURNAMENT_STATE['GAME_NUMBER'] == WEB_GUI_NUM_GAMES:
                # the javascript code checks for this text to know when to stop making repeated ajax requests for status updates
                self.moreoutput('(Refresh page to run a new tournament.)')

    # Returns JavaScript that will be evaluated by eval() in the web page (elegant solution, I know) to update the score table.
    def renderScoreJavascript(self):
        self.send_header('Content-type','text/html')
        self.end_headers()

        if TOURNAMENT_RUNNING and WEB_GUI_NUM_GAMES is not None and START_TIME is not None and TOURNAMENT_STATE['GAME_NUMBER'] is not None:
            for zombieName in [bot.name for bot in BOTS]:
                predictedMaxWidth = int(SCORE_BAR_MAX_WIDTH * max(int(len(BOTS) / 2.0), 1)) # We'll assume that the bots mostly evenly win games
                #predictedMaxWidth = SCORE_BAR_MAX_WIDTH # If the score bar keeps getting too long, just uncomment this line

                scoreBarLength = int((TOURNAMENT_STATE['WINS'][zombieName] / float(WEB_GUI_NUM_GAMES)) * predictedMaxWidth)
                scoreBarColor = getScoreBarColor(zombieName, TOURNAMENT_STATE['WINS'])
                wins = TOURNAMENT_STATE['WINS'][zombieName]
                ties = TOURNAMENT_STATE['TIES'][zombieName]

                escapedZombieName = zombieName.replace(' ', '_') # JavaScript code can't handle zombie name with spaces. TODO - probably can't handle other characters too.
                self.moreoutput("$('#%s_scorebar').css('width', '%spx'); " % (escapedZombieName, scoreBarLength))
                self.moreoutput("$('#%s_scorebar').css('background-color', '#%s'); " % (escapedZombieName, scoreBarColor))
                self.moreoutput("$('#%s_wins').text('%s'); " % (escapedZombieName, wins))
                self.moreoutput("$('#%s_ties').text('%s'); " % (escapedZombieName, ties))

    def beginTournamentButtonPressed(self):
        global WEB_GUI_NUM_GAMES, TOURNAMENT_RUNNING, START_TIME

        # path will be set to "/start/<NUM GAMES>"
        mo = re.search(r'(\d+)', self.path)
        if mo is not None:
            WEB_GUI_NUM_GAMES = int(mo.group(1))
        else:
            WEB_GUI_NUM_GAMES = 1000 # default to 1000
        START_TIME = time.time()

        # start the tournament simulation in a separate thread
        tournamentThread = TournamentThread()
        tournamentThread.start()
        TOURNAMENT_RUNNING = True # TOURNAMENT_RUNNING remains True after the tournament completes, until the "/" page is reloaded. Then it is set to False.
        self.output('') # return blank http reply so the browser doesn't try the request again.

    def renderMainPage(self):
        global WEB_GUI_NUM_GAMES, TOURNAMENT_RUNNING, START_TIME

        # when this page is loaded, if the previous tournmaent completed then restart the tournament:
        if WEB_GUI_NUM_GAMES is not None and TOURNAMENT_STATE['GAME_NUMBER'] == WEB_GUI_NUM_GAMES:
            TOURNAMENT_RUNNING = False # set to True after user clicks the "Begin Tournament" button in the web ui and the tournamentThread starts running.
            WEB_GUI_NUM_GAMES = None # TODO - make this a member variable instead of a global
            START_TIME = None # timestamp of when the tournament started, used for the "estimated time remaining"
            #TOURNAMENT_STATE['GAME_NUMBER'] = 0 #

        # create the table where each bot has a row for its score
        scoreTableHtml = []
        for zombieName in sorted([bot.name for bot in BOTS]):
            escapedZombieName = zombieName.replace(' ', '_') # JavaScript code can't handle zombie name with spaces. TODO - probably can't handle other characters too.
            scoreTableHtml.append('
%s

' % (zombieName, SCORE_BAR_MAX_WIDTH, escapedZombieName, escapedZombieName, escapedZombieName))
        scoreTableHtml = ''.join(scoreTableHtml)

        # output the main page's html (with the score table)
        self.output("""<!DOCTYPE html>

            <title>Zombie Dice Simulator</title>

By Al Sweigart
https://inventwithpython.com
Buy Zombie Dice Online
Program your own Zombie Dice bot.

	Wins	Ties
            %s

            """ % (scoreTableHtml))

# The bot in the lead has a bright red #FF0000 bar, whereas a bot at 0 wins has a black #000000 bar. Depending on where in between the bot's score is, the appropriate black-to-red color is returned. Returns a string like 'FF0000', without the leading '#' character.
def getScoreBarColor(zombieName, winsState):
    maxScore = max(winsState.values())
    myScore = winsState[zombieName]

    if maxScore == 0:
        return '000000' # return black color to prevent zero division error

    redness = int((myScore / float(maxScore)) * 255)
    redness = hex(redness)[2:].upper()
    if len(redness) == 1:
        redness = '0' + redness
    return redness + '0000' # return the HTML RGB color for the bar

# Calculates amount of time remaining for this tournament, given how long it has taken to run the previous games.
def estTimeRemaining(startTime, currentGame, totalGames):
    lapsed = time.time() - startTime
    if currentGame == 0:
        return 'Unknown' # prevent zero division
    totalEstTime = lapsed * (totalGames / float(currentGame))
    return prettyTime(int(totalEstTime - lapsed))

# Takes parameter that is a number of seconds and returns a pretty string with the time in weeks, days, hours, minutes, and seconds.
def prettyTime(t): # t is in seconds
    wk = day = hr = min = sec = 0
    if t > 604800:
        wk = t // 604800
        t = t % 604800
    if t > 86400:
        day = t // 86400
        t = t % 86400
    if t > 3600:
        hr = t // 3600
        t = t % 3600
    if t > 60:
        min = t // 60
        t = t % 60
    sec = t

    t_str = []
    if wk > 0:
        t_str.append('%s wk' % (wk))
    if wk > 0 or day > 0:
        t_str.append('%s day' % (day))
    if wk > 0 or day > 0 or hr > 0:
        t_str.append('%s hr' % (hr))
    if wk > 0 or day > 0 or hr > 0 or min > 0:
        t_str.append('%s min' % (min))
    t_str.append('%s sec' % (sec))

    return ' '.join(t_str[:2])

# Runs the zombie dice tournament in a separate thread.
class TournamentThread(threading.Thread):
    def run(self):
        runTournament(BOTS, WEB_GUI_NUM_GAMES)

class BrowserOpener(threading.Thread):
    def run(self):
        time.sleep(0.4) # give the server a bit of time to start
        webbrowser.open('http://localhost:%s' % (WEB_SERVER_PORT))

from . import examples

def demo():
    zombies = (
        examples.RandomCoinFlipZombie(name='Random'),
        examples.MonteCarloZombie(name='Monte Carlo', riskiness=40, numExperiments=20),
        examples.RollsUntilInTheLeadZombie(name='Until Leading'),
        examples.MinNumShotgunsThenStopsZombie(name='Stop at 2 Shotguns', minShotguns=2),
        examples.MinNumShotgunsThenStopsZombie(name='Stop at 1 Shotgun', minShotguns=1),
        examples.AlwaysRollsTwiceZombie(name='Roll Twice'),
        # Add any other zombie players here.
    )

    # Uncomment one of the following lines to run in CLI or Web GUI mode:
    #zombiedice.runTournament(zombies=zombies, numGames=100)
    runWebGui(zombies=zombies, numGames=1000)


import redis
from django.conf import settings

class RedisClient:
    conn = None

    @classmethod
    def get_connection(cls):
        if cls.conn:
            return cls.conn
        cls.conn = redis.Redis(
            host=settings.REDIS_HOST,
            port=settings.REDIS_PORT,
            db=settings.REDIS_DB,
        )
        return cls.conn

    @classmethod
    def clear_db(cls):
        if not settings.TESTING:
            raise Exception('You can not clear redis in production')
        conn = cls.get_connection()
        conn.flushdb()