import json
import socket
import time
import sys
import random
import numpy
import threading
from Priority_Queue import PriorityQueue
import logging
import datetime

logging.basicConfig(filename='YACS_logs.log', filemode='w',
                    format='%(message)s', level=logging.INFO)

# Priority Queue that contains all the jobs
jobs_pq = PriorityQueue()
pq_lock = threading.Lock()
workers_lock = {}
sockets = {}
lock = threading.Lock()

# Function to send tasks to the worker
def send_tasks(data, port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('localhost', port))
    s.send(data.encode())
    s.close()

# Function that runs on a separate thread and listens to requests
def listen_to_requests():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('localhost', 5000))
    s.listen(1)

    while(1):
        try:
            host, _ = s.accept()
            with host:
                data = host.recv(1024)
                # Structure the jobs to this format:
                # [
                #  {'0': [[('0_M0', 2)], [('0_R0', 4), ('0_R1', 1)]]},
                #  {'1': [[('1_M0', 1)], [('1_R0', 2), ('1_R1', 4)]]}
                # ]
                x = json.loads(data)
                d = dict()
                p = []
                l = []
                for i in x['map_tasks']:
                    l.append((i['task_id'], i['duration']))
                    lock.acquire()
                    date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    logging.info(date + " " + "task arrival" + " " +
                                 x["job_id"] + " " + i["task_id"])
                    lock.release()
                p.append(l)
                r = []
                for i in x['reduce_tasks']:
                    r.append((i['task_id'], i['duration']))
                    lock.acquire()
                    date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    logging.info(date + " " + "task arrival" +
                                 " " + x["job_id"] + " " + i["task_id"])
                    lock.release()

                p.append(r)
                d[x['job_id']] = p

                pq_lock.acquire()
                jobs_pq.insert(d)
                pq_lock.release()
            print("Received Request")
        except KeyboardInterrupt:
            break

# Function to schedule tasks using Round-Robin Algorithm
def handle_roundrobin(workers):
    # Initialise prev_worker_id to -1
    prev_worker_id = -1

    while(1):
        time.sleep(0.05)
        pq_lock.acquire()
        task = jobs_pq.getTask()
        pq_lock.release()
        # If the queue is empty or
        # if reduce tasks cannot be run because map tasks of a job are still running
        if(task == None):
            continue

        found = False
        while(not found):
            worker_id = (prev_worker_id + 1) % len(worker_ids)
            count = 0
            # Iterate from prev_worker_id+1 through pre_worker_id
            while(count < len(worker_ids)):
                time.sleep(0.1)
                count += 1
                workers_lock[worker_id+1].acquire()
                freeSlotisAvailable = workers[worker_id+1]["free_slots"]
                workers_lock[worker_id+1].release()
                # If a free slot is available, assign the task to the worker
                if(freeSlotisAvailable):
                    prev_worker_id = worker_id
                    workers_lock[worker_id+1].acquire()
                    workers[worker_id+1]["free_slots"] -= 1
                    workers_lock[worker_id+1].release()

                    print("Task %s assigned to worker %d" %
                            (str(task), worker_id+1))
                    #####################
                    # Send task to worker
                    send_tasks(str(task[0])+","+str(task[1]),
                                workers[worker_id+1]["port"])
                    #####################
                    found = True
                    break
                worker_id = (worker_id + 1) % len(worker_ids)

# Function to schedule tasks using Random Algorithm
def handle_random(workers, worker_ids):

    while(1):
        time.sleep(0.05)
        pq_lock.acquire()
        task = jobs_pq.getTask()
        pq_lock.release()
        # If the queue is empty or
        # if reduce tasks cannot be run because map tasks of a job are still running
        if(task == None):
            continue
        worker_found = False
        # Make a random choices until a worker with free slots is found
        while(not worker_found):
            worker_id = random.choice(worker_ids)
            workers_lock[worker_id].acquire()
            freeSlotisAvailable = workers[worker_id]["free_slots"]
            workers_lock[worker_id].release()
            # If a free slot is available, assign the task to the worker
            if(freeSlotisAvailable):
                worker_found = True
                workers_lock[worker_id].acquire()
                workers[worker_id]["free_slots"] -= 1
                workers_lock[worker_id].release()

                print("Task %s assigned to worker %d" %
                        (str(task), worker_id))
                #####################
                # Send task to worker
                send_tasks(str(task[0])+","+str(task[1]),
                            workers[worker_id]["port"])
                #####################

    print("Exitting random")

# Function to schedule tasks using Least-Loaded Algorithm
def handle_LL(workers):
    while(1):
        time.sleep(0.05)
        pq_lock.acquire()
        task = jobs_pq.getTask()
        pq_lock.release()
        # If the queue is empty or
        # if reduce tasks cannot be run because map tasks of a job are still running
        if(task == None):
            continue
        worker_found = False
        max_slots = 0
        max_id = 0
        while(not worker_found):
            # Iterate through all workers select a worker with the maximum number of free slots
            for worker_id in workers:
                workers_lock[worker_id].acquire()
                if(workers[worker_id]["free_slots"] > max_slots):
                    max_slots = workers[worker_id]["free_slots"]
                    max_id = worker_id
                workers_lock[worker_id].release()
            # If the maximum number of free slots is greater than 0,
            # assign the task to the worker
            if(max_slots):
                worker_found = True
                workers_lock[max_id].acquire()
                workers[max_id]["free_slots"] -= 1
                workers_lock[max_id].release()
                print("Task %s assigned to worker %d" %
                        (str(task), max_id))
                #####################
                # Send task to worker
                send_tasks(str(task[0])+","+str(task[1]),
                            workers[max_id]["port"])
                #####################

# Function that runs on a separate thread and listens for updates from the worker
# Worker sends an update upon completion of execution of the task
def listen_to_workers():
    print("Listening to workers")
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('localhost', 5001))
    s.listen(1)

    while(1):
        try:
            host, _ = s.accept()
            with host:
                msg = host.recv(1024)
                msg = msg.decode()
                # Message received from the worker contains:
                # start time of the task, end time of the task,
                # taskId and worker_id that executed the task
                start_time, end_time, task_id, worker_id = msg.split(',')
                worker_id = int(worker_id)
                print("Message %s received from worker %d " %
                      (task_id, worker_id))
                lock.acquire()
                job_id = task_id.split("_")[0]
                # Log the starting and ending time of the task
                logging.info(start_time + " " + "starting task" +
                             " " + job_id + " " + task_id + " " + str(worker_id))
                logging.info(end_time + " " + "ending task" + " " +
                             job_id + " " + task_id + " " + str(worker_id))
                lock.release()

                # Increment the number of free slots in the worker
                workers_lock[worker_id].acquire()
                workers[worker_id]["free_slots"] += 1
                workers_lock[worker_id].release()

                # Remove the task from the job
                pq_lock.acquire()
                jobs_pq.popTask(task_id)
                pq_lock.release()
        except KeyboardInterrupt:
            break

    print("NOT LISTENING TO UPDATES FROM WORKERS ANYMORE")
    s.close()

if __name__ == '__main__':
    if(len(sys.argv) != 3):
        print("Usage: python master.py <path to config file> <name of algorithm: RR,LL,RANDOM>")
        exit()
    args = sys.argv
    path = args[1]
    algo = args[2]
    logging.info(args[2])

    if (algo != "RR") and (algo != "RANDOM") and (algo != "LL"):
        print("INVALID ALGORITHM! ENTER RR,LL or RANDOM")
        exit()

    f = open(path, 'r')
    data = json.load(f)

    print("Connect with Workers..............")
    workers = {}
    worker_ids = []
    for worker in data["workers"]:
        print(worker)
        # Dictionary indexed by the worker_id which contains:
        # Total number of slots available in a worker, port to which it is connnected and
        # the number of free slots available
        workers[worker["worker_id"]] = {
            "slots": worker["slots"],
            "port": worker["port"],
            "free_slots": worker["slots"]
        }
        worker_ids.append(worker["worker_id"])
        # Dictionary indexed by worker_id which contains the lock for each worker
        workers_lock[worker["worker_id"]] = threading.Lock()

    print("Connection with Workers successful!!\n")

    # Create and start a thread to listen to requests and listen to workers for updates
    requests_listener = threading.Thread(target=listen_to_requests)
    worker_listener = threading.Thread(target=listen_to_workers)
    requests_listener.start()
    worker_listener.start()

    print("Continue processing on the master thread to assign jobs")
    time.sleep(5)
    if algo == "RR":
        handle_roundrobin(workers)
    elif algo == "RANDOM":
        handle_random(workers, worker_ids)
    elif algo == "LL":
        handle_LL(workers)

    worker_listener.join()
    requests_listener.join()


"""
Contains the script for evaluating the baseline models
On the newly unseen data
Preprocessing done in Notebook "2020.12.3_genotype_baselines_newdataset.ipynb"

M. Togninalli, 01.2021
"""

import numpy as np
import pandas as pd
import os
import json
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from scipy.stats import pearsonr
from sklearn.model_selection import train_test_split, GridSearchCV, RandomizedSearchCV
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler

from sklearn.linear_model import LassoCV

from sklearn.linear_model import ElasticNet, Lasso, Ridge
from sklearn.ensemble import RandomForestRegressor
import pickle

# Reduced set of features available for newer data set, to get same order
FEATURE_COLUMNS = ['BLUE_Mean', 'GNDVI_Mean',
       'GREEN_Mean', 'NDRE_Mean', 'NDVI_Mean',
       'NIR_Mean','REDEDGE_Mean', 'RED_Mean']

# Train and test
def get_xy_df(df):
    x = df.drop(columns=['GRYLD', 'Plot_ID']).values
    # x = df[FEATURE_COLUMNS].values
    y = df['GRYLD'].values
    return x,y

def main():
    # OPTION:
    optimize_params = True

    # Load the genotypes of new plots.
    df_inference = pd.read_csv('/links/groups/borgwardt/Data/Jesse_2018/202010_new_testset/csv/'\
                            'df_20201110_numpy_MIL_npy_coordinates_geno_filtered.csv')
    # 2. Load genotypes
    # Alternative way of loading genotypes
    base_path = '/links/groups/borgwardt/Data/Jesse_2018/numpy_MIL_resized/genotypes'
    # Remove known genotypes
    old_gids = np.loadtxt('/links/groups/borgwardt/Data/Jesse_2018/genotypes/gids.txt', dtype=str)

    index = df_inference.query(
        "gid not in @old_gids")['gid'].unique()
    X = np.array([np.load(f'{base_path}/{x}.npy') for x in df_inference.query(
        "gid not in @old_gids")['gid'].unique()]).astype(np.float32)
    df_genotype_processed = pd.DataFrame(X, index=index)

    # 3. Load VIs
    df_vis = pd.read_csv('/links/groups/borgwardt/Data/Jesse_2018/202010_new_testset/csv/df_20210125_VIs.csv')
    date_group = 2 # Change this potentially
    # Only keep the relevant group
    df_vis = df_vis[df_vis['date_group']==date_group]

    # Concatenate the dataframes
    df_vis['gid'] = df_vis['Plot_ID'].map(df_inference.set_index('plot_id').gid)
    df_vis = df_vis.dropna()
    df_vis = df_vis.join(df_genotype_processed, on='gid', how='left').dropna()
    # Remove unnecessary infor
    gids = df_vis['gid']
    plot_ids = df_vis['Plot_ID']
    df_vis = df_vis.drop(columns=['date_group', 'gid'])
    del df_genotype_processed
    print(f'DFs loaded and concatenated: dims {df_vis.shape}')

    results = []
    split_id_range = 5

    # Obtain X and y
    X_test, y_test = get_xy_df(df_vis)
    print(f'{len(X_test)} inference samples.')

    # Iterate over splits
    output_path = f'/home/tomatteo/Projects/yield_prediction/results/baselines_202012/VIs'
    for i in range(split_id_range):
        # Retrieve the splitted data
        print(f'=============== VIs - Genotype baseline - Data Preprocessing - split {i} ===============')
        yscaler = pickle.load(open(os.path.join(output_path,f'y_scaler_fold{i}.pkl'), 'rb'))
        scaler = pickle.load(open(os.path.join(output_path,f'scaler_fold{i}.pkl'), 'rb'))
        lasso = pickle.load(open(os.path.join(output_path,f'trained_geno_vis_lasso_fold{i}_hyperopt_{optimize_params}.pkl'), 'rb'))
        rf = pickle.load(open(os.path.join(output_path,f'trained_geno_vis_rf_fold{i}_hyperopt_{optimize_params}.pkl'), 'rb'))

        X_test = scaler.transform(X_test)

        print('\tData loaded and scaled.')

        print(f'=============== VIs - Genotype baseline - Random Forest - split {i} ===============')
        # Evaluation
        y_pred = yscaler.inverse_transform(rf.predict(X_test))

        r2_sc = r2_score(y_test, y_pred)
        mse = mean_squared_error(y_test, y_pred)
        mae = mean_absolute_error(y_test, y_pred)
        pears = pearsonr(y_test, y_pred)[0]

        results.append(['full_randomforest', i, mae, mse, r2_sc, pears, date_group])
        print('MSE: {:.4f}, R2_SCORE: {:.4f}, Pearson: {:.4f}'.format(mse, r2_sc, pears))
        print()

        pd.DataFrame(np.array([plot_ids,gids,y_test,y_pred]).T, columns=['plot_id','gid','target_yield', 'predicted_yield']).to_csv(
                 os.path.join(output_path, f'results_rf_hyperopt_{optimize_params}_split{i}.csv'), index=False)

        print(f'=============== VIs - Genotype baseline - Lasso - split {i} ===============')

        y_pred = yscaler.inverse_transform(lasso.predict(X_test))

        r2_sc = r2_score(y_test, y_pred)
        mse = mean_squared_error(y_test, y_pred)
        mae = mean_absolute_error(y_test, y_pred)
        pears = pearsonr(y_test, y_pred)[0]

        results.append(['Lasso', i, mae, mse, r2_sc, pears, date_group])
        print('MSE: {:.4f}, R2_SCORE: {:.4f}, Pearson: {:.4f}'.format(mse, r2_sc, pears))
        print()

        pd.DataFrame(np.array([plot_ids,gids,y_test,y_pred]).T, columns=['plot_id','gis','target_yield', 'predicted_yield']).to_csv(
                 os.path.join(output_path, f'results_lasso_hyperopt_{optimize_params}_split{i}.csv'), index=False)

    # Save results
    pd.DataFrame(results, columns=['method', 'split_id', 'mae', 'mse', 'r2_sc', 'pears', 'date_group']).to_csv(
        f'/home/tomatteo/Projects/yield_prediction/results/baselines_202012/VIs/new_testset_results_20210126_hyperopt_{optimize_params}.csv', index=False
    )
    return

if __name__== '__main__':
    main()


import unittest2
import dulwich.object_store
import dulwich.objects
import logging

from quantumkeep.tree import *

class TestTree(unittest2.TestCase):

    def setUp(self):
        self.git_store = dulwich.object_store.MemoryObjectStore()

    def test_tree_append(self):
        tree = Tree(git_store=self.git_store)
        self.assertEqual(list(tree), [])
        msg1 = "Hello World"
        tree.append((
            "hi",
            TreeEntry(
                mode=tree_entry_modes.raw_blob,
                target=msg1,
            ),
        ))
        (k, entry) = tree[0]
        self.assertEqual(k, "hi")
        self.assertEqual(entry.target, msg1)
        self.assertEqual(tree["hi"].target, msg1)

    def test_tree_slice(self):
        tree = Tree(git_store=self.git_store)
        self.assertEqual(list(tree), [])
        for i in xrange(0, 11):
            tree.append((
                "key%i" % i,
                TreeEntry(
                    mode=tree_entry_modes.raw_blob,
                    target="value%i" % i
                ),
            ))

        def row_tuples(iterable):
            return [(key, entry.target) for key, entry in iterable]

        def expected_tuples(iterable):
            return [("key%i" % i, "value%i" % i) for i in iterable]

        s1 = tree[2:]
        self.assertEqual(
            row_tuples(s1),
            expected_tuples(xrange(2, 11)),
        )

        del tree[4:]
        self.assertEqual(
            row_tuples(tree),
            expected_tuples(xrange(0, 4))
        )

        tree[2:4] = [(
            "key16",
            TreeEntry(
                mode=tree_entry_modes.raw_blob,
                target="value16",
            ),
        )]
        self.assertEqual(
            row_tuples(tree),
            expected_tuples((0, 1, 16))
        )

    def test_subtree(self):
        tree = Tree(git_store=self.git_store)
        subtree = Tree()
        subtree.append((
            "hi",
            TreeEntry(
                mode=tree_entry_modes.raw_blob,
                target="Hello World",
            ),
        ))
        tree["foo"] = subtree
        entry = tree["foo"]
        self.assertEqual(type(entry.target), Tree)
        self.assertEqual(entry.mode, tree_entry_modes.tree)
        self.assertEqual(entry.target["hi"].target, "Hello World")

    def test_write(self):
        tree = Tree(git_store=self.git_store)
        subtree = Tree()
        subtree.append((
            "hi",
            TreeEntry(
                mode=tree_entry_modes.raw_blob,
                target="Hello World",
            ),
        ))
        tree["foo"] = subtree
        git_tree_id = tree.write_git_tree()
        git_tree = self.git_store[git_tree_id]
        git_subtree_id = git_tree["foo"][1]
        git_subtree = self.git_store[git_subtree_id]
        git_blob_id = git_subtree["hi"][1]
        git_blob = self.git_store[git_blob_id]
        self.assertEqual(git_blob.as_raw_string(), "Hello World")


from typing import Optional

from pydantic import BaseModel, EmailStr, Field

'''
Define the Schema for which data will be based on, which will represent
how data is stored in the MongoDB databse.
This schema will help users send HTTP request with the proper shape to the API
- the type of data to send and how to send it.
Here Pydantic Schema is used for validating data along with serializing
(JSON to Python) and deserializing (Python to JSON).
Note that pydantic does NOT serve as a Mongo shcema validator.
'''

class StudentSchema(BaseModel):
    fullname: str = Field(...)  # "..." means the field is required.
    email: EmailStr = Field(...)
    course_of_study: str = Field(...)
    year: int = Field(..., gt=0, lt=9)  # year should be 1 to 8 inclusive.
    gpa: float = Field(..., le=4.0)  # gpa should be less than 4.0

    # This is a common use case of adding an example that will be shown in the docs.
    class Config:
        schema_extra = {
            "example": {
                "fullname": "John Doe",
                "email": "johndoe@example.com",
                "course_of_study": "Computer Science",
                "year": 2,
                "gpa": "3.0",
            }
        }

class UpdateStudentModel(BaseModel):
    fullname: Optional[str]
    email: Optional[EmailStr]
    course_of_study: Optional[str]
    year: Optional[int]
    gpa: Optional[float]

    # This is a common use case of adding an example that will be shown in the docs.
    class Config:
        schema_extra = {
            "example": {
                "fullname": "John Doe",
                "email": "johndoe@example.com",
                "course_of_study": "Statistics",
                "year": 4,
                "gpa": "4.0",
            }
        }

def ResponseModel(data, message):
    return {
        "data": [data],
        "code": 200,
        "message": message,
    }

def ErrorResponseModel(error, code, message):
    return {"error": error, "code": code, "message": message}


from __future__ import absolute_import
from __future__ import print_function
from __future__ import division

import os
from PIL import Image
import numpy as np
import os.path as osp
import lmdb
import io

import torch
from torch.utils.data import Dataset

def read_image(img_path, mode='RGB'):
    """Keep reading image until succeed.
    This can avoid IOError incurred by heavy IO process."""
    got_img = False
    if not osp.exists(img_path):
        raise IOError("{} does not exist".format(img_path))
    while not got_img:
        try:
            img = Image.open(img_path).convert(mode)
            got_img = True
        except IOError:
            print("IOError incurred when reading '{}'. Will redo. Don't worry. Just chill.".format(img_path))
            pass
    return img

class ImageDataset(Dataset):
    """Image Person ReID Dataset"""
    def __init__(self, dataset, transform=None):
        self.dataset = dataset
        self.transform = transform

    def __len__(self):
        return len(self.dataset)

    def __getitem__(self, index):
        img_path, pid, camid = self.dataset[index]
        img = read_image(img_path)
        if self.transform is not None:
            img = self.transform(img)
        return img, pid, camid

class ImageDataset_seg(Dataset):
    """Image Person ReID Dataset"""
    def __init__(self, dataset, transform=None):
        self.dataset = dataset
        self.transform = transform

    def __len__(self):
        return len(self.dataset)

    def __getitem__(self, index):
        path, pid, camid = self.dataset[index]
        img_path, head_path, upper_body_path, lower_body_path, shoes_path, foreground_path = path

        img = read_image(img_path, mode='RGB')
        head = read_image(head_path, mode='L')
        upper_body = read_image(upper_body_path, mode='L')
        lower_body = read_image(lower_body_path, mode='L')
        shoes = read_image(shoes_path, mode='L')
        foreground = read_image(foreground_path, mode='L')

        sequence = [img, head, upper_body, lower_body, shoes, foreground]

        if self.transform is not None:
            self.transform.randomize_parameters()
            sequence = [self.transform(img) for img in sequence]

        sequence = torch.cat(sequence, 0) #[3+1+1+1, h, w]

        return sequence, pid, camid


my_list = ["David", "Michael", "Steve", "Simon", "Kirstin", "Luke", "Emma", "Matthew",
           "Lorna", "Michael", "Anne", "Mark", "James", "Iain", ]

#for name in my_list:
#    print(name)

itr = my_list.__iter__()
while True:
    try:
        print(next(itr))
    except StopIteration:
        break


import os

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.nn.functional import interpolate
from loguru import logger
from tqdm import tqdm

import wandb

from draw_concat import draw_concat
from generate_noise import generate_spatial_noise
from mario.level_utils import group_to_token, one_hot_to_ascii_level, token_to_group
from mario.tokens import TOKEN_GROUPS as MARIO_TOKEN_GROUPS
from mariokart.tokens import TOKEN_GROUPS as MARIOKART_TOKEN_GROUPS
from models import calc_gradient_penalty, save_networks

def update_noise_amplitude(z_prev, real, opt):
    """ Update the amplitude of the noise for the current scale according to the previous noise map. """
    RMSE = torch.sqrt(F.mse_loss(real, z_prev))
    return opt.noise_update * RMSE

def train_single_scale(D, G, reals, generators, noise_maps, input_from_prev_scale, noise_amplitudes, opt):
    """ Train one scale. D and G are the current discriminator and generator, reals are the scaled versions of the
    original level, generators and noise_maps contain information from previous scales and will receive information in
    this scale, input_from_previous_scale holds the noise map and images from the previous scale, noise_amplitudes hold
    the amplitudes for the noise in all the scales. opt is a namespace that holds all necessary parameters. """
    current_scale = len(generators)
    real = reals[current_scale]

    if opt.game == 'mario':
        token_group = MARIO_TOKEN_GROUPS
    else:  # if opt.game == 'mariokart':
        token_group = MARIOKART_TOKEN_GROUPS

    nzx = real.shape[2]  # Noise size x
    nzy = real.shape[3]  # Noise size y

    padsize = int(1 * opt.num_layer)  # As kernel size is always 3 currently, padsize goes up by one per layer

    if not opt.pad_with_noise:
        pad_noise = nn.ZeroPad2d(padsize)
        pad_image = nn.ZeroPad2d(padsize)
    else:
        pad_noise = nn.ReflectionPad2d(padsize)
        pad_image = nn.ReflectionPad2d(padsize)

    # setup optimizer
    optimizerD = optim.Adam(D.parameters(), lr=opt.lr_d, betas=(opt.beta1, 0.999))
    optimizerG = optim.Adam(G.parameters(), lr=opt.lr_g, betas=(opt.beta1, 0.999))
    schedulerD = torch.optim.lr_scheduler.MultiStepLR(optimizer=optimizerD, milestones=[1600, 2500], gamma=opt.gamma)
    schedulerG = torch.optim.lr_scheduler.MultiStepLR(optimizer=optimizerG, milestones=[1600, 2500], gamma=opt.gamma)

    if current_scale == 0:  # Generate new noise
        z_opt = generate_spatial_noise([1, opt.nc_current, nzx, nzy], device=opt.device)
        z_opt = pad_noise(z_opt)
    else:  # Add noise to previous output
        z_opt = torch.zeros([1, opt.nc_current, nzx, nzy]).to(opt.device)
        z_opt = pad_noise(z_opt)

    logger.info("Training at scale {}", current_scale)
    for epoch in tqdm(range(opt.niter)):
        step = current_scale * opt.niter + epoch
        noise_ = generate_spatial_noise([1, opt.nc_current, nzx, nzy], device=opt.device)
        noise_ = pad_noise(noise_)

        ############################
        # (1) Update D network: maximize D(x) + D(G(z))
        ###########################
        for j in range(opt.Dsteps):
            # train with real
            D.zero_grad()

            output = D(real).to(opt.device)

            errD_real = -output.mean()
            errD_real.backward(retain_graph=True)

            # train with fake
            if (j == 0) & (epoch == 0):
                if current_scale == 0:  # If we are in the lowest scale, noise is generated from scratch
                    prev = torch.zeros(1, opt.nc_current, nzx, nzy).to(opt.device)
                    input_from_prev_scale = prev
                    prev = pad_image(prev)
                    z_prev = torch.zeros(1, opt.nc_current, nzx, nzy).to(opt.device)
                    z_prev = pad_noise(z_prev)
                    opt.noise_amp = 1
                else:  # First step in NOT the lowest scale
                    # We need to adapt our inputs from the previous scale and add noise to it
                    prev = draw_concat(generators, noise_maps, reals, noise_amplitudes, input_from_prev_scale,
                                       "rand", pad_noise, pad_image, opt)

                    # For the seeding experiment, we need to transform from token_groups to the actual token
                    if current_scale == (opt.token_insert + 1):
                        prev = group_to_token(prev, opt.token_list, token_group)

                    prev = interpolate(prev, real.shape[-2:], mode="bilinear", align_corners=False)
                    prev = pad_image(prev)
                    z_prev = draw_concat(generators, noise_maps, reals, noise_amplitudes, input_from_prev_scale,
                                         "rec", pad_noise, pad_image, opt)

                    # For the seeding experiment, we need to transform from token_groups to the actual token
                    if current_scale == (opt.token_insert + 1):
                        z_prev = group_to_token(z_prev, opt.token_list, token_group)

                    z_prev = interpolate(z_prev, real.shape[-2:], mode="bilinear", align_corners=False)
                    opt.noise_amp = update_noise_amplitude(z_prev, real, opt)
                    z_prev = pad_image(z_prev)
            else:  # Any other step
                prev = draw_concat(generators, noise_maps, reals, noise_amplitudes, input_from_prev_scale,
                                   "rand", pad_noise, pad_image, opt)

                # For the seeding experiment, we need to transform from token_groups to the actual token
                if current_scale == (opt.token_insert + 1):
                    prev = group_to_token(prev, opt.token_list, token_group)

                prev = interpolate(prev, real.shape[-2:], mode="bilinear", align_corners=False)
                prev = pad_image(prev)

            # After creating our correct noise input, we feed it to the generator:
            noise = opt.noise_amp * noise_ + prev
            fake = G(noise.detach(), prev, temperature=1 if current_scale != opt.token_insert else 1)

            # Then run the result through the discriminator
            output = D(fake.detach())
            errD_fake = output.mean()

            # Backpropagation
            errD_fake.backward(retain_graph=False)

            # Gradient Penalty
            gradient_penalty = calc_gradient_penalty(D, real, fake, opt.lambda_grad, opt.device)
            gradient_penalty.backward(retain_graph=False)

            # Logging:
            if step % 10 == 0:
                wandb.log({f"D(G(z))@{current_scale}": errD_fake.item(),
                           f"D(x)@{current_scale}": -errD_real.item(),
                           f"gradient_penalty@{current_scale}": gradient_penalty.item()
                           },
                          step=step, sync=False)
            optimizerD.step()

        ############################
        # (2) Update G network: maximize D(G(z))
        ###########################

        for j in range(opt.Gsteps):
            G.zero_grad()
            fake = G(noise.detach(), prev.detach(), temperature=1 if current_scale != opt.token_insert else 1)
            output = D(fake)

            errG = -output.mean()
            errG.backward(retain_graph=False)
            if opt.alpha != 0:  # i. e. we are trying to find an exact recreation of our input in the lat space
                Z_opt = opt.noise_amp * z_opt + z_prev
                G_rec = G(Z_opt.detach(), z_prev, temperature=1 if current_scale != opt.token_insert else 1)
                rec_loss = opt.alpha * F.mse_loss(G_rec, real)
                rec_loss.backward(retain_graph=False)  # TODO: Check for unexpected argument retain_graph=True
                rec_loss = rec_loss.detach()
            else:  # We are not trying to find an exact recreation
                rec_loss = torch.zeros([])
                Z_opt = z_opt

            optimizerG.step()

        # More Logging:
        if step % 10 == 0:
            wandb.log({f"noise_amplitude@{current_scale}": opt.noise_amp,
                       f"rec_loss@{current_scale}": rec_loss.item()},
                      step=step, sync=False, commit=True)

        # Rendering and logging images of levels
        if epoch % 500 == 0 or epoch == (opt.niter - 1):
            if opt.token_insert >= 0 and opt.nc_current == len(token_group):
                token_list = [list(group.keys())[0] for group in token_group]
            else:
                token_list = opt.token_list

            img = opt.ImgGen.render(one_hot_to_ascii_level(fake.detach(), token_list))
            img2 = opt.ImgGen.render(one_hot_to_ascii_level(
                G(Z_opt.detach(), z_prev, temperature=1 if current_scale != opt.token_insert else 1).detach(),
                token_list))
            real_scaled = one_hot_to_ascii_level(real.detach(), token_list)
            img3 = opt.ImgGen.render(real_scaled)
            wandb.log({f"G(z)@{current_scale}": wandb.Image(img),
                       f"G(z_opt)@{current_scale}": wandb.Image(img2),
                       f"real@{current_scale}": wandb.Image(img3)},
                      sync=False, commit=False)

            real_scaled_path = os.path.join(wandb.run.dir, f"real@{current_scale}.txt")
            with open(real_scaled_path, "w") as f:
                f.writelines(real_scaled)
            wandb.save(real_scaled_path)

        # Learning Rate scheduler step
        schedulerD.step()
        schedulerG.step()

    # Save networks
    torch.save(z_opt, "%s/z_opt.pth" % opt.outf)
    save_networks(G, D, z_opt, opt)
    wandb.save(opt.outf)
    return z_opt, input_from_prev_scale, G


import datetime, time, playsound

hour = input("Enter hour : ")
min = input("Enter min : ")
sec = input("Enter sec : ")
set_alarm_timer = f"{hour} : {min} : {sec} "
while True:
    time.sleep(1)
    current_time = datetime.datetime.now()
    now = current_time.strftime("%H:%M:%S")
    split_alarm_time = set_alarm_timer.split(":")
    split_now_time = now.split(":")
    if int(split_now_time[0]) == int(split_alarm_time[0]):
        if int(split_now_time[1]) == int(split_alarm_time[1]):
            if int(split_now_time[2]) == int(split_alarm_time[2]):
                print("Time to wake up")
                playsound('/alarm.mp3')  # for playing note.mp3 file
                break


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat May 27 12:14:33 2023

@author: ishita
"""
#ask user to input how much currency they have
yuan = int(input('What do you have left in yuan? '))
yen = int(input('What do you have left in yen? '))
won = int(input('What do you have left in won? '))

#assign currency value to variable to exchange from yuan to USD
x = yuan * 0.14
print(x)

#assign currency value to variable to exchange from yen to USD
y = yen * 0.0071
print(y)

#assign currency value to variable to exchange from won to USD
z = won * 0.00076
print(z)

#to calculate the total amount left in USD
totalUSD = x + y + z

#print total USD amount
print(totalUSD)


#!/usr/bin/python3
import sys
from rai import *
rai = load_default_rai()

reg = sys.argv[1]

for bname, block in rai.blocks.items():
    if reg in block.registers:
        break
else:
    print("Unknown register")
    sys.exit(0)

reg = block.registers[reg]

if len(sys.argv) < 3:
    print(reg)
else:
    print(reg.value(int(sys.argv[2], 0)))


import cv2
import dlib
import torch
import argparse
import torchvision
import numpy as np
import torch.nn as nn
from skimage.transform import resize

# image = cv2.imread('./test_pic/AF1.jpg')
# image = cv2.imread('./test_pic/AF1675.jpg')
# image = cv2.imread('./test_pic/a.png')
# image = cv2.imread('./test_pic/b.png')
image = cv2.imread('./test_pic/AF1053.jpg')
detector = dlib.get_frontal_face_detector()
rects = detector(image, 1)

def key_points(img):
    points_keys = []
    PREDICTOR_PATH = "shape_predictor_68_face_landmarks.dat"
    predictor = dlib.shape_predictor(PREDICTOR_PATH)

    # for i in range(len(rects)):
    # len(rects)代表人脸个数
    landmarks = np.matrix([[p.x,p.y] for p in predictor(img,rects[0]).parts()])
    maxv = np.amax(landmarks, axis=0)
    minv = np.amin(landmarks, axis=0)
    img = img.copy()
    for idx, point in enumerate(landmarks):
        pos = (point[0,0],point[0,1])
        points_keys.append(pos)
        cv2.circle(img,pos,2,(255,0,0),-1)
    return img, maxv, minv

face_key, maxv, minv = key_points(image)
lefttop_x = minv.A[0][0]
lefttop_y = minv.A[0][1]
rightbottom_x = maxv.A[0][0]
rightbottom_y = maxv.A[0][1]
# print(lefttop_x,lefttop_y,rightbottom_x,rightbottom_y)
cv2.rectangle(face_key, (lefttop_x, lefttop_y), (rightbottom_x, rightbottom_y), (0, 255, 0), 2)

cv2.namedWindow('result', cv2.WINDOW_AUTOSIZE)
cv2.imshow('result', face_key)
cv2.waitKey(0)
cv2.destroyAllWindows()


import pickle
import shutil, psutil
import time
from os import execl
from sys import executable
from pyrogram import (
    Client,
    filters
)
from pyrogram.types import Message
from bot import (
    botStartTime,
    AUTHORIZED_CHATS,
    OWNER_ID
)
import bot.helper.ext_utils.fs_utils
from bot.helper.ext_utils import fs_utils
from bot.helper.telegram_helper.bot_commands import BotCommands
from bot.helper.telegram_helper.message_utils import *
from bot.helper.ext_utils.bot_utils import get_readable_file_size, get_readable_time

@Client.on_message(
    filters.command(BotCommands.StatsCommand) &
    filters.chat(AUTHORIZED_CHATS)
)
def stats(client: Client, message: Message):
    currentTime = get_readable_time((time.time() - botStartTime))
    total, used, free = shutil.disk_usage('.')
    total = get_readable_file_size(total)
    used = get_readable_file_size(used)
    free = get_readable_file_size(free)
    cpuUsage = psutil.cpu_percent(interval=0.5)
    memory = psutil.virtual_memory().percent
    netio = psutil.net_io_counters()
    sent = netio[0]
    recieved = netio[1]
    stats = f'Bot Uptime: {currentTime}\n' \
            f'Total Disk space: {total}\n' \
            f'Used: {used}\n' \
            f'Free: {free}\n' \
            f'CPU: {cpuUsage}%\n' \
            f'RAM: {memory}%\n' \
            f"Sent: {get_readable_file_size(sent)}\n" \
            f"Recieved: {get_readable_file_size(recieved)}"
    sendMessage(stats, client, message)

@Client.on_message(
    filters.command(BotCommands.StartCommand) &
    filters.chat(AUTHORIZED_CHATS)
)
def start(client: Client, message: Message):
    start_string = start_string = f'''
Ya! I'm Alive Send A Magnet/.torrent/Https Link
'''
    sendMessage(start_string, client, message)

@Client.on_message(
    filters.command(BotCommands.RestartCommand) &
    filters.user(OWNER_ID)
)
def restart(client: Client, message: Message):
    restart_message = sendMessage(
        "Restarting, Please wait!",
        client,
        message
    )
    # Save restart message object in order to reply to it after restarting
    fs_utils.clean_all()
    with open('restart.pickle', 'wb') as status:
        pickle.dump(restart_message, status)
    execl(executable, executable, "-m", "bot")

@Client.on_message(
    filters.command(BotCommands.PingCommand) &
    filters.chat(AUTHORIZED_CHATS)
)
def ping(client: Client, message: Message):
    start_time = int(round(time.time() * 1000))
    reply = sendMessage("Starting Ping", client, message)
    end_time = int(round(time.time() * 1000))
    editMessage(f'{end_time - start_time} ms', reply)

@Client.on_message(
    filters.command(BotCommands.LogCommand) &
    filters.user(OWNER_ID)
)
def log(client: Client, message: Message):
    sendLogFile(client, message)


def findDivide(nums):
    left = 0
    right = len(nums) - 1
    while left < right - 1:
        mid = (left + right + 1) // 2

        if nums[right] >= nums[mid] >= nums[left]: return left
        if nums[mid] <= nums[right]: right = mid
        if nums[mid] >= nums[left]: left = mid
    return left

def search(nums, target):
    """
    :type nums: List[int]
    :type target: int
    :rtype: int
    """
    divide = findDivide(nums)
    try:
        if target < nums[0]:
            return nums.index(target, divide, len(nums))
        else:
            return nums.index(target, 0, divide + 1)
    except:
        return -1

search([1], 1)


class Node:
    def __init__(self,data):
        self.left = None
        self.right = None
        self.data = data
class BinaryTree:
    def __init__(self):
        self.root = None
    def insert(self,data):
        newNode = Node(data)

        if (self.root == None):
            self.root = newNode
        else:
            currNode = self.root
            while True:
                if (data < currNode.data):
                    if(currNode.left == None):
                        currNode.left = newNode
                        return
                    else:
                        currNode = currNode.left
                elif(data > currNode.data):
                    if(currNode.right == None):
                        currNode.right = newNode
                        return
                    else:
                        currNode = currNode.right
    def checkExist(self,data):
        curr = self.root
        while True:
            if curr == None:
                return False
            if curr.data == data:
                return True
            elif(data < curr.data):
                curr = curr.left
            else:
                curr = curr.right

bst = BinaryTree()
bst.insert(10)
bst.insert(20)
bst.insert(30)
bst.insert(40)

print(bst.checkExist(50))


from classes.DataManager import DataManager
from classes.Perceptron import Perceptron
from classes.UserInterface import UserInterface

perceptron = Perceptron()
data_manager = DataManager()
user_interface = UserInterface()

def manual_mode():
  car_horsepower, car_weight = user_interface.input_car_data()
  params = [car_horsepower, car_weight]
  prediction = perceptron.make_prediction(params)
  expected = user_interface.input_user_answer(prediction)
  data_manager.append(params, expected)
  perceptron.handle_single_row(params, expected, prediction)

def auto_mode():
  perceptron.train_model(data_manager.data)

def main():
  while(True):
    user_interface.print_menu()
    exit_code = user_interface.handle_user_choice(
        enter_data_function=manual_mode,
        train_model_function=auto_mode
    )
    if exit_code == 0:
      break
  data_manager.generate_data_chart()

main()


from datetime import datetime

#armazenamento de compras
compras = {
  "Pedro": {
    "cliente": "Pedro",
    "produtos": ["1", "5"],
    "total": 35.00,
    "created_at": datetime.today().strftime('%d/%m/%Y')
    },
    "Sergio": {
    "cliente": "Sergio",
    "produtos": ["3"],
    "total": 18.00,
    "created_at": datetime.today().strftime('%d/%m/%Y')
    },
}

class SalesRepo:

  #adicionar uma compra
  def add(cliente: str, codigosProdutos: list, precoTotal: float) -> dict:
    #cria dicionario de dados da compra
    compra = {"cliente": cliente, "produtos": codigosProdutos, "total": precoTotal, "created_at": datetime.today().strftime('%d/%m/%Y')}
    #adiciona a compra ao armazenamento
    compras['cliente'] = compra
    return compra

  #retorna o dicionario de compras
  def getAll() -> dict:
    return compras


import requests
from lxml import etree

import os
import time

import conf

def getHtml(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
    }
    resp = requests.get(url, headers=headers)
    return resp.text

def parseChapterURL(html):
    root = etree.HTML(html)
    title = root.xpath('//*[(@id = "list")]//a/text()')
    # print(title)
    chapterUrlList = root.xpath('//*[(@id = "list")]//a/@href')
    print('title:', len(title))
    print('url:', len(chapterUrlList))
    result = []
    for i in zip(title, chapterUrlList):
        # 判断是否为章节链接：使用正则表达式
        if i[0].startswith('第'):
            result.append(i)
        # result.append(i)
    return result

def parseChapterContent(html):
    root = etree.HTML(html)
    content = []
    title = ''.join(root.xpath('//*[(@id = "neirongDiv")]//h1/text()')).strip()
    content.append(title)
    paragraph = root.xpath('//*[(@id = "zjneirong")]/p/text()') if root.xpath('//*[(@id = "zjneirong")]/p/text()') else root.xpath('//*[(@id = "zjneirong")]/text()')
    for text in paragraph:
        content.append(text.strip())
    return (os.linesep * 2).join(content)

def saveTxt(content, fileName=conf.FILE_NAME, mode='a', encoding='utf-8'):
    with open(file=fileName, mode=mode, encoding=encoding) as fw:
        fw.write(content)
        fw.write(os.linesep*2)

def test():
    # print(parseChapterURL(getHtml(conf.TXT_URL)))
    # print(parseChapterContent(getHtml(conf.CHAPTER_TEST_URL)))
    chapterUrlList =parseChapterURL(getHtml(conf.TXT_URL))
    print(len(chapterUrlList))
    for url in chapterUrlList:
        content = parseChapterContent(getHtml(url[1]))
        saveTxt(content)
        print(url[0], '下载完成')
        time.sleep(1)
    print('全部下载完成')

def main():
    test()

if __name__ == '__main__':
    main()


# Models and related classes and functions
import torch
from torch.utils.data import Dataset
import transformers
from sklearn.metrics import precision_recall_fscore_support, confusion_matrix
import numpy as np
import time

def n_tokens_in_core_sentences(idx_first_sent, idx_last_sent, tokenized_sentences):
    '''
    Computes the number of tokens from a set of core sentences.
    Arguments:
        idx_first_sent: index of the first core sentence (integer).
        idx_last_sent: index of the last core sentence (integer).
        tokenized_sentences: the representation of a tokenized chunk as a list (sentences) of list of integers (token IDs).
    Returns:
        The number of tokens in the sequence of sentences indicated by the idx_first_sent and idx_last_sent arguments.
    '''
    n = 0
    for i in range(idx_first_sent, idx_last_sent + 1):
        n += (len(tokenized_sentences[i]) + 1) # +1 because future SEP token
    return n

def core_sentences_idx(tokenized_sentences, max_seq_len, min_context_len):
    '''
    Sets the number of chunks and their respective core sentences.
    Arguments:
        max_seq_len: maximum number of tokens in a chunk.
        min_context_len: desired minimum number of edge tokens.
        tokenized_sentences: list (sentences) of list (tokens in a sentence) of integers (token IDs).
    Returns:
        List of tuples. Each tuple represents a chunk and it has two
        integers which are the index of first and last core sentence in the chunk.
    '''
    threshold_ctx = max_seq_len - min_context_len - 2 # -2 because CLS and first SEP

    n_sentences = len(tokenized_sentences)
    idx_core_sentences = [] # list of tuple of 2 integers (indexes of first and last core sentences in a chunk)
    idx_current_sent = 0
    while idx_current_sent < n_sentences:
        # new chunk
        idx_start = idx_current_sent # begining of current chunk
        idx_end = idx_current_sent   # end of current chunk
        idx_current_sent += 1
        len_core_sent_in_chunk = n_tokens_in_core_sentences(idx_start, idx_end, tokenized_sentences)
        while len_core_sent_in_chunk < threshold_ctx and idx_current_sent < n_sentences:
            # checking if current sentence fits into the current chunk while maintain minimum context length
            if len_core_sent_in_chunk + len(tokenized_sentences[idx_current_sent]) < threshold_ctx:
                idx_end = idx_current_sent
                idx_current_sent += 1
                len_core_sent_in_chunk = n_tokens_in_core_sentences(idx_start, idx_end, tokenized_sentences)
            else:
                break
        idx_core_sentences.append((idx_start, idx_end))
    return idx_core_sentences

def tokenize_sentences(sentences, tokenizer, max_seq_len, min_context_len):
    '''
    This function assembles a set of DFCSCs for a sequence of sentences (document). The sentences
    are tokenized and each chunk is created as a sequence of token IDs. The chunks include the core
    sentences and the edge sentence. The chunks' length
    is limited by max_seq_len argument. This function aims to entirely encode the core
    sentences while it try to maintain a minimum number of tokens in the left and right
    edges. The min_context_len parameter sets the minimum number of edge
    tokens. The number of core sentences is dynamically set and it depends on the length
    of the core sentences and on the min_context_len param.
    Arguments:
        sentences: list of strings. Each string represents a sentence and the list represents a document.
        tokenizer: tokenizer of respective sentence encoder.
        max_seq_len: maximum number of tokens in a chunk.
        min_context_len: desired minimum number of edge tokens.
    Returns:
        List of list of integers. The first level list represents a DFCSC. The sublist holds the IDs of the
        tokens in the chunk. A sublist will have the following
        layout: [CLS] left edge tokens [SEP] core sentence 1 [SEP] core sentence 2 [SEP] ...
        core sentence M [SEP] right edge tokens [SEP]
    '''
    # tokenizing sentences to get their number of tokens
    tokenized_sentences = tokenizer(
        sentences,
        add_special_tokens=False,
        padding=False,
        return_token_type_ids=False,
        return_attention_mask=False,
        truncation=True,
        max_length=max_seq_len - 4 # -4 because of special tokens
    )['input_ids']

    # chunks with indexes of respective core sentences
    idx_chunks = core_sentences_idx(tokenized_sentences, max_seq_len, min_context_len)

    # assembling DFCSCs
    chunks = []
    for core_start, core_end in idx_chunks:
        current_chunk = []
        # inserting core sentences
        for i in range(core_start, core_end + 1):
            sent = tokenized_sentences[i].copy()
            sent.append(tokenizer.sep_token_id)
            current_chunk.extend(sent)
        # inserting edges
        current_chunk.insert(0, tokenizer.sep_token_id)
        len_ctx = max_seq_len - len(current_chunk) - 2 # -2 because [CLS] and final [SEP]

        if len_ctx > 0:
            # flattening left edge tokens in a unique list
            left_tokens = []
            for j in range(core_start):
                left_tokens.extend(tokenized_sentences[j])
            # flattening right edge tokens in a unique list
            right_tokens = []
            for j in range(core_end + 1, len(tokenized_sentences)):
                right_tokens.extend(tokenized_sentences[j])
            # inserting edge tokens
            count = 0
            idx_left = len(left_tokens) - 1
            idx_right = 0
            while count < len_ctx and (idx_left >= 0 or idx_right < len(right_tokens)):
                if idx_left >= 0:
                    current_chunk.insert(0, left_tokens[idx_left])
                    idx_left -= 1
                    count += 1
                if idx_right < len(right_tokens) and count < len_ctx:
                    current_chunk.append(right_tokens[idx_right])
                    idx_right += 1
                    count += 1
        # inserting special tokens
        current_chunk.insert(0, tokenizer.cls_token_id)
        current_chunk.append(tokenizer.sep_token_id)

        chunks.append(current_chunk)

    return chunks

def pad_sentences(sentences_as_ids, pad_token_id, max_seq_len):
    """
    Inserts [PAD] tokens in tokenized sentences. The tokens are inserted at right
    untill each tokenized sentence reachs max_seq_len. sentences_as_ids is
    modified in place.
    Arguments:
        sentences_as_ids : list of token ids as returned by the tokenize_sentences function.
        pad_token_id : id of [PAD] token.
        max_seq_len : the desired final length for all sentences.
    """
    for s in sentences_as_ids:
        diff = max_seq_len - len(s)
        if diff > 0:
            for i in range(diff):
                s.append(pad_token_id)

class DFCSC_Dataset(torch.utils.data.Dataset):
    """
    A dataset object representing a set of documents structured as DFCSCs.
    Each item of the dataset represents a DFCSC, i.e., a set of core sentences and edge tokens (check
    the tokenize_sentences function).
    The beginning and end of documents are lost after creation of DFCSCs.
    """
    def __init__(self, dic_docs, labels_to_idx, tokenizer, max_seq_len, min_context_len):
        """
        Arguments:
            dic_docs: a dictionary whose each item is a document (key: docId, value: pandas DataFrame).
            labels_to_idx : dictionary that maps each label (string) to a index (integer).
            tokenizer : tokenizer of the encoder model.
            max_seq_len: maximum sequence length (the maximum number of tokens in a DFCSC).
            min_context_len: desired minimum number of edge tokens.
        """
        self.labels = []    # list of list of strings (n chunks, n core sentences in chunks)
        self.targets = []   # list of unidimensional tensors (n chunks, n core sentences in chunks)
        self.input_ids = [] # tensor of shape (n chunks, max_seq_len)
        self.len = 0

        # tokenizing and spliting sentences in DFCSCs
        for df in dic_docs.values():
            # chunks
            chunks = tokenize_sentences(df['sentence'].tolist(), tokenizer, max_seq_len, min_context_len)
            pad_sentences(chunks, tokenizer.pad_token_id, max_seq_len)
            chunks = torch.tensor(chunks, dtype=torch.long)
            self.input_ids.append(chunks)

            # labels and targets
            labels_in_doc = df['label'].tolist()
            idx_start = 0
            for i in range(chunks.shape[0]):
                n_sep_tokens = torch.count_nonzero(chunks[i] == tokenizer.sep_token_id)
                n_labels_in_chunk = n_sep_tokens - 2 # -2 because SEP token of each context side
                idx_end = idx_start + n_labels_in_chunk
                chunk_labels = labels_in_doc[idx_start:idx_end]
                assert n_labels_in_chunk == len(chunk_labels)
                self.labels.append(chunk_labels)
                self.targets.append(torch.tensor(
                    [labels_to_idx[l] for l in chunk_labels],
                    dtype=torch.long
                ))
                idx_start = idx_end
        self.input_ids = torch.vstack(self.input_ids)
        self.len = self.input_ids.shape[0]

        # creating attention masks
        self.masks = torch.ones(self.input_ids.shape)
        self.masks[self.input_ids == tokenizer.pad_token_id] = 0

    def __getitem__(self, index):
        return {
            'ids': self.input_ids[index],       # shape: (max_seq_len)
            'mask': self.masks[index],          # shape: (max_seq_len)
            'targets': self.targets[index],     # shape: (num of core sentences in current sequence)
            'labels': self.labels[index]        # shape: (num of core sentences in current sequence)
        }

    def __len__(self):
        return self.len

def collate_batch(batch):
    '''
    Prepares a batch of dataset items.
    Arguments:
        batch: list of dataset items (dictionaries).
    Returns:
        A dictionary with following items:
            'ids': tensor of input ids. Shape: (n sequences in batch, max_seq_len)
            'masks': tensor of attention masks. Shape: (n sequences in batch, max_tokens)
            'targets': tensor of golden class ids. Shape: (n core sentences in batch)
            'labels': list of golden labels. Shape: (n core sentences in batch)
    '''
    labels = []
    targets = []
    input_ids = None
    masks = None
    for entry in batch:
        labels.extend(entry['labels'])
        targets.extend(entry['targets'])
        if input_ids is None:
            input_ids = entry['ids'].reshape(1,-1) # reshape to assure a 2-D tensor when the batch contains just one element
            masks = entry['mask'].reshape(1,-1)
        else:
            input_ids = torch.vstack((input_ids, entry['ids']))
            masks = torch.vstack((masks, entry['mask']))
    return {
        'ids': input_ids,
        'mask': masks,
        'targets': torch.tensor(targets, dtype=torch.long),
        'labels': labels
    }

def count_labels(ds, labels):
    """
    Returns the number of sentences by label for a provided DFCSC_Dataset.
    Arguments:
        ds : a DFCSC_Dataset.
        labels: list of strings. The set of possible labels.
    Returns:
       A dictionary mapping each label (string) to its number of sentences (integer).
    """
    count_by_label = {l: 0 for l in labels}
    for sublist in ds.labels:
        for l in sublist:
            count_by_label[l] = count_by_label[l] + 1
    return count_by_label

class MockDFCSC_BERT(torch.nn.Module):
    '''
    A mock of DFCSC_BERT_Classifier. It's usefull to accelerate the validation
    of the training loop.
    '''
    def __init__(self, n_classes, sep_token_id):
        super(MockDFCSC_BERT, self).__init__()
        self.classifier = torch.nn.Linear(10, n_classes) # 10 => random choice
        self.sep_token_id = sep_token_id

    def forward(self, input_ids, attention_mask):
        # getting number of labels/targets
        n_chunks = input_ids.shape[0]
        n_sep_tokens = torch.count_nonzero(input_ids == self.sep_token_id).item()
        n_targets = n_sep_tokens - 2 * n_chunks

        mock_data = torch.rand((n_targets, 10), device=input_ids.device)
        logits = self.classifier(mock_data)    # shape: (n_targets, n_classes)

        return logits

class DFCSC_BERT_Classifier(torch.nn.Module):
    """
    Sentence Classifier based on a BERT kind encoder. This model expects to
    get as inputs sentence representations from a DFCSC_Dataset object.
    The sentence encoder must be a pre-trained model based on BERT's architecture
    like BERT, RoBERTa and ALBERT.
    """
    def __init__(self, encoder_id, sep_token_id, n_classes, dropout_rate, embedding_dim):
        '''
        This model comprises a pre-trained sentence encoder and a classification head.
        The sentence encoder must be a model following BERT architecture.
        The classification head is a single feedforward layer.
        Arguments:
            encoder_id: ID (string) of the encoder model in Hugging Faces repository.
            sep_token_id: ID (integer) of the [SEP] token.
            n_classes: number of classes.
            dropout_rate: dropout rate of classification layer.
            embedding_dim: dimension of hidden units in the sentence encoder (e.g., 768 for BERT).
        '''
        super(DFCSC_BERT_Classifier, self).__init__()
        self.sep_token_id = sep_token_id
        self.encoder = transformers.AutoModel.from_pretrained(encoder_id)

        dropout = torch.nn.Dropout(dropout_rate)
        dense_out = torch.nn.Linear(embedding_dim * 2, n_classes) # multiplication by because we concatenate [CLS] and [SEP] embeddings
        torch.nn.init.xavier_uniform_(dense_out.weight)
        self.classifier = torch.nn.Sequential(
            dropout, dense_out
        )

    def forward(self, input_ids, attention_mask):
        '''
        Each call to this method process a batch of DFCSCs. The model exploits the
        edge tokens and the core sentences to produce embeddings for the core sentences,
        but it doesn't produce embeddings for edge tokens.
        This method returns one logit tensor for each core sentence in the batch.
        Arguments:
            input_ids : tensor of shape (batch_size, seq_len)
            attention_mask : tensor of shape (batch_size, seq_len)
        Returns:
            logits : tensor of shape (n of core sentences in batch, n of classes)
        '''
        output_1 = self.encoder(
            input_ids=input_ids,             # input_ids.shape: (batch_size, seq_len)
            attention_mask=attention_mask    # attention_mask.shape: (batch_size, seq_len)
        )
        hidden_state = output_1.last_hidden_state  # hidden states of last encoder's layer => shape: (batch_size, seq_len, embedd_dim)

        ### In this approach we concatenate [CLS] embedding with [SEP] embedding
        # getting embeddings of core [SEP] and [CLS]tokens
        batch_size = input_ids.shape[0]
        core_embeddings = [] # core_embeddings.shape: (n core sentences in batch, embedding_dim)
        for i in range(batch_size): # iterates chunks
            cls_embedding = hidden_state[i, 0, :] # cls_embedding.shape: (embedd_dim)
            idx_seps = torch.nonzero(input_ids[i] == self.sep_token_id, as_tuple=True)[0] # indexes of all [SEP] tokens in currrent sentence
            # we dont want the embeddings of the 1st and last [SEP] tokens
            idx_core_sep = idx_seps[1:-1] # index of core [SEP] tokens in current chunk
            core_sep_emb = hidden_state[i, idx_core_sep, :] # embeddings of core [SEP]. shape: (n core sentences in chunk, embedd_dim)
            for j in range(core_sep_emb.shape[0]):
                core_embeddings.append(
                    torch.hstack((cls_embedding, core_sep_emb[j]))
                )
        core_embeddings = torch.vstack(core_embeddings)

        logits = self.classifier(core_embeddings)   # logits.shape: (n core sentences in batch, num of classes)

        return logits

class LongformerEncoder(torch.nn.Module):
    '''
    Uses a Longformer model to encode DFCSCs.
    '''

    def __init__(self, encoder_id, sep_token_id):
        super(LongformerEncoder, self).__init__()
        self.sep_token_id = sep_token_id
        self.encoder = transformers.AutoModel.from_pretrained(encoder_id)

    def forward(self, input_ids, attention_mask):
        '''
        Encodes a batch of DFCSCs.
        Arguments:
            input_ids: PyTorch tensor with shape (batch_size, chunk_len).
            attention_mask: PyTorch tensor with shape (batch_size, chunk_len).
        Returns:
            PyTorch tensor with shape (n core sentences in batch, embedding_dim * 2). This tensor
            holds the embeddings of the core sentences from all chunks in the batch. Each embedding
            is the concatenation of the last hidden state of the <s> token of the respective chunk and
            the respective last hidden state of the </s> token of the respective core sentence.
        '''
        # The global_attention_mask tells which tokens must have global attention.
        # Here, <s> and </s> have global attention
        global_attention_mask = torch.zeros(
            input_ids.shape,
            dtype=torch.long,
            device=input_ids.device
        )
        global_attention_mask[:, 0] = 1 # global attention for <s> since it must be the first token in a chunk
        idx_sep = torch.nonzero(input_ids == self.sep_token_id, as_tuple=True)
        for i in range(idx_sep[0].shape[0]):
            global_attention_mask[idx_sep[0][i], idx_sep[1][i]] = 1 # global attention for </s>

        # Encoding
        output_1 = self.encoder(
            input_ids=input_ids,             # input_ids.shape: (batch_size, chunk_len)
            attention_mask=attention_mask    # attention_mask.shape: (batch_size, chunk_len)
        )
        hidden_state = output_1.last_hidden_state  # hidden states of last encoder's layer => shape: (batch_size, chunk_len, embedd_dim)

        ### In this approach we concatenate <s> embedding with </s> embedding
        # getting embeddings of core </s> and <s> tokens
        batch_size = input_ids.shape[0]
        core_embeddings = []
        for i in range(batch_size): # iterates chunks
            cls_embedding = hidden_state[i, 0, :] # cls_embedding.shape: (embedd_dim)
            idx_seps = torch.nonzero(input_ids[i] == self.sep_token_id, as_tuple=True)[0] # indexes of all </s> tokens in currrent sentence
            # we dont want the embeddings of the 1st and last </s> tokens
            idx_core_sep = idx_seps[1:-1] # index of core </s> tokens in current chunk
            core_sep_emb = hidden_state[i, idx_core_sep, :] # embeddings of core </s>. shape: (n core sentences in chunk, embedd_dim)
            for j in range(core_sep_emb.shape[0]):
                core_embeddings.append(
                    torch.hstack((cls_embedding, core_sep_emb[j]))
                )
        core_embeddings = torch.vstack(core_embeddings) # core_embeddings.shape: (n core sentences in batch, embedding_dim * 2)
        return core_embeddings

class DFCSC_Longformer_Classifier(torch.nn.Module):
    """
    Sentence Classifier based on a Longformer kind encoder. This model expects to
    get as inputs sentence represenations from a DFCSC_Dataset object.
    The sentence encoder must be a pre-trained model based on Longformer's architecture.
    """
    def __init__(self, encoder_id, sep_token_id, n_classes, dropout_rate, embedding_dim):
        '''
        This model comprises a pre-trained sentence encoder and a classification head.
        The sentence encoder must be a model following Longformer architecture.
        The classification head is a linear classifier (a single feedforward layer).
        Arguments:
            encoder_id: ID (string) of the encoder model in Hugging Faces repository.
            sep_token_id: ID (integer) of the [SEP] token.
            n_classes: number of classes.
            dropout_rate: dropout rate of classification layer.
            embedding_dim: dimension of hidden units in the sentence encoder (e.g., 768 for Longformer).
        '''
        super(DFCSC_Longformer_Classifier, self).__init__()
        self.encoder = LongformerEncoder(encoder_id, sep_token_id)

        dropout = torch.nn.Dropout(dropout_rate)
        dense_out = torch.nn.Linear(embedding_dim * 2, n_classes)
        torch.nn.init.xavier_uniform_(dense_out.weight)
        self.classifier = torch.nn.Sequential(
            dropout, dense_out
        )

    def forward(self, input_ids, attention_mask):
        '''
        Each call to this method process a batch of DFCSCs. The model exploits the
        edge tokens and the core sentences to produce embeddings for the core sentences,
        but it doesn't produce embeddings for edge tokens.
        This method returns one logit tensor for each core sentence in the batch.
        Arguments:
            input_ids : tensor of shape (batch_size, seq_len)
            attention_mask : tensor of shape (batch_size, seq_len)
        Returns:
            logits : tensor of shape (n of core sentences in batch, n of classes)
        '''
        core_embeddings = self.encoder(input_ids, attention_mask)

        logits = self.classifier(core_embeddings)   # logits.shape: (n core sentences in batch, num of classes)

        return logits

def evaluate(model, test_dataloader, loss_function, device):
    """
    Evaluates a provided DFCSC_Classifier model.
    Arguments:
        model: the model to be evaluated.
        test_dataloader: torch.utils.data.DataLoader instance containing the test data.
        loss_function: instance of the loss function used to train the model.
        device: device where the model is located.
    Returns:
        eval_loss (float): the computed test loss score.
        precision (float): the computed test macro Precision score.
        recall (float): the computed test macro Recall score.
        f1 (float): the computed test macro F1 score.
        confusion_matrix: the computed test confusion matrix.
    """
    predictions = torch.tensor([]).to(device)
    y_true = torch.tensor([]).to(device)
    eval_loss = 0
    model.eval()
    with torch.no_grad():
        for data in test_dataloader:
            ids = data['ids'].to(device)
            mask = data['mask'].to(device)
            y_true_batch = data['targets'].to(device)
            y_hat = model(ids, mask)
            # ignores classes with negative ID
            idx_valid = (y_true_batch >= 0).nonzero().squeeze()
            y_true_batch_valid = y_true_batch[idx_valid]
            y_hat_valid = y_hat[idx_valid]
            loss = loss_function(y_hat_valid, y_true_batch_valid)
            eval_loss += loss.item()
            predictions_batch = y_hat_valid.argmax(dim=1)
            predictions = torch.cat((predictions, predictions_batch))
            y_true = torch.cat((y_true, y_true_batch_valid))
        predictions = predictions.detach().to('cpu').numpy()
        y_true = y_true.detach().to('cpu').numpy()
    eval_loss = eval_loss / len(test_dataloader)
    t_metrics_macro = precision_recall_fscore_support(
        y_true,
        predictions,
        average='macro',
        zero_division=0
    )
    cm = confusion_matrix(
        y_true,
        predictions
    )

    return eval_loss, t_metrics_macro[0], t_metrics_macro[1], t_metrics_macro[2], cm

def fit(train_params, ds_train, ds_test, device):
    """
    Creates and train an instance of DFCSC_Classifier.
    Arguments:
        train_params: dictionary storing the training params.
        ds_train: instance of DFCSC_Dataset storing the train data.
        tokenizer: the tokenizer of the chosen pre-trained sentence encoder.
        device: device where the model is located.
    """
    learning_rate = train_params['learning_rate']
    weight_decay = train_params['weight_decay']
    n_epochs = train_params['n_epochs']
    batch_size = train_params['batch_size']
    encoder_id = train_params['encoder_id']
    n_classes = train_params['n_classes']
    dropout_rate = train_params['dropout_rate']
    embedding_dim = train_params['embedding_dim']
    use_mock = train_params['use_mock']

    dl_train = torch.utils.data.DataLoader(ds_train, batch_size=batch_size, shuffle=True, collate_fn=collate_batch)
    dl_test = torch.utils.data.DataLoader(ds_test, batch_size=batch_size, shuffle=True, collate_fn=collate_batch)

    if use_mock:
        sentence_classifier = MockDFCSC_BERT(n_classes, train_params['sep_token_id']).to(device)
    elif encoder_id.lower().find('longformer') > -1:
        sentence_classifier = DFCSC_Longformer_Classifier(
            encoder_id,
            train_params['sep_token_id'],
            n_classes,
            dropout_rate,
            embedding_dim
        ).to(device)
    else:
        sentence_classifier = DFCSC_BERT_Classifier(
            encoder_id,
            train_params['sep_token_id'],
            n_classes,
            dropout_rate,
            embedding_dim
        ).to(device)

    criterion = torch.nn.CrossEntropyLoss().to(device)
    optimizer = torch.optim.Adam(
        sentence_classifier.parameters(),
        lr=learning_rate,
        betas=(0.9, 0.999),
        eps=train_params['eps'],
        weight_decay=weight_decay
    )
    lr_scheduler = transformers.get_linear_schedule_with_warmup(
        optimizer,
        num_warmup_steps = 0,
        num_training_steps = len(dl_train) * n_epochs
    )

    metrics = {} # key: epoch number, value: numpy tensor storing train loss, test loss, Precision (macro), Recall (macro), F1 (macro)
    confusion_matrices = {} # key: epoch number, value: scikit-learn's confusion matrix
    start_train = time.perf_counter()
    for epoch in range(1, n_epochs + 1):
        print(f'Starting epoch {epoch}... ', end='')
        start_epoch = time.perf_counter()
        epoch_loss = 0
        sentence_classifier.train()
        for train_data in dl_train:
            optimizer.zero_grad()
            ids = train_data['ids'].to(device)
            mask = train_data['mask'].to(device)
            y_hat = sentence_classifier(ids, mask)
            y_true = train_data['targets'].to(device)
            # ignores classes with negative ID
            idx_valid = (y_true >= 0).nonzero().squeeze()
            y_true_valid = y_true[idx_valid]
            y_hat_valid = y_hat[idx_valid]
            loss = criterion(y_hat_valid, y_true_valid)
            epoch_loss += loss.item()
            loss.backward()
            torch.nn.utils.clip_grad_norm_(sentence_classifier.parameters(), 1.0)
            optimizer.step()
            lr_scheduler.step()
        epoch_loss = epoch_loss / len(dl_train)
        # evaluation
        optimizer.zero_grad()
        eval_loss, p_macro, r_macro, f1_macro, cm = evaluate(
            sentence_classifier,
            dl_test,
            criterion,
            device
        )
        #storing metrics
        metrics[epoch] = np.array([epoch_loss, eval_loss, p_macro, r_macro, f1_macro])
        confusion_matrices[epoch] = cm
        end_epoch = time.perf_counter()
        print('finished! Time: ', time.strftime("%Hh%Mm%Ss", time.gmtime(end_epoch - start_epoch)))

    end_train = time.perf_counter()

    return metrics, confusion_matrices, time.strftime("%Hh%Mm%Ss", time.gmtime(end_train - start_train))


# yarrow.py

# returns random integer from 1-64 using a simulation of the yarrow-stalk oracle,
# as described on pages 721-723 of The I-Ching, Wilhelm/Baynes, Third Edition, Bollinbger Foundation, Princeton University Press 1980 printing.

import random

# this is intended to produce a gaussian distribution that weights towards the center, since people will typically produce more even splits
def split_pile(v):
	a1 = random.randint(1,v-1)
	a2 = random.randint(1,v-1)
	heap_left = int((a1 + a2)/2.0+0.5) # rounded average
	heap_right = v - heap_left
	return (heap_left, heap_right)

def get_trigram_line():
	nbr_stalks = 50-1

	# first sum
	pile_left,pile_right = split_pile(nbr_stalks)
	p1 = 1
	pile_right -= p1
	p2 = (pile_left % 4)
	p2 = 4 if p2 == 0 else p2
	p3 = (pile_right % 4)
	p3 = 4 if p3 == 0 else p3
	sum_a = (p1+p2+p3)

	nbr_stalks -= sum_a

	# second sum
	pile_left,pile_right = split_pile(nbr_stalks)
	p1 = 1
	pile_right -= p1
	p2 = (pile_left % 4)
	p2 = 4 if p2 == 0 else p2
	p3 = (pile_right % 4)
	p3 = 4 if p3 == 0 else p3
	sum_b = (p1+p2+p3)

	nbr_stalks -= sum_b

	# third sum
	pile_left,pile_right = split_pile(nbr_stalks)
	p1 = 1
	pile_right -= p1
	p2 = (pile_left % 4)
	p2 = 4 if p2 == 0 else p2
	p3 = (pile_right % 4)
	p3 = 4 if p3 == 0 else p3
	sum_c = (p1+p2+p3)
	v = (2 if sum_a == 9 else 3) + (2 if sum_b == 8 else 3) + (2 if sum_c == 8 else 3)
	return (v, sum_a, sum_b, sum_c)

# return number from 1-64
# convert trigram patterns to hexagram numbers
tri_table = [ 1, 43, 14, 34,  9,  5, 26, 11,
             10, 58, 38, 54, 61, 60, 41, 19,
             13, 49, 30, 55, 37, 63, 22, 36,
             25, 17, 21, 51, 42,  3, 27, 24,
             44, 28, 50, 32, 57, 48, 18, 46,
              6, 47, 64, 40, 59, 29,  4,  7,
             33, 31, 56, 62, 53, 39, 52, 15,
             12, 45, 35, 16, 20,  8, 23,  2,
]

def get_hexagram():
	bits = 0
	for b in range(6):
		(v,sa,sb,sc) = get_trigram_line()
		if (v == 7 or v == 9): # yang
			bits |= (1 << b)
	return tri_table[bits]

def get_hexagram_frange(min_v, max_v):
	return min_v + (max_v - min_v)*(get_hexagram()/64.0)

def get_hexagram_irange(min_v, max_v):
	return int(min_v + (max_v - min_v)*(get_hexagram()/64.0) + 0.5)

# buckets = [0] * 64
# nbr_tests = 50

# for i in range(nbr_tests):
# 	t = get_hexagram()
# 	buckets[t-1] += 1
# 	print(t)

# max_bucket = max(buckets)
# for i in range(64):
# 	v = int(buckets[i]/float(max_bucket)*64 + 0.5)
# 	print("%02d %s" % (i+1,"*" * v))

# buckets = [0] * 64
# for i in range(nbr_tests):
# 	t = random.randint(1,64)
# 	buckets[t-1] += 1

# max_bucket = max(buckets)
# for i in range(64):
# 	v = int(buckets[i]/float(max_bucket)*64 + 0.5)
# 	print("%02d %s" % (i+1,"*" * v))


import sys
import base64

def get_license_key(name, license):

    # Compute the base64 license key. This key will be provided to you by Vade Secure,
    # and has the following format: <CUSTOMER_NAME>:<CUSTOMER_LICENSE>.
    url_key = u'{0}:{1}'.format(name, license)

    # It must be Base64-encoded. Handled different on Python 2 vs 3.
    if sys.version_info[0] == 2:
        auth_token = base64.b64encode(bytes(url_key).encode("utf-8"))
    else:
        auth_token = base64.b64encode(bytes(url_key, 'ascii')).decode('ascii')

    return auth_token


from telethon.sync import TelegramClient
from telethon.tl.functions.channels import CreateChannelRequest, CheckUsernameRequest, UpdateUsernameRequest
from telethon.tl.types import InputPeerChannel
from telethon.tl.types import DocumentAttributeVideo
from telethon import functions

class TelethonHelpers(TelegramClient):
    def __init__(self, api_id, api_hash, client_name, restrict_users=False) :
        self.client = self.get_client(api_id, api_hash, client_name, init=True)
        self.restrict_users = restrict_users

    def get_client(self, api_id, api_hash, client_name, init=False):
        if init == False and self.client:
            return self.client
        else:
            client = TelegramClient.__init__(client_name, api_id, api_hash)
            client.start()
            return client

    def get_chat_id(self, entity_name=None, entity_id=None):
        dialogs = self.client.get_dialogs()
        for dialog in dialogs:
            if (entity_name and dialog.name == entity_name) \
                or (entity_id and dialog.message.peer_id == entity_id):
                return dialog

        return None

    def create_group_if_not_exist(self, entity_name, group_about, messages, avatar=None):
        chat_id = self.get_chat_id(entity_name=entity_name)

        if not chat_id:
            chat_id = self.create_group(entity_name, group_about)
            if avatar:
                upload_file_result = self.client.upload_file(file=avatar)
                try:
                    result = self.client(functions.channels.EditPhotoRequest(
                        channel=chat_id,
                        photo=upload_file_result
                    ))
                except Exception as e:
                    print(f'Error setting group avatar.\n{e}')

            for message in messages:
                if message.get('type') == 'message':
                    self.send_message(entity_name, message.get('message'))
                else:
                    self.send_file(entity_name, message.get('path'))

    def send_message(self, entity_name, message):
        try:
            dialog = self.get_chat_id(entity_name=entity_name)

            if dialog:
                response = self.client.send_message(entity=dialog, message=message)
            else:
                print(f'No chat found - {entity_name}')
        except Exception as e:
            print(f'Error while sending telegram message\n{e}')

    def send_file(self, entity_name, file, text=None):
        try:
            dialog = self.get_chat_id(entity_name=entity_name)
            if dialog:
                attributes = ()
                if '.mp4' in file:
                    attributes=(DocumentAttributeVideo(0, 0, 0),)
                response = self.client.send_file(entity=dialog, file=file, attributes=attributes, caption=text)
            else:
                print(f'No chat found - {entity_name}')
        except Exception as e:
            print(f'Error while sending telegram message\n{e}')

    def create_group(self, group_name, group_about=None):
        createdPrivateChannel = self.client(CreateChannelRequest(group_name,group_about,megagroup=True))

        newChannelID = createdPrivateChannel.__dict__["chats"][0].__dict__["id"]
        newChannelAccessHash = createdPrivateChannel.__dict__["chats"][0].__dict__["access_hash"]
        desiredPublicUsername = "myUsernameForPublicChannel"
        checkUsernameResult = self.client(CheckUsernameRequest(InputPeerChannel(channel_id=newChannelID, access_hash=newChannelAccessHash), desiredPublicUsername))
        if(checkUsernameResult==True):
            publicChannel = self.client(UpdateUsernameRequest(InputPeerChannel(channel_id=newChannelID, access_hash=newChannelAccessHash), desiredPublicUsername))
        if self.restrict_users:
            try:
                self.client.edit_permissions(entity=group_name, view_messages=True, send_games=False, send_gifs=False, send_inline=False, send_media=False, send_messages=False, send_polls=False, send_stickers=False, change_info=False)
            except Exception as e:
                print(f'Error while trying to edit group permissions.\nError: {e}')
        return newChannelID


#!/usr/bin/env python2.7
# bamEvaluateStep.py module holds BamEvaluateStep class which descends from LogicalStep class.
# It takes a sample of a bam and characterizes the library complexity
#
# Inputs: 1 bam, pre-registered in the analysis keyed as: 'alignmentRep' + replicate + '.bam'
# Outputs: 1 interim Corr       file, keyed as: 'strandCorr' + suffix +    '.txt'
#          1 target json        file, keyed as: 'bamEvaluate' + suffix +   '.json'

from src.logicalStep import LogicalStep
from src.settings import Settings

class BamEvaluateStep(LogicalStep):

    def __init__(self, analysis, replicate="1", suffix=""):
        self.replicate = str(replicate)
        self.suffix = suffix
        LogicalStep.__init__(self, analysis, 'bamEvaluate' + self.suffix)

    def writeVersions(self,raFile=None,allLevels=False,scriptName=None):
        '''Writes versions to to the log or a file.'''
        if allLevels:
            LogicalStep.writeVersions(self, raFile)
        if raFile != None:
            if scriptName != None:
                raFile.add(scriptName, self.getToolVersion(scriptName))
            raFile.add('edwBamStats', self.getToolVersion('edwBamStats'))
            raFile.add('samtools', self.getToolVersion('samtools'))
            raFile.add('Rscript', self.getToolVersion('Rscript'))
            raFile.add('run_spp.R', self.getToolVersion(self.ana.toolsDir+'run_spp.R'))
        else:
            if scriptName != None:
                self.getToolVersion(scriptName)
            self.getToolVersion('edwBamStats')
            self.getToolVersion('samtools')
            self.getToolVersion('Rscript')
            self.getToolVersion(self.ana.toolsDir+'run_spp.R')

    def onRun(self):
        # Inputs:
        bam = self.ana.getFile('alignmentRep' + self.replicate + '.bam')

        # Outputs:
        strandCorr = self.declareInterimFile('strandCorr'+ self.suffix + '.txt')
        bamSample  = self.declareGarbageFile('alignment' + self.suffix + '_5M.bam')

        # Run the val script
        statsRa  = self.declareGarbageFile('stats.ra')
        if self.ana.type == 'DNase':
            tagLen = 36 # 100? TODO find this info from somewhere
            spotsRa  = self.declareGarbageFile('spots.ra')
            self.eap_dnase_stats(bam,self.ana.genome,tagLen,statsRa,strandCorr,spotsRa)
        else:
            self.eap_eval_bam(bam,bamSample,statsRa,strandCorr)

        # json summary of stats:
        if not self.ana.dryRun:
            stats = Settings(statsRa)
            for key in stats.keys():   # sorted( stats.keys() )
                if key == 'isPaired':
                    self.json[key] = stats.getBoolean(key)
                    if stats.getBoolean(key):
                        self.ana.readType = "paired"
                    else:
                        self.ana.readType = "single"
                elif key == 'isSortedByTarget':
                    self.json[key] = stats.getBoolean(key)
                elif key == 'readCount' or key == 'readBaseCount' or key == 'mappedCount' \
                  or key == 'uniqueMappedCount' or key == 'u4mReadCount' or key == 'u4mUniquePos' \
                  or key == 'targetBaseCount':
                    self.json[key] = long(stats.get(key))
                elif key == 'readSizeMean' or key == 'readSizeStd' or key == 'readSizeMin' \
                  or key == 'readSizeMax' or key == 'targetSeqCount':
                    self.json[key] = int(stats.get(key))
                elif key == 'u4mUniqueRatio':
                    self.json[key] = float(stats.get(key))
                else:
                    self.json[key] = stats.get(key)
        else:
            self.ana.readType = "single"
            self.json['alignedBy'        ] = 'unknown'
            self.json['isPaired'         ] = False
            self.json['isSortedByTarget' ] = True
            self.json['readCount'        ] = 0
            self.json['readBaseCount'    ] = 0
            self.json['mappedCount'      ] = 0
            self.json['uniqueMappedCount'] = 0
            self.json['readSizeMean'     ] = 50
            self.json['readSizeStd'      ] = 0
            self.json['readSizeMin'      ] = 0
            self.json['readSizeMax'      ] = 0
            self.json['u4mReadCount'     ] = 0
            self.json['u4mUniquePos'     ] = 0
            self.json['u4mUniqueRatio'   ] = 0
            self.json['targetSeqCount'   ] = 0
            self.json['targetBaseCount'  ] = 0

        self.json['strandCorrelation'] = {}
        if not self.ana.dryRun:
            with open(strandCorr, 'r') as strandFile:
                for line in strandFile:
                    splits = line.split('\t')
                    self.json['strandCorrelation']['Frag'] = splits[2]
                    self.json['strandCorrelation']['RSC'] = splits[8]
                    self.json['strandCorrelation']['NSC'] = splits[9]
        else:
            self.json['strandCorrelation']['Frag'] = 0
            self.json['strandCorrelation']['RSC'] = 0
            self.json['strandCorrelation']['NSC'] = 0

        if self.ana.type == 'DNase':
            self.json['hotspots'] = {}
            if not self.ana.dryRun:
                spots = Settings(spotsRa)
                for key in spots.keys():   # sorted( stats.keys() )
                    if key == 'spotRatio' or key == 'enrichment' or key == 'maxEnrichment' \
                    or key == 'sumSignal' or key == 'spotSumSignal':
                        self.json['hotspots'][key] = float(spots.get(key))
                    elif key == 'basesInGenome' or key == 'basesInSpots' \
                      or key == 'basesInSpotsWithSignal':
                        self.json['hotspots'][key] = long(spots.get(key))
                    else:
                        self.json['hotspots'][key] = spots.get(key)
            else:
                self.json['hotspots']['spotRatio'] = 0.1
                self.json['hotspots']['enrichment']   = 0.2
                self.json['hotspots']['maxEnrichment']   = 0.3
                self.json['hotspots']['basesInGenome']     = 4
                self.json['hotspots']['basesInSpots']         = 5
                self.json['hotspots']['basesInSpotsWithSignal']  = 6
                self.json['hotspots']['sumSignal']              = '7.654321e+08'
                self.json['hotspots']['spotSumSignal']         = '8.765432e+08'

        self.createAndWriteJsonFile( 'bamEvaluate'+ self.suffix, target=True )

    def eap_eval_bam(self, inBam, outSampleBam, outStats, outStrandCorr):
        '''Evaluate bam file'''

        cmd = "eap_eval_bam {bamIn} {bamOut} {statsOut} {strandCorrOut}".format( \
              bamIn=inBam, bamOut=outSampleBam, statsOut=outStats, strandCorrOut=outStrandCorr)

        toolName = 'eap_eval_bam'
        self.toolBegins(toolName)
        self.writeVersions(scriptName=toolName)

        self.err = self.ana.runCmd(cmd, log=self.log)
        self.toolEnds(toolName,self.err)

    def eap_dnase_stats(self, inBam, genome, tagLen, outBamStats, outStrandCorr, outSpots):
        '''Evaluate bam file for DNase experiments'''

        #eap_dnase_stats in.bam target readSize bamStats.ra sppStats.ra spotStats.ra
        cmd = "eap_dnase_stats {bamIn} {db} {readSize} {bamStatsOut} {sppOut} {spotsOut}".format( \
              bamIn=inBam, db=genome, readSize=tagLen, bamStatsOut=outBamStats, \
              sppOut=outStrandCorr, spotsOut=outSpots)

        toolName = 'eap_dnase_stats'
        self.toolBegins(toolName)
        self.writeVersions(scriptName=toolName)

        self.err = self.ana.runCmd(cmd, log=self.log)
        self.toolEnds(toolName,self.err)


from tkinter import *
from PIL import Image, ImageTk
from random import randint
window = Tk()
window.title("Rock paper Scissors")
window.configure(background="black")

rock_l = ImageTk.PhotoImage(Image.open("rock left.png"))
paper_l = ImageTk.PhotoImage(Image.open("paper left.png"))
scissors_l = ImageTk.PhotoImage(Image.open("scissors left.png"))

rock_r = ImageTk.PhotoImage(Image.open("rock right.png"))
paper_r = ImageTk.PhotoImage(Image.open("paper right.png"))
scissors_r = ImageTk.PhotoImage(Image.open("scissors right.png"))

label_player = Label(window, image=scissors_r)
label_computer = Label(window, image=scissors_l)
label_computer.grid(row=1, column=0)
label_player.grid(row=1, column=4)

compuer_score = Label(window, text=" 0 ", font=(
    'arial', 60, "bold"),  fg="blue")
player_score = Label(window, text=" 0 ", font=('arial', 60, "bold"), fg="blue")

compuer_score.grid(row=1, column=1)
player_score.grid(row=1, column=3)

player_indicator = Label(window, font=(
    'arial', 40, "bold"), text="PLAYER", bg="cyan", fg="blue")
computer_indicator = Label(window, font=(
    'arial', 40, "bold"), text="COMPUTER", bg="cyan", fg="blue")

computer_indicator.grid(row=0, column=1)
player_indicator.grid(row=0, column=3)

final_message = Label(window, font=('arial', 40, "bold"),
                      text="Start", bg="red", fg="white")
final_message.grid(row=3, column=2)

def msg_update(a):
    final_message['text'] = a

def Comp_update():
    final = int(compuer_score['text'])
    final += 1
    compuer_score['text'] = str(final)

def Player_update():
    final = int(player_score['text'])
    final += 1
    player_score['text'] = str(final)

def winner(p, c):
    if (p == c):
        msg_update("Its a tie")
    elif p == "rock":
        if c == "paper":
            msg_update("Computer Wins !!!")
            Comp_update()
        else:
            msg_update("Player Wins !!!")
            Player_update()
    elif p == "paper":
        if (c == "scissors"):
            msg_update("Computer Wins !!!")
            Comp_update()
        else:
            msg_update("Player Wins !!!")
            Player_update()
    elif p == "scissors":
        if c == "rock":
            msg_update("Computer Wins !!!")
            Comp_update()
        else:
            msg_update("Player Wins !!!")
            Player_update()
    else:
        pass

to_select = ["rock", "paper", "scissors"]

def choice_update(a):
    choice_comp = to_select[randint(0, 2)]
    if (choice_comp == "rock"):
        label_computer.configure(image=rock_l)
    elif (choice_comp == "paper"):
        label_computer.configure(image=paper_l)
    else:
        label_computer.configure(image=scissors_l)

    if (a == "rock"):
        label_player.configure(image=rock_r)
    elif (a == "paper"):
        label_player.configure(image=paper_r)
    else:
        label_player.configure(image=scissors_r)

    winner(a, choice_comp)

button_rock = Button(window, text="Rock", width=20, height=4, font=(
    "arial", 20, "bold"), bg="magenta", fg="black", command=lambda: choice_update("rock")).grid(row=2, column=1)
button_paper = Button(window, text="Paper", width=20, height=4, font=(
    "arial", 20, "bold"), bg="magenta", fg="black", command=lambda: choice_update("paper")).grid(row=2, column=2)
button_sci = Button(window, text="Scissors", width=20, height=4, font=(
    "arial", 20, "bold"), bg="magenta", fg="black", command=lambda: choice_update("scissors")).grid(row=2, column=3)
window.mainloop()