#-*- encoding:UTF-8 -*-
import os, shutil, hashlib

# 获取文件夹下所有文件夹路径，包含子文件夹，以列表返回
def get_folder_path(path):
    path_list = []
    for dirpath, dirnames, filenames in os.walk(path):
        path_list.append(dirpath)
    return path_list[1:]

# 获取文件夹下所有文件夹名（不含路径），包含子文件夹，以列表返回
def get_folder_name(path):
    path_name_list = []
    for dirpath, dirnames, filenames in os.walk(path):
        for dirname in dirnames:
            path_name_list.append(dirname)
    return path_name_list

# 获取文件夹下所有文件路径，包含子文件夹下所有文件，以列表返回
def get_file_path(path):
    file_path_list = []
    for dirpath, dirnames, filenames in os.walk(path):
        for name in filenames:
            file_path_list.append(os.path.join(dirpath,name))
    return file_path_list

# 获取文件夹下所有文件名（不含路径），包含子文件夹下所有文件，以列表返回
def get_file_name(path):
    file_name_list = []
    for dirpath, dirnames, filenames in os.walk(path):
        for name in filenames:
            file_name_list.append(name)
    return file_name_list

# 获取路径下的子文件夹，不包含子文件夹里面的文件夹，以列表形式返回
def get_son_folder(path):
    folder_list = []
    for folder in os.listdir(path):
        if (os.path.isdir(path + '\\' + folder)):
            folder_list.append(path + '\\' + folder)
        else:
            continue
    return folder_list

# 获取路径下的子文件，不包含子文件夹里面的文件，以列表形式返回
def get_son_file(path):
    file_list = []
    for file in os.listdir(path):
        if (os.path.isfile(path + '\\' + file)):
            file_list.append(path + '\\' + file)
        else:
            continue
    return file_list

# 获取路径下文件和文件夹的数量，不包含子文件夹下文件
def get_file_amount(path):
    count = 0
    for fn in os.listdir(path):  # fn 表示的是文件名
        count = count + 1
    return count

# 查找以name为开头的文件，以列表形式返回带路径的文件名,不支持带子文件的路径
def find_startswith_file_path(path, name):
    new_file_list = []
    file_list = get_file_name(path)
    for file in file_list:
        if file.startswith(name):
            new_file_list.append(os.path.join(path,file))
        else:
            continue
    return new_file_list

# 查找以name为开头的文件，以列表形式返回文件名（不包含路径）
def find_startswith_file_name(path, name):
    new_file_list = []
    file_list = get_file_name(path)
    for file in file_list:
        if file.startswith(name):
            new_file_list.append(file)
        else:
            continue
    return new_file_list

# 查找以name为结尾的文件，以列表形式返回带路径的文件名,不支持带子文件的路径
def find_endswith_file_path(path, name):
    new_file_list = []
    file_list = get_file_name(path)
    for file in file_list:
        if file.endswith(name):
            new_file_list.append(os.path.join(path, file))
        else:
            continue
    return new_file_list

# 查找以name为结尾的文件，以列表形式返回文件名（不包含路径）
def find_endswith_file_name(path, name):
    new_file_list = []
    file_list = get_file_name(path)
    for file in file_list:
        if file.endswith(name):
            new_file_list.append(file)
        else:
            continue
    return new_file_list

# 查找包含name的文件，以列表形式返回带路径的文件名,不支持带子文件的路径
def find_in_file_path(path, name):
    new_file_list = []
    file_list = get_file_name(path)
    for file in file_list:
        if name in file:
            new_file_list.append(os.path.join(path, file))
        else:
            continue
    return new_file_list

# 查找包含name的文件，以列表形式返回文件名（不包含路径）
def find_in_file_name(path, name):
    new_file_list = []
    file_list = get_file_name(path)
    for file in file_list:
        if name in file:
            new_file_list.append(file)
        else:
            continue
    return new_file_list

# 获取文件的md5值
def get_md5(path):
    m5 = hashlib.md5()
    f = open(path, 'rb')
    m5.update(f.read())
    return m5.hexdigest()

'''
# -----------复制-----------

# 复制带path的文件到新的path
shutil.copyfile(old_path, new_path)

# -----------跨平台----------

# 可以取代操作系统特定的路径分隔符。windows下为 “\\”
os.sep

# 字符串指示你正在使用的平台。比如对于Windows，它是'nt'，而对于Linux/Unix用户，它是'posix'。
os.name

# 字符串给出当前平台使用的行终止符。例如，Windows使用'\r\n'，Linux使用'\n'而Mac使用'\r'。
os.linesep

# ------------路径-------------

# 函数得到当前工作目录，即当前Python脚本工作的目录路径。
os.getcwd()

# 改变工作目录到dirname
os.chdir(dirname)

# 返回当前目录（'.')
os.curdir

# 连接目录与文件名或目录;使用“\”连接
os.path.join(path,name)

# 返回文件路径
os.path.dirname(path)

# 判断路径是否为一个文件
os.path.isfile()

# 判断路径是否为一个目录
os.path.isdir()

# 函数用来检验给出的路径是否真地存在
os.path.existe()

# 获得绝对路径
os.path.abspath(name)

# 规范path字符串形式
os.path.normpath(path)

# 将path分割成目录和文件名二元组返回。
os.path.split(path)

# -----------获取文件属性--------

# 遍历文件夹
# dirnames：目录下的所有文件夹带路径
# filenames：目录下的所有文件
# dirpath：要列出指定目录的路径
for dirpath,dirnames,filenames in os.walk(path):

# 返回指定目录下的所有文件和目录名
os.listdir(path)

# 返回文件名
os.path.basename(path)

# 获得文件大小，如果name是目录返回0L
os.path.getsize(name)

# 分离文件名与扩展名
os.path.splitext()

# ------------删除------------

# 函数用来删除一个文件
os.remove(path)

# ------------shell----------

# 函数用来运行shell命令。
os.system(command)

'''


import sys
import pathlib
pth=str(pathlib.Path().absolute())
print(pth)
sys.path.append(('\\').join(pth.split('\\')[:-1])+"\\Utils")
from Utilities import *
from MF import *
from Visualization_utilities import *
from transformation import Transformation
from Signatures import Signature
import os
import open3d as o3d
import numpy as np

def getRotationMatrix(vect, ang):
    cosA = np.cos(ang)
    sinA = np.sin(ang)
    oneMinusCosA = 1-cosA
    out = np.zeros((3, 3))
    out[0, 0] = (cosA + vect[0] * vect[0] * oneMinusCosA)
    out[0, 1] = (vect[1] * vect[0] * oneMinusCosA + vect[2] * sinA)
    out[0, 2] = (vect[2] * vect[0] * oneMinusCosA - vect[1] * sinA)
    out[1, 0] = (vect[0] * vect[1] * oneMinusCosA - vect[2] * sinA)
    out[1, 1] = (cosA + vect[1] * vect[1] * oneMinusCosA)
    out[1, 2] = (vect[2] * vect[1] * oneMinusCosA + vect[0] * sinA)
    out[2, 0] = (vect[0] * vect[2] * oneMinusCosA + vect[1] * sinA)
    out[2, 1] = (vect[1] * vect[2] * oneMinusCosA - vect[0] * sinA)
    out[2, 2] = (cosA + vect[2] * vect[2] * oneMinusCosA)
    return out

def getRotationMatrixFromVector(vect1, vect2):
    vect_normal = np.cross(vect1, vect2)
    vect_normal = vect_normal/np.linalg.norm(vect_normal)
    dotProduct = np.dot(vect1/np.linalg.norm(vect1), vect2/np.linalg.norm(vect2))
    ang = np.arccos(dotProduct)
    return getRotationMatrix(vect1, ang)

#pts = 'Beam-W_W8x2_pulg_ac_210.pts'
#pts = 'Beam-W_W8x1000_pulg_mb_368.pts'
pts = 'Beam-W_W4x1301_pulg_mb_261.pts'
#pts = "Beam-W_W8x2102_pulg_ac_208.pts"
dpf=o3d.io.read_point_cloud("E:\\INNOVATE\\DB_TRAINING_1024_PTS\\"+pts.split('_')[0]+"\\"+pts)

ev1,ev2=project_pcl_pca(dpf,
             voxel_size=0.02,
             nb=5,
             std_ratio=1.5,
             p_th=0.1,
             ig="Linearity",
             upper=True
            )

rot = getRotationMatrixFromVector(ev1, np.array([0.0, 0.0, 1.0]))
cloud = dpf
mesh_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=80.0, origin=cloud.get_center())
cloud.rotate(rot)

p = np.asarray(cloud.points)
n_p = np.copy(p)

low_bound = np.min(n_p[:, 2])
high_bound = np.max(n_p[:, 2])
l = high_bound - low_bound
N_SLICES = 10
nd = {i:0 for i in range(N_SLICES)}
pd = {i:[] for i in range(N_SLICES)}

for cont, i in enumerate(n_p):
    for j in range(N_SLICES):
        if i[2]>= low_bound+j*l/N_SLICES and i[2] < low_bound+(j+1)*l/N_SLICES:
            nd[j] += 1
            pd[j].append([n_p[cont, 0], n_p[cont, 1]])
            n_p[cont, 2] = low_bound+j*l/N_SLICES
print(nd)
test = o3d.geometry.PointCloud()
test.points = o3d.utility.Vector3dVector(n_p)
#o3d.visualization.draw_geometries([test])

ww = []
hh = []
mm = []
for i in pd.keys():
    pd[i] = np.array(pd[i])
    co = pd[i].T.dot(pd[i])
    _, v = np.linalg.eig(co)
    aux = pd[i]
    init = np.random.random((aux.shape[0], 3))
    init[:, :2] = aux
    naux = o3d.geometry.PointCloud()
    naux.points = o3d.utility.Vector3dVector(init)
    m_ = np.array([0., 0., np.arctan(v[0, 1]/v[0, 0])])
    mm.append([0., 0., np.arctan(v[0, 1]/v[0, 0])])
    naux.rotate(naux.get_rotation_matrix_from_axis_angle(m_))
    puntex = np.asarray(naux.points)
    w = np.max(puntex[:, 1]) - np.min(puntex[:, 1])
    h = np.max(puntex[:, 0]) - np.min(puntex[:, 0])
    ww.append(w)
    hh.append(h)

    #naux.translate(-naux.get_center())
    #mesh_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=8.0, origin=np.array([0., 0., 0.]))
    #o3d.visualization.draw_geometries([naux, mesh_frame])
w_out = np.mean(np.array(ww))
h_out = np.mean(np.array(hh))

mm =np.array(mm)
N = len(dpf.points)
if N>= 5000:
    dpf, _ = dpf.remove_statistical_outlier(nb_neighbors=int(N/200), std_ratio = 0.9)
bb = dpf.get_oriented_bounding_box()
R = bb.R

x, y, z = R[0, 0], R[1, 0], R[2, 0] #Extrusion around x axis of bounding box
N = np.array([x, y, z])
P = np.array(dpf.get_center())
s = []
for i in dpf.points:
    v = i-P
    dist = np.dot(v, N)
    #pp = i-dist*N
    s.append(dist)

l = max(s) - min(s)
base = P+N*max(s)
mm_ = np.mean(mm, 0)
print(0.9*w_out, 0.9*h_out, base, l, N, naux.get_rotation_matrix_from_axis_angle(mm_))


import pygame,sys
import pygame.transform
from pygame.locals import *
import numpy
import random

pygame.init()

clock = pygame.time.Clock()

tileWidth = 16
tileHeight = 16

rowSize = 24
columnSize = 10

WINDOWHEIGHT=tileHeight*rowSize
WINDOWWIDTH=tileWidth*columnSize

SURFACE = pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT))

pygame.display.set_caption('Tetris New Version')

AQUA      = (0, 255, 255)
BLUE      = (0, 0, 255)
BLACK     = (0, 0, 0)
# Fuchsia   = (255, 0,255)
# Gray      = (128, 128, 128)
GREEN     = (0, 205, 0)
# Maroon    = (128, 0, 0)
# Navy Blue = (0, 0, 128)
# Olive     = (128, 128, 0)
PURPLE    = (153, 50, 204)
#RED       = (255, 0, 0)
# Silver    = (192, 192, 192)
# Teal      = (0, 128, 128)
WHITE     = (255, 255, 255)
RED       = (255, 0, 0)
YELLOW    = (255, 255, 0)
ORANGE    = (255, 140, 0)

field = numpy.zeros((rowSize, columnSize),dtype=numpy.int)

def drawLevel():
    SURFACE.fill(BLACK)
    #Draw the level
    for row in range(rowSize):
        for column in range(columnSize):
            if field[row,column] == 1:
                pygame.draw.rect(SURFACE,YELLOW,(tileWidth*column,row*tileHeight,tileWidth,tileHeight))
            elif field[row,column] == 2:
                pygame.draw.rect(SURFACE,PURPLE,(tileWidth*column,row*tileHeight,tileWidth,tileHeight))
            elif field[row,column] == 3:
                pygame.draw.rect(SURFACE,AQUA,(tileWidth*column,row*tileHeight,tileWidth,tileHeight))
            elif field[row,column] == 4:
                pygame.draw.rect(SURFACE,BLUE,(tileWidth*column,row*tileHeight,tileWidth,tileHeight))
            elif field[row,column] == 5:
                pygame.draw.rect(SURFACE,ORANGE,(tileWidth*column,row*tileHeight,tileWidth,tileHeight))
            elif field[row,column] == 6:
                pygame.draw.rect(SURFACE,GREEN,(tileWidth*column,row*tileHeight,tileWidth,tileHeight))
            elif field[row,column] == 7:
                pygame.draw.rect(SURFACE,RED,(tileWidth*column,row*tileHeight,tileWidth,tileHeight))

figure=random.randint(1,7)
def putFigure(figure,figureRow,figureColumn,check=True):

    if figure == 1:
        if figureRow == rowSize:
            return False
        if numpy.sum(field[figureRow-1:figureRow+1,figureColumn-1:figureColumn+1]) > 0:
            return False
        if figureColumn-1 < 0:
            return False
        if figureColumn > columnSize - 1:
            return False
        if check == False:
            field[figureRow-1:figureRow+1,figureColumn-1:figureColumn+1]=1
    elif figure == 2:
        if figureRow+1 == rowSize:
            return False
        if figureColumn-1 < 0:
            return False
        if figureColumn+1 > columnSize - 1:
            return False
        if numpy.sum(field[figureRow,figureColumn-1:figureColumn+2]) > 0:
            return False
        if numpy.sum(field[figureRow:figureRow+2,figureColumn]) > 0:
            return False
        if check == False:
            field[figureRow,figureColumn-1:figureColumn+2]=2
            field[figureRow:figureRow+2,figureColumn]=2
    elif figure == 3:
        if figureRow == rowSize:
            return False
        if figureColumn-1 < 0:
            return False
        if figureColumn+2 > columnSize - 1:
            return False
        if numpy.sum(field[figureRow,figureColumn-1:figureColumn+3]) > 0:
            return False
        if check == False:
            field[figureRow,figureColumn-1:figureColumn+3]=3
    elif figure == 4:
        if figureRow+1 == rowSize:
            return False
        if figureColumn < 0:
            return False
        if figureColumn+1 > columnSize-1:
            return False
        if numpy.sum(field[figureRow-1:figureRow+2,figureColumn]) > 0:
            return False
        if numpy.sum(field[figureRow+1,figureColumn:figureColumn+2]) > 0:
            return False
        if check == False:
            field[figureRow-1:figureRow+2,figureColumn]=4
            field[figureRow+1,figureColumn:figureColumn+2]=4
    elif figure == 5:
        if figureRow+1 == rowSize:
            return False
        if figureColumn-1 < 0:
            return False
        if figureColumn > columnSize - 1:
            return False
        if numpy.sum(field[figureRow-1:figureRow+2,figureColumn]) > 0:
            return False
        if numpy.sum(field[figureRow+1,figureColumn-1:figureColumn+1]) > 0:
            return False
        if check == False:
            field[figureRow-1:figureRow+2,figureColumn]=5
            field[figureRow+1,figureColumn-1:figureColumn+1]=5
    elif figure == 6:
        if figureRow == rowSize:
            return False
        if figureColumn-1 < 0:
            return False
        if figureColumn+1 > columnSize -1:
            return False
        if numpy.sum(field[figureRow-1,figureColumn:figureColumn+2]) > 0:
            return False
        if numpy.sum(field[figureRow,figureColumn-1:figureColumn+1]) > 0:
            return False
        if check == False:
            field[figureRow-1,figureColumn:figureColumn+2]=6
            field[figureRow,figureColumn-1:figureColumn+1]=6
    elif figure == 7:
        if figureRow == rowSize:
            return False
        if figureColumn-1 < 0:
            return False
        if figureColumn+1 > columnSize - 1:
            return False
        if numpy.sum(field[figureRow-1,figureColumn-1:figureColumn+1]) > 0:
            return False
        if numpy.sum(field[figureRow,figureColumn:figureColumn+2]) > 0:
            return False
        if check == False:
            field[figureRow-1,figureColumn-1:figureColumn+1]=7
            field[figureRow,figureColumn:figureColumn+2]=7
    return True

def removeFigure(figure,figureRow,figureColumn):

    if figure == 1:
        field[figureRow-1:figureRow+1,figureColumn-1:figureColumn+1]=0
    elif figure == 2:
        field[figureRow,figureColumn-1:figureColumn+2]=0
        field[figureRow:figureRow+2,figureColumn]=0
    elif figure == 3:
        field[figureRow,figureColumn-1:figureColumn+3]=0
    elif figure == 4:
        field[figureRow-1:figureRow+2,figureColumn]=0
        field[figureRow+1,figureColumn:figureColumn+2]=0
    elif figure == 5:
        field[figureRow-1:figureRow+2,figureColumn]=0
        field[figureRow+1,figureColumn-1:figureColumn+1]=0
    elif figure == 6:
        field[figureRow-1,figureColumn:figureColumn+2]=0
        field[figureRow,figureColumn-1:figureColumn+1]=0
    elif figure == 7:
        field[figureRow-1,figureColumn-1:figureColumn+1]=0
        field[figureRow,figureColumn:figureColumn+2]=0

def EndGame():
    basicFont = pygame.font.SysFont(None, 30)
    text = basicFont.render('Game Over!', True, RED)
    textRect = text.get_rect()
    textRect.centerx = WINDOWWIDTH/2
    textRect.centery = WINDOWHEIGHT/2
    SURFACE.blit(text,textRect)
    pygame.display.update()

gameOver = False
figureRow=1
figureColumn=int(columnSize/2)-1
putFigure(figure,figureRow,figureColumn)

SPEED = 60
downSpeed = SPEED/5
counter = 0

while True:
    removeFigure(figure, figureRow, figureColumn)
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if gameOver == True:
            continue
        if event.type == KEYDOWN:
            figureNewRow = figureRow
            figureNewColumn = figureColumn
            if event.key == K_RIGHT:
                figureNewColumn = figureColumn+1
            if event.key == K_LEFT:
                figureNewColumn = figureColumn-1
            if event.key == K_DOWN:
                figureNewRow = figureRow+1
            if putFigure(figure,figureNewRow,figureNewColumn) == True:
                figureRow = figureNewRow
                figureColumn = figureNewColumn
    if gameOver == True:
        continue

    counter = counter + 1
    if counter % downSpeed == 0:
        if putFigure(figure,figureRow+1,figureColumn) == True:
            figureRow = figureRow+1
        else:
            putFigure(figure,figureRow,figureColumn,False)
            figure=random.randint(1,7)
            figureRow=1
            figureColumn=int(columnSize/2)-1
            if putFigure(figure,figureRow, figureColumn) == False:
                EndGame()
                gameOver = True
                continue

    putFigure(figure,figureRow,figureColumn,False)

    drawLevel()
    clock.tick(SPEED)
    pygame.display.update()


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

urlpatterns = [
    # 后台添加文章
    url(r"backend/add_article/",views.add_article),
    # 点赞和反对
    url(r'up_down/', views.up_down),
    # 评论
    url(r'comment/', views.comment),
    # 评论树
    url(r'comment_tree/(\d+)/', views.comment_tree),
    # 左侧内容
    url(r'(\w+)/(tag|category|archive)/(.+)/', views.home),  # home(request, username, tag, 'python')
    # 文章详情
    url(r'(\w+)/article/(\d+)/$', views.article_detail),  # (\d+)会捕获url中的值
    # 个人博客
    url(r'(\w+)', views.home),  # (\w+)会传到views.py的home中
]


from torch import nn
from .. import utils

class MLP(nn.Module):
    """
    A multilayer perceptron with Leaky ReLU nonlinearities
    """

    def __init__(
        self,
        layers,
        leaky=0.0,
        score_scale=None,
        output_fn=None,
        output_scale=None,
        init_zeros=False,
        dropout=None,
    ):
        """
        layers: list of layer sizes from start to end
        leaky: slope of the leaky part of the ReLU, if 0.0, standard ReLU is used
        score_scale: Factor to apply to the scores, i.e. output before output_fn.
        output_fn: String, function to be applied to the output, either None, "sigmoid", "relu", "tanh", or "clampexp"
        output_scale: Rescale outputs if output_fn is specified, i.e. ```scale * output_fn(out / scale)```
        init_zeros: Flag, if true, weights and biases of last layer are initialized with zeros (helpful for deep models, see [arXiv 1807.03039](https://arxiv.org/abs/1807.03039))
        dropout: Float, if specified, dropout is done before last layer; if None, no dropout is done
        """
        super().__init__()
        net = nn.ModuleList([])
        for k in range(len(layers) - 2):
            net.append(nn.Linear(layers[k], layers[k + 1]))
            net.append(nn.LeakyReLU(leaky))
        if dropout is not None:
            net.append(nn.Dropout(p=dropout))
        net.append(nn.Linear(layers[-2], layers[-1]))
        if init_zeros:
            nn.init.zeros_(net[-1].weight)
            nn.init.zeros_(net[-1].bias)
        if output_fn is not None:
            if score_scale is not None:
                net.append(utils.ConstScaleLayer(score_scale))
            if output_fn == "sigmoid":
                net.append(nn.Sigmoid())
            elif output_fn == "relu":
                net.append(nn.ReLU())
            elif output_fn == "tanh":
                net.append(nn.Tanh())
            elif output_fn == "clampexp":
                net.append(utils.ClampExp())
            else:
                NotImplementedError("This output function is not implemented.")
            if output_scale is not None:
                net.append(utils.ConstScaleLayer(output_scale))
        self.net = nn.Sequential(*net)

    def forward(self, x):
        return self.net(x)


"""
Modified Settler Bot
"""
# Let's start by importing the Halite Starter Kit so we can interface with the Halite engine
import hlt
import time
from datetime import datetime
import logging
import pprint

from random import sample, randint

VERSION = "0.9.3"
DEBUG = False

def closest_target_from_list(gamemap, source, target_list, range=1000):
    #find closest enemy ignoring obstacles
    closest = None
    for foreign_entity in target_list:
        if source.owner == foreign_entity.owner:
            continue
        distance = source.calculate_distance_between(foreign_entity)
        if distance < range:
            range = distance
            closest = foreign_entity
    return closest

def find_nearby_target(gamemap, entity, range=40):
    logger = logging.getLogger(__name__)
    all_nearby = gamemap.nearby_entities_by_distance(entity)
    if len(all_nearby) == 0:
        return None
    logger.debug("nearby_entities_by_distance(entity={}) len: {}"
                 .format(entity.id, len(all_nearby)))
    try:
        index = 0
        for key in all_nearby.keys():
            nearby_entity = all_nearby[key][0]
            if 'Ship' == nearby_entity.__class__.__name__:
                logger.debug("nearby from {},{}: {}, {} x,y {},{}"
                             .format(entity.x, entity.y,
                                     nearby_entity.__class__.__name__,
                                     nearby_entity,
                                     nearby_entity.x, nearby_entity.y))
                return nearby_entity
    except KeyError:
        pass
    return None

def find_closest_unowned_planet(gamemap, ship, planetlist, range=1000):
    logger = logging.getLogger(__name__)
    best_planet = None
    logger.debug(" ship: {} find_closest_unowned_planet, len planetlist: {}"
                 .format(ship.id, len(planetlist)))
    for planet in planetlist:
        distance = ship.calculate_distance_between(planet)
        logger.debug(" ship: {} planet: {} distance: {:.02f}, prior: {:.02f}"
                     .format(ship.id, planet.id, distance, range))
        if distance < range:
            best_planet = planet
            range = distance
    return best_planet

def planet_dock_if_nearby(gamemap, ship):
    for planet in gamemap.all_planets():
        if ship.can_dock(planet) and not planet.is_owned():
            return planet
        if planet.owner == ship.owner and not planet.is_full() and ship.can_dock(planet):
            return planet

def avoid_collision_with_list(ship, entity_list):
    '''Given a list of ships, compute destination away from center'''
    center = hlt.entity.Position((sum(entity.x for entity in entity_list)/len(entity_list)),
                                 (sum(entity.y for entity in entity_list)/len(entity_list)))
    # now we compute the anti-position for a closest_point_to analog
    target = hlt.entity.Position((2*ship.x - center.x),
                                 (2*ship.y - center.y))
    return target

def navigate(ship, destination, game_map, speed=hlt.constants.MAX_SPEED/2, ignore_ships=True):
    return ship.navigate(destination, game_map, speed, ignore_ships=ignore_ships)

def halite2_main():
    logger = logging.getLogger(__name__)
    # GAME START
    # Here we define the bot's name as Settler and initialize the game, including communication with the Halite engine.
    game = hlt.Game("Settler %s" % VERSION)
    turn = 0

    while True:
        # TURN START
        # Update the map for the new turn and get the latest version

        turn_start_time = time.time()
        turn += 1

        logger.info("turn: {} start at: {}".format(turn, datetime.now().isoformat('T')))
        game_map = game.update_map()
        logger.info("turn: {} game.update_map took: {}"
                    .format(turn, time.time() - turn_start_time))

        #
        # variables that may factor in policy
        #
        enemy_ships = [s for s in game_map._all_ships() if s.owner != game_map.get_me()]
        enemy_docked_ships = [s for s in enemy_ships if s.docking_status != s.DockingStatus.UNDOCKED]
        enemy_undocked_ships = [s for s in enemy_ships if s.docking_status == s.DockingStatus.UNDOCKED]
        my_ships = game_map.get_me().all_ships()
        my_docked_ships = [s for s in my_ships if s.docking_status != s.DockingStatus.UNDOCKED]
        my_undocked_ships = [s for s in my_ships if s.docking_status == s.DockingStatus.UNDOCKED]
        logger.info("turn: {} enemy: {} enemy docked: {} my ships: {} my undocked: {}"
                     .format(turn, len(enemy_ships), len(enemy_docked_ships),
                             len(my_ships), len(my_undocked_ships)))

        # ship_time_limit: total seconds permitted
        # ship_speed: limit navigation speed
        # ship_action_limit: total permitted navigate calls or other expensive checks
        # action_ships: give the above limit, these ships will act this turn
        #               sample/shuffle ships to avoid deadlocks
        # action_planet_percent: percentage of above (based on id hash) that will dock
        #               or move to planets
        if turn > 200 or len(my_undocked_ships) > 100:
            ship_time_limit = 1.5
            ship_speed = hlt.constants.MAX_SPEED*0.4
            ship_action_limit = 50
            ship_dock_ratio = 5
            ship_dock_enemy_range = 6
            ship_shun_center_planets = False
            action_ships = my_undocked_ships[:min(ship_action_limit, len(my_undocked_ships))]
            action_ship_long_range = 50
            action_planet_percent = 15
            action_docked_percent = 15
            action_target_docked_range = 50
            navigate_ignore_ships_threshold = 100
        elif turn > 100 and len(my_undocked_ships) > 100:
            ship_time_limit = 1.6
            ship_speed = hlt.constants.MAX_SPEED*0.5
            ship_action_limit = 70
            ship_dock_ratio = 4
            ship_dock_enemy_range = 7
            ship_shun_center_planets = False
            action_ships = my_undocked_ships[:min(ship_action_limit, len(my_undocked_ships))]
            action_ship_long_range = 100
            action_planet_percent = 15
            action_docked_percent = 15
            action_target_docked_range = 50
            navigate_ignore_ships_threshold = 100
        elif turn > 50:
            ship_time_limit = 1.8
            ship_speed = hlt.constants.MAX_SPEED*0.95
            ship_action_limit = 100
            ship_dock_ratio = 2
            ship_dock_enemy_range = 10
            ship_shun_center_planets = False
            action_ships = my_undocked_ships[:min(ship_action_limit, len(my_undocked_ships))]
            action_ship_long_range = 100
            action_planet_percent = 70
            action_docked_percent = 70
            action_target_docked_range = 100
            navigate_ignore_ships_threshold = 100
        elif turn > 10:
            ship_time_limit = 1.9
            ship_speed = hlt.constants.MAX_SPEED
            ship_action_limit = 100
            ship_dock_ratio = 1
            ship_dock_enemy_range = 10
            ship_shun_center_planets = False
            action_ships = my_undocked_ships
            action_ship_long_range = 50
            action_planet_percent = 75
            action_docked_percent = 75
            action_target_docked_range = 100
            navigate_ignore_ships_threshold = 100
        else:
            ship_time_limit = 1.9
            ship_speed = hlt.constants.MAX_SPEED
            ship_action_limit = 100
            ship_dock_ratio = 1
            ship_dock_enemy_range = 20
            ship_shun_center_planets = True
            action_ships = sample(my_undocked_ships, min(ship_action_limit, len(my_undocked_ships)))
            action_ship_long_range = 50
            action_planet_percent = 90
            action_docked_percent = 0
            action_target_docked_range = 100
            navigate_ignore_ships_threshold = 100

        unowned_planets = [p for p in game_map.all_planets() if not p.is_owned()]
        unowned_planets.sort(key=lambda x: x.id)
        if ship_shun_center_planets:
            unowned_planets = [p for p in unowned_planets if p.id > 3]

        if len(my_undocked_ships) + len(enemy_undocked_ships) > navigate_ignore_ships_threshold:
            ignore_ships = True
        else:
            ignore_ships = False

        logger.info("turn: {} end of prep, start of ship loop after: {:.03f}"
                    .format(turn, (time.time() - turn_start_time)))

        #
        # local variables
        #
        ship_actions = 0
        #only 1 ship can dock per turn, so limit our attempts?
        planet_docking = []
        # Here we define the set of commands to be sent to the Halite engine at the end of the turn
        command_queue = []

        for ship in action_ships:
            if (time.time() - turn_start_time) > ship_time_limit:
                logger.debug("turn: {} ship: {} skipping due to time {}"
                             .format(turn, ship.id, (time.time() - turn_start_time)))
                break

            # limit the number of ships we look at per turn, just in case
            ship_actions += 1
            if ship_actions > ship_action_limit:
                logger.debug("turn: {} ship: {} skipping due to ship actions {} after {:.03f} seconds"
                             .format(turn, ship.id, ship_actions, time.time() - turn_start_time))
                continue

            if turn == 1:
                avoidance = avoid_collision_with_list(ship, my_undocked_ships)
                logger.debug("turn: {} ship: {} collision avoidance: {},{}"
                             .format(turn, ship.id, avoidance.x, avoidance.y))
                navigate_command = ship.navigate(avoidance,
                                                 game_map, speed = hlt.constants.MAX_SPEED,
                                                 ignore_ships=False)
                if navigate_command:
                    command_queue.append(navigate_command)
                continue

            # if we're next to a planet, maybe do that
            elif ship_dock_ratio == 1 or randint(0, ship_dock_ratio-1) == 0:
                # before docking check for nearby enemies
                target_ship = closest_target_from_list(game_map, ship,
                                                       enemy_ships, ship_dock_enemy_range)
                if target_ship:
                    logger.info("turn: {} ship: {} defensive targetting ship: {}"
                                .format(turn, ship.id, target_ship.id))
                    navigate_command = ship.navigate(ship.closest_point_to(target_ship, 4.5),
                                                     game_map, speed = hlt.constants.MAX_SPEED,
                                                     ignore_ships=ignore_ships)
                    if navigate_command:
                        command_queue.append(navigate_command)
                    continue
                if not target_ship:
                    docking_target = planet_dock_if_nearby(game_map, ship)
                    if docking_target:
                        if docking_target.id not in planet_docking:
                            logger.info("turn: {} ship: {} docking to planet: {}"
                                        .format(turn, ship.id, docking_target.id))
                            planet_docking.append(docking_target.id)
                            command_queue.append(ship.dock(docking_target))
                        # this will not fall through and instead wait a turn and check again
                        continue
                    # else fall through to planet/ship navigation

            if hash(ship.id) % 100 < action_planet_percent:
                # find ship's closest planet to navigate to
                logger.debug("ship id {} hash {} is planet action (percent: {})"
                             .format(ship.id, hash(ship.id) % 100, action_planet_percent))
                planet = find_closest_unowned_planet(game_map, ship, unowned_planets, 100)
                if planet:
                    logger.info("turn: {} ship: {} x,y {},{} off to planet: {} x,y {},{}"
                                .format(turn, ship.id, ship.x, ship.y,
                                        planet.id, planet.x, planet.y))
                    target_point = ship.closest_point_to(planet)
                    navigate_command = ship.navigate(target_point, game_map,
                                                     speed=ship_speed,
                                                     ignore_ships=ignore_ships)
                    if ship_shun_center_planets and planet.id < 4:
                        logger.debug("planet: {} used when shunned".format(planet.id))
                    if navigate_command:
                        command_queue.append(navigate_command)
                    unowned_planets.remove(planet)
                    continue
                # else this will fall through to aggressive ship behavior

            # anti-ship action
            logger.debug("ship id {} hash {} is ship action"
                         .format(ship.id, hash(ship.id) % 100))
            if hash(ship.id) % 100 < action_docked_percent and len(enemy_docked_ships) > 0:
                time_1 = time.time()
                target_ship = closest_target_from_list(game_map, ship,
                                                       enemy_docked_ships,
                                                       action_target_docked_range)
                if target_ship:
                    logger.debug("ship: {} targetting docked closest id,x,y {},{},{} in {:.06f}"
                                 .format(ship.id,
                                         target_ship.id, target_ship.x, target_ship.y,
                                         time.time() - time_1))
            if not target_ship and hash(ship.id) % 100 < action_ship_long_range:
                time_1 = time.time()
                target_ship_1 = closest_target_from_list(game_map, ship, enemy_ships, 200)
                if target_ship_1:
                    logger.debug("ship: {} targetting closest returned id,x,y {},{},{} in {:.06f}"
                                 .format(ship.id,
                                         target_ship_1.id, target_ship_1.x, target_ship_1.y,
                                         time.time() - time_1))

            if target_ship:
                logger.info("turn: {} ship: {} targetting ship: {}"
                            .format(turn, ship.id, target_ship.id))
                distance = 2.75 + (ship.id % 3)*0.5
                navigate_command = ship.navigate(ship.closest_point_to(target_ship, distance),
                                                 game_map, speed=ship_speed,
                                                 ignore_ships=ignore_ships)
                if navigate_command:
                    command_queue.append(navigate_command)
            else:
                logger.info("turn: {} ship: {} no work".format(turn, ship.id))

        # Send our set of commands to the Halite engine for this turn
        turn_end_presend = time.time()
        logger.info("turn: {} end ships time: {:.03f}"
                         .format(turn, turn_end_presend - turn_start_time))
        game.send_command_queue(command_queue)
        #logger.debug("turn: {} send_command_queue len: {} took: {:.03f}"
        #             .format(turn, len(command_queue), time.time() - turn_end_presend))
        logger.info("turn: {} end, took: {:.03f}"
                    .format(turn, time.time() - turn_start_time))
        # TURN END
    # GAME END

if __name__ == "__main__":
        #logging.basicConfig(level=logging.INFO)
        halite2_main()


#!/usr/bin/env python3

# Thanks to Jacob Vlijim for this scripts
# Edited a bit by Ichirou2910 for easier customizing
# Source: https://askubuntu.com/questions/556723/make-a-text-file-my-automatically-updated-background

# Update 8 Feb 2020:
#   - You can use tab, define your tab so it will replace the "TAB" you enter
#   - Remove strip() when reading from file to be able to use tab

import subprocess
import os
import time

curr_dir = os.path.dirname(os.path.realpath(__file__))

if os.path.isfile(curr_dir + '/' + 'wallpaper.png'):
    curr_wall = curr_dir + "/" + "wallpaper.png"
elif os.path.isfile(curr_dir + '/' + 'wallpaper.jpg'):
    curr_wall = curr_dir + "/" + "wallpaper.jpg"

notes = curr_dir + "/" + "notes.txt"

# -- CUSTOM PARAMETERS
shell = "/bin/zsh"              # your shell binary path, I use zsh

# font = "Comic-Sans-MS-Bold"     # execute convert -list font for possible font choices
font = "Choco-cooky"
text_color = "black"            # text color
size = "25"                     # text size (real size depends on the scale factor of your wallpaper)
tab = "    "                    # Imagemagick doesn't accept \t so just define it yourself
done = "×"                      # Completed mark. Use whatever you want.
borderX = 120                   # x offset
borderY = 175                   # y offset
columns = 1                     # (max) number of columns
n_lines = 18                    # (max) number of lines per column
set_bg_cmd = "feh --bg-scale "  # your command to change background goes here, but without the path to image
# --

def run_command(cmd):
    subprocess.call([shell, "-c", cmd])

def get_value(cmd):
    return subprocess.check_output([shell, "-c", cmd]).decode("utf-8").strip()

def read_text(file):
    with open(file) as src:
        if os.stat(file).st_size != 0:
            return [l.replace('\t', tab).replace("\\x", done) for l in src.readlines()]
        return ['(None)']

def slice_lines(lines, n_lines, columns):
    markers = [i for i in range(len(lines)) if i % n_lines == 0]
    last = len(lines)
    markers = markers + [last] if markers[-1] != last else markers
    textblocks = [lines[markers[i]:markers[i + 1]] for i in range(len(markers) - 1)]
    filled_blocks = len(textblocks)
    if filled_blocks < columns:
        for n in range(columns - filled_blocks):
            textblocks.insert(len(textblocks), [])
    for i in range(columns):
        textblocks[i] = ("").join(textblocks[i])
    return textblocks[:columns]

def create_section(psize, text, layer):
    print(text)
    run_command("convert -background none"
                + " -fill " + text_color
                + " -border " + str(borderX) + "x" + str(borderY)
                + " -bordercolor none"
                + " -pointsize " + size
                + " -size " + psize
                + " -font " + font
                + " caption:" + '"' + text + '" ' + layer)

def set_overlay():
    boxes = slice_lines(read_text(notes), n_lines, columns)
    resolution = get_value('identify -format "%wx%h" ' + curr_wall).split("x")
    w = str(int(int(resolution[0]) / columns) - 2 * borderX)
    h = str(int(resolution[1]) - 2 * borderY)
    layers = []
    for i in range(len(boxes)):
        layer = curr_dir + "/" + "layer_" + str(i + 1) + ".png"
        create_section(w + "x" + h, boxes[i], layer)
        layers.append(layer)
    run_command("convert " + (" ").join(layers) + " " + "+append " + curr_dir + "/" + "layer_span.png")
    wall_img = curr_dir + "/" + "walltext.jpg"
    run_command("convert " + curr_wall + " " + curr_dir + "/" + "layer_span.png" + " -background None -layers merge " + wall_img)

    run_command(set_bg_cmd + wall_img)
    for img in [img for img in os.listdir(curr_dir) if img.startswith("layer_")]:
        os.remove(curr_dir + "/" + img)

while True:
    text_1 = read_text(notes)
    time.sleep(5)
    text_2 = read_text(notes)
    if text_2 != text_1:
        set_overlay()


import argparse
import os
import pugsql
from dateutil.parser import parse
from datetime import datetime, timedelta
import pytz

def date_to_unix(date_str, timezone="Asia/Taipei"):
    date = parse(date_str)
    tw = pytz.timezone(timezone)
    loc_dt = tw.localize(date)
    start_unix = loc_dt.timestamp()
    end_unix = start_unix + 86400
    return start_unix, end_unix

def main(args):
    queries = pugsql.module("queries")
    queries.connect(os.getenv("DB_URL"))

    date_start_unix, date_end_unix = date_to_unix(args.date)

    for stats in queries.count_site_stats(
        time_start=date_start_unix, time_end=date_end_unix
    ):
        queries.upsert_stats({**stats, "date": args.date})

if __name__ == "__main__":
    utc_now = datetime.utcnow()
    tw_now = utc_now + timedelta(hours=8)
    tw_yesterday = tw_now.date() - timedelta(days=1)
    str_tw_yesterday = tw_yesterday.strftime("%Y-%m-%d")

    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-d",
        "--date",
        help="date to calculate stats on",
        type=str,
        default=str_tw_yesterday,
    )

    args = parser.parse_args()
    main(args)


######################
## Imports

import numpy
import pandas

######################
## Functions

def parse_input(input_file: str) :

  # Ingest file into list
  with open(input_file, 'r') as f:
    list_input = f.read().splitlines()

  list_cleaned = [x.split(" -> ") for x in list_input]
  list_structures = [[(int(x.split(",")[1]), int(x.split(",")[0])) for x in sub_list] for sub_list in list_cleaned]

  return list_structures

def create_array_plot(list_structures: list, floor_present: bool = False) :
  min_height = 0
  min_width = min([min(x, key=lambda tup: tup[1]) for x in list_structures], key=lambda tup: tup[1])[1] - 1
  max_height = max([max(x, key=lambda tup: tup[0]) for x in list_structures], key=lambda tup: tup[0])[0] + 3
  max_width = max([max(x, key=lambda tup: tup[1]) for x in list_structures], key=lambda tup: tup[1])[1] + 2

  array_plot = numpy.empty([max_height, max_width+max_height], dtype=str)
  array_plot[:, :] = "."

  if floor_present :
    array_plot[max_height-1:max_height, :] = "~"
  else :
    array_plot[max_height-1:max_height, :] = "-"
    array_plot[:, min_width:min_width+1] = "|"
    array_plot[:, max_width-1:max_width] = "|"

  return array_plot

def plot_structures(array_plot: numpy.array, list_structures: list) :
  for list_structure in list_structures :
    for x in range(len(list_structure) - 1) :
      starting_height = min(list_structure[x][0], list_structure[x+1][0])
      ending_height = max(list_structure[x][0], list_structure[x+1][0]) + 1
      starting_width = min(list_structure[x][1], list_structure[x+1][1])
      ending_width = max(list_structure[x][1], list_structure[x+1][1]) + 1
      array_plot[starting_height:ending_height, starting_width:ending_width] = "#"
  return array_plot

def drop_grain_of_sand(array_plot: numpy.array, tuple_starting_location: tuple) :
  tuple_current_location = tuple_starting_location
  sand_finished = False
  sand_at_rest = False
  while sand_at_rest == False and sand_finished == False :
    if array_plot[tuple(numpy.add(tuple_current_location, (1, 0)))] == "." :
      tuple_current_location = tuple(numpy.add(tuple_current_location, (1, 0)))
    elif array_plot[tuple(numpy.add(tuple_current_location, (1, -1)))] == "." :
      tuple_current_location = tuple(numpy.add(tuple_current_location, (1, -1)))
    elif array_plot[tuple(numpy.add(tuple_current_location, (1, 1)))] == "." :
      tuple_current_location = tuple(numpy.add(tuple_current_location, (1, 1)))
    elif array_plot[tuple(numpy.add(tuple_current_location, (1, -1)))] in ("|", "-") \
      or array_plot[tuple(numpy.add(tuple_current_location, (1, 1)))] in ("|", "-") :
      sand_finished = True
    else :
      array_plot[tuple_current_location] = "o"
      sand_at_rest = True
      if tuple_current_location == tuple_starting_location :
        sand_finished = True

  return array_plot, sand_finished

def drop_sand(array_plot: numpy.array, tuple_starting_location: tuple) :
  sand_finished = False
  while sand_finished == False :
    array_plot, sand_finished = drop_grain_of_sand(array_plot=array_plot, tuple_starting_location=tuple_starting_location)

  return array_plot

def main(input_file: str, output_file: str = "", floor_present: bool = False) :

  tuple_starting_location = (0, 500)
  list_structures = parse_input(input_file)

  array_plot = create_array_plot(list_structures=list_structures, floor_present=floor_present)
  array_plot = plot_structures(array_plot=array_plot, list_structures=list_structures)
  array_plot = drop_sand(array_plot=array_plot, tuple_starting_location=tuple_starting_location)

  # Write plot into text
  if len(output_file) > 0 :
    populated_widths = numpy.where((array_plot == "o") | (array_plot == "#") | (array_plot == "|"))[1]
    min_width = populated_widths.min()
    max_width = populated_widths.max() + 1
    array_populated = array_plot[:, min_width:max_width]
    numpy.savetxt(output_file, array_populated, delimiter="", fmt="%s")

  result = (array_plot=="o").sum()
  return result

######################
## Part 1

main(input_file="14/sample_input.txt", output_file="14/part_1_sample_visualised.txt")
main(input_file="14/input.txt", output_file="14/part_1_visualised.txt")

######################
## Part 2

main(input_file="14/sample_input.txt", output_file="14/part_1_sample_visualised.txt", floor_present=True)
main(input_file="14/input.txt", output_file="14/part_1_visualised.txt", floor_present=True)


#!flask/bin/python
from flask import Flask, request, abort, jsonify, make_response
from chacha20poly1305 import ChaCha20Poly1305
from flask_httpauth import HTTPBasicAuth
from cryptography.fernet import Fernet
import paho.mqtt.client as mqtt
from datetime import datetime
import threading
import hashlib
import base64
import click
import time
import json
import hmac
import os
import sys

from sys import path
path.append( "../" ) # Add path to get utils.py
import utils as utils # Include different common fucntions.

KMS_ID                  = "kms-muii"
TOPIC_FILE              = 'registeredDeviceTopics.json'
SECRET_FILE             = 'secrets.json'
HASH_KEY                = b'kkpo-kktua'

topicsPublishNewKeys    = {}
secretRegisteredDevices = {}

app                     = Flask( __name__ )
auth                    = HTTPBasicAuth()

class FlaskThread( threading.Thread ):

    @auth.get_password
    def get_password( username ):
        """
            Provides the password for an user by a given username.
            Authentication method.
            Reference:
            https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask
        """
        if username == "platform":

            return "platform-MUII"
        return None

    @app.route( '/register-device', methods=['POST'] )
    @auth.login_required
    def register( ):
        """
            Registers a new device into the KMS according
            to the JSON provided by the platform.
        """
        global topicsPublishNewKeys, secretRegisteredDevices
        if not request.json or not 'id' in request.json or \
        not 'key_topic' in request.json or \
        not 'shared_key' in request.json or \
        not 'symmetric' in request.json:

            abort( 400 )
        topicsPublishNewKeys[request.json["id"]] = request.json['key_topic']
        secretRegisteredDevices[request.json["id"]] = {
            "secrets": {
                "0": request.json["shared_key"]
            },
            "symmetric": request.json["symmetric"]
        }
        # Save into `registeredDeviceTopics.json` file
        with open( TOPIC_FILE, 'w' ) as file:

            json.dump( topicsPublishNewKeys, file, indent=4 )
        # Save into `secret.json` file
        with open( SECRET_FILE, 'w' ) as file:
            json.dump( secretRegisteredDevices, file, indent=4 )
        return jsonify( { "status": "OK" } ), 201

    @app.route( '/remove-device', methods=['POST'] )
    @auth.login_required
    def remove( ):
        """
            Removes a device from the KMS according to the given ID.
        """
        global topicsPublishNewKeys, secretRegisteredDevices
        if not request.json or not 'id' in request.json:

            abort( 400 )
        # Delete from both dictionaries.
        del topicsPublishNewKeys[request.json["id"]]
        del secretRegisteredDevices[request.json["id"]]
        # Save into `registeredDeviceTopics.json` file
        with open( TOPIC_FILE, 'w' ) as file:

            json.dump( topicsPublishNewKeys, file, indent=4 )
        # Save into `secret.json` file
        with open( SECRET_FILE, 'w' ) as file:

            json.dump( secretRegisteredDevices, file, indent=4 )
        return jsonify( { "status": "OK" } ), 201

    @app.route( '/get-key', methods=['POST'] )
    @auth.login_required
    def get_key( ):
        """
            Returns the keys for an specified ID.
        """
        global secretRegisteredDevices
        if not request.json or not 'id' in request.json:

            abort( 400 )
        return jsonify( secretRegisteredDevices[request.json["id"]] ), 201

    @app.route( '/get-all-keys', methods=['POST'] )
    @auth.login_required
    def get_all_keys( ):
        """
            Returns all keys for all devices.
        """
        global secretRegisteredDevices
        return jsonify( secretRegisteredDevices ), 201

    @auth.error_handler
    def unauthorized( ):

        return make_response( jsonify( { 'error': 'Unauthorized access' } ), 401 )

    @app.errorhandler( 404 )
    def not_found( error ):

        return make_response( jsonify( { 'error': 'Not found' } ), 404 )

    def run( self ):

        app.run()

def add_header_message( message, topic ):
    """
        This functions adds information about the device
        to send it to the platform. It also include
        a hash as a sign.
    """
    message["id"]       = KMS_ID
    message["topic"]    = topic
    message["timestamp"]= str( datetime.now() )
    header = {
        "id": KMS_ID,
        "topic": topic,
        "timestamp": message["timestamp"]
    }
    message["sign"] = hmac.new( HASH_KEY, json.dumps( header ).encode(), \
        hashlib.sha384 ).hexdigest()
    return message

def start_flask():
    """
        Starting flask thread. RESTful service.
    """
    flaskThread = FlaskThread()
    # Need to be a daemon to close it with Ctrl+C
    flaskThread.daemon = True
    flaskThread.start()

def load_registered_device_topics():
    """
        Load from the `registerDeviceTopics.json` file to get the topics
        where to publish the keys for a device. If the file does not exit,
        it will be created.
    """
    if not os.path.exists( TOPIC_FILE ):
        # If file does not exit, it will be created.
        with open( TOPIC_FILE, 'w') as file:

            file.write("{}")
    with open( TOPIC_FILE ) as file:
        # Group previous devices registered
        data = json.load( file )
    return data

def load_registered_device_secrets():
    """
        Load from the `secrets.json` file to get the topics
        where to publish the keys for a device. If the file does not exit,
        it will be created.
    """
    if not os.path.exists( SECRET_FILE ):
        # If file does not exit, it will be created.
        with open( SECRET_FILE, 'w') as file:

            file.write("{}")
    with open( SECRET_FILE ) as file:
        # Group previous devices registered
        data = json.load( file )
    return data

@click.command()
@click.option( '-s', '--server', 'server', required=True, type=str, \
    show_default=True, default='broker.shiftr.io', help="The MQTT Server to send keys." )
@click.option( '-P', '--port', 'port', required=True, type=int, \
    show_default=True, default=1883, help="Port of theMQTT Server to send keys." )
@click.option( '-u', '--user', 'user', required=True, type=str, \
    help="The user to connect to the MQTT Serve." )
@click.option( '-p', '--password', 'password', required=True, type=str, \
    prompt=True, hide_input=True, help="The password for the user to connect to the MQTT Serve.\
    If you do not include this option, a prompt will appear to you introduce the password." )
@click.option( '-t', '--time', 't', required=True, type=int, default=10, \
    help=" Time taken to send new keys." )
def connect( server, port, user, password, t ):
    """
        Start KMS. It will start the RESTful at port 5000 and start the Key Rotation process.
    """
    global topicsPublishNewKeys
    global secretRegisteredDevices
    # Initiate Flask as a new thread.
    start_flask()
    # Load the information saved of the registered devices.
    topicsPublishNewKeys = load_registered_device_topics()
    secretRegisteredDevices = load_registered_device_secrets()
    # Connect to MQTT Server.
    client = mqtt.Client( client_id=KMS_ID )
    client.username_pw_set( user, password )
    client.connect( server, port, 60 )
    client.loop_start()
    while True:
        # Start key rotation process. Send a new key for each device according to
        # its symmetric algorithm specified.
        for device, topic in topicsPublishNewKeys.items():

            device_keys = secretRegisteredDevices[device]["secrets"]
            old_key = device_keys.get( "0", "" )
            if old_key != "":
                # Generates a new key
                if secretRegisteredDevices[device]["symmetric"] == "fernet":

                    new_key = Fernet.generate_key().decode( "utf-8" )
                elif secretRegisteredDevices[device]["symmetric"] == "chacha":

                    new_key = os.urandom( 32 ).decode( "latin-1" )
                if new_key != "":
                    # Rotate keys. Discard the oldest key.
                    if device_keys.get( "1", "" ) != "":

                        old_key = device_keys.get( "1" )
                        secretRegisteredDevices[device]["secrets"]["0"] = old_key
                    secretRegisteredDevices[device]["secrets"]["1"] = new_key
                    if secretRegisteredDevices[device]["symmetric"] == "fernet":

                        encriptor = Fernet( old_key.encode() )
                    elif secretRegisteredDevices[device]["symmetric"] == "chacha":

                        encriptor = ChaCha20Poly1305( old_key.encode( "latin-1" ) )
                    # Send the new generated key to the device
                    secret_message = {
                        "deviceID": device,
                        "key": new_key
                    }
                    message = add_header_message( secret_message, topic )
                    utils.send( client, encriptor, message )
            else:

                print( "Device: ", device, " has not a first key. \
                    Remove it from list and connect it again.")

        # Save all changes into the `secret.json` file
        with open( SECRET_FILE, 'w' ) as file:

            json.dump( secretRegisteredDevices, file, indent=4 )
        time.sleep( t )

@click.group()
def cli():

    pass

if __name__ == '__main__':
    """
        This main process only include the `connect` command.
        If you need help to run this command, check: `python server.py connect --help`
    """
    cli.add_command( connect )
    cli()


from code.main import suma, resta, multiplicacion, division, modulo
import pytest

@pytest.mark.parametrize(
        (
            "a",
            "b",
            "valor_esperado"
        ),
        [
            (
                3,
                4,
                7
            ),
            (
                10,
                2,
                12
            )
        ],
        ids=[
            "Test1",
            "Test2"
        ]
)
def test_suma(a, b, valor_esperado):
    assert suma(a, b) == valor_esperado

#@pytest.mark.skip(reason = "Funcion a depricar")
@pytest.mark.xfail()
def test_division():
    assert division(1, 0) == 1

def test_division():
    assert modulo(3, 1) == 0


import os
from configparser import ConfigParser

PATH = os.path.abspath(os.path.expanduser("~/.esgp.cfg"))

class Configuration(object):

    def __init__(self):
        self.algorithm = 'MD5'
        self.length = 10
        self.domain_settings = []

    def read(self):

        parser = ConfigParser()
        parser.read(PATH)

        if parser.has_section('defaults'):
            self.algorithm = parser.get('defaults', 'algorithm', fallback=self.algorithm)
            self.length = int(parser.get('defaults', 'length', fallback=self.length))

        for section in parser.sections():
            if section == "defaults":
                continue

            self.domain_settings.append({
                'domain': section,
                'algorithm': parser.get(section, 'algorithm', fallback=self.algorithm),
                'length': parser.get(section, 'length', fallback=self.length)
            })

    def write(self):

        parser = ConfigParser()

        parser.add_section('defaults')
        parser.set('defaults', 'algorithm', self.algorithm)
        parser.set('defaults', 'length', str(self.length))

        for settings in self.domain_settings:
            domain = settings['domain']
            if not domain:
                continue
            parser.add_section(domain)
            parser.set(domain, 'algorithm', settings['algorithm'])
            parser.set(domain, 'length', str(settings['length']))

        with open(PATH, 'w') as o:
            parser.write(o)

    def get_domain_settings(self, domain):
        for d in self.domain_settings:
            if d.get('domain', '') == domain:
                return d


"""
Preprocess mcd (modis land cover) dataset.

MODIS land cover fractions
https://lpdaac.usgs.gov/product_search/?collections=Combined+MODIS&collections=Terra+MODIS&collections=Aqua+MODIS&view=list

In:
Spatial:  0.0083 deg

Out:
Spatial:  0.033 deg

Steps:
1) Harmonize
2) Regrid

"""

import os
import xarray as xr
import logging

import numpy as np
from utils.pyutils import exit_if_exists, rm_existing
from utils.cdo_wrappers import cdo_remap
from dataprocessing.plotting import plot_var
from dataprocessing.datasets.config import \
    dir_source, \
    dir_target, \
    overwrite

logging.info('Processing dataset: mcd')

file_in = os.path.join(
    dir_source, '0d0083_static/MCD12Q1/V005/Data/v005_2/MCD12Q1plusC4_fraction.GLOBAL01KM.2001001.LC.01KM.nc'
)
file_out = os.path.join(
    dir_target, 'processed/0d033/static/mcd.nc'
)
file_tmp = file_out.replace('.nc', '_tmp.nc')

exit_if_exists(file_out, overwrite)
os.makedirs(os.path.dirname(file_out), exist_ok=True)

ds = xr.open_dataset(file_in)
ds = ds.rename({
    'MCD12Q1plusC4_fraction': 'data',
    'longitude': 'lon',
    'latitude': 'lat'})

lat_attrs = dict(
    long_name='Latitude',
    standard_name='latitude',
    units='degrees_north',
    axis='Y',
    valid_min=-90.0,
    valid_max=90.0
)
lon_attrs = dict(
    long_name='Longitude',
    standard_name='longitude',
    units='degrees_east',
    axis='X',
    modulo=360.0,
    topology='circular',
    valid_min=-180.0,
    valid_max=180.0,
)
ds.lat.attrs.update(lat_attrs)
ds.lon.attrs.update(lon_attrs)

ds.attrs['classes'] = np.array([
    l[1][1:].decode('utf-8').replace(' ', '_').lower()
    for l in ds.Legend.values
    ])[:-2]

ds = ds.drop('Legend')
ds['data'] = ds.data.expand_dims('var', 0)

ds = ds.where(~ds.data.isnull(), 0)

ds.to_netcdf(file_tmp)
ds.close()

cdo_remap(
    in_files=file_tmp,
    out_files=file_out,
    nlat_target=180*30,
    nlon_target=360*30,
    remap_alg='laf')

rm_existing(file_tmp)

plot_path = __file__.replace('.py', '.jpg')
plot_var(path=file_out, plot_path=plot_path)

logging.info('Done processing dataset: mcd')


from flask import request
from flask_json import FlaskJSON, JsonError
from mongoengine import *
from __init__ import app, fasthash_dictionary
from decorators import *
from models import *
import json

@userRoute('/api/profile')
def profile(postdata):
    print("profile with", postdata)
    username = postdata.get("username", None)
    user = User.objects.get(username=username)
    try:
        profile = Profile.objects.get(user=user)
    except Profile.DoesNotExist:
        print("Realize there is no profile")
        profile = Profile(user=user)
        profile.save()
    for node in Node.objects.all():
        if node.serial not in profile.means.keys():
            profile.means[node.serial] = 0
    profile.save()
    # nodes = Node.objects()
    return {'profile': json.loads(profile.to_json())}

@freeRoute('/api/question')
def question(postdata):
    question_serial = postdata.get('serial', None)
    question_id = postdata.get('id', None)
    if question_serial:
        question = Question.objects.get(serial=question_serial)
    elif question_id:
        question = Question.objects.get(id=question_id)
    else:
        return {'status':400, 'description':'Can\'t find the question. No serial or id provided.'}
    return {'status':200, 'question':json.loads(question.to_json())}

@userRoute('/api/auto_question')
def auto_question(postdata):
    import random
    from pprint import pprint
    username = postdata.get('username', '')
    user = User.objects.get(username=username)
    try:
        profile = Profile.objects.get(user=user)
    except Profile.DoesNotExist:
        profile = Profile(user=user).save()
    for node in Node.objects.all():
        if node.serial not in profile.means.keys():
            profile.means[node.serial] = 0
    profile.save()
    means = profile.means
    is_complete = lambda k: means[k] >= 0.9
    is_incomplete = lambda k: means[k] < 0.9
    is_accesible = lambda k: all( is_complete(node.serial) for node in Node.objects.get(serial=k).antes )
    relevant_means = { key:value for (key, value) in profile.means.items() if is_incomplete(key) and is_accesible(key) }
    pprint(relevant_means)
    try:
        node_serial = sorted(relevant_means.keys())[0]
    except:
        node_serial = random.choice([ node.serial for node in Node.objects.all() ])
    node = Node.objects.get(serial=node_serial)
    question = random.choice(list(Question.objects.filter(node=node.id)))
    return {'status':200, 'question':json.loads(question.to_json())}

@userRoute('/api/update_mean')
def update_mean(postdata):
    username = postdata.get('username', '')
    user = User.objects.get(username=username)
    try:
        profile = Profile.objects.get(user=user)
    except Profile.DoesNotExist:
        profile = Profile(user=user).save()
    for node in Node.objects.all():
        if node.serial not in profile.means.keys():
            profile.means[node.serial] = 0
    profile.save()
    question_serial = postdata.get('question_serial', '')
    node = Question.objects.get(serial=question_serial).node
    mean_delta = postdata.get('mean_delta', 0)
    profile.means[node.serial] = max(0, min(1, profile.means[node.serial]+mean_delta))
    profile.save()
    return {'status':200}

@freeRoute('/api/node_questions')
def node_questions(postdata):
    node_id = postdata.get('node_id', None)
    node_serial = postdata.get('node_serial', None)
    if node_serial:
        node = Node.objects.get(serial=node_serial)
    elif node_id:
        node = Node.objects.get(id=node_id)
    else:
        return {'status':400, 'description':'Can\'t find the node. No serial or id provided.'}
    questions = Question.objects.filter(node=node.id)
    return {'status':200, 'name':node.name, 'questions':json.loads(questions.to_json())}

@freeRoute('/api/node')
def node(postdata):
    node_serial = postdata.get('node_serial', None)
    if node_serial:
        try:
            node = Node.objects.get(serial=node_serial)
        except Node.DoesNotExist:
            return {'status':400, 'description':'The serial provider is not in the database.'}
    else:
        return {'status':400, 'description':'Can\'t find the node. No serial or id provided.'}
    return {'status':200, 'node':json.loads(node.to_json())}


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

import os
import numpy as np
from threading import Thread
from Queue import Queue
# import copy
import chimera
from chimera.tasks import Task
import prody
from cclib.parser import Gaussian
from NormalModesTable import NormalModesTableDialog
# from new_gui import NormalModesResultsDialog, NormalModesMovieDialog

ALGORITHMS = {'Full atom': 'full_atom',
              'Group by residues': 'residues',
              'Group by mass': 'mass',
              'Group by graph': 'graph',
              'Extend from C-alpha': 'calpha'}

class Controller(object):

    """docstring for Controller"""

    def __init__(self, gui=None, *args, **kwargs):
        self.gui = gui
        self.vibrations = None
        self._molecules = None
        self.ENGINES = dict(prody=self._run_prody,
                            gaussian=self._run_gaussian)

    def run(self):
        self._failure()
        self.gui.buttonWidgets['Run']['state'] = 'disabled'
        self.gui.buttonWidgets['Run']['text'] = 'Running...'
        engine = self.ENGINES[self.gui.engine]
        # Run external task with the following to prevent UI freezes
        task = StatusTask('Normal Modes Analysis', cancelCB=self._failure)
        task.put = task.updateStatus
        queue = ChimeraTaskQueue(task=task)
        engine(queue=queue)
        task.put('Done!')
        self.gui.buttonWidgets['Run']['state'] = 'normal'
        self.gui.buttonWidgets['Run']['text'] = 'Run'
        self._success()
        task.finished()

    def _run_prody(self, queue=None, threaded=True):
        self._molecules = self.gui.ui_molecules.getvalue()
        if not self._molecules:
            self.gui.buttonWidgets['Run']['state'] = 'normal'
            self.gui.buttonWidgets['Run']['text'] = 'Run'
            raise chimera.UserError("Please select at least one molecule")

        algorithm = ALGORITHMS[self.gui.ui_algorithms_menu.getvalue()]
        algorithm_param = int(self.gui.ui_algorithms_param.get())
        n_modes = int(self.gui.ui_n_modes.get())
        cutoff = float(self.gui.ui_cutoff.get())
        gamma = float(self.gui.ui_gamma.get())
        mw = 'Mass-Weighted' in self.gui.ui_extra_options_chk.getvalue()
        if threaded:
            thread = Thread(target=VibrationalMolecule.from_chimera,
                            args=(self._molecules,),
                            kwargs=dict(algorithm=algorithm,
                                        n=algorithm_param,
                                        queue=queue,
                                        max_modes=n_modes,
                                        gamma=gamma,
                                        mass_weighted=mw,
                                        cutoff=cutoff))
            thread.start()
            while thread.isAlive():
                chimera.tkgui.app.update()
            self.vibrations = queue.get_nowait()
        else:
            self.vibrations = VibrationalMolecule.from_chimera(self._molecules,
                                                              algorithm=algorithm,
                                                              n=algorithm_param,
                                                              queue=queue.task,
                                                              max_modes=n_modes,
                                                              gamma=gamma,
                                                              mass_weighted=mw,
                                                              cutoff=cutoff)
        return True

    def _run_gaussian(self, **kwargs):
        path = self.gui.ui_gaussian_file_entry.get()
        if not os.path.exists(path):
            raise chimera.UserError("File {} not available".format(path))
        self.vibrations = VibrationalMolecule.from_gaussian(path)
        self._molecules = self.vibrations.molecule
        return True

    def _success(self):
        if self.vibrations is None:
            return
        frequencies = self.vibrations.frequencies()[1]
        # results_dialog = NormalModesResultsDialog(self.gui, controller=self)
        # results_dialog.enter()
        # results_dialog.populate_data(frequencies)
        # movie_dialog = NormalModesMovieDialog(self.gui, controller=self)
        # movie_dialog.enter()
        dialog = NormalModesTableDialog(self._molecules, self.vibrations.modes)

    def _failure(self):
        self.vibrations = None
        # print some kind of message

class VibrationalMolecule(object):
    """
    class to handle prody normal modes
    """

    def __init__(self, molecule, modes, task=None):
        self.molecule = molecule
        self.modes = modes

    @classmethod
    def from_chimera(cls, chimera_molecule, queue=None, **kwargs):
        """
        initializes the VibrationalMolecule class with a chimera molecule
        """
        molecule, chimera2prody = convert_chimera_molecule_to_prody(chimera_molecule)
        modes = calculate_vibrations(molecule, queue=queue, **kwargs)
        instance = cls(chimera_molecule, modes)
        if queue is not None:
            queue.put_nowait(instance)
        return instance

    @classmethod
    def from_gaussian(cls, gaussian_path, task=None):
        """
        initializes from a gaussian output file
        """
        gaussian_parser = Gaussian(gaussian_path).parse()
        modes = gaussian_modes(gaussian_parser)
        chimera_molecule = convert_gaussian_molecule_to_chimera(gaussian_parser)
        chimera.openModels.add([chimera_molecule])
        return cls(chimera_molecule, modes)

    def frequencies(self):
        """
        normal modes vectors and frequencies
        """
        return self.modes.getEigvecs(), self.modes.getEigvals()

    def trajectory(self, mode, n_steps, rmsd=1.5):
        """
        given a number of a selected mode and a number of stemps returns
        a list with n_steps coordsets
        """
        ensemble = prody.traverseMode(mode=mode, atoms=self.molecule,
                                      n_steps=n_steps, rmsd=rmsd)
        return ensemble.getCoordsets()

    def sample(self,modes,rmsd):
        """
        modes : list
        rmsd : float
        """
        conformation = prody.sampleModes(modes=modes, atoms=self.molecule,
                                         n_confs=1, rmsd=rmsd)
        return conformation.getCoords()

class ChimeraTaskQueue(Queue):

    def __init__(self, task=None, *args, **kwargs):
        self.task = task
        Queue.__init__(self, *args, **kwargs)

    def _put(self, item):
        if isinstance(item, basestring) and self.task is not None:
            self.task.updateStatus(item)
        else:
            Queue._put(self, item)

class StatusTask(Task):

    def updateStatus(self, msg):
        chimera.statusline.show_message('{}: {}'.format(self.title, msg),
            clickCallback=self._status_click_callback)
        Task.updateStatus(self, msg)

    def _status_click_callback(self, *a, **kw):
        chimera.dialogs.display(chimera.tasks.TaskPanel.name)

def convert_chimera_molecule_to_prody(molecule):
    """
    Function that transforms a chimera molecule into a prody atom group

    Parameters
    ----------
    molecule : chimera.Molecule

    Returns
    -------
    prody_molecule : prody.AtomGroup()
    chimera2prody : dict
        dictionary: chimera2prody[chimera_atom.coordIndex] = i-thm element prody getCoords() array
    """
    if not isinstance(molecule, chimera.Molecule):
        raise TypeError('`molecule` must be a chimera.Molecule')

    prody_molecule = prody.AtomGroup()
    coords, elements, serials = [], [], []
    names, resnums, resnames = [], [], []
    chids, betas, masses = [], [], []
    chimera2prody = {}
    offset_chimera_residue = min(r.id.position for r in molecule.residues)

    for i, atm in enumerate(molecule.atoms):
        chimera2prody[atm.serialNumber] = i
        coords.append(tuple(atm.coord()))  # array documentation to improve
        elements.append(atm.element.name)
        serials.append(atm.serialNumber)
        names.append(atm.name)
        resnums.append(atm.residue.id.position - offset_chimera_residue)
        resnames.append(atm.residue.type)
        chids.append(atm.residue.id.chainId)
        masses.append(atm.element.mass)
        betas.append(atm.bfactor)

    prody_molecule.setCoords(coords)
    prody_molecule.setElements(elements)
    prody_molecule.setSerials(serials)
    prody_molecule.setNames(names)
    prody_molecule.setResnums(resnums)
    prody_molecule.setResnames(resnames)
    prody_molecule.setChids(chids)
    prody_molecule.setBetas(betas)
    prody_molecule.setMasses(masses)
    prody_molecule.setTitle(str(molecule.name))
    prody_molecule.setBonds([(chimera2prody[bond.atoms[0].serialNumber],
                                chimera2prody[bond.atoms[1].serialNumber]) for bond in molecule.bonds])

    return prody_molecule, chimera2prody

def convert_gaussian_molecule_to_chimera(path_or_parser):
    if isinstance(path_or_parser, basestring):
        parsed = Gaussian(path_or_parser).parse()
    else:
        parsed = path_or_parser

    elements = parsed.atomnos
    coords = parsed.atomcoords[-1]
    m = chimera.Molecule()
    r = m.newResidue("UNK", " ", 1, " ")
    for i, (element, xyz) in enumerate(zip(elements, coords)):
        element = chimera.Element(element)
        a = m.newAtom('{}{}'.format(element, i), element)
        r.addAtom(a)
        a.setCoord(chimera.Point(*xyz))
        a.serialNumber = i
    chimera.connectMolecule(m)
    return m

def calculate_vibrations(molecule, max_modes=20, algorithm='calpha', queue=None,
                         mass_weighted=False, cutoff=15.0, gamma=1.0,
                         **options):
    """
    Parameters
    ----------
    molecule : prody.AtomGroup
    nax_modes : int
        number of modes to calculate
    algorithm : callable, optional, default=None
        coarseGrain(prm) which make molecule.select().setBetas(i) where i
        is the index Coarse Grain group
        and prm is prody AtomGroup
    options : dict, optional
        Parameters for algorithm callable

    Returns
    -------
    modes : ProDy modes ANM or RTB
    """
    if queue is None:
        queue = Queue()
    modes = None
    hessian_kwargs = dict(cutoff=cutoff, gamma=gamma)
    if algorithm in ('residues', 'mass', 'graph'):
        queue.put('Building model...')
        title = 'normal modes for {}'.format(molecule.getTitle())
        molecule = GROUPERS[algorithm](molecule, **options)
        modes = prody.RTB(title)
        queue.put('Building hessian...')
        modes.buildHessian(molecule.getCoords(), molecule.getBetas(), **hessian_kwargs)
        if mass_weighted:
            queue.put('Mass weighting...')
            _mass_weighted_hessian(molecule, modes)
        queue.put('Calculating {} modes...'.format(max_modes))
        modes.calcModes(n_modes=max_modes)
    elif algorithm == 'calpha':
        queue.put('Building model...')
        calphas_modes = prody.ANM('normal modes for {}'.format(molecule.getTitle()))
        calphas = molecule = molecule.select(algorithm)
        queue.put('Building hessian...')
        calphas_modes.buildHessian(calphas, **hessian_kwargs)
        if mass_weighted:
            queue.put('Mass weighting...')
            _mass_weighted_hessian(molecule, modes)
        queue.put('Calculating {} modes...'.format(max_modes))
        calphas_modes.calcModes(n_modes=max_modes)
        queue.put('Extending model...')
        modes = prody.extendModel(calphas_modes, calphas, molecule, norm=True)[0]
    else:
        queue.put('Building model...')
        modes = prody.ANM('normal modes for {}'.format(molecule.getTitle()))
        queue.put('Building hessian...')
        modes.buildHessian(molecule, **hessian_kwargs)
        if mass_weighted:
            queue.put('Mass weighting...')
            _mass_weighted_hessian(molecule, modes)
        queue.put('Calculating {} modes...'.format(max_modes))
        modes.calcModes(n_modes=max_modes)
    print(modes)
    return modes

def _mass_weighted_hessian(molecule, modes):
    N = molecule.numAtoms()
    masses = np.zeros(3 * N, float)
    for i in xrange(len(molecule.getMasses())):
        masses[i * 3:i * 3 + 3] = molecule.getMasses()[i]
    reduced_masses = 1 / np.sqrt(masses)
    v, H = reduced_masses, modes.getHessian()
    hessian = ((v * H).T * v).T
    modes.setHessian(hessian)

def gaussian_modes(path_or_parser):
    """
    Read the modes
    Create a prody.modes instance

    Parameters
    ----------
    path_or_parser : str or cclib.Gaussian
        gaussian frequencies output path
        or already parsed cclib.Gaussian instance

    Returns
    -------
    modes : ProDy modes ANM or RTB
    """
    if isinstance(path_or_parser, basestring):
        parsed = Gaussian(path_or_parser).parse()
    else:
        parsed = path_or_parser
    shape = parsed.vibdisps.shape
    modes_vectors = parsed.vibdisps.reshape(shape[0], shape[1]*shape[2]).T
    modes_frequencies = np.abs(parsed.vibfreqs)
    modes = prody.NMA()
    modes.setEigens(vectors=modes_vectors, values=modes_frequencies)
    return modes

def group_by_residues(molecule, n=7):
    """
    Coarse Grain Algorithm 1: groups per residues

    Parameters
    ----------
    molecule : prody.AtomGroup
    n : int, optional, default=7
        number of residues per group

    Returns
    ------
    molecule : prody.AtomGroup
        New betas added
    """
    group = 1
    for chain in molecule.iterChains():
        residues_indices = sorted(list(set(chain.getResnums())))
        chain_name = chain.getChid()
        for a, b in chunker(len(residues_indices), n):
            try:
                start, end = residues_indices[a-1], residues_indices[b-1]
                selector = 'chain {} and resnum {} to {}'.format(chain_name, start, end)
                selection = molecule.select(selector)
                selection.setBetas(group)
                group += 1
            except AttributeError as e:
                print(e)
    return molecule

def group_by_mass(molecule, n=100):
    """
    Coarse Grain Algorithm 2: groups per mass percentage

    Parameters
    ----------
    molecule : prody.AtomGroup
    n: int, optional, default=100
        Intended number of groups. The mass of the system will be divided by this number,
        and each group will have the corresponding proportional mass. However, the final
        number of groups can be slightly different.

    Returns
    -------
    molecule: prody.AtomGroup
        New Betas added
    """
    group = 1

    total_mass = sum(molecule.getMasses())
    chunk_mass = total_mass/n

    for chain in molecule.iterChains():
        selection = molecule.select('chain {}'.format(chain.getChid()))
        mass_accumulator = 0.

        for atom in iter(selection):
            atom.setBeta(group)
            mass_accumulator += atom.getMass()
            if mass_accumulator > chunk_mass:
                mass_accumulator = 0.
                group += 1
        group += 1
    return molecule

def group_by_graph(moldy, n=2):
    """
    Coarse Grain Algorithm 3: Graph algorithm.
        New group when a vertice: have more than n,
                                  have 0 edges
                                  new chain

    Parameters
    ----------
    moldy : prody.AtomGroup
    n : int, optional, default=2
        maximum bonds number

    Returns
    -------
    moldy: prody.AtomGroup
        New Betas added
    """
    group = 1
    title = 'Graphs'

    for chain in moldy.iterChains():
        # selection = moldy.select('chain {}'.format(chain.getChid()))
        # for atom in iter(selection):
        for atom in chain.iterAtoms():
            atom.setBeta(group)
            if atom.numBonds() > n or atom.numBonds() == 0:
                group += 1
        group += 1
    return moldy

def chunker(end, n):
    """
    divide end integers in closed groups of n
    """
    for i in range(0, end-n+1, n):
        yield i+1, i+n
    if end % n:
        yield end-end % n+1, end

GROUPERS = dict(residues= group_by_residues,
               mass=group_by_mass,
               graph=group_by_graph)


#Семестровое задание
#Шиленков Алексей ИУ7-15Б
#первая инструкция
reg_instructions = """
    Для начала введите название книг и фамилию с именем автора.
    Вводить стоит в форме, предоставленной ниже:

        "Название книги" by Фамилия, И.

    Для окончания записи вам следует ввести END
    Для вывода данного сообщения вы можете ввести HELP

    Ограничения: Название книги и строчка автора не должны превышать 60 символов
    """
#вторая инструкция
instruction = """
    Доступные команды:

            BORROW - взятие книги из библиотеки
                >: BORROW "Название"

            RETURN - возврат книги в библиотеку
                >: RETURN "Название"

            SHELVE - выдача последовательности команд для правильного
                     расстановки книг на полке
                >: SHELVE

            END - конец работы со списком
                >: END

            HELP - вывести данное руководство
                >: HELP
    """
#--------------------------------------------------------------------------------------
#Code
#кэш для работы
database = []

def Help(flag_info):
    # команда для возврата подсказки
    if flag_info == 'main':
        print(instruction) # вторая
    if flag_info == 'register':
        print(reg_instructions) # первая

def register():
    # регистрация книг
    # переменная ввода
    answer = ''
    while(answer != 'END'):
        # ввод
        answer = input()
        if answer == "END":
            print("Регистрация завершена")
        elif answer == 'HELP':
            # Вывод инструкции
            Help('register')
        else:
            # ячейка, которая принимает значения парсинга строки, принимает 0 если ввод неверный
            cell = parse(answer)
            if cell is 0:
                print("Вы ввели неверно, пожалуйста, повторите ввод.")
            else:
                # проверка на длину
                if len(cell[0]) > 60: print('Слишком большая длина названия')
                elif len(cell[1]) > 60: print('Слишком большая длина ФИО автора')
                else: database.append(cell)

def sort(inp):
    # ключ сортировки
    return not inp[2], inp[1], inp[0], inp[3]

def sorting():
    # сортировка TimSort (намного быстрее quicksort)
    database.sort(key=sort)

def parse(answer):
    # форматирование строки
    if answer[0] is '"' and '" by ' in answer:
        #удаляем первую кавычку
        answer = answer[1:]
        #разделяй и властвуй
        name,author = answer.split('" by ')
        # название, автор, наличие книги в реальном времени, наличие на полке
        box = [name, author, True , True]
        return box
    else:
        return 0

def commands(command):
    # команды для работы с книгами
    tail = command[1]
    command = command[0]
    if command == "BORROW":
        Borrow(tail)
    elif command == "RETURN":
        Return(tail)
    else:
        print("Такой команды нет")

def Borrow(tail):
    # команда для взятия книг
    catched = False
    for cell in database:
        if cell[0] == tail:
            #есть ли книга на руках
            if cell[2]:
                cell[2] = False
                cell[3] = False
            else:
                print("Эту книгу уже взяли")
            catched = True
            break
    if not catched:
        print("Такой книги нет в базе")

def Return(tail):
    # команда для возврата книг
    catched = False
    for cell in database:
        if cell[0] == tail:
            if not cell[2]:
                cell[2] = True
            else:
                print("Книга была возвращена")
            catched = True
            break
    if not catched:
        print("Такой книги нет в базе")

def Shelve():
    # команда для воспроизведения последовательности команд пользователю
    sorting()
    ending = False
    count = 0
    # определяем до какой строки идти
    for cell in database:
        if cell[2] == True:
            count += 1
    for iteration in range(count):
        if not database[iteration][3]:
            ending = True
            if iteration == 0:
                print('Put "' + database[iteration][0] + '" first')
            else:
                print('Put "' + database[iteration][0] + '" after "' + database[iteration-1][0] + '"')
            database[iteration][3] = True
    if ending:
        print("END")

def main():
    #Основное тело, принцип вызова функций тот же
    command = ''
    while (command != 'END'):
        command = input()
        if command == "HELP":
            Help('main')
        elif command == "SHELVE":
            #Сортирует и выводит последовательность
            Shelve()
        elif command == "END":
            print('Программа завершена')
        else:
            #Парсинг команд
            command = command.split('"')[:-1]
            if len(command) == 2:
                command[0] = command[0][:-1]
                commands(command)
            else:
                print("Неправильный синтаксис")

# вывод помощи
Help('register')
# регистрация книг в базу
register()
Help('main')
# тело функции, основная, управляющая
main()


"""
Classe qui se charge de créer des vector document

https://radimrehurek.com/gensim/auto_examples/tutorials/run_doc2vec_lee.html#sphx-glr-auto-examples-tutorials-run-doc2vec-lee-py

Author : bsanchez@starclay.fr
date : 06/08/2020
"""

import gensim
import tempfile
import pickle
from datetime import datetime
import pandas as pd
from datetime import datetime
import os
import s3fs
from .Process import Process
from .utils import *
import scipy
from gensim.models.phrases import Phrases, Phraser

from tensorflow.keras.preprocessing.text import Tokenizer
from gensim.models.fasttext import FastText
import numpy as np
import nltk
from string import punctuation
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer
from nltk.tokenize import sent_tokenize
from nltk import WordPunctTokenizer
from gensim.models.doc2vec import Doc2Vec, TaggedDocument
from nltk.tokenize import word_tokenize
import time

class ProcessDoc2Vec(Process):

    def __init__(self,list_df_cols,path_run,id_run,dict_models={}):
        """
        Classe permettant de transformer les inputs textuels en doc2vec

        :param list_df_cols: liste des colonnes à traiter
        :param path_run: chemin de svg sur minio
        :param id_run: id du run de training
        :param dict_models: dictionnaires modèles (list of dict)
        """
        self.name = "Doc2Vec"
        self.id_run = id_run
        self.path_run = path_run
        self.list_df_cols = list_df_cols
        self.list_model = []
        self.dict_models = dict_models
        for col in self.list_df_cols:
            self.list_model.append(None)

    def train_model(self,df_path):
        """
        Crée un modèle Doc2Vec par colonne

        :param df_path: fichier à traiter
        """

        start = time.time()

        flatten = lambda l: [item for sublist in l for item in sublist]

        for index,col in enumerate(self.list_df_cols):
            if col in self.dict_models[col]:
                with open(self.dict_models[col], 'rb') as handle:
                    self.list_model[index] = pickle.load(handle)
            else:
                train_corpus = []
                for df in pd.read_csv(df_path,chunksize=60000,dtype=str,sep=";",usecols=[str(col)]):
                        train_corpus.append(df[col].tolist())

                #On transforme le corpus en document
                train_corpus = flatten(train_corpus)
                train_corpus = [TaggedDocument(words=word_tokenize(str(_d).lower()), tags=[str(i)]) for i, _d in enumerate(train_corpus)]

                #Hyperparametre, comment les choisir ?
                epochs = 60
                vec_size = 30
                 # normalement on utilise cpu_count mais vu qu'il s'agit d'un environnement en contenaire
                 # la fonction retour tout les cores du clusteurs (~72)
                cores = 9

                model = gensim.models.doc2vec.Doc2Vec(vector_size=vec_size, min_count=2, epochs=epochs)
                model.build_vocab(train_corpus)
                model.train(train_corpus, total_examples=model.corpus_count, epochs=model.epochs)

                self.list_model[index] = model
                phaseC = time.time()
                print(f"Doc2Vec train complete {col} {phaseC - start}")

                # Sanity check: On calcule le vector pour chaque document du training
                # Si le modèle est sain, le document le plus similaire est lui même.
                # La doc officielle est fausse => TODO

        phase1 = time.time()
        print(f"Doc2Vec train complete: {phase1 - start}")

        self.save_model(distant_file=True)

    def run_model(self,df_path):
        """
        transforme les données de :param df_path: via les modèles de Doc2Vec entrainés

        :param df_path: fichier à traiter
        """

        start = time.time()

        flatten = lambda l: [item for sublist in l for item in sublist]

        new_name = incrementTmpFile(df_path)

        for df in pd.read_csv(df_path,chunksize=60000,dtype=str,sep=";"):
            list_document_vector = []
            for index_row,row in df.iterrows():
                document_vector = []
                for index,col in enumerate(self.list_df_cols):
                    col_tokens = str(row[col]).split(" ")
                    col_vector = self.list_model[index].infer_vector(col_tokens)
                    document_vector.append(col_vector)
                document_vector = flatten(document_vector)
                list_document_vector.append(document_vector)
            df_document_vector = pd.DataFrame(list_document_vector)
            with open(F"tmp/{new_name}.csv", 'a') as f:
                df_document_vector.to_csv(f, mode='a', sep=';', header=f.tell()==0, index=False)

        phase1 = time.time()
        print(f"Time consumed in working: {phase1 - start}")

    def apply(self,df_path):
        """
        extrait puis applique les modèles Doc2Vec sur les données

        :param df_path: fichier à traiter
        """
        self.train_model(df_path)
        self.run_model(df_path)

    def save_model(self, distant_file=False, local_file=False):
        """
        Sauve les modèles localement et/ou sur minio
        """
        if local_file:
            for index,mod in enumerate(self.list_model):
                pickle.dump(mod, open(f"{self.id_run}_d2v_{list_df_cols[index]}", "wb" ))
        else:
            for index,mod in enumerate(self.list_model):
                f = tempfile.NamedTemporaryFile('w', delete=False)
                f.name = f"{self.id_run}_{self.name}_{self.list_df_cols[index]}"
                pickle.dump(mod, open(f.name, "wb"))
                save_file_on_minio(f.name, dir_name=self.path_run)
                os.remove(f.name)

    # def load_model(distant_file=None, local_file=None):
    #     if not distant_file and not local_file:
    #         raise ValueError("At least 1 path must be given")
    #     if local_file:
    #         cl = CleanerRsX()
    #         cl.model = pickle.load(local_file)
    #         return cl
    #     else:
    #         f = tempfile.NamedTemporaryFile('w', delete=False)
    #         self.load_model_from_minio(distant_file, f.name)
    #         cl = CleanerRsX()
    #         cl.model = pickle.load(f.name)
    #         os.delete(f)
    #         return cl


# coding: utf-8

"""
    CyberSource Merged Spec

    All CyberSource API specs merged together. These are available at https://developer.cybersource.com/api/reference/api-reference.html

    OpenAPI spec version: 0.0.1

    Generated by: https://github.com/swagger-api/swagger-codegen.git
"""

from __future__ import absolute_import

import sys
import os
import re

# python 2 and python 3 compatibility library
from six import iteritems

from ..configuration import Configuration
from ..api_client import ApiClient

class AuthenticationExemptionsApi(object):
    """
    NOTE: This class is auto generated by the swagger code generator program.
    Do not edit the class manually.
    Ref: https://github.com/swagger-api/swagger-codegen
    """

    def __init__(self, merchant_config, api_client=None):
        config = Configuration()
        if api_client:
            self.api_client = api_client
        else:
            if not config.api_client:
                config.api_client = ApiClient()
            self.api_client = config.api_client
        self.api_client.set_configuration(merchant_config)

    def authentication_exemptions(self, authentication_exemptions_request, **kwargs):
        """
        Authentication Exemptions Service
        A new CYBS branded service to connect to VISA’s REST API to enable Visa Transaction Advisor (VTA) as a standalone service for merchants to support PSD2/SCA adoption and exemptions processing startegy in Europe.VTA Provides intelligent risk data to merchants as inputs to their in-house fraud management tools for fraud mitigation on Visa transactions.
        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please define a `callback` function
        to be invoked when receiving the response.
        >>> def callback_function(response):
        >>>     pprint(response)
        >>>
        >>> thread = api.authentication_exemptions(authentication_exemptions_request, callback=callback_function)

        :param callback function: The callback function
            for asynchronous request. (optional)
        :param AuthenticationExemptionsRequest authentication_exemptions_request: (required)
        :return: RiskV1AuthenticationExemptionsPost201Response
                 If the method is called asynchronously,
                 returns the request thread.
        """
        kwargs['_return_http_data_only'] = True
        if kwargs.get('callback'):
            return self.authentication_exemptions_with_http_info(authentication_exemptions_request, **kwargs)
        else:
            (data) = self.authentication_exemptions_with_http_info(authentication_exemptions_request, **kwargs)
            return data

    def authentication_exemptions_with_http_info(self, authentication_exemptions_request, **kwargs):
        """
        Authentication Exemptions Service
        A new CYBS branded service to connect to VISA’s REST API to enable Visa Transaction Advisor (VTA) as a standalone service for merchants to support PSD2/SCA adoption and exemptions processing startegy in Europe.VTA Provides intelligent risk data to merchants as inputs to their in-house fraud management tools for fraud mitigation on Visa transactions.
        This method makes a synchronous HTTP request by default. To make an
        asynchronous HTTP request, please define a `callback` function
        to be invoked when receiving the response.
        >>> def callback_function(response):
        >>>     pprint(response)
        >>>
        >>> thread = api.authentication_exemptions_with_http_info(authentication_exemptions_request, callback=callback_function)

        :param callback function: The callback function
            for asynchronous request. (optional)
        :param AuthenticationExemptionsRequest authentication_exemptions_request: (required)
        :return: RiskV1AuthenticationExemptionsPost201Response
                 If the method is called asynchronously,
                 returns the request thread.
        """

        all_params = ['authentication_exemptions_request']
        all_params.append('callback')
        all_params.append('_return_http_data_only')
        all_params.append('_preload_content')
        all_params.append('_request_timeout')

        params = locals()
        for key, val in iteritems(params['kwargs']):
            if key not in all_params:
                raise TypeError(
                    "Got an unexpected keyword argument '%s'"
                    " to method authentication_exemptions" % key
                )
            params[key] = val
        del params['kwargs']
        # verify the required parameter 'authentication_exemptions_request' is set
        if ('authentication_exemptions_request' not in params) or (params['authentication_exemptions_request'] is None):
            raise ValueError("Missing the required parameter `authentication_exemptions_request` when calling `authentication_exemptions`")

        collection_formats = {}

        path_params = {}

        query_params = []

        header_params = {}

        form_params = []
        local_var_files = {}

        body_params = None
        if 'authentication_exemptions_request' in params:
            body_params = params['authentication_exemptions_request']
        # HTTP header `Accept`
        header_params['Accept'] = self.api_client.\
            select_header_accept(['application/hal+json;charset=utf-8'])

        # HTTP header `Content-Type`
        header_params['Content-Type'] = self.api_client.\
            select_header_content_type(['application/json;charset=utf-8'])

        # Authentication setting
        auth_settings = []

        return self.api_client.call_api(f'/risk/v1/authentication-exemptions', 'POST',
                                        path_params,
                                        query_params,
                                        header_params,
                                        body=body_params,
                                        post_params=form_params,
                                        files=local_var_files,
                                        response_type='RiskV1AuthenticationExemptionsPost201Response',
                                        auth_settings=auth_settings,
                                        callback=params.get('callback'),
                                        _return_http_data_only=params.get('_return_http_data_only'),
                                        _preload_content=params.get('_preload_content', True),
                                        _request_timeout=params.get('_request_timeout'),
                                        collection_formats=collection_formats)