import os
import numpy as np
from typing import List, Dict, Tuple
import random
from math import *
import gym
import argparse
import time
import numpy as np
import random
import argparse
from collections import namedtuple
import gym
import os
import tqdm

def soft_binarization(x,tau=3):
    return exp(tau*x)/(exp(tau*x)+exp(tau*(1-x)))

initial_holding_allowed=False

class Map():
    def __init__(self,map_size:Tuple[int,int]):
        self.map_size=map_size
        self.map=[['-' for i in range(map_size[0])] for j in range(map_size[1])]
        self.agent=[]
        self.num_agents=2

    def is_boundary(self,i,j):
        return i==0 or j==0 or i==self.map_size[0]-1 or j==self.map_size[1]-1

    def build_boundary(self):
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if self.is_boundary(i,j):
                    self.map[i][j]='-'

    # p: percentage of obstacles
    def generate_path(self,p=0.3):
        dist=1/3*(self.map_size[0]+self.map_size[1])
        self.points=[]
        # modify=log(log(self.map_size[0]*self.map_size[1]))
        modify=1
        cnt=round(self.map_size[0]*self.map_size[1]*(1-p)/dist*modify)
        cnt=max(cnt,2)
        for i in range(cnt):
            point=(1+round((self.map_size[0]-3)*soft_binarization(random.random())),1+round((self.map_size[1]-3)*soft_binarization(random.random())))
            self.points.append(point)
            self.map[point[0]][point[1]]='f'
        self.points.append(self.points[0])
        for i in range(cnt):
            cur_x=self.points[i][0]
            cur_y=self.points[i][1]
            while cur_x!=self.points[i+1][0] or cur_y!=self.points[i+1][1]:
                delta_x=np.sign(self.points[i+1][0]-cur_x)
                delta_y=np.sign(self.points[i+1][1]-cur_y)
                if random.random()<0.5:
                    cur_x=cur_x+delta_x
                else:
                    cur_y=cur_y+delta_y
                self.map[cur_x][cur_y]='f'

    def check_connectivity(self,p):
        vis=np.zeros([self.map_size[0],self.map_size[1]],np.int32)
        cc=0
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if p[i][j]>0 and vis[i][j]==0:
                    cc+=1
                    dfs_list=[(i,j)]
                    while True:
                        if len(dfs_list)==0:
                            break
                        for ii,jj in dfs_list:
                            if vis[ii][jj]!=0:
                                dfs_list.remove((ii,jj))
                                continue
                            vis[ii][jj]=cc
                            for iii,jjj in [(ii+1,jj),(ii,jj+1),(ii-1,jj),(ii,jj-1)]:
                                if p[iii][jjj]==1 and vis[iii][jjj]==0:
                                    dfs_list.append((iii,jjj))
        for i in range(1,cc+1):
            if np.sum(vis==i)<=2:
                return 0   # CC that consists of less than 2 grids is not allowed
        self.vis=vis
        return cc

    def generate_convex_area(self):
        random_img=np.zeros([self.map_size[0],self.map_size[1]],np.int32)
        num_pts=random.randint(6,10)
        for i in range(num_pts):
            random_img[random.randint(1,self.map_size[0]-2)][random.randint(1,self.map_size[1]-2)]=1

        element_count=np.sum(random_img)
        while True:
            for i in range(self.map_size[0]):
                idx1=np.argwhere(random_img[i]>0)
                if len(idx1)==0:
                    continue
                idx_min=np.min(idx1)
                idx_max=np.max(idx1)
                random_img[i][idx_min:idx_max+1]=1
            for j in range(self.map_size[1]):
                idx1=np.argwhere(random_img[:,j]>0)
                if len(idx1)==0:
                    continue
                idx_min=np.min(idx1)
                idx_max=np.max(idx1)
                random_img[idx_min:idx_max+1][:,j]=1
            if np.sum(random_img)==element_count:
                break
            element_count=np.sum(random_img)

        cc=self.check_connectivity(random_img)
        if cc!=1:
            raise Exception("CC={}".format(cc))

        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if random_img[i][j]>0:
                    self.map[i][j]='f'

    def generate_two_cc(self):
        while True:
            random_img=np.zeros([self.map_size[0],self.map_size[1]],np.int32)
            for i in range(1,self.map_size[0]-1):
                for j in range(1,self.map_size[1]-1):
                    if random.randint(0,1)==0:
                        random_img[i][j]=1

            cc=self.check_connectivity(random_img)
            if cc==2:
                break
            # else:
            #     print("CC={}".format(cc))

        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if random_img[i][j]>0:
                    self.map[i][j]='f'

    def generate_fixed_path(self):
        self.map=[['-','-','-','-','-','-','-','-'],
                  ['-','f','f','-','f','f','f','-'],
                  ['-','f','f','-','f','f','f','-'],
                  ['-','f','f','-','f','f','f','-'],
                  ['-','f','f','-','-','f','f','-'],
                  ['-','f','f','f','-','f','f','-'],
                  ['-','f','f','f','-','f','f','-'],
                  ['-','-','-','-','-','-','-','-']]

        self.vis=[[0,0,0,0,0,0,0,0],
                  [0,1,1,0,2,2,2,0],
                  [0,1,1,0,2,2,2,0],
                  [0,1,1,0,2,2,2,0],
                  [0,1,1,0,0,2,2,0],
                  [0,1,1,1,0,2,2,0],
                  [0,1,1,1,0,2,2,0],
                  [0,0,0,0,0,0,0,0]]

    def save(self,path:str="./utils/levels/random/example.txt"):
        with open(path,"w") as f:
            f.write('\n'.join([' '.join(x) for x in self.map]))
            f.write('\n\n')

            for idx,agent in enumerate(self.agent):
                f.write(str(agent[0])+" "+str(agent[1]))
                if initial_holding_allowed:
                    if random.random()<0.2:
                        f.write(" "+random.choice(["o","t","Co","Ct","p","q","pCo","pCt","CoCt","pCoCt","e"]))
                f.write('\n')
    def cnt_grid(self):
        return self.map_size[0]*self.map_size[1]

    def is_occupied(self,i,j):
        return self.map[i][j]!='f' or (j,i) in self.agent

    def count_empty(self):
        cnt=0
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if not self.is_occupied(i,j):
                    cnt+=1
        return cnt

    def count_empty_i(self,idx):
        cnt=0
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if not self.is_occupied(i,j) and self.vis[i][j]==idx:
                    cnt+=1
        return cnt

    def add_agent(self):
        empty_cnt=self.count_empty()
        x=random.randint(0,empty_cnt-1)
        cnt=0
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if not self.is_occupied(i,j):
                    if(cnt==x):
                        self.agent.append((j,i))
                        return
                    cnt+=1

    def add_agent_i(self,idx):
        empty_cnt=self.count_empty_i(idx)
        x=random.randint(0,empty_cnt-1)
        cnt=0
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if not self.is_occupied(i,j) and self.vis[i][j]==idx:
                    if(cnt==x):
                        self.agent.append((j,i))
                        return
                    cnt+=1

    def random_accessible_center(self):
        cnt=1
        point=None
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if self.map[i][j]=="-":
                    if self.map[max(i-1,0)][j]=='f' or self.map[min(i+1,self.map_size[0]-1)][j]=='f' or self.map[i][max(j-1,0)]=='f' or self.map[i][min(j+1,self.map_size[1]-1)]=='f':
                        if random.random()<1/cnt:
                            point=(i,j)
                        cnt+=1
        if point is None:
            raise ValueError
        return point

    def random_accessible_bridge(self):
        cnt=1
        point=None
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if self.map[i][j]=="-":
                    reachable=True
                    for agent_id in range(1,3):
                        if not(self.vis[max(i-1,0)][j]==agent_id or self.vis[min(i+1,self.map_size[0]-1)][j]==agent_id or self.vis[i][max(j-1,0)]==agent_id or self.vis[i][min(j+1,self.map_size[1]-1)]==agent_id):
                            reachable=False
                    if reachable:
                        if random.random()<1/cnt:
                            point=(i,j)
                        cnt+=1
        if point is None:
            raise ValueError
        return point

    def random_accessible_i(self,idx):
        if not isinstance(idx,List):
            idx=[idx]
        cnt=1
        point=None
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if self.map[i][j]=="-":
                    reachable=True
                    for agent_id in idx:
                        if not(self.vis[max(i-1,0)][j]==agent_id or self.vis[min(i+1,self.map_size[0]-1)][j]==agent_id or self.vis[i][max(j-1,0)]==agent_id or self.vis[i][min(j+1,self.map_size[1]-1)]==agent_id):
                            reachable=False
                    if not (self.map[max(i-1,0)][j]=='f' or self.map[min(i+1,self.map_size[0]-1)][j]=='f' or self.map[i][max(j-1,0)]=='f' or self.map[i][min(j+1,self.map_size[1]-1)]=='f'):
                        reachable=False
                    if reachable:
                        if random.random()<1/cnt:
                            point=(i,j)
                        cnt+=1
        if point is None:
            raise ValueError
        return point

    def add_delivery(self,idx):
        point=self.random_accessible_i(idx)
        self.map[point[0]][point[1]]='*'

    def add_dishwasher(self,idx):
        point=self.random_accessible_i(idx)
        self.map[point[0]][point[1]]='w'

    def add_fire(self,idx):
        cnt=1
        point=None
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                if self.map[i][j]=="f" and self.vis[i][j]==idx and not (self.map[max(i-1,0)][j]=='e' or self.map[min(i+1,self.map_size[0]-1)][j]=='e' or self.map[i][max(j-1,0)]=='e' or self.map[i][min(j+1,self.map_size[1]-1)]=='e'):
                    if random.random()<1/cnt:
                        point=(i,j)
                    cnt+=1
        self.map[point[0]][point[1]]='h'

    def add_plate(self,idx):
        point=self.random_accessible_i(idx)
        self.map[point[0]][point[1]]='p'

    def add_random_plate(self,idx):
        point=self.random_accessible_i(idx)
        if random.randint(0,1)==1:
            self.map[point[0]][point[1]]='p'
        else:
            self.map[point[0]][point[1]]='q'

    def add_random_dish(self,idx):
        point=self.random_accessible_i(idx)
        x=random.randint(0,3)
        if x==0:
            self.map[point[0]][point[1]]='pCt'
        elif x==1:
            self.map[point[0]][point[1]]='pCo'
        elif x==2:
            self.map[point[0]][point[1]]='pCoCt'
        elif x==3:
            self.map[point[0]][point[1]]='CoCt'

    def add_random_chopped(self,idx):
        point=self.random_accessible_i(idx)
        if random.randint(0,1)==1:
            self.map[point[0]][point[1]]='Ct'
        else:
            self.map[point[0]][point[1]]='Co'

    def add_random_fresh(self,idx):
        point=self.random_accessible_i(idx)
        if random.randint(0,1)==1:
            self.map[point[0]][point[1]]='t'
        else:
            self.map[point[0]][point[1]]='o'

    def add_object_str(self,s,idx):
        point=self.random_accessible_i(idx)
        self.map[point[0]][point[1]]=s

    def add_vases(self):
        self.map[4][3]='v'
        self.map[4][4]='v'

if __name__=="__main__":
    # training levels for different training stages
    # sast: single agent single subtask, train single agent policies
    # samt: single agent multi subtask, train single agent policies on completing series of subtasks
    # mast: multi agent single subtask
    # mast-cooperation: multi agent single subtask, agent 1 is the leading agent, agent 2 is the follower agent
    # perception: train the perception module
    # mamt: multi agent multi subtask, integrate all the modules
    level=input("level: sast | samt | mast | mast-cooperation | mamt | perception \n")

    os.makedirs(os.path.join("./levels/",level),exist_ok=True)

    if level in ["perception","sast","mast","mast-cooperation"]:
        initial_holding_allowed=True

    program_dir="./programs"

    for i in tqdm.tqdm(range(0,20000)):
        while True:
            try:
                random_cc_agent=random.randint(1,2)
                if level=="sast" or level=="samt":
                    random_cc_fixed=random_cc_agent
                    random_cc_movable=random_cc_agent
                elif level in ["mast","mast","perception","mamt"]:
                    random_cc_fixed=[]
                    random_cc_movable=[]
                elif level=="mast-cooperation":
                    random_cc_fixed=random_cc_agent
                    random_cc_movable=[]
                else:
                    raise ValueError("level not found")
                name="random"+str(i)
                map=Map((8,8))
                map.generate_fixed_path()
                map.add_vases()

                map.add_object_str("*",random_cc_fixed)
                map.add_object_str("w",random_cc_fixed)
                map.add_object_str("T",random_cc_fixed)
                map.add_object_str("O",random_cc_fixed)
                map.add_object_str("/",random_cc_fixed)
                map.add_object_str("e",random_cc_fixed)

                map.add_agent_i(random_cc_agent)
                map.add_agent_i(3-random_cc_agent)
                map.add_agent_i(random_cc_agent)
                map.add_agent_i(3-random_cc_agent)

                # randomly generate one agent and one subtask
                if level in ["sast","mast-cooperation","mast"]:
                    h1=0
                    h2=random.randint(0,15)
                    if h2==0:
                        map.add_object_str("pCoCt",random_cc_movable)
                    if h2==1:
                        map.add_object_str("pCo",random_cc_movable)
                    if h2==2:
                        map.add_object_str("pCt",random_cc_movable)
                    if h2==3:
                        map.add_object_str("p",random_cc_movable)
                        map.add_object_str("CoCt",random_cc_movable)
                    if h2==4:
                        map.add_object_str("p",random_cc_movable)
                        map.add_object_str("Ct",random_cc_movable)
                        map.add_object_str("Co",random_cc_movable)
                    if h2==5:
                        map.add_random_plate(random_cc_movable)
                        map.add_object_str("Co",random_cc_movable)
                        map.add_object_str("Ct",random_cc_movable)
                    if h2==6:
                        map.add_object_str("p",random_cc_movable)
                        map.add_object_str("Co",random_cc_movable)
                        map.add_object_str("Ct",random_cc_movable)
                    if h2==7:
                        map.add_object_str("pCo",random_cc_movable)
                        map.add_object_str("Ct",random_cc_movable)
                    if h2==8:
                        map.add_object_str("pCt",random_cc_movable)
                        map.add_object_str("Co",random_cc_movable)
                    if h2==9 or h2==10:
                        map.add_random_plate(random_cc_movable)
                    if h2==11:
                        map.add_random_plate(random_cc_movable)
                        map.add_object_str("t",random_cc_movable)
                        map.add_object_str("o",random_cc_movable)
                    if h2==12:
                        map.add_random_plate(random_cc_movable)
                        map.add_object_str("t",random_cc_movable)
                        map.add_object_str("o",random_cc_movable)
                    if h2==13 or h2==14:
                        map.add_object_str("q",random_cc_movable)
                        map.add_random_chopped(random_cc_movable)
                    if h2==15:
                        map.add_fire(random_cc_fixed)
                    for _ in range(random.randint(1,3)):
                        map.add_random_dish([])
                    if not level=="sast":
                        if random.random()<0.1 and not h2==15:
                            map.add_fire(random.randint(0,1))

                # randomly generate a perception query
                elif level in ["perception"]:
                    h1=5
                    h2=random.randint(1,4)
                    for _ in range(3):
                        map.add_random_dish([])
                    for _ in range(3):
                        map.add_random_chopped([])
                    if random.random()<0.5:
                        map.add_fire(random.randint(0,1))

                elif level in ["samt","mast-cooperation"]:
                    h1=1
                    h2=random.randint(1,13)
                    if h2==3:
                        map.add_object_str("q",random_cc_movable)
                        map.add_object_str("Ct",random_cc_movable)
                    if h2==4:
                        map.add_object_str("q",random_cc_movable)
                        map.add_object_str("Co",random_cc_movable)
                    if h2==5:
                        map.add_object_str("p",random_cc_movable)
                        map.add_object_str("Co",random_cc_movable)
                    if h2==6:
                        map.add_object_str("p",random_cc_movable)
                        map.add_object_str("Ct",random_cc_movable)
                    if h2==7:
                        map.add_object_str("CoCt",random_cc_movable)
                        map.add_object_str("p",random_cc_movable)
                    if h2==8:
                        map.add_fire(random_cc_movable)
                    if h2==9:
                        map.add_fire(random_cc_movable)
                        map.add_object_str("o",random_cc_movable)
                    if h2==10:
                        map.add_fire(random_cc_movable)
                        map.add_object_str("Ct",random_cc_movable)
                        map.add_object_str("p",random_cc_movable)
                    if h2==11:
                        map.add_fire(random_cc_movable)
                        map.add_object_str("pCoCt",random_cc_movable)
                    if h2==12:
                        map.add_fire(random_cc_movable)
                    if h2==13:
                        map.add_fire(random_cc_movable)
                        map.add_object_str("q",random_cc_movable)

                elif level in ["mamt"]:
                    h1=4
                    h2=random.randint(0,3)
                    map.add_object_str("p",random_cc_movable)
                    map.add_object_str("q",random_cc_movable)
                break
            except:
                continue

        program=open(os.path.join(program_dir,str(h1)+"_"+str(h2)+".txt")).read()
        map.save(os.path.join("./levels/",level,name+".txt"))
        with open(os.path.join("./levels/",level,name+"_program"+".txt"),"w") as f:
            f.write(program)


import copy
import enum
import random
import sys
import termios
import tty

class Direction(enum.Enum):
    UP = 1
    RIGHT = 2
    DOWN = 3
    LEFT = 4

class Game:

    EMPTY_BOARD = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

    def __init__(self, score=0, board=EMPTY_BOARD, exp=False):
        self.score = score
        self.board = copy.deepcopy(board)
        self.exp = exp
        if self.board == Game.EMPTY_BOARD:
            self._place_random()
            self._place_random()

    # todo: make this obey `self.exp`
    def get_max_tile(self): return 2**max([max(row) for row in self.board])

    def move(self, direction):
        snapshot = copy.deepcopy(self.board)
        if direction == Direction.RIGHT:
            for r in range(4):
                new, score_change = Game._move_unit(reversed(self.board[r]), self.exp)
                self.board[r] = list(reversed(new))
                self.score += score_change
        elif direction == Direction.LEFT:
            for r in range(4):
                new, score_change = Game._move_unit(self.board[r], self.exp)
                self.board[r] = new
                self.score += score_change
        elif direction == Direction.UP:
            for c in range(4):
                unit = [self.board[0][c], self.board[1][c], self.board[2][c], self.board[3][c]]
                new, score_change = Game._move_unit(unit, self.exp)
                self.board[0][c] = new[0]
                self.board[1][c] = new[1]
                self.board[2][c] = new[2]
                self.board[3][c] = new[3]
                self.score += score_change
        elif direction == Direction.DOWN:
            for c in range(4):
                unit = [self.board[3][c], self.board[2][c], self.board[1][c], self.board[0][c]]
                new, score_change = Game._move_unit(unit, self.exp)
                self.board[0][c] = new[3]
                self.board[1][c] = new[2]
                self.board[2][c] = new[1]
                self.board[3][c] = new[0]
                self.score += score_change

        if self.board != snapshot:
            self._place_random()

    def ongoing(self):
        return self.get_move_mask() != [False] * 4

    def get_move_mask(self):
        moves = set()
        for r, c in [(x, y) for x in range(4) for y in range(3)]:
            if self.board[r][c] == self.board[r][c+1] != 0: moves.update([Direction.LEFT, Direction.RIGHT])
            if self.board[r][c] == 0 and self.board[r][c+1] > 0: moves.add(Direction.LEFT)
            if self.board[r][c] > 0 and self.board[r][c+1] == 0: moves.add(Direction.RIGHT)
        for r, c in [(x, y) for x in range(3) for y in range(4)]:
            if self.board[r][c] == self.board[r+1][c] != 0: moves.update([Direction.UP, Direction.DOWN])
            if self.board[r][c] == 0 and self.board[r+1][c] > 0: moves.add(Direction.UP)
            if self.board[r][c] > 0 and self.board[r+1][c] == 0: moves.add(Direction.DOWN)

        return [
            Direction.UP in moves,
            Direction.RIGHT in moves,
            Direction.DOWN in moves,
            Direction.LEFT in moves
        ]

    def _find_empties(self):
        """Return the index-pairs of all empty board positions"""
        return [(r, c) for r in range(4) for c in range(4) if self.board[r][c] == 0]

    def _place_random(self):
        empties = self._find_empties()
        if empties != []:
            r, c = random.choice(empties)
            self.board[r][c] = 1 if random.random() < 0.9 else 2

    @staticmethod
    def _move_unit(unit, exp=False):
        score_change = 0
        unit = Game._collapse(unit)
        for i in range(3):
            if unit[i] > 0 and unit[i] == unit[i+1]:
                unit[i] += 1
                score_change += 2**unit[i] # todo does this need to be `2**(unit[i]-1)`?
                unit[i+1] = 0
        unit = Game._collapse(unit)
        return unit, score_change

    @staticmethod
    def _collapse(unit):
        unit = [x for x in unit if 0 < x]
        unit += [0] * (4 - len(unit)) # pad the 0s on the right
        return unit

    def _tile_repr(self, tile): return f'{(2**tile if tile > 0 and self.exp else tile):>4}'

    def __repr__(self):
        return "\n".join(["  ".join(self._tile_repr(x) for x in row) for row in self.board])

def play_game():
    def _get_input_char():
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        tty.setraw(fd)
        input_char = sys.stdin.read(1)
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return input_char

    game = Game(exp=True)
    for _ in range(100): print()
    print(game)
    while True:
        move = _get_input_char()
        if move == 'j':
            game.move(Direction.LEFT)
        if move == 'k':
            game.move(Direction.DOWN)
        if move == 'l':
            game.move(Direction.RIGHT)
        if move == 'i':
            game.move(Direction.UP)
        if move == 'q':
            exit()
        for _ in range(100): print()
        print(game)
        print(game.get_move_mask())


from machine import Pin
import time

# program to display numbers from 1 to 5

# initialize the variable
i = 10
n = 1

onboardLED = Pin(25, Pin.OUT)

# while loop from i = 1 to 5
while i >= n:
    print(i)
    i = i - 1
    onboardLED.value(0)
    time.sleep(0.5)
    onboardLED.value(1)
    time.sleep(0.5)


"""
class SelectBuilder:
----
This class functions as an object for assembling an SQL SELECT statement based on the passed arguments.
SelectBuilder takes a list of columns specified in classes that subclass the Table class, using PostgreSQL datatypes.

SelectBuilder also contains some settings:
lower_case_tables=False --- When True is passed, table names will be automatically lowercase to allow class names to conform with PEP-8
"""
from objects.join import Join

class SelectBuilder:
    def __init__(self, columns: list, lower_case_tables=False):
        # args passed on class instantiation
        self.columns = columns

        # set by join()
        self.joins = []

        # set by group_by()
        self.group_by_column = ""

        # variables that will change
        self.tables_encountered = []

        # Settings
        self.lower_case_tables = lower_case_tables

    # todo replace with the same arguments from the Join object
    # Appends a Join object
    def join(self, join: Join):
        self.joins.append(join)
        return self

    # Sets a group by column
    def group_by(self, group_by):
        self.group_by_column = group_by
        return self

    # Return the SQL command
    def __str__(self):
        base_sql = "SELECT"
        first_table_encountered = ""

        # add columns
        for element, column in enumerate(self.columns):
            if self.lower_case_tables:
                table_name = column.table.__class__.__name__.lower()
            else:
                table_name = column.table.__class__.__name__

            # keep track of all tables we've seen, this will allow us to determine our 'FROM' table name
            self.tables_encountered.append(table_name)

            # if we're at the first and only element
            if element == 0 and len(self.columns) == 1:
                base_sql += " {0}.{1} ".format(table_name, column.name)

            # if we're not at the last element
            elif not element == len(self.columns) - 1:
                base_sql += " {0}.{1}, ".format(table_name, column.name)

            # if we're at the last element, and we have more than one
            elif element == len(self.columns) - 1:
                base_sql += "{0}.{1} ".format(table_name, column.name)

            # set the first table encountered
            if element == 0:
                if self.lower_case_tables:
                    first_table_encountered = column.table.__class__.__name__.lower()
                else:
                    first_table_encountered = column.table.__class__.__name__

        # add from table (if we've only seen one kind of table, append it...)
        if len(set(self.tables_encountered)) == 1:
            base_sql += "FROM {0}".format(first_table_encountered)
        else:
            # looks like there was more than one table specified, attempting to get 'JOIN'
            base_sql += "FROM {0}".format(first_table_encountered)

            # lets figure out how we're gonna get this join done :/
            for element, join in enumerate(self.joins):
                if self.lower_case_tables:
                    to_join_table_name = join.to_join_table.__name__.lower()
                    left_table_name = join.left_column.table.__class__.__name__.lower()
                    right_table_name = join.right_column.table.__class__.__name__.lower()
                else:
                    to_join_table_name = join.to_join_table.__name__
                    left_table_name = join.left_column.table.__class__.__name__
                    right_table_name = join.right_column.table.__class__.__name__

                # determine if this is a left or right join
                if join.join_type == join.LEFT_JOIN:
                    base_sql += " LEFT JOIN {0}".format(to_join_table_name)
                elif join.join_type == join.RIGHT_JOIN:
                    base_sql += " RIGHT JOIN {0}".format(to_join_table_name)

                base_sql += " ON {0}.{1}={2}.{3}".format(left_table_name, join.left_column.name, right_table_name, join.right_column.name)

        # append ';'
        base_sql += ";"
        return base_sql


# %%
import sys
import os
import centernet

# sys.path.append("TrackingSort")
from sort import Sort
from lib import VisTrack, show_video, create_video

import numpy as np
import PIL.Image
import cv2
import matplotlib.pyplot as plt
from tqdm.auto import tqdm
import shutil
# %%
video_file = "data-input/video.mp4"
show_video(video_file)

# %%
# Default: num_classes=80
obj = centernet.ObjectDetection(num_classes=80)
# num_classes=80 and weights_path=None: Pre-trained COCO model will be loaded.
obj.load_weights(weights_path=None)

# %%
# EXAMPLE IMAGE
p_img = PIL.Image.open('data-input/img.jpeg')
img = cv2.cvtColor(np.array(p_img), cv2.COLOR_BGR2RGB)

boxes, classes, scores = obj.predict(img)

# Initialize the vision tracker
vt = VisTrack()
vt.draw_bounding_boxes(p_img, boxes, classes, scores)

# %%
##### PREDICT ON VIDEO ##########
vidcap = cv2.VideoCapture(video_file)
fps = vidcap.get(cv2.CAP_PROP_FPS)
length = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))

folder_out = "Track"
if os.path.exists(folder_out):
    shutil.rmtree(folder_out)
os.makedirs(folder_out)

draw_imgs = []

# Tracking algorithm
sort = Sort(max_age=1, min_hits= 3, iou_threshold = 0.3)

pbar = tqdm(total = length)
i = 0
while True:
    ret, frame = vidcap.read()
    if not ret:
        break

    boxes, classes, scores = obj.predict(frame)
    detections_in_frame = len(boxes)
    if detections_in_frame:
        # centernet will do detection in all COCO classes. "person" is class 0
        idxs = np.where(classes == 0)[0]
        boxes = boxes[idxs]
        scores = scores[idxs]
        classes = classes[idxs]
    else:
        boxes = np.empty((0,5))

    dets = np.hstack((boxes, scores[:, np.newaxis]))

    #Update Sort algorithm to track bounding boxes using Kalman Filter
    res = sort.update(dets)

    boxes_track = res[:,:-1]
    boxes_id = res[:, -1].astype(int)

    p_frame = PIL.Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
    if detections_in_frame:
        p_frame = vt.draw_bounding_boxes(p_frame, boxes_track, boxes_id, scores)
    p_frame.save(os.path.join(folder_out, f"{i:03d}.png"))

    i+=1
    pbar.update(1)
# %%
############# CREATE THE VIDEO ####################
track_video_file = 'tracking'

def crear_video(folder_out, nombre_video, fps=25):
    # Obtener la lista de archivos en la carpeta
    archivos = os.listdir(folder_out)
    archivos.sort()  # Ordenar los archivos en orden alfabético

    # Leer la primera imagen para obtener el tamaño del video
    ruta_primera_imagen = os.path.join(folder_out, archivos[0])
    imagen = cv2.imread(ruta_primera_imagen)
    alto, ancho, _ = imagen.shape

    # Crear el objeto VideoWriter
    ruta_video = nombre_video + ".avi"
    video = cv2.VideoWriter(ruta_video, cv2.VideoWriter_fourcc(*"MJPG"), fps, (ancho, alto))

    # Recorrer todas las imágenes y agregarlas al video
    for archivo in archivos:
        ruta_imagen = os.path.join(folder_out, archivo)
        imagen = cv2.imread(ruta_imagen)
        video.write(imagen)

    # Liberar el objeto VideoWriter y mostrar un mensaje de finalización
    video.release()
    print("Se ha creado el video:", ruta_video)

crear_video(folder_out, track_video_file, fps = fps)


"""
# ------------------------------------------------------------------------------------------------ #
# Project   :-> 8 Tile Puzzle solver
# Authors   :-> Sudharsan
# E-mail    :-> sudharsansci@gmail.com
# ------------------------------------------------------------------------------------------------ #
"""
# ------------------------------------------------------------------------------------------------ #
# Import Section for Importing library
# ------------------------------------------------------------------------------------------------ #
from queue import Queue
import numpy as np

# ------------------------------------------------------------------------------------------------ #
# Class for the solver Algorithm
# ------------------------------------------------------------------------------------------------ #
class Node:
    """
    Custom made data structure for storing Node info using class Node.
    """

    def __init__(self, unique_number: int, state: np.array, index: int = None, parent: int = None,
                 hist: int = None) -> None:
        """
        Args:
            state: np.array
                Current State of the node
            index: int
                Index of the blank tile
            parent: int
                current state's parent id
            hist: int
                Current state's parent state's blank_tile_index
        """
        self.id = unique_number
        self.current_state = state
        self.blank_tile_index = index
        self.parent_id = parent
        self.hist_blank_tile_index = {hist}

class TilePuzzleSolver:
    """
    A 8 tile puzzle solver class with adequate methods

    Attributes:
        -> init_state               :-> Initial State of the given puzzle
        -> init_id                  :-> Unique number based on puzzle's Initial state
        -> goal_state               :-> Goal State of the given puzzle
        -> goal_id                  :-> Unique number based on puzzle's Goal state
        -> neighbours               :-> neighbor location map for each tile location in puzzle
        -> que                      :-> A deque object to implement FIFO logic for Breadth first
                                        search Algorithm
        -> nodes                    :-> A dictionary with node object as value and node's id as key
        -> current_node             :-> generate a node object based on initial state of puzzle
        -> exit_flag                :-> True if goal is found, False by default
        -> solvable                 :-> True if the puzzle is solvable, else False

    static methods:
        -> isSolvable               :-> Checks the solution feasibility of the puzzle
        -> generateUniqueNumber     :-> generates a unique number based on the puzzle's current state

    methods:
        -> __init__()               :-> Initializes the attributes
        -> findNeighbors            :-> finds the possible moves based on puzzle's current state
        -> findChildStates          :-> finds the new states based on the moves possible
        -> backTrack                :-> based on the node id back tracks back to the start node and
                                        returns the solution
        -> bruteForceExplorationBFS :-> A function to implement brute force search using breadth
                                        first search algorithm
        -> autoSolve                :-> Calls all the necessary function upon obj creation and
                                        solves and returns the solution

    """

    def __init__(self, init_state: np.array, goal_state: np.array) -> None:
        """
        Args:
            args.init_state: np.array
                Its a 1D numpy array
            args.goal_state: np.array
                Its a 1D numpy array
        """
        # ---> Initialize variables <--- #
        self.init_state = init_state
        self.goal_state = goal_state
        self.neighbours = {0: {1, 3},
                           1: {0, 2, 4},
                           2: {1, 5},
                           3: {0, 4, 6},
                           4: {1, 3, 5, 7},
                           5: {2, 4, 8},
                           6: {3, 7},
                           7: {4, 6, 8},
                           8: {5, 7},
                           }
        self.nodes = dict()
        self.que = Queue()
        self.exit_flag = False
        self.solvable = self.isSolvable(self.init_state.tolist(), self.goal_state.tolist())
        self.init_id = self.generateUniqueNumber(init_state)
        self.goal_id = self.generateUniqueNumber(goal_state)

        # ---> Find the blank Tile Location <--- #
        blank_tile_index = np.argmin(self.init_state)

        # ---> Create a Node for init_state <--- #
        self.current_node = Node(self.init_id, self.init_state,
                                 int(blank_tile_index),
                                 self.init_id,
                                 int(blank_tile_index))

        # ---> Add Current node to the Nodes dictionary & queue <--- #
        self.nodes[self.current_node.id] = self.current_node
        self.que.put(self.current_node)

    @staticmethod
    def isSolvable(initial_state: list, goal_state: list) -> bool:
        """
        isSolvable(initial_state, goal_state) -> bool

        It takes the initial state and goal state unique number and checks for solution
        feasibility.

        Args:
            initial_state: int
                A 9 digit integer
            goal_state: int
                A 9 digit integer

        Returns: bool
            Returns True if the solution is feasible for the given states else False.

        """

        # ---> Step 01: Copy the arg <--- #
        init, goal = initial_state.copy(), goal_state.copy()

        # initialize Miscellaneous variable
        n = len(init)

        for i in range(0, n):
            if i not in init or i not in goal:
                return False

        # ---> Step 02: Remove zero from the args <--- #
        init.remove(0)
        goal.remove(0)

        # ---> Step 03: Initialize flip counter <---#
        init_flip_count, goal_flip_count = 0, 0

        for i in range(0, n):
            temp_i = init[i:]
            temp_g = goal[i:]
            for j in range(0, len(temp_i)):
                if temp_i[j] < init[i]:
                    init_flip_count += 1
                if temp_g[j] < goal[i]:
                    goal_flip_count += 1
        return init_flip_count % 2 == 0 and goal_flip_count % 2 == 0

    @staticmethod
    def generateUniqueNumber(node: np.array, unique_seq: np.array = np.array(
        [100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1])) -> int:
        """
        Args:
            unique_seq: np.array
                np.array([100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1])
                the multiplier
            node: np.array
                Its a 1D array

        Returns: int
                A 9 digit int

        """
        return node @ unique_seq

    def bruteForceExplorationBFS(self) -> bool:
        """
        This Function explores the possible states of the Tile Puzzle using breadth first
        search Algorithm
        Returns: bool
            Returns True if goal is found or else False.

        """
        while not self.que.empty() and not self.exit_flag:
            self.current_node = self.que.get()
            move = self.findNeighbors(self.current_node)
            self.findChildStates(move, self.current_node)

        return self.exit_flag

    def findNeighbors(self, node: Node) -> set:
        """
        A Function to get the possible neighbor index based on blank tile location
        Returns: None

        """
        return self.neighbours[node.blank_tile_index].difference(node.hist_blank_tile_index)

    def findChildStates(self, moves: set, node: Node) -> None:
        """
        Based on the moves available for current state, it generates new possible states.
        Args:
            node: Node
                its the current node object
            moves: set
                set of possible moves

        Returns:

        """
        parent_state = np.copy(node.current_state)
        parent_id = node.id
        index = node.blank_tile_index
        for move in moves:
            new_state = np.copy(parent_state)
            new_state[move], new_state[index] = parent_state[index], parent_state[move]
            unique_id = self.generateUniqueNumber(new_state)
            if unique_id not in self.nodes:
                child_node = Node(unique_id, new_state, move, parent_id, index)
                self.nodes[unique_id] = child_node
                if unique_id == self.goal_id:
                    self.exit_flag = True
                self.que.put(child_node)

    def backTrack(self, uniq_id: int = None) -> list:
        """

        Args:
            uniq_id: int
                Unique id of a node to start the back tracking

        Returns: list
            Returns a list containing the solution to the puzzle

        """
        if uniq_id is None:
            uniq_id = self.goal_id
        current_node = self.nodes[uniq_id]
        current_id = current_node.id
        path = [current_node.current_state]

        while current_id != current_node.parent_id:
            current_node = self.nodes[current_node.parent_id]
            path.append(current_node.current_state)
            current_id = current_node.id
        return path[::-1]

    def autoSolve(self) -> list:
        """
        In one function checks for solution feasibility, runs the
        bruteForceExplorationBFS() and backTrack() and returns the solution
        Returns: list
            Solution to the given puzzle
        """
        if self.solvable:
            self.bruteForceExplorationBFS()
            return self.backTrack(self.goal_id)
        else:
            return ["Not Solvable!"]


import os
import json
import urllib3

class SmsClient:

    def __init__(self):
        self.host = os.environ.get('SMS_API')
        self.sender_id = os.environ.get('SMS_SENDER_ID')
        self.api_key = os.environ.get('SMS_API_KEY')
        self.api_token = os.environ.get('SMS_API_TOKEN')
        self.http_client = urllib3.PoolManager()

    def send_sms(self, to, message):
        url = "{}?sendsms&apikey={}&apitoken={}&type=sms&from={}&to={}&text={}&scheduledate=2020-09-21%2009:13:05&route=0"\
            .format(self.host, self.api_key, self.api_token, self.sender_id, to, message)
        response = self.http_client.request('GET', url)
        if response.status == 200:
            return True, response.data
        else:
            print('Error in send sms {}'.format(response.data))
            return False, response.data


import _init_paths
from fast_rcnn.config import cfg
from fast_rcnn.test import im_detect
from fast_rcnn.nms_wrapper import nms
from utils.timer import Timer
import matplotlib.pyplot as plt
import numpy as np
import scipy.io as sio
import caffe, os, sys, cv2
import argparse

import rospy
from ros_faster_rcnn.msg import *
from sensor_msgs.msg import Image
from cv_bridge import CvBridge, CvBridgeError

RUNNING = False
IMAGE = Image()

def detect(image):
	"""Detect object classes in an image using pre-computed object proposals."""
	global NET

	# Detect all object classes and regress object bounds
	rospy.loginfo("Starting detection")
	timer = Timer()
	timer.tic()
	scores, boxes = im_detect(NET, image)
	timer.toc()
	rospy.loginfo('Detection took %f seconds for %d object proposals', timer.total_time, boxes.shape[0])
	return (scores, boxes)

def imageCallback(im):
	global IMAGE
	global RUNNING
	rospy.loginfo('Image received')
	if (RUNNING):
		rospy.logwarn('Detection already running, message omitted')
	else:
		RUNNING = True
		IMAGE = im

def parse_args():
	"""Parse input arguments."""
	# Filter roslaunch arguments
	sys.argv = filter(lambda arg: not arg.startswith('__'), sys.argv)
	sys.argc = len(sys.argv)

	# Parse the other arguments
	parser = argparse.ArgumentParser(description='Faster R-CNN demo')
	parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]',
						default=0, type=int)
	parser.add_argument('--cpu', dest='cpu_mode',
						help='Use CPU mode (overrides --gpu)',
						action='store_true')
	parser.add_argument('--tresh', dest='treshold', help='The treshold for the detection',
						default=0.8, type=float)
	parser.add_argument('--prototxt', dest='prototxt', help='The proto file',
						default='../libraries/py-faster-rcnn/models/pascal_voc/VGG16/faster_rcnn_alt_opt/faster_rcnn_test.pt')
	parser.add_argument('--model', dest='model', help='The model file',
						default='../libraries/py-faster-rcnn/data/faster_rcnn_models/VGG16_faster_rcnn_final.caffemodel')
	parser.add_argument('--classes', dest='classes', help='The file containing the classes',
						default='classes.txt')

	args = parser.parse_args()

	return args

def parseClasses(classFile):
	with open(classFile) as f:
		content = f.readlines()
	return ['__background__'] + map(lambda x: x[:-1], content)

def generateDetections (scores, boxes, classes, threshold):
	# Visualize detections for each class
	NMS_THRESH = 0.3
	res = []

	for cls_ind, cls in enumerate(classes[1:]):
		cls_ind += 1 # because we skipped background
		cls_boxes = boxes[:, 4*cls_ind:4*(cls_ind + 1)]
		cls_scores = scores[:, cls_ind]
		dets = np.hstack((cls_boxes, cls_scores[:, np.newaxis])).astype(np.float32)
		keep = nms(dets, NMS_THRESH)
		dets = dets[keep, :]

		inds = np.where(dets[:, -1] >= threshold)[0]

		for i in inds:
			bbox = dets[i, :4]
			score = dets[i, -1]

			msg = Detection()
			msg.x = bbox[0]
			msg.y = bbox[1]
			msg.width =  bbox[2] - bbox[0]
			msg.height = bbox[3] - bbox[1]
			msg.object_class = classes[cls_ind]
			msg.p = score
			res.append(msg)
	return res

def getResultImage (detections, image):
	font = cv2.FONT_HERSHEY_SIMPLEX
	textSize = cv2.getTextSize("test", font, 1, 2)
	delta = (textSize[1] * .3, textSize[1] * 2.4)

	for det in detections:
		cv2.rectangle(image, (det.x, det.y), (det.x + det.width, det.y + det.height), (0, 0, 255), 3)
		text = "{}: p={:.2f}".format(det.object_class, det.p)
		cv2.putText(image, text, (int(det.x + delta[0]), int(det.y + delta[1])), font, 1, (0, 0, 255), 2, cv2.LINE_AA)
	return image

if __name__ == '__main__':
	cfg.TEST.HAS_RPN = True  # Use RPN for proposals

	args = parse_args()

	pub_single = rospy.Publisher('rcnn/res/single', Detection, queue_size = 10)
	pub_array = rospy.Publisher('rcnn/res/array', DetectionArray, queue_size = 2)
	pub_full = rospy.Publisher('rcnn/res/full', DetectionFull, queue_size = 2)

	rospy.init_node('simpleDetect')
	sub_image = rospy.Subscriber("rcnn/image_raw", Image, imageCallback)

	prototxt = os.path.join(os.path.dirname(__file__), args.prototxt)
	caffemodel = os.path.join(os.path.dirname(__file__), args.model)
	classes = parseClasses(os.path.join(os.path.dirname(__file__), args.classes))

	if not os.path.isfile(caffemodel):
		rospy.logerr('%s not found.\nDid you run ./data/script/fetch_faster_rcnn_models.sh?', caffemodel)

	if args.cpu_mode:
		caffe.set_mode_cpu()
	else:
		caffe.set_mode_gpu()
		caffe.set_device(args.gpu_id)
		cfg.GPU_ID = args.gpu_id
	NET = caffe.Net(prototxt, caffemodel, caffe.TEST)

	rospy.loginfo('Loaded network %s', caffemodel)
	rospy.loginfo('Running detection with these classes: %s', str(classes))
	rospy.loginfo('Warmup started')
	im = 128 * np.ones((300, 500, 3), dtype=np.uint8)
	timer = Timer()
	timer.tic()
	for i in xrange(2):
		_, _= im_detect(NET, im)
	timer.toc()
	rospy.loginfo('Warmup done in %f seconds. Starting node', timer.total_time)

	rate = rospy.Rate(10)
	bridge = CvBridge()
	while not rospy.is_shutdown():
		if (RUNNING):
			rate.sleep()
			cv_image = bridge.imgmsg_to_cv2(IMAGE)
			(scores, boxes) = detect(cv_image)
			detections = generateDetections(scores, boxes, classes, args.treshold)
			if (pub_single.get_num_connections() > 0):
				for msg in detections:
					pub_single.publish(msg)

			if (pub_array.get_num_connections() > 0 or pub_full.get_num_connections() > 0):
				array = DetectionArray()
				array.size = len(detections)
				array.data = detections

				if (pub_full.get_num_connections() > 0):
					msg = DetectionFull()
					msg.detections = array
					msg.image =  bridge.cv2_to_imgmsg(getResultImage(detections, bridge.imgmsg_to_cv2(IMAGE)))
					pub_full.publish(msg)
				else :
					pub_array.publish(array)

			RUNNING = False
		else:
			rate.sleep()


# recap of classes
# credit to https://www.youtube.com/watch?v=2AK7j8pIh-0

class AngryPerson:                                              #CamelCase
    def __init__(self, name, lifestage, max_health=100):        #=None makes it not required anymore, but optional
        self.name = name
        self.lifestage = lifestage
        self.health = max_health
        self.max_health = max_health

    def __repr__(self):                                     #__init__ and __repry__ are instance method: on each Pokemon instnace
        return f'Angry person: {self.name}, {self.lifestage}, {self.health} health score'

    #making an angry person stronger
    def exercise(self):
        if self.health < self.max_health:
            self.health = self.health +5
            print(f'{self.name} has a health score of {self.health}!')
        else:
            print(f'{self.name} is already fit!')

    #making it possible for angry people to battle against each other
    def battle(self, other):
        print(f'Battle: {self.name} is starting to fight against {other.name}!')
        result = self.typewheel(self.lifestage, other.lifestage)
        #scenario: losing
        if result == 'lost':
            self.health -= 10
            other.health +=10
            print(f'{self.name} fought against {other.name} and {result}! New health score: {self.health}')
        #scenario: tie
        if result == 'got a tie':
            self.health == self.health
            print(f'{self.name} fought against {other.name} and {result}! Health score is still: {self.health}')
        #scenario: wining
        if result == 'won':
            self.health += 10
            other.health -=10
            print(f'{self.name} fought against {other.name} and {result}! New health score: {self.health}')

    #deciding on who wins a
    @staticmethod
    def typewheel(type1, type2):
        result = {-1: 'lost', 0: 'got a tie', 1: 'won'}
        # mapping between the types and the result conditions
        game_map = {'youngster': 0, 'boring adult': 1, 'cool adult': 2}
        # implement win-lose matrix
        win_lose_matrix = [
            [0, 1, -1],  #youngster
            [-1, 0, -1], #boring adult
            [1, 1, 0]    #cool adult
        ]
        # declare a winner
        win_lose_result = win_lose_matrix[game_map[type1]][game_map[type2]]
        return result[win_lose_result]

if __name__ == '__main__':
    # creating an angry person 1
    Margit = AngryPerson(name='Margit', lifestage='cool adult')

    # checking if the if / else statement worked
    Margit.exercise()
    Margit.health
    print(Margit)

    # creating an angry person 2
    Justin = AngryPerson(name='Justin', lifestage='youngster')
    print(Justin)

    # battle between angry person 1 and 2
    Margit.battle(Justin)

    # checking the score of self.health and other.health after a battle
    print(Margit.health)
    print(Justin.health)

    #checking if .exercises works to improve Justin´s health after loosing a battle
    print(Justin.exercise())


import glob
import hashlib
import tempfile
import os
import numpy as np
import random
import subprocess
from rich import print
import mlflow
import torch

class MLFCore:
    __instance = None

    def __new__(cls):
        if cls.__instance is None:
            print('Creating the object')
            cls._instance = super(MLFCore, cls).__new__(cls)
        return cls._instance

    @staticmethod
    def set_general_random_seeds(seed):
        os.environ['PYTHONHASHSEED'] = str(seed)  # Python general
        np.random.seed(seed)  # Numpy random
        random.seed(seed)  # Python random

    @staticmethod
    def log_system_intelligence(reports_output_dir: str):
        # Scoped import to prevent issues like RuntimeError: Numba cannot operate on non-primary CUDA context
        from system_intelligence.query import query_and_export

        print(f'[bold blue]Writing reports locally to {reports_output_dir}\n')
        print('[bold blue]Running system-intelligence')
        query_and_export(query_scope={'all'},
                         verbose=False,
                         export_format='json',
                         generate_html_table=True,
                         output=f'{reports_output_dir}/system_intelligence.json')
        print('[bold blue]Uploading system-intelligence report as a run artifact...')
        mlflow.log_artifacts(reports_output_dir, artifact_path='reports')

    @staticmethod
    def log_conda_environment(reports_output_dir: str):
        print('[bold blue]Exporting conda environment...')
        conda_env_filehandler = open(f'{reports_output_dir}/liver_ct_segmentation_conda_environment.yml', 'w')
        subprocess.call(['conda', 'env', 'export', '--name', 'liver_ct_segmentation'], stdout=conda_env_filehandler)
        print('[bold blue]Uploading conda environment report as a run artifact...')
        mlflow.log_artifact(f'{reports_output_dir}/liver_ct_segmentation_conda_environment.yml', artifact_path='reports')

    @staticmethod
    def set_pytorch_random_seeds(seed, num_gpus):
        torch.manual_seed(seed)
        torch.set_deterministic(True)
        if num_gpus > 0:
            torch.cuda.manual_seed(seed)
            torch.cuda.manual_seed_all(seed)  # For multiGPU

    @classmethod
    def log_sys_intel_conda_env(cls):
        reports_output_dir = tempfile.mkdtemp()
        cls.log_system_intelligence(reports_output_dir)
        cls.log_conda_environment(reports_output_dir)

    @staticmethod
    def md5(fname: str):
        """Generate md5 sum for input file"""
        hash_md5 = hashlib.md5()
        with open(fname, 'rb') as f:
            for chunk in iter(lambda: f.read(4096), b''):
                hash_md5.update(chunk)

        md5sum = hash_md5.hexdigest()
        return md5sum

    @classmethod
    def get_md5_sums(cls, dir, md5_sums=None):
        """
        Recursively go through directories and subdirectories
        and generate tuples of (<file_path>, <md5sum>)
        returns: list of tuples
        """
        if not md5_sums:
            md5_sums = []
        elements = glob.glob(dir + "/*")
        for elem in elements:
            # if file, get md5 sum
            if os.path.isfile(elem):
                elem_md5 = cls.md5(elem)
                md5_sums.append((elem, elem_md5))
            # if directory, apply recursion
            if os.path.isdir(elem):
                md5_sums = cls.get_md5_sums(elem, md5_sums)
            else:
                continue

    @classmethod
    def log_input_data(cls, input_data: str):
        print('[bold blue]Hashing input data...')
        input_hash = cls.get_md5_sums(input_data)
        mlflow.log_param("input_hash", input_data + "-" + input_hash)


from django.db import models
from django.contrib.auth import get_user_model
from cloudinary.models import CloudinaryField
from tinymce.models import HTMLField
User = get_user_model()

# Create your models here.
class AllPlantTable(models.Model):
    """All Plants."""

    TOXICITY_LEVEL = [
        ("LOW", "LOW"),
        ("MEDIUM", "MEDIUM"),
        ("HIGH", "HIGH"),
    ]
    TYPE_BY_SIZE = [
        ("Herbs", "Herbs"),
        ("Shrubs", "Shrubs"),
        ("Trees", "Trees"),
        ("Herbaceous", "Herbaceous"),
        ("Creepers", "Creepers"),
        ("Climbers", "Climbers"),
    ]
    LIFE_CYCLE = [
        ("Perennial", "Perennial"),
        ("Annual", "Annual"),
        ("Bi-Annual", "Bi-Annual"),
    ]
    name = models.CharField(max_length=500)
    botanical_name = models.CharField(max_length=500, blank=True, null=True)
    type_by_size = models.CharField(max_length=500, choices=TYPE_BY_SIZE, blank=True, null=True)
    type_by_life_cycle = models.CharField(max_length=500, choices=LIFE_CYCLE, blank=True, null=True)
    plant_care = models.TextField(blank=True, null=True)
    toxicity = models.TextField(blank=True, null=True)
    toxicity_level = models.CharField(max_length=500,choices=TOXICITY_LEVEL, blank=True, null=True)
    plan_removes_toxins = models.TextField(blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    image = CloudinaryField('image', default="image/upload/v1677969748/sample.jpg")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        """Return name."""
        return self.name

class Blog(models.Model):
    """All Plants."""

    blog_title = models.CharField(max_length=500, blank=True, null=True)
    blog_user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
    blog_body = HTMLField()
    created_at = models.DateTimeField(auto_now_add=True)
    count = models.IntegerField(default=0)
    image = CloudinaryField('image', default="image/upload/v1677969748/sample.jpg")
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['created_at']

    def __str__(self):
        return f'Blog {self.blog_title} by {self.blog_user}'

class Forum(models.Model):

    forum_title = models.CharField(max_length=500, blank=True, null=True)
    forum_user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
    forum_body = models.TextField()
    active = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['created_at']

class Comment(models.Model):
    blog = models.ForeignKey(Blog,on_delete=models.CASCADE,related_name='blog_comments', blank=True, null=True)
    forum = models.ForeignKey(Forum,on_delete=models.CASCADE,related_name='forum_comments', blank=True, null=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
    body = models.TextField()
    active = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['created_at']

    def __str__(self):
        return 'Comment {} by {}'.format(self.body, self.user.username)


import numpy as np

from constrainedzoom.fft_wrapper import in_fourier_space, in_real_space, unitary_inverse_fft, unitary_fft, complex_dot
from constrainedzoom.methods import FilteredZoomConstrained

class FilteredZoomConstrainedOriginal(FilteredZoomConstrained):
    """The original implementation of fast-filter zooms, which corresponds more closely to the genetIC code but
    shifts various factors around and therefore looks quite different from the FilteredZoomConstrained implementation
    above."""
    description = "Fast Filter (original implementation)"
    constrain_noise_directly = False

    @in_fourier_space
    def _separate_fields(self, deltaP, deltaW):
        k_high, k_low = self._get_ks()
        deltaW *= np.sqrt(1. - self.filter_low(k_high) ** 2)  # keep original power spectrum
        self.delta_low_supplement = deltaP * (1. - self.filter_low(
            k_low))  # store the filtered-out components of the low-res field
        deltaP *= self.filter_low(k_low)
        return deltaP, deltaW

    @in_real_space
    def _recombine_fields(self, deltaP, deltaW):
        deltaW += self.upsample_cubic(deltaP)
        deltaP += self.delta_low_supplement.in_real_space()
        return deltaP, deltaW

    @in_fourier_space
    def high_k_vector_from_low_k_vector(self, low_harmonics):
        pixelized_highres = self.upsample_cubic(unitary_inverse_fft(low_harmonics))
        return unitary_fft(pixelized_highres)

    @in_fourier_space
    def low_k_vector_from_high_k_vector(self, high_harmonics):
        pixelized_lowres = self.downsample(unitary_inverse_fft(high_harmonics))
        return unitary_fft(pixelized_lowres)

    @in_fourier_space
    def covector_vector_inner_product(self, low1, high1, low2, high2, more_accurate=True):

        # The low.low part picks up a pixel_size_ratio factor. We can see this as follows.
        # Take the ideal case where low1 = f_low v1, high1 = f_high v1, f_low+f_high=1 and f_low f_high = 0.
        # Without varying pixel sizes, v1 = (low1+high1), and v1.v2 = (low1.low1)+(high1.high1), exactly.
        # Now, let's downsample the low1 pixel scale. The convention we've adopted is that the big
        # low1 pixels take on the mean value of the original, finer low1 pixels. So, the new dot product
        # in real space has been multiplied by 1/pixel_size_ratio. (Because FFTs are unitary, this
        # applies also to the harmonic space dot product). We need to multiply by pixel_size_ratio to
        # cancel out this change.
        product = complex_dot(low1,low2)* self.pixel_size_ratio+complex_dot(high1,high2)
        if more_accurate:
            # add in the low1^dagger C high2 + high1 C low2^dagger terms
            low1_as_high = self.high_k_vector_from_low_k_vector(low1)
            low2_as_high = self.high_k_vector_from_low_k_vector(low2)
            product+=(complex_dot(low1_as_high,high2) + complex_dot(high1, low2_as_high))
        return product

    @in_fourier_space
    def covector_to_vector(self, low, high):
        return self.C_low * low, self.C_high * high

    @in_fourier_space
    def zoom_covec_from_uniform_covec_in_window(self, hr_covec, potential):

        high = hr_covec * (1. - self.filter_low(self.k_high) )
        low = self.downsample(hr_covec - high)

        high.in_fourier_space()
        low.in_fourier_space()

        if potential:
            high *= (self.C_high_potential / self.C_high) ** 0.5
            low *= (self.C_low_potential / self.C_low) ** 0.5

        return low, high

    @in_fourier_space
    def _apply_constraints(self, noise_or_delta_low_k, noise_or_delta_high_k):
        """This is a slightly ugly re-intepretation of _apply_constraints from the base class, but is required
        to handle the delta_low_supplement part"""
        for (al_low_k, al_high_k), d in zip(self.constraints, self.constraints_val):
            scale = d - self.covector_vector_inner_product(al_low_k, al_high_k, noise_or_delta_low_k,
                                                           noise_or_delta_high_k)
            vec_low_k, vec_high_k = self.covector_to_vector(al_low_k, al_high_k)
            noise_or_delta_low_k += vec_low_k * scale
            noise_or_delta_high_k += vec_high_k * scale

            self.delta_low_supplement += self.low_k_vector_from_high_k_vector(vec_high_k * scale)

        return noise_or_delta_low_k, noise_or_delta_high_k


## Regularization

## Import packages
import numpy as np
import matplotlib.pyplot as plt
from reg_utils import sigmoid, relu, plot_decision_boundary, initialize_parameters, load_2D_dataset, predict_dec
from reg_utils import compute_cost, predict, forward_propagation, backward_propagation, update_parameters
import sklearn
import sklearn.datasets
import scipy.io
from testCases import *

plt.figure(1)
# %matplotlib inline
plt.rcParams['figure.figsize'] = (7.0, 4.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

train_X, train_Y, test_X, test_Y = load_2D_dataset()
print(train_X.shape, test_X.shape)

# ## Visualize the data
# plt.scatter(X[0, :], X[1, :], c=Y.ravel().tolist(), s=40, cmap=plt.cm.Sepctral);

plt.figure(2)
## Non-regularized Model
def model(X, Y, learning_rate = 0.3, num_iteration = 30000, print_cost = True, lambd = 0, keep_prob = 1):

    grads = {}
    costs = []
    m = X.shape[1]
    layers_dims = [X.shape[0], 20, 3, 1]

    ## Initialize parameters dictionary
    parameters = initialize_parameters(layers_dims)

    ## Gradient descent LOOP
    for i in range(0, num_iteration):

        ## Forward prop (linear -> relu) -> (linear -> relu) -> (linear -> sigmoid)
        if keep_prob == 1:      # Non-dropout
            a3, cache = forward_propagation(X, parameters)
        elif keep_prob < 1:     # Dropout
            a3, cache = forward_propagation_with_dropout(X, parameters, keep_prob)

        ## Cost func
        if lambd == 0:          # Without L2-Regularization
            cost = compute_cost(a3, Y)
        else:                   # L2 Regularization
            cost = compute_cost_with_regularization(a3, Y, parameters, lambd)

        ## Backward prop
        assert(lambd==0 or keep_prob)

        if lambd==0 or keep_prob==1 :
            grads = backward_propagation(X, Y, cache)
        elif lambd != 0:
            grads = backward_propagation_with_regularization(X, Y, cache, lambd)
        elif keep_prob < 1:
            grads = backward_propagation_with_dropout(X, Y, cache, keep_prob)

        ## Update
        parameters = update_parameters(parameters, grads, learning_rate)

        ## Print Loss
        if print_cost and i % 10000 == 0:
            print("Cost after iteration {}: {}".format(i, cost))
        if print_cost and i % 1000 == 0:
            costs.append(cost)

    plt.plot(costs)
    plt.ylabel('cost')
    plt.xlabel('iterations (x1,000)')
    plt.title("Learning rate =" + str(learning_rate))
    plt.show()

    return parameters

parameters = model(train_X, train_Y)
print("On the training set:")
prediction_train = predict(train_X, train_Y, parameters)
print("On the test set:")
prediction_test = predict(test_X, test_Y, parameters)

def _plot_decision_boundary(model, X, y):

    x_min, x_max = X[0, :].min() -1, X[0, :].max() + 1
    y_min, y_max = X[1, :].min() -1, X[1, :].max() + 1
    h = 0.01

    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))

    Z = model(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)

    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    plt.ylabel('x2')
    plt.xlabel('x1')
    plt.scatter(X[0, :], X[1, :], c=y.ravel().tolist(), cmap=plt.cm.Spectral)
    plt.show()

plt.figure(3)
plt.title("Model without regularization")
axes = plt.gca()
axes.set_xlim([-0.75, 0.40])
axes.set_ylim([-0.75, 0.65])
_plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)
plt.show()

def compute_cost_with_regularization(A3, Y, parameters, lambd):
    m = Y.shape[1]
    W1 = parameters["W1"]
    W2 = parameters["W2"]
    W3 = parameters["W3"]

    cross_entropy_cost = compute_cost(A3, Y)

    L2_regularization_cost = (lambd/2) * (np.sum(np.square(W1))+np.sum(np.square(W2))+np.sum(np.square(W3))) / m

    cost = cross_entropy_cost + L2_regularization_cost

    return cost

A3, Y_assess, parameters = compute_cost_with_regularization_test_case()
print("cost = " + str(compute_cost_with_regularization(A3, Y_assess, parameters, lambd = 0.1)))

def backward_propagation_with_regularization(X, Y, cache, lambd):
    m = X.shape[1]
    (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache

    dZ3 = A3 - Y

    dW3 = (np.dot(dZ3, A2.T) + lambd * W3) / m
    db3 = 1. / m * np.sum(dZ3, axis=1, keepdims=True)

    dA2 = np.dot(W3.T, dZ3)
    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    dW2 = 1./m * np.dot(dZ2, A1.T) + lambd*W2/m
    db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)

    dA1 = np.dot(W2.T, dZ2)
    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
    dW1 = 1./m * np.dot(dZ1, X.T) + lambd*W1/m
    db1 = 1. / m * np.sum(dZ1, axis=1, keepdims=True)

    gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3, "dA2": dA2,
                 "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1,
                 "dZ1": dZ1, "dW1": dW1, "db1": db1}

    return gradients

X_assess, Y_assess, cache = backward_propagation_with_regularization_test_case()

grads = backward_propagation_with_regularization(X_assess, Y_assess, cache, lambd = 0.7)
print("dW1 = " + str(grads["dW1"]))
print("dW2 = " + str(grads["dW2"]))
print("dW3 = " + str(grads["dW3"]))

## Graph
parameters = model(train_X, train_Y, lambd = 0.7)
print("On the train set:")
predictions_train = predict(train_X, train_Y, parameters)
print("On the test set:")
predictions_test = predict(test_X, test_Y, parameters)

plt.figure(4)
plt.title("Model with L2-regularization")
axes = plt.gca()
axes.set_xlim([-0.75,0.40])
axes.set_ylim([-0.75,0.65])
_plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)

def forward_propagation_with_dropout(X, parameters, keep_prob = 0.5):

    np.random.seed(1)

    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]
    W3 = parameters["W3"]
    b3 = parameters["b3"]

    Z1 = np.dot(W1, X) + b1
    A1 = relu(Z1)

    D1 = np.random.rand(A1.shape[0], A1.shape[1])
    D1 = D1 < keep_prob
    A1 = np.multiply(D1, A1)
    A1 = A1/keep_prob

    Z2 = np.dot(W2, A1) + b2
    A2 = relu(Z2)

    D2 = np.random.rand(A2.shape[0], A2.shape[1])
    D2 = D2 < keep_prob
    A2 = np.multiply(D2, A2)
    A2 = A2 / keep_prob

    Z3 = np.dot(W3, A2) + b3
    A3 = sigmoid(Z3)

    cache = (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3)

    return A3, cache

X_assess, parameters = forward_propagation_with_dropout_test_case()

A3, cache = forward_propagation_with_dropout(X_assess, parameters, keep_prob=0.7)
print("A3 = " + str(A3))

def backward_propagation_with_dropout(X, Y, cache, keep_prob):
    m = X.shape[1]
    (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3) = cache

    dZ3 = A3 - Y
    dW3 = 1. / m * np.dot(dZ3, A2.T)
    db3 = 1. / m * np.sum(dZ3, axis=1, keepdims=True)
    dA2 = np.dot(W3.T, dZ3)

    dA2 = np.multiply(dA2, D2)
    dA2 = dA2 / keep_prob

    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    dW2 = 1. / m * np.dot(dZ2, A1.T)
    db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)

    dA1 = np.dot(W2.T, dZ2)

    dA1 = np.multiply(dA1, D1)
    dA1 = dA1 / keep_prob

    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
    dW1 = 1. / m * np.dot(dZ1, X.T)
    db1 = 1. / m * np.sum(dZ1, axis=1, keepdims=True)

    gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3, "dA2": dA2,
                 "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1,
                 "dZ1": dZ1, "dW1": dW1, "db1": db1}

    return gradients

X_assess, Y_assess, cache = backward_propagation_with_dropout_test_case()

gradients = backward_propagation_with_dropout(X_assess, Y_assess, cache, keep_prob = 0.8)

print ("dA1 = " + str(gradients["dA1"]))
print ("dA2 = " + str(gradients["dA2"]))

parameters = model(train_X, train_Y, keep_prob = 0.86, learning_rate = 0.3)

print ("On the train set:")
predictions_train = predict(train_X, train_Y, parameters)
print ("On the test set:")
predictions_test = predict(test_X, test_Y, parameters)


# Setting Scale-Based Visibility for a Layer

# https://github.com/GeospatialPython/Learn/raw/master/GIS_CensusTract.zip

lyr = QgsVectorLayer("/qgis_data/census/GIS_CensusTract_poly.shp", "Census", "ogr")

lyr.toggleScaleBasedVisibility(True)

lyr.setMinimumScale(22945.0)
lyr.setMaximumScale(1000000.0)

QgsMapLayerRegistry.instance().addMapLayer(lyr)


import discord
import os
import http.client
import json

connection = http.client.HTTPConnection('api.football-data.org')
headers = { 'X-Auth-Token': os.getenv('FUT_APITOKEN') }
connection.request('GET', '/v2/teams/586', None, headers )
response = connection.getresponse().read().decode()

arquivo = open('file1.txt', 'w')
arquivo.write(response)
arquivo.close()

print (response)

client = discord.Client()

@client.event
async def on_ready():
  print('Logado como: {0.user}' .format(client))

@client.event
async def on_message(message):
  if message.author == client.user:
    return

  if message.content.startswith('-coe'):
    await message.channel.send('Salve menozada botzao aqui B)')
  if message.content.startswith('-ajuda'):
    await message.channel.send('Tamo trabalhando ainda menor, novidades em breve!')

#Inicia o bot e pega o token através de uma variável do ambiente
client.run(os.getenv('TOKEN'))


import pygame
from ui import game_screen, show
from services import deal_cards
from services import check
from services import check_winner
from services import win_count
from ui import show
from ui import game_screen
class Blackjack():
    """Luokka missä peliä pyörittävä silmukka sekä pygame ruudun ja nappien alustus
    """
    def __init__(self):
        """Luokan konstruktori
        """
        self.player = []
        self.dealer = []
        self.player_end = False
        self.dealer_end = False
        self.one_count = False
        self.screen = pygame.display.set_mode([1000, 600])
        self.game_screen = game_screen.Game_screen(self.screen)
        self.py_screen = self.game_screen.show_screen
        self.hit_button = pygame.Rect(500, 500, 70, 70)
        self.stop_button = pygame.Rect(300, 500, 70, 70)
        self.re_button = pygame.Rect(50, 500, 100, 70)
        self.white = (255, 255, 255)
        self.font = pygame.font.SysFont('Corbel', 35)
        pygame.display.set_caption("Blackjack")
        self.score = win_count.Score(self.screen)
        self.running()
    def running(self):
        """Peli silmukka
        """
        self.game_screen.show_screen()
        self.start = deal_cards.Deal()
        self.cards =self.start.start_position()
        show.Draw(self.screen).shows(self.cards[0],self.cards[1 ],self.score)
        while True:
            self.events()

    def events(self):
        """tarkkailee käyttäjän hiiren toimintaa
        """

        for action in pygame.event.get():
            if action.type == pygame.QUIT:
                exit()
            if action.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = action.pos
                if self.hit_button.collidepoint(mouse_pos):
                    if check.Checks(self.cards[0],self.cards[1]).count_player() < 22 and self.player_end is False:
                        self.start.player_hits()
                        hit=self.start.get_hands()
                        show.Draw(self.screen).shows(hit[0],hit[1],self.score)
                if self.stop_button.collidepoint(mouse_pos):
                    self.player_end = True
                    hit = self.start.get_hands()
                    dealer_count = check.Checks(hit[0], hit[1]).count_dealer()
                    if len(self.dealer) < 2 and  self.dealer_end is False:
                        self.dealer_end = True
                        self.start.dealer_hits()
                        hit=self.start.get_hands()
                        show.Draw(self.screen).shows(hit[0],hit[1 ],self.score)
                    if  dealer_count < 16 and dealer_count < 18 :
                        while check.Checks(hit[0], hit[1]).count_dealer() < 16 and check.Checks(hit[0], hit[1]).count_dealer() < 18:
                            self.start.dealer_hits()
                            hit=self.start.get_hands()
                            show.Draw(self.screen).shows(hit[0],hit[1 ],self.score)
                    hit = self.start.get_hands()
                    final_player_hand = check.Checks(hit[0], hit[1]).count_player()
                    final_dealer_hand = check.Checks(hit[0], hit[1]).count_dealer()
                    winner = check_winner.Winner(final_player_hand,final_dealer_hand).get_winner()
                    winner_text = self.font.render(winner,True,self.white)
                    self.screen.blit(winner_text, (700, 250))
                    if self.one_count is False:
                        self.score.win_counter(winner)
                        self.one_count = True
                    self.score.win_count_draw()
                    pygame.display.flip()

                if self.re_button.collidepoint(mouse_pos) and self.dealer_end is True:
                    self.player_end = False
                    self.dealer_end = False
                    self.one_count = False
                    self.game_screen.show_screen()
                    self.player = []
                    self.dealer = []
                    self.start = deal_cards.Deal()
                    self.cards =self.start.start_position()
                    show.Draw(self.screen).shows(self.cards[0],self.cards[1],self.score)


"""Tests for GPI parsing."""

import os
import unittest

from kg_covid_19.transform_utils.sars_cov_2_gene_annot.sars_cov_2_gene_annot import \
    _gpi12iterator

class TestGpiFile(unittest.TestCase):
    """Test parsing GPI files."""

    def setUp(self) -> None:
        """Set up the tests."""
        self.gpi_file = "curated/ORFs/uniprot_sars-cov-2.gpi"
        self.expected_sars_cov2_genes = 32

    def test_gpi_file_exists(self):
        """Test if GPI file exists."""
        self.assertTrue(os.path.exists(self.gpi_file))

    def test_gpi_parsing(self):
        """Test if GPI input file can be parsed."""
        count = 0
        with open(self.gpi_file, "r") as gpi_fh:
            for _ in _gpi12iterator(gpi_fh):
                count += 1
        self.assertEqual(self.expected_sars_cov2_genes, count)


import numpy as np
import scipy.interpolate, scipy.linalg
import matplotlib.pyplot as plt
from numpy import array, cos, sin, pi, exp, log, log10, abs, max
from scipy.integrate import quad, odeint
from scipy.linalg import norm, solve

plt.ion()
plt.show()

## Question 1

f = lambda t: exp(t)/log(t)
df = lambda t: exp(t)/log(t)**2 * (log(t) - 1/t)
a, b = 2, 4
vm = []
vI = []

for k in range(1, 16):
    m = 2**k
    vt = np.linspace(a, b, m+1)
    h = 2**(1-k)
    I = 0
    for i in range(m):
        I += 2*f(vt[i]) + 4*f(vt[i+1]) - h*df(vt[i+1])
    vm.append(m)
    vI.append(h/6 * I)

I_exact = quad(f, a, b)[0]

vm = array(vm)
verr = abs(array(vI) - I_exact)

[p1, p0] = np.polyfit(log10(vm), log10(verr), 1)

plt.figure(0)
plt.clf()
plt.xlabel("m")
plt.ylabel("|I - I_m|")
plt.loglog(vm, 10**(p1*log10(vm) + p0), label="log |I - I_m| ~ a log m")
plt.loglog(vm, verr, linestyle='None', marker='o', label="Données")
plt.legend()
plt.title("Approximation de l'intégrale, a = {:.2f}".format(p1))

## Question 2

def enright(f, df, y0, vt):
    # y = np.asarray(y0).ravel()
    y = y0
    d = len(y)
    h = vt[1] - vt[0]
    vy = [y]
    for i in range(len(vt)-1):
        z = y
        err = 1
        while norm(err) > 1e-8:
            M = 2/3*h*df(z) - 1/6*h**2* df(y + h*f(y)) @ df(z) - np.eye(d)
            N = y + 1/3*h*f(y) + 2/3*h*f(z) - 1/6*h**2*df(y + h*f(y)) @ f(z) - z
            err = solve(M, N)
            z = z - err
        y = z
        vy.append(y)
    return array(vy)

## Question 3

f = lambda x: x*(1 - x)
df = lambda x: 1 - 2*x

y0 = array([0.1])
y_exact = np.vectorize(lambda t: y0 / (y0 - exp(-t)*(y0 - 1)))
t0, tf = 0, 2

vh = []
verr = []
for k in range(3, 13):
    h = 2**-k
    vt = np.arange(t0, tf+h, h)
    vy = enright(f, df, y0, vt)[:,0]
    vy_exact = y_exact(vt)
    err = max(abs(vy - vy_exact))
    vh.append(h)
    verr.append(err)

vh, verr = array(vh), array(verr)
[p1, p0] = np.polyfit(log10(vh), log10(verr), 1)

plt.figure(1)
plt.clf()
plt.xlabel("h")
plt.ylabel("|I - I_h|")
plt.loglog(vh, 10**(p1*log10(vh) + p0), label="log |I - I_h| ~ a log h")
plt.loglog(vh, verr, linestyle='None', marker='o', label="Données")
plt.legend()
plt.title("Modèle logistique avec Enright, a = {:.2f}".format(p1))

## Question 4

a = 30
f = lambda x: array([x[1], -x[0] + a*(1 - x[0]**2)*x[1]])
df = lambda x: array([[0, 1], [-1 + -2*a*x[0]*x[1], a*(1 - x[0]**2)]])

y0 = array([1, 0])
t0, tf = 0, 5*a
vy_exact = odeint(lambda y,t: f(y), y0, np.arange(t0, tf+0.001, 0.001))
vy = enright(f, df, y0, np.arange(t0, tf+0.01, 0.01))

plt.figure(2)
plt.clf()
plt.xlabel("theta(t)")
plt.ylabel("theta'(t)")
plt.plot(vy_exact[:,0], vy_exact[:,1], label="odeint")
plt.plot(vy[:,0], vy[:,1], label="enright", linestyle='None', marker='o', markersize=2)
plt.legend()
plt.title("Van der Pol avec Enright")


import torch
import torch.nn as nn
import torch.nn.functional as F

import math
from transformers import BertPreTrainedModel, BertConfig, BertModel

# ------------------------------------------------
# --------- attention sum feature ----------------
# ------------------------------------------------

class AttSum(nn.Module):
    def __init__(self, C):
        super(AttSum, self).__init__()

        self.att = nn.Sequential(
            nn.Linear(C.HIDDEN_SIZE, C.HIDDEN_SIZE),
            nn.ReLU(inplace=True),
            nn.Dropout(C.DROPOUT_R),
            nn.Linear(C.HIDDEN_SIZE, 1)
        )
        self.fc = nn.Linear(C.HIDDEN_SIZE, C.HIDDEN_SIZE)

    def forward(self, x):
        att_w = self.att(x)
        att_w = F.softmax(att_w, dim=1)
        x_att = torch.sum(att_w * x, dim=1)
        x_att = self.fc(x_att)

        return x_att

# ----------------------------------------------
# ---------------- MLP -------------------------
# ----------------------------------------------

class MlpModel(nn.Module):
    def __init__(self, C, answer_size):
        super(MlpModel, self).__init__()

        self.att_img = AttSum(C)

        self.proj = nn.Sequential(
            GeLU(),
            nn.Linear(C.HIDDEN_SIZE, answer_size)
        )

    def forward(self, v, q):

        q = q[:,0,:]
        v = self.att_img(v)

        pred = v + q
        pred = self.proj(pred)

        return pred

class GeLU(nn.Module):
    """Implementation of the gelu activation function.
        For information: OpenAI GPT's gelu is slightly different (and gives slightly different results):
        0.5 * x * (1 + torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3))))
        Also see https://arxiv.org/abs/1606.08415
    """
    def __init__(self):
        super().__init__()

    def forward(self, x):
        return x * 0.5 * (1.0 + torch.erf(x / math.sqrt(2.0)))


# Suppose a Python thread needs to be stopped cleanly (it might need to perform cleanup).

# For illustration, we will take a very simple program in with a single “worker” thread that displays a message when it
# is done. The message is a placeholder for real cleanup, and the thread itself sleeps for a given number of iterations
# (as a placeholder for significant work). In example, we want to stop the thread through a keyboard interrupt (Ctrl + C).

# By default, the thread is not stopped cleanly

import os
import signal
import threading as th
import time

# 1) The program can be stopped by hitting Ctrl + C, but the thread keeps running.
def do_some_work(n_iter):
    for i in range(n_iter):
        print(f'iteration {i + 1}/{n_iter}')
        time.sleep(0.5)
    print('Thread done')

def send_ctrl_c(pid=None):
    pid = pid or os.getpid()
    if hasattr(signal, 'CTRL_C_EVENT'):
        # windows. Need CTRL_C_EVENT to raise the signal in the whole process group
        os.kill(pid, signal.CTRL_C_EVENT)
    else:
        # unix
        os.kill(pid, signal.SIGINT)

# ~/projects/.venv/bin/python ~/.local/share/JetBrains/IntelliJIdea/python/helpers/pydev/pydevd.py --multiprocess
# --qt-support=auto --client 127.0.0.1 --port 33495 --file ~/Projects/projects/async/002_superfastpy/01_threading/07_thread_stop_cleanly.py
if __name__ == '__main__':
    n_iter = 20
    thread = th.Thread(target=do_some_work, args=(n_iter,))
    thread.start()

    # The first Ctrl + C stops the main program, but not the thread. The second time, the thread is stopped as well.
    send_ctrl_c()
    # Traceback (most recent call last):
    #   File "~/projects/async/002_superfastpy/01_threading/07_thread_stop_cleanly.py", line 37, in <module>
    #     send_ctrl_c()
    #   File "~/projects/async/002_superfastpy/01_threading/07_thread_stop_cleanly.py", line 28, in send_ctrl_c
    #     os.kill(pid, signal.SIGINT)
    # KeyboardInterrupt
    # iteration 1/20

    thread.join()
    print('Program done')

# 2) Using a daemon thread is not a good idea
# The Python threading documentation explains that a thread may be started as a daemon, meaning that “the entire Python
# program exits when only daemon threads are left”. The main program itself is not a daemon thread.
# Note: Daemon threads are abruptly stopped at shutdown. Their resources (such as open files, database transactions, etc.)
# may not be released properly. If you want your threads to stop gracefully, make them non-daemonic and use a suitable
# signalling mechanism such as an Event.

# 3) A clean thread exit using events and signals
# https://docs.python.org/3/library/threading.html#threading.Event
# Event is a simple object that can be set or cleared. It can be used to signal to the thread that it needs perform
# its cleanup and then stop.

# The idea is to use such an event here (let us call it a stop event). Initially not set, the stop event becomes set when a keyboard interrupt is received. The worker thread then breaks out from the loop if the stop event is set and performs its cleanup.

stop_event = th.Event()


# Advent of Code 2019
#
# From https://adventofcode.com/2019/day/2
#

from Advent2019_Intcode import Intcode
# Givens from the problem

NOUN = 12
VERB = 2
OUTPUT = 19690720

def iterate_noun(inputs, noun_, verb):
    for noun in range(noun_, 99):
        result = Intcode(inputs[:], noun, verb).run()
        if result >= OUTPUT:
            if result > OUTPUT:
                noun -= 1
            return noun, verb, result

def iterate_verb(inputs, noun, verb_):
    for verb in range(verb_, 99):
        result = Intcode(inputs[:], noun, verb).run()
        if result >= OUTPUT:
            if result > OUTPUT:
                verb -= 1
            return noun, verb, result

def main():
    inputs = list(map(int, [mass.strip().split(',') for mass in open('../inputs/Advent2019_02.txt', 'r')][0]))

    print(f'AoC 2019 Day 2, Part 1 answer is {Intcode(inputs[:], NOUN, VERB).run()}')

    # determine whether to iterate nouns or verbs first, whichever has the greatest effect on result:
    # Would be an O(2n) solution rather than a nested loop O(n**2) solution

    if Intcode(inputs[:], NOUN + 1, VERB).run() > Intcode(inputs[:], NOUN, VERB + 1).run():
        noun, verb, result = iterate_noun(inputs[:], NOUN, VERB)
        noun, verb, result = iterate_verb(inputs[:], noun, VERB)
    else:
        noun, verb, result = iterate_verb(inputs[:], NOUN, VERB)
        noun, verb, result = iterate_noun(inputs[:], NOUN, verb)
    if Intcode(inputs[:], noun, verb).run() == OUTPUT:
        print(f'AoC 2019 Day 2, Part 2 answer is {noun * 100 + verb}')
    else:
        print(f'Error')

if __name__ == '__main__':
    main()


from student import Student
from nss_person import Nss_person

class Instructor(Nss_person):
    def __init__(self, first, last, handle, specialty):
        super().__init__(first, last, handle)
        self.specialty = specialty

    def assign_exercise(self, student, exercise):
        try:
            if self.cohort == student.cohort:
                student.add_exercise(exercise)
            else:
                print("That student is not in your cohort")
        except AttributeError:
            print(f"this instructor is not assigned to a cohort")


#!/usr/bin/env python
#DEPRECATED
#BUT MAY BE USED AGAIN SOON FOR V2 IF WE USE SATELITE FULL COVERAGE DATA
"""
Author julien
Date 4/13/2020
Description: First version of a script to achieve first 2 steps of data sciece
pipeline:
* data extraction
* data wrangling
In order to make it possible in a local computer an exeption has been made add
the extracted data is clipped around DC reducing from 20Gb to 10Mb without
altering the DC area data.
In this first version a full DC metro area was used as template
to cover all 25k pixels or coordinates with a 1km resolution in DC
We may coming back to this later.
"""
config_worldpop = {
#'ppp_2010':{'column':'Population', 'date':'2010-*', '24hour':'*'},
#'ppp_2011':{'column':'Population', 'date':'2011-*', '24hour':'*'},
#'ppp_2012':{'column':'Population', 'date':'2012-*', '24hour':'*'},
#'ppp_2013':{'column':'Population', 'date':'2013-*', '24hour':'*'},
#'ppp_2014':{'column':'Population', 'date':'2014-*', '24hour':'*'},
#'ppp_2015':{'column':'Population', 'date':'2015-*', '24hour':'*'},
#'ppp_2016':{'column':'Population', 'date':'2016-*', '24hour':'*'},
#'ppp_2017':{'column':'Population', 'date':'2017-*', '24hour':'*'},
#'ppp_2018':{'column':'Population', 'date':'2018-*', '24hour':'*'},
'ppp_2019':{'column':'Population', 'date':'2019-*', '24hour':'*'},
#'ppp_2020':{'column':'Population', 'date':'2020-*', '24hour':'*'},
#'ppp_2020':{'column':'Road_density', 'date':'2020-*', '24hour':'*'},
#Distance to OSM major roads 2016
'osm_dst_road_100m_2016':{'column':'Dist-MRoads', 'date':'*', '24hour':'*'},
#Distance to interpolated built-settlement area edges 2010
#'dst_bsgme_100m_2010':{'column':'Dist-Setl', 'date':'2010-*', '24hour':'*'},
#'dst_bsgme_100m_2011':{'column':'Dist-Setl', 'date':'2011-*', '24hour':'*'},
#'dst_bsgme_100m_2012':{'column':'Dist-Setl', 'date':'2012-*', '24hour':'*'},
#'dst_bsgme_100m_2013':{'column':'Dist-Setl', 'date':'2013-*', '24hour':'*'},
#'dst_bsgme_100m_2014':{'column':'Dist-Setl', 'date':'2014-*', '24hour':'*'},
#'dst_bsgme_100m_2015':{'column':'Dist-Setl', 'date':'2015-*', '24hour':'*'},
#'dst_bsgme_100m_2016':{'column':'Dist-Setl', 'date':'2016-*', '24hour':'*'},
#'dst_bsgme_100m_2017':{'column':'Dist-Setl', 'date':'2017-*', '24hour':'*'},
#'dst_bsgme_100m_2018':{'column':'Dist-Setl', 'date':'2018-*', '24hour':'*'},
'dst_bsgme_100m_2019':{'column':'Dist-Setl', 'date':'2019-*', '24hour':'*'},
#'dst_bsgme_100m_2020':{'column':'Dist-Setl', 'date':'2020-*', '24hour':'*'},
#Distance to coastline 2000-2020
'dst_coastline_100m_2000_2020':{'column':'Dist-Coast', 'date':'*', '24hour':'*'},
#Distance to ESA-CCI-LC woody-tree area edges 2010
#'esaccilc_dst040_100m_2010':{'column':'Dist-Forest', 'date':'2010-*', '24hour':'*'},
#'esaccilc_dst040_100m_2011':{'column':'Dist-Forest', 'date':'2011-*', '24hour':'*'},
#'esaccilc_dst040_100m_2012':{'column':'Dist-Forest', 'date':'2012-*', '24hour':'*'},
#'esaccilc_dst040_100m_2013':{'column':'Dist-Forest', 'date':'2013-*', '24hour':'*'},
#'esaccilc_dst040_100m_2014':{'column':'Dist-Forest', 'date':'2014-*', '24hour':'*'},
#'esaccilc_dst040_100m_2015':{'column':'Dist-Forest', 'date':'2015-*', '24hour':'*'},
#'esaccilc_dst040_100m_2016':{'column':'Dist-Forest', 'date':'2016-*', '24hour':'*'},
#'esaccilc_dst040_100m_2017':{'column':'Dist-Forest', 'date':'2017-*', '24hour':'*'},
#'esaccilc_dst040_100m_2018':{'column':'Dist-Forest', 'date':'2018-*', '24hour':'*'},
'esaccilc_dst040_100m_2015':{'column':'Dist-Forest', 'date':'2019-*', '24hour':'*'},
#'esaccilc_dst040_100m_2020':{'column':'Dist-Forest', 'date':'2020-*', '24hour':'*'},
#SRTM slope 2000
'srtm_slope_100m':{'column':'Slope', 'date':'*', '24hour':'*'},
#SRTM elevation 2000
'srtm_topo_100m':{'column':'Elevation', 'date':'*', '24hour':'*'}
}

import pandas as pd

from osgeo import gdal

#import gdal
import os
from gdalconst import GA_ReadOnly
from ftplib import FTP
import os
from wpgpDownload.utils.convenience_functions import refresh_csv
#has to been done before importing Products
refresh_csv()
from wpgpDownload.utils.wpcsv import Product
#from wpgpDownload.utils.wpcsv import ISO_LIST
import logging

logging.basicConfig(filename='import-data.log',
                    level=logging.INFO,
                    format='%(asctime)s [%(levelname)s] >> %(message)s',
                    datefmt='%Y-%m-%d %I:%M:%S %p')

logging.info('Starting an import job for WorlPop data')
logging.info('OS current directory {}'.format(os.getcwd()))

iso_country = 'USA'
USA_products = Product(iso_country)  # Where instead of GRC it could be any valid ISO code.
USA_manifest = pd.DataFrame(USA_products, columns = ['idx', 'dataset_name', 'path',
                                            'numeric', 'alpha3','country_name',
                                            'description'])

import pdb; pdb.set_trace()

def load(ftpURL, ftpFilePath):
    logging.info('Load FTP fie without auth ({} fromm {})'.format(ftpURL, ftpFilePath))
    logging.info('OS current directory {}'.format(os.getcwd()))
    ftp = FTP(ftpURL)
    r = ftp.login()
    logging.info('FTP login response: {}'.format(r))
    relativeFilename = './' + ftpFilePath
    if not os.path.exists(os.path.dirname(relativeFilename)):
        try:
            os.makedirs(os.path.dirname(relativeFilename))
        except OSError as exc: # Guard against race condition
            if exc.errno != errno.EEXIST:
                raise
    logging.info('FTP CWD change to: {}'.format(os.path.dirname(ftpFilePath)))
    ftp.cwd(os.path.dirname(ftpFilePath))
    ftp.retrlines('LIST', logging.info)

    with open(relativeFilename, 'wb') as f:
        ftp.retrbinary("RETR {}".format(os.path.basename(ftpFilePath)), f.write)

    ftp.quit()

#worldpopManifest = "assets/wpgpDatasets.csv"
worldpopftp = "ftp.worldpop.org.uk"

#logging.info('Load last version of worldpop manifest ({} fromm {})'.format(worldpopftp, worldpopManifest))
#load(worldpopftp, worldpopManifest)
#dfManifest=pd.read_csv('./' + worldpopManifest)
#is_USA = dfManifest["ISO3"] == "USA"
#USA_manifest = dfManifest[is_USA]

csv_to_update = 'bigtable.csv'
csv_template = '1km_t0.csv'
df_template=pd.read_csv(csv_template)
df_bigtable=pd.read_csv(csv_to_update)

buffer_deg=0.0027
xmax_MetroArea = -76.3881880661718 + buffer_deg
ymax_MetroArea = 39.5284754397557 + buffer_deg
xmin_MetroArea = -77.7356609923511 - buffer_deg
ymin_MetroArea = 38.3969644315365 - buffer_deg
extent = [xmin_MetroArea, ymax_MetroArea, xmax_MetroArea, ymin_MetroArea]
skip_if_DC_file_is_here = True

for Covariate_key, config in config_worldpop.items():
    logging.info('import job of {} convariate'.format(Covariate_key))
    date_filter=config["date"].rstrip('*')
    h24_filter=config["24hour"]
    column_name=config["column"]
    logging.info('Column {} using date filter {} wit hour filter {}'.format(column_name, date_filter, h24_filter))

    filePath = USA_manifest[USA_manifest["Covariate"]==Covariate_key]["PathToRaster"].iloc[0]
    logging.info('filePath FTP from manifest is {}'.format(filePath))
    bigTiff = './' + filePath
    DCtiff = "{}/DC_{}".format(os.path.dirname(bigTiff),os.path.basename(bigTiff))

    if (skip_if_DC_file_is_here and os.path.exists(DCtiff)):
        logging.info("We skip download of source tiff. DCtiff {} already here and skip is activated.".format(DCtiff))
    else:
        #download by FTP the big tiff
        load(worldpopftp, filePath)

        #clip and keep metro area tiff
        data = gdal.Open(bigTiff, GA_ReadOnly)
        logging.info("bigTiff: {}".format(bigTiff))
        logging.info("DCtiff: {}".format(DCtiff))
        #https://gdal.org/programs/gdal_translate.html
        data = None
        data = gdal.Open(bigTiff)
        data = gdal.Translate(DCtiff, data, projWin = extent)
        data = None
        logging.info("DC area tiff clip done")

    #delete big tiff
    if os.path.exists(bigTiff):
        os.remove(bigTiff)
    else:
        logging.warning('Original big tiff file {} not deleted, file not found.'.format(bigTiff))

    #update column values, create new line if no update available
    driver = gdal.GetDriverByName('GTiff')
    dataset = gdal.Open(DCtiff)
    band = dataset.GetRasterBand(1)
    cols = dataset.RasterXSize
    rows = dataset.RasterYSize
    transform = dataset.GetGeoTransform()
    xOrigin = transform[0]
    yOrigin = transform[3]
    pixelWidth = transform[1]
    pixelHeight = -transform[5]

    data = band.ReadAsArray(0, 0, cols, rows)
    coordinates = df_template[['x', 'y', 'fid']].apply(tuple, axis=1).tolist()
    #get list of 25k coordinates tuples as [(-77.7356609923511, 39.5284754397557), (-77.7266778395099, 39.5284754397557), ...
    #import pdb; pdb. set_trace()
    #points_list = [ (355278.165927, 4473095.13829), (355978.319525, 4472871.11636) ] #list of X,Y coordinates
    #value = []
    Update_csv=True
    if Update_csv:
        updated,created=0,0
        logging.info("CSV len {} before update".format(len(df_bigtable)))
        for point in coordinates:
            col = int((point[0] - xOrigin) / pixelWidth)
            row = int((yOrigin - point[1] ) / pixelHeight)
            #print ("{},{}={}".format(row, col, data[row][col]))
            #value += [data[row][col]]
            value = data[row][col]
            filtering=((df_bigtable['fid']==point[2])
                        & (df_bigtable['date'].str.startswith(date_filter, na=False)))
            if len(df_bigtable.loc[filtering]) > 0:
                #update existing rows
                logging.debug('Updating CSV {} row(s) for coord{}/{} @{}, column {} => {}'.format(len(df_bigtable.loc[filtering]),point[0],point[1],date_filter,column_name,value))
                df_bigtable.loc[filtering, column_name]=value
                updated += len(df_bigtable.loc[filtering])
            else:
                #add a new row with wildcards*
                logging.debug('Appending 1 row in CSV for coord{}/{} @{}, column {} => {}'.format(point[0],point[1],date_filter,column_name,value))
                df_bigtable=df_bigtable.append({'fid':point[2],'x':point[0],'y':point[1],'date':date_filter+'*','24hour':'*'},ignore_index=True)
                #import pdb; pdb. set_trace()
                created += 1
        logging.info("Saving CSV as {}".format(csv_to_update))
        logging.info("CSV len {}, {} created and {} updated".format(len(df_bigtable),created,updated))
        df_bigtable.to_csv(csv_to_update, index=False)
    #import pdb; pdb. set_trace()


#!/usr/bin/env python

import rospy
from spacetrack import SpaceTrackClient
import time
import schedule
import json

def tleSchedule():
	time.sleep(1)
	getTles()

	schedule.every(4).hours.do(getTles)
	while True:
		schedule.run_pending()
		time.sleep(60)

	# schedule.every().day.at("14:00").do(getTles)
	# while True:
	# 	print("hey")
	# 	schedule.run_pending()
	# 	time.sleep(60)

def getTles():
	catalog_number = 39161
	for line in open('/home/rasmus/catkin_ws/src/antenna_controller/tle/current_satellite.txt','r').readlines():
		catalog_number = int(line)
	st = SpaceTrackClient('rasmustomsen@hotmail.com', '!kK!Ft3-W6sKGa8X')
	tle = st.tle_latest(norad_cat_id=catalog_number, ordinal=1, format='3le').encode("utf-8").strip()
	rospy.loginfo(tle)
	formatTle(tle)

def formatTle(tle):
	tles_string = tle[2:]

	print(tles_string)
	print(len(tles_string.split("\n")))
	with open('/home/rasmus/catkin_ws/src/antenna_controller/tle/tle.txt', 'w') as f:
		f.write(tles_string)

if __name__ == '__main__':
	tleSchedule()

before changing multiple passes to one/antenna_controller/scripts/tle_getter_node.py