from os.path import exists, join
from collections import defaultdict
import numpy as np
import os
import time
import logging
import re
import pickle
import shutil
import signal
from multiprocessing import Pool

try:
    from spython.main import Client as client
except:
    pass

BUFFER_PATH = os.getenv('BUFFER_PATH')
if not BUFFER_PATH:
    BUFFER_PATH = "local_buffer"

def run_actor_in_container(id=0):
    out = client.execute(
        join(BUFFER_PATH, "nav_benchmark.sif"),
        ['/bin/bash', '/jackal_ws/src/ros_jackal/entrypoint.sh', 'python3', 'actor.py', '--id=%d' %id],
        bind=['%s:%s' %(BUFFER_PATH, BUFFER_PATH), '%s:%s' %(os.getcwd(), "/jackal_ws/src/ros_jackal")],
        options=["-i", "-n", "--network=none", "-p"], nv=True
    )
    return out

class LocalCollector(object):
    def __init__(self, policy, env, replaybuffer):
        self.policy = policy
        self.env = env
        self.buffer = replaybuffer

        self.last_obs = None

        self.global_episodes = 0
        self.global_steps = 0

    def collect(self, n_steps):
        n_steps_curr = 0
        env = self.env
        policy = self.policy
        results = []

        ep_rew = 0
        ep_len = 0

        if self.last_obs is not None:
            obs = self.last_obs
        else:
            obs = env.reset()
        while n_steps_curr < n_steps:
            act = policy.select_action(obs)
            obs_new, rew, done, info = env.step(act)
            ep_rew += rew
            ep_len += 1
            n_steps_curr += 1
            self.global_steps += 1

            world = int(info['world'].split(
                "_")[-1].split(".")[0])  # task index
            collision_reward = -int(info['collided'])
            self.buffer.add(obs, act,
                            obs_new, rew,
                            done, world, collision_reward)
            obs = obs_new
            if done:
                obs = env.reset()
                results.append(dict(
                    ep_rew=ep_rew,
                    ep_len=ep_len,
                    success=info['success'],
                    ep_time=info['time'],
                    world=info['world'],
                    collision=info['collision']
                ))
                ep_rew = 0
                ep_len = 0
                self.global_episodes += 1
            print("n_episode: %d, n_steps: %d" %(self.global_episodes, self.global_steps), end="\r")
        self.last_obs = obs
        return n_steps_curr, results

class ContainerCollector(object):
    def __init__(self, policy, env, replaybuffer, config):
        '''
        This collector create Singularty containers and collect tracjories from them
        '''
        super().__init__()
        self.config = config
        self.policy = policy
        self.buffer = replaybuffer

        self.num_actor = config['container_config']['num_actor']
        self.ids = list(range(self.num_actor))
        self.ep_count = [0] * self.num_actor

        if not exists(BUFFER_PATH):
            os.mkdir(BUFFER_PATH)

        # save the current policy to the buffer
        # which the actors in the containers can load from
        self.update_policy("policy")

        # save the env config the actor should read from
        shutil.copyfile(
            config["env_config"]["config_path"],
            join(BUFFER_PATH, "config.yaml")
        )

        # These two env variable ensure ROS running correctly in the container
        os.environ["ROS_HOSTNAME"] = "localhost"
        os.environ["ROS_MASTER_URI"] = "http://localhost:11311"

    def buffer_expand(self, traj):
        for i in range(len(traj)):
            state, action, reward, done, info = traj[i]
            state_next = traj[i+1][0] if i < len(traj)-1 else traj[i][0]
            world = int(info['world'].split(
                "_")[-1].split(".")[0])  # task index

            # For safeRL, separate the collision reward
            collision_reward = -int(info['collided'])
            assert collision_reward <= 0, "%.2f" %collision_reward

            self.buffer.add(state, action,
                            state_next, reward,
                            done, world, collision_reward)

    def natural_keys(self, text):
        return int(re.split(r'(\d+)', text)[1])

    def sort_traj_name(self, traj_files):
        ep_idx = np.array([self.natural_keys(fn) for fn in traj_files])
        idx = np.argsort(ep_idx)
        return np.array(traj_files)[idx]

    def update_policy(self, name):
        self.policy.save(BUFFER_PATH, "%s_copy" %name)
        # To prevent failure of actors when reading the saved policy
        shutil.move(
            join(BUFFER_PATH, "%s_copy_actor" %name),
            join(BUFFER_PATH, "%s_actor" %name)
        )
        shutil.move(
            join(BUFFER_PATH, "%s_copy_noise" %name),
            join(BUFFER_PATH, "%s_noise" %name)
        )
        if os.path.exists(join(BUFFER_PATH, "%s_copy_model" %name)):
            shutil.move(
                join(BUFFER_PATH, "%s_copy_model" %name),
                join(BUFFER_PATH, "%s_model" %name)
            )

    def collect(self, n_steps):
        """ This method searches the buffer folder and collect all the saved trajectories
        """
        # collect happens after policy is updated
        # save the updated policy to the buffer
        self.update_policy("policy")
        steps = 0
        results = []

        while steps < n_steps:
            # Launch containers to collect trajectories
            # Each subprocess is a container running an actor and collect 5 trajectories
            if not exists(join(BUFFER_PATH, "nav_benchmark.sif")):
                image, puller = client.pull('library://zifanxu/ros_jackal_image/image:latest', stream=True, pull_folder=BUFFER_PATH, name='nav_benchmark.sif')
                for line in puller:
                    print(line)

            with Pool(self.num_actor) as p:
                output = p.map(run_actor_in_container, self.ids)
            for o in output:
                print(o[0])

            np.random.shuffle(self.ids)
            for id in self.ids:
                id_steps, id_trajs, id_results = self.collect_worker_traj(id)
                steps += id_steps
                results.extend(id_results)
                for t in id_trajs:
                    self.buffer_expand(t)
        return steps, results

    def collect_worker_traj(self, id, skip_first=False):
        steps = 0
        trajs = []
        results = []
        base = join(BUFFER_PATH, 'actor_%d' % (id))
        try:
            traj_files = os.listdir(base)
        except:
            traj_files = []
        if skip_first:
            traj_files = self.sort_traj_name(traj_files)[:-1]
        else:
            traj_files = self.sort_traj_name(traj_files)
        for p in traj_files:
            try:
                target = join(base, p)
                if os.path.getsize(target) > 0:
                    with open(target, 'rb') as f:
                        traj = pickle.load(f)
                        ep_rew = sum([t[2] for t in traj])
                        ep_len = len(traj)
                        success = float(traj[-1][-1]['success'])
                        ep_time = traj[-1][-1]['time']
                        world = traj[-1][-1]['world']
                        collision = traj[-1][-1]['collision']
                        assert not np.isnan(ep_rew), "%s" %target
                        results.append(dict(ep_rew=ep_rew, ep_len=ep_len, success=success, ep_time=ep_time, world=world, collision=collision))
                        trajs.append(traj)
                        steps += ep_len
                    os.remove(join(base, p))
            except:
                logging.exception('')
                print("failed to load actor_%s:%s" % (id, p))
                # os.remove(join(base, p))
                pass
        return steps, trajs, results


# -*- coding: utf-8 -*-

import numpy as np
import pandas as pd

import argparse
import glob
import sys
import os
import sys
import random
import copy
import json
import time
from datetime import datetime as dt
import matplotlib.pyplot as plt

path = os.path.join(os.path.dirname(__file__), '../')
sys.path.append(path)
import ddim_change_symptom.gmm_generator as gg
import ddim_change_symptom.ts_clustering as tc
import ddim_change_symptom.dynamic_model_selection as dms
import ddim_change_symptom.descriptional_dimension as ddim
import ddim_change_symptom.symptom_evaluation as sym_eval
import ddim_change_symptom.fixed_share as fs

class simDdimRealData():
    '''Simulate Descriptional Dimension (Ddim)
    [Parameters]
        random_state: int, optional (default:0)
            The state of random element

        conf_file: string, optional (default:'ddim_paramset_single.json')
            The file of paramter set

        c_pattern: string, optional (default:'single')
            The gradual change point pattern

        temp_beta: float, optional (default:1)
            The temperature parameter 'beta' in Ddim

        out_flg: boolean, optional (default:False)
            Whether to output result or not

    '''

    def __init__(self, data_file, random_state=0, conf_file='ddim_paramset_realdata.json',
                 temp_beta=None, out_flg=False):

        now_time = dt.now()
        now_str = now_time.strftime("%Y%m%d-%H%M%S")
        date_str = now_time.strftime("%Y%m%d")

        ### params
        self.random_state = random_state
        self.conf_file = conf_file
        self.temp_beta = temp_beta
        self.out_flg = out_flg

        ### internal parameter
        self.data_column = 'amount_data'
        self.sim_title = 'ddim_realdata'
        self.date_str = date_str
        self.file_name = 'ddim_result'

        self.FILE_DIR = os.path.dirname(os.path.abspath(__file__))
        self.OUT_DIR = os.path.normpath(os.path.join(self.FILE_DIR, './data/out/%s_%s'%(self.sim_title,date_str)))
        self.CONF_DIR = os.path.normpath(os.path.join(self.FILE_DIR, './conf'))
        self.out_data_file = '%s/%s_rand%d_%s.json'%(self.OUT_DIR,self.file_name,random_state,now_str)

        self.data_file = os.path.normpath(os.path.join(self.FILE_DIR, './data/%s'%(data_file)))

        if not os.path.isdir(self.OUT_DIR):
            os.mkdir(self.OUT_DIR)

        ### args
        self._read_conf()

        ### input dataset
        self._read_dataset()

        ### output
        self.out = {}

    def _read_conf(self):
        '''Read conf file
        '''
        conf_file = self.CONF_DIR + '/' + self.conf_file

        ### read json
        f = open(conf_file, 'r')
        params = json.load(f)
        f.close()

        self.params = params

    def _read_dataset(self):
        '''Read given dataset
        '''
        ### read json
        f = open(self.data_file, 'r')
        j_data = json.load(f)
        f.close()

        amount_dataset = np.array(j_data[self.data_column])

        self.dataset = amount_dataset

    def simulate_algorithm_to_dataset(self):
        '''Simulate algorithm using given dataset
        '''

        ### parameter of simulation
        norm_file = self.params['norm_file']
        iter_count = self.params['simulation']['iter']

        ### parameter of clustering
        num_em = self.params['clustering_params']['num_em']
        max_clu = self.params['clustering_params']['max_clu']

        ### parameter of SDMS
        sdms_lam = self.params['sdms_params']['sdms_lam']
        sdms_beta = self.params['sdms_params']['sdms_beta']

        ### parameter of Ddim
        temp_beta_unit = self.params['sim_params']['temp_beta_unit']
        th_change_diff = self.params['sim_params']['th_change_diff']
        th_change_th = self.params['sim_params']['th_change_th']
        th_change_sdms = self.params['sim_params']['th_change_sdms']
        th_change_fs = self.params['sim_params']['th_change_fs']
        th_change_ws_fs = self.params['sim_params']['th_change_ws_fs']
        th_change_ws_fs_th = self.params['sim_params']['th_change_ws_fs_th']

        ### parameter of fixed share
        fs_alpha = self.params['sim_params']['fs_alpha']
        fs_temp_beta = self.params['sim_params']['fs_temp_beta']

        # num = self.dataset.shape[1]
        if self.temp_beta is None:
            # temp_beta = temp_beta_unit / np.sqrt(num)
            temp_beta = 0.05
        else:
            temp_beta = self.temp_beta

        st = time.time()
        res_opt_k = np.zeros((iter_count,self.dataset.shape[0]))
        res_ent = np.zeros((iter_count,self.dataset.shape[0]))
        res_ddim = np.zeros((iter_count,self.dataset.shape[0]))
        res_fs = np.zeros((iter_count,self.dataset.shape[0]))
        res_ws_fs = np.zeros((iter_count,self.dataset.shape[0]))
        for i in range(iter_count):
            tc_obj = tc.gmmSeqClustering(max_clu=max_clu,num_em=num_em,norm_file=norm_file,sdms_lam=sdms_lam,sdms_beta=sdms_beta)
            tc_obj.fit(self.dataset,progress=True)

            dms_obj = dms.dynamicModelSelection(sdms_lam=sdms_lam,sdms_beta=sdms_beta)
            dms_obj.calc_simple_sdms(tc_obj.ts_nml)

            ### calculate Descriptional Dimension
            ddim_obj = ddim.calcDdim(sdms_lam=sdms_lam,sdms_beta=sdms_beta,temp_beta=temp_beta)
            ddim_value = ddim_obj.calc_ts_of_Ddim(tc_obj.ts_nml)

            ### fixed share
            fs_obj = fs.calcFS(sdms_lam=sdms_lam, sdms_beta=sdms_beta, temp_beta=fs_temp_beta, fs_alpha=fs_alpha)
            fs_obj.calc_ts_of_FS(tc_obj.ts_nml)

            res_opt_k[i,:] = dms_obj.l_opt_k
            res_ddim[i,:] = ddim_value
            res_fs[i,:] = fs_obj.l_bx_fs
            res_ws_fs[i,:] = fs_obj.l_bx_fs_weightsum

            elapsed_time = time.time() - st
            print('=== optimal k (iter:%d,elapsed-time:%d[s]) ==='%(i,elapsed_time))
            print(dms_obj.l_opt_k)

        ### evaluation
        eval_obj = sym_eval.evalSymptom()

        self.tc_obj = tc_obj

        avg_opt_k = np.mean(res_opt_k,axis=0)
        avg_ddim = np.mean(res_ddim,axis=0)
        avg_fs = np.mean(res_fs,axis=0)
        avg_ws_fs = np.mean(res_ws_fs,axis=0)

        ### symptom detection: Diff
        t_criterion_diff = [np.nan]
        t_criterion_diff.extend( ( np.diff(avg_ddim) ).tolist())
        t_criterion_diff = np.array(t_criterion_diff)
        t_criterion_diff = np.abs(t_criterion_diff)
        sy_diff = eval_obj.symptom_detection(t_criterion_diff,th_change=th_change_diff)

        ### symptom detection: Threshold
        t_criterion_th = np.absolute( avg_opt_k - avg_ddim )
        sy_th = eval_obj.symptom_detection(t_criterion_th,th_change=th_change_th)

        ### SDMS
        t_criterion_sdms = [np.nan]
        t_criterion_sdms.extend( ( np.diff(avg_opt_k) ).tolist())
        t_criterion_sdms = np.array(t_criterion_sdms)
        sy_sdms = eval_obj.symptom_detection(t_criterion_sdms,th_change=th_change_sdms)

        ### fixed share
        t_criterion_fs = [np.nan]
        t_criterion_fs.extend( ( np.diff(avg_fs) ).tolist())
        t_criterion_fs = np.array(t_criterion_fs)
        sy_fs = eval_obj.symptom_detection(t_criterion_fs,th_change=th_change_fs)

        ### weighted average fixed share: Diff
        t_criterion_ws_fs = [np.nan]
        t_criterion_ws_fs.extend( ( np.diff(avg_ws_fs) ).tolist())
        t_criterion_ws_fs = np.array(t_criterion_ws_fs)
        sy_ws_fs = eval_obj.symptom_detection(t_criterion_ws_fs,th_change=th_change_ws_fs)

        ### weighted average fixed share: Threshold
        t_criterion_ws_fs_th = np.absolute( avg_opt_k - avg_ws_fs )
        sy_ws_fs_th = eval_obj.symptom_detection(t_criterion_ws_fs_th,th_change=th_change_ws_fs_th)

        self.out['params'] = self.params
        self.out['params']['file'] = self.conf_file
        self.out['params']['temp_beta'] = self.temp_beta
        self.out['result'] = {}
        self.out['result']['opt_k'] = res_opt_k.tolist()
        self.out['result']['ddim'] = res_ddim.tolist()
        self.out['result']['avg_opt_k'] = avg_opt_k.tolist()
        self.out['result']['avg_ddim'] = avg_ddim.tolist()
        self.out['result']['avg_fs'] = avg_fs.tolist()
        self.out['result']['avg_ws_fs'] = avg_ws_fs.tolist()
        self.out['result']['symptom_diff'] = sy_diff
        self.out['result']['symptom_th'] = sy_th
        self.out['result']['symptom_sdms'] = sy_sdms
        self.out['result']['symptom_fs'] = sy_fs
        self.out['result']['symptom_ws_fs'] = sy_ws_fs

        self.out['result']['evaluation'] = {}
        ### Diff
        self.out['result']['evaluation']['t_criterion_diff'] = t_criterion_diff.tolist()
        ### TH
        self.out['result']['evaluation']['t_criterion_th'] = t_criterion_th.tolist()
        ### SDMS
        self.out['result']['evaluation']['t_criterion_sdms'] = t_criterion_sdms.tolist()
        ### FS
        self.out['result']['evaluation']['t_criterion_fs'] = t_criterion_fs.tolist()
        ### Weighted average FS: Diff
        self.out['result']['evaluation']['t_criterion_ws_fs'] = t_criterion_ws_fs.tolist()
        ### Weighted average FS: TH
        self.out['result']['evaluation']['t_criterion_ws_fs_th'] = t_criterion_ws_fs_th.tolist()

        if self.out_flg:
            f = open(self.out_data_file, 'w')
            json.dump(self.out, f, indent=4)
            f.close()

    def create_graph(self,initial_period=0,ts_ind=None,c_ind_th=None,c_ind_diff=None,fs_flg=False,tofile=False,file_format='eps'):

        '''Create graph with result of Ddim
        [Parameters]
            tofile: boolean, optional (default:False)
                Whether to out a file or not

            file_format: string, optional (default:'eps')
                Output file type (Usually 'eps' or 'png')
        '''

        opt_k = np.array(self.out['result']['avg_opt_k'])
        l_ddim = np.array(self.out['result']['avg_ddim'])
        avg_ws_fs = np.array(self.out['result']['avg_ws_fs'])

        ### Result of Diff
        t_criterion_diff = self.out['result']['evaluation']['t_criterion_diff']
        sy_diff = self.out['result']['symptom_diff']

        ### Result of TH
        t_criterion_th = self.out['result']['evaluation']['t_criterion_th']
        sy_th = self.out['result']['symptom_th']

        ### SDMS
        sy_sdms = self.out['result']['symptom_sdms']

        sy_th_move = ( np.array(sy_th) + initial_period ).tolist()
        sy_diff_move = ( np.array(sy_diff) + initial_period ).tolist()
        sy_sdms_move = ( np.array(sy_sdms) + initial_period ).tolist()

        ### Define directory name and file name
        now_time = dt.now()
        now_str = now_time.strftime("%Y%m%d-%H%M%S")
        date_str = now_time.strftime("%Y%m%d")

        dir_name = self.OUT_DIR
        if not os.path.isdir(dir_name):
            os.mkdir(dir_name)

        file_name = '%s/gmm_clu_ddim_realdata'%(dir_name)
        out_fig = '%s_%s.%s'%(file_name,now_str,file_format)

        max_time = opt_k.shape[0]
        ts = np.array([i for i in range(max_time)])
        if ts_ind is None:
            ts_ind = ts
        if c_ind_th is None:
            c_ind_th = sy_th
        if c_ind_diff is None:
            c_ind_diff = sy_diff

        ts_sdms, opt_k_sdms, ts_ind_sdms = self._create_sequence(ts,ts_ind,opt_k)

        alpha_true = 1.
        l_width = 5.
        ddim_width = 12.

        fig = plt.figure(figsize=(20,12))
        plt.rcParams["font.size"] = 24
        ax = fig.add_subplot(111)

        title_str = 'Ddim for Gradual Change \n<Symptom> TH:%s, Diff:%s, SDMS:%s' \
                                %(str(sy_th_move), str(sy_diff_move), str(sy_sdms_move) )
        ax.plot(ts_sdms[ts_ind_sdms]+initial_period,opt_k_sdms[ts_ind_sdms],'b-',linewidth=l_width,label='SDMS')
        if fs_flg:
            ax.plot(ts[ts_ind]+initial_period,avg_ws_fs[ts_ind],'c-',linewidth=l_width,label='WA-FS')
        avg_ws_fs
        ax.plot(ts[ts_ind]+initial_period,l_ddim[ts_ind],'g-',linewidth=ddim_width, label='Ddim')
        plt.ylabel('SDMS / Ddim')
        plt.xlabel('Time')

        ax2 = ax.twinx()

        cri_min, cri_max = 0, 1.5
        ax2.plot(ts[ts_ind]+initial_period,np.array(t_criterion_th)[ts_ind],'r--',linewidth=l_width, label='TH')
        for c in c_ind_th:
            ax2.plot([c+initial_period,c+initial_period],[cri_min,cri_max],'r--',linewidth=l_width)
        ax2.plot(ts[ts_ind]+initial_period,np.array(t_criterion_diff)[ts_ind],'m-.',linewidth=l_width,label='Diff')
        for c in c_ind_diff:
            ax2.plot([c+initial_period,c+initial_period],[cri_min,cri_max],'m-.',linewidth=l_width)
        ax.legend(loc=2)
        ax2.legend(loc=4)
        plt.ylabel('TH / Diff')
        # plt.title(title_str)
        plt.xlabel('time')
        if tofile:
            plt.savefig(out_fig, format=file_format)
            plt.show()
        else:
            plt.show()

    def _create_sequence(self,ts,ts_ind,opt_k):
        ts_out = []
        opt_k_out = []
        ts_ind_out = []
        curr_o = None
        curr_lag = 0
        for i,(t,o) in enumerate(zip(ts,opt_k)):
            if i == 0:
                ts_out.append(t)
                opt_k_out.append(o)
                if t in ts_ind:
                    ts_ind_out.append(t + curr_lag)
                curr_o = o
                continue

            if curr_o == o:
                ts_out.append(t)
                opt_k_out.append(o)
                if t in ts_ind:
                    ts_ind_out.append(t + curr_lag)
                curr_o = o
            else:
                ts_out.append(t)
                ts_out.append(t)
                opt_k_out.append(curr_o)
                opt_k_out.append(o)
                if t in ts_ind:
                    ts_ind_out.append(t + curr_lag)
                    ts_ind_out.append(t + curr_lag + 1)
                curr_lag += 1
                curr_o = o
        ts_out = np.array(ts_out)
        opt_k_out = np.array(opt_k_out)

        return ts_out, opt_k_out, ts_ind_out


import socket
import threading

HOST = '200.239.165.217'
PORT = 13333

class Server (threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.socket = None
    def run(self):
        self.isRunning = True
        while self.isRunning:
            servmsn = self.socket.recv(2048) # esperando por ate 2048 bytes...
            #messageType, messageBody = servmsn.decode()
            print("Resposta do servidor  while 2:" ,servmsn.decode())

server = Server()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

print('Digite seu nome:')
nome = input()
s.sendall(str.encode(nome))

print('Digite a sua sala:')
sala = input()
s.sendall(str.encode(sala))

#server.start()

while True:

    servmsn =  s.recv(1024)
    print("Resposta do servidor while 1:" ,servmsn.decode())

    nome = input()

    if nome == 'sair':
        s.sendall(str.encode(nome))
        break

    s.sendall(str.encode(nome))

s.close()


from flask import Flask, request
from flask_cors import CORS
import requests
from datetime import datetime
from dateutil.relativedelta import relativedelta
from geopy.geocoders import Nominatim
from random import *
import json

app = Flask(__name__)
CORS(app)

@app.route("/getSamplePoints")
def samplePoints():
    # sample_points_url = "https://environment.data.gov.uk/water-quality/id/sampling-point?samplingPointStatus=open"
    # sample_points = requests.get(sample_points_url).json()['items']

    # # loop through items and check samples up to 3 months ago
    # cutoff_date = datetime.now() - relativedelta(months = 3)
    # for sample in sample_points:
    #     sample_url = sample["@id"]

    return sample_points

@app.route("/getSamplePoint/<location>")
def samplePoint(location):
    # sample_point_url = "https://environment.data.gov.uk/water-quality/id/sampling-point/" + location
    # sample_point = requests.get(sample_point_url).json()['items'][0]

    sample_point = sample_points_dict[location]

    geolocator = Nominatim(user_agent="geoapiExercises")
    sample_point['location'] = geolocator.reverse(str(sample_point["lat"])+","+str(sample_point["long"])).raw['address']

    return sample_point

@app.route("/closeBounty/<location>", methods=['POST'])
def closeBounty(location):
    sample_points_dict[location]['isClaimed'] = 1
    return json.dumps({'success':True}), 200, {'ContentType':'application/json'}

@app.route("/updateBounty/<location>", methods = ['POST'])
def updateBounty(location):
    sample_points_dict[location]["bounty"] += request.get_json()['donation']
    return json.dumps({'success':True}), 200, {'ContentType':'application/json'}

if __name__ == '__main__':
    sample_points_url = "http://environment.data.gov.uk/water-quality/id/sampling-point/AN-BUR140"
    sample_points = requests.get(sample_points_url).json()['items']

    sample_points_dict = dict()
    for sample_point in sample_points:
        sample_point['bounty'] = 0
        sample_point['isClaimed'] = 0
        sample_point['isConfirmed'] = 0
        sample_points_dict[sample_point['notation']] = sample_point

    app.run(host='localhost', port=8000, debug=True)


# importing the necessary packages
import os
from dotenv import load_dotenv
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy.util as util

sp = spotipy.Spotify()

load_dotenv()
# setting up authorization
cid = os.getenv('SPOTIFY_CID')
secret = os.getenv('SPOTIFY_SID')

# saving the info you're going to need
username = os.getenv('SPOTIFY_USER')
scope = 'user-library-read' #check the documentation
authorization_url = 'https://accounts.spotify.com/authorize'
token_url = 'https://accounts.spotify.com/authorize?client_id=client_id_number&response_type=code&redirect_uri=https%3A%2F%2Flocalhost.com%2Fcallback%2F&scope=user-library-read'
redirect_uri ='https://accounts.spotify.com/authorize?client_id=client_id_number&response_type=code&redirect_uri=https%3A%2F%2Flocalhost.com%2Fcallback%2F&scope=user-library-read'

def getClient():
    # token = "BQDZnXnyMHMs0eVrRq6m1-jsx7Zm6qk3NmC-VvqCYQxUHC9-1gGlXP8f7Y41GCcPXbKWu9Pgc0upYQGBWiCjHlpHnzbmxwJYtvdIgSM9Fnq1uRvjNIi0thXEBRGN_qQlopgWIn_u6diQ3L0Z-JljaMTfN77Gv1YuvWqOC68eaHrpkb3O"
    # token = util.prompt_for_user_token(username,scope,client_id='client_id_number',client_secret='client_secret',redirect_uri='https://localhost.com/callback/')
    client_credentials_manager = SpotifyClientCredentials(client_id=cid, client_secret=secret)
    sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)

    # retrieving you access token
    auth = SpotifyClientCredentials(client_id= cid, client_secret=secret)

    # save your token
    token = auth.get_access_token()
    spotify = spotipy.Spotify(auth=token)

    # check if everything is in order
    print(token)
    print(spotify)

    return sp

# used by the spotipy client to find URL from id from track name and artist
def queryForURL(sp, artist, track):
    # queries client for URL from artist and track
    q = "artist:" + artist + " track:" + track
    results = sp.search(q=q, type='track', limit=5, market='US')

    URL = results["tracks"]["items"][0]["external_urls"]["spotify"]

    return URL

# used by spotipy client to find artist and track from id
def queryForTrackData(id):
    result = sp.tracks(id)

    artist = result["album"]["artists"][0]["name"]
    track  = result["album"]["name"]

    return artist, track

def getISRC(id):
    result = sp.tracks(id)

    isrc = result["tracks"]["items"][0]["external_ids"]["isrc"]

    return isrc


import random   #Libreria utilizada para generar un numero aleatorio para el comprobante
import datetime #Libreria utilizada para usar la fecha y hora

def cargar_apuestas_quiniela():
    '''
    Esta funcion lee las apuestas en el archivo y almacena cada linea en la lista datos,
    el elemento 1 de datos corresponde al modo en que se hizo la apuesta (Quiniela o Quini 6 Tradicional),
    por lo que se utiliza este elemento para comprobar si la linea leida corresponde a una apuesta de
    Quiniela o de Quini 6 Tradicional. En esta funcion solo interesan las apuestas de Quiniela, por lo que
    las apuestas de Quini 6 no se almacenaran el la lista 'apuestas_quiniela'.
    '''
    apuestas_quiniela = []
    with open("ProyectoFinal\\apuestas.txt", "r") as file:
        for linea in file:
            datos = linea.strip().split(";") #dividir cada linea de apuesta leida en base al elemento ';'
            if datos[1] == "Quiniela": #comprobar si la apuesta corresponde a Quiniela
                apostador = datos[3] #Si corresponde, almacenar el dni del apostador
                numero_apostado = int(datos[5]) #Tambien almacenar el numero apostado, convertido a entero
                apuestas_quiniela.append([apostador, numero_apostado]) #Agregar dni y apuesta de la linea leida en un elemento de la lista
    return apuestas_quiniela

def cargar_apuestas_quini_6():
    '''
    Similar a la funcion cargar_apuestas_quiniela() pero con la finalidad de comprobar si la linea leida
    es de una apuesta de Quini 6 Tradicional.
    '''
    apuestas_quini_6 = []
    numeros_apostados = []
    with open("ProyectoFinal\\apuestas.txt", "r") as file:
        for linea in file:
            datos = linea.strip().split(";") #dividir cada linea de apuesta leida en base al elemento ';'
            if datos[1] == "Quini 6 tradicional": #comprobar si la apuesta corresponde a Quini 6 Tradicional
                apostador = datos[3] #Si corresponde, almacenar el dni del apostador
                numeros_apostados = [int(numero) for numero in datos[5].split(",")] #Tambien almacenar el numero apostado, convertido a entero
                apuestas_quini_6.append([apostador, numeros_apostados]) #Agregar dni y apuesta de la linea leida en un elemento de la lista
    return apuestas_quini_6

def imprimir_comprobante(quiniela, apostador, cifras_apostadas, monto):
    '''
    Esta funcion se encarga de imprimir en pantalla el comprobante, se le debe
    pasar como parametro el nombre de la quiniela, el dni del apostador, la/s
    cifras apostadas y el monto apostado. Teniendo estos datos los mostrara en
    pantalla
    '''
    fecha_hora = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") #obtener la fecha y hora actual, y luego lo formatearla como una cadena en el formato "AAAA-MM-DD HH:MM:SS"
    numero_comprobante = random.randint(10000, 99999)                  #genera un numero entero random de 5 cifras
    print("***************************************************")
    print(f"******** Comprobante - {quiniela} ********")
    print("***************************************************")
    print(f"Fecha y hora de la apuesta: {fecha_hora}")
    print(f"Número de comprobante: {numero_comprobante}")
    print(f"DNI del apostador: {apostador}")
    print(f"Monto: {monto}")
    if isinstance(cifras_apostadas, list):  #Esto se hace debido a que cifras apostadas puede ser una lista de 6 numeros (quini 6) o un solo numero (quiniela)
        print(f"Cifras apostadas: {', '.join(str(cifra) for cifra in cifras_apostadas)}")  #compresion de lista para convertir cada cifra en cifras apostadas en dato de tipo string. Luego mediante ", ".join se combina cada cifra para formar una cadena con los 6 numeros que contiene la lista cifras_apostadas
    else:
        print(f"Cifra apostada: {cifras_apostadas}") #Si no es una lista 'isinstance(cifras_apostadas, list) devolvera false y se ingresara al else, por lo que cifras_apostadas tendra solo un numero que es el de la quiniela
    print("****************************************************\n")

def guardar_apuesta_en_archivo(quiniela, apostador, cifras_apostadas, monto):
    '''
    Esta funcion se utiliza para registrar o guardar cada apuesta en un archivo que esta
    dentro de la carpeta del proyecto final, se lo abre al archivo en el modo append para
    que no se sobreescriba todo el archivo y la apuesta se registre al final del archivo.
    '''
    with open("ProyectoFinal\\apuestas.txt", "a") as archivo:
        fecha_hora = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        numero_comprobante = random.randint(10000, 99999)
        apuesta = f"{fecha_hora};{quiniela};{numero_comprobante};{apostador};{monto};"
        if isinstance(cifras_apostadas, list): # esta funcion retorna true si cifras_apostadas es una instancia de lista, es decir, si es una lista. Sino, retorna false
            apuesta += ",".join(str(cifra) for cifra in cifras_apostadas) #se recorre cada cifra en la lista cifras_apostadas y se las convierte en string, seguido a esto se las escribe en la misma linea en el archivo. Cada cifra esta separada por una coma
        else:
            apuesta += str(cifras_apostadas) # si no es una lista, es el numero de la quiniela, por lo que se escribe el numero convertido a string, en la misma linea
        apuesta += "\n" # Añadir un salto de línea para separar cada apuesta en líneas diferentes
        archivo.write(apuesta)

def validar_dni(dni):
    '''
    Funcion que se encarga de verificar si el dni pasado como parametro
    es solo numericos y tambien se encarga de comprobar si su longitud
    es de 7 u 8 cifras (debido a que en Argentina lo habitual es que
    una persona cuente con dni de 7 u 8 cifras)
    '''
    # Verificar que el DNI tenga 7 u 8 caracteres
    if len(dni) not in [7, 8]:
        return False

    # Verificar que el DNI esté compuesto solo de dígitos numéricos
    if not dni.isdigit():
        return False

    return True

def quiniela():
    '''
    Esta funcion se utiliza cuando el usuario escoge apostar a la Quiniela, primero solicita
    la cantidad de cifras que va a tener el numero a apostar y hasta que el usuario no coloque
    un numero correcto, el programa no dejara de pedirle. Seguido a esto se le solicita el dni
    del apostador y luego el numero a apostar, el cual debe coincidir con la cantidad de cifras
    que el apostador eligio.
    '''
    print("Has seleccionado: Quiniela")
    cifras_apostadas = [] #lista vacia, la cual va a almacenar el numero apostado
    while True:
        num_cifras = input("Selecciona el número de cifras a apostar (2, 3 o 4): ")
        if num_cifras in ('2', '3', '4'): #Si el numero de num_cifras esta dentro de esta tupla, sale del while y continua con el codigo
            break
        else:
            print("Opción inválida. Por favor, selecciona 2, 3 o 4.")

    while True:
        dni_apostador = input("Ingresa tu DNI: ")
        if validar_dni(dni_apostador):  #si retorna true, sale del while, dando como indicio de que el numero ingresado cumple con un dni argentino (7 u 8 digitos y solo numerico)
            break
        else:
            print("El DNI no es válido. Por favor, intenta nuevamente.")

    while True:
        cifra = input(f"Ingrese {num_cifras} cifras para apostar: ")
        if str(len(cifra)) == num_cifras: #comprobar si el largo de cifra es igual al numero de cifras que el usario eligio
            break #salir del while y continuar con las siguientes lineas de codigo
        else:
            print("El numero ingresado no coincide con las cifras elegidas. Por favor ingrese el numero correcto.")

    while True: #while 1
        # Se utiliza manejo de excepciones debido a que si el usuario ingresa un dato que no es tipo numerico, se provoca un error, por lo que se imprimira en pantalla un mensaje en vez del error
        try:
            while True: #while 2
                monto = input("Ingrese el monto a apostar: ")
                if int(monto) == 0: # No se puede apostar 0 pesos
                    print(f"Debes apostar un monto superior a {monto} pesos")
                elif int(monto) < 0: # Tampoco se puede apostar numeros negativos
                    print("No se permite apostar numeros inferiores a 0")
                else:
                    break # Sale del while 2
            break #sale del while 1
        except ValueError:
            print("Debes ingresar solo numeros")
    imprimir_comprobante("Quiniela 'LOS POLLOS HERMANOS'", dni_apostador, cifra, monto)
    guardar_apuesta_en_archivo("Quiniela", dni_apostador, cifra, monto)

def generar_numeros_aleatorios(cantidad):
    '''
    Esta funcion solamente se encarga de generar numeros aleatorios entre 0 y 45(inclusive)
    por lo que se utiliza la funcion sample() que toma como parametro el rango de numeros
    y la cantidad. Esta funcion retorna una lista de 6 numeros aleatorios generados, en el
    rango de 0 a 45.
    '''
    return random.sample(range(46), cantidad)

def quini_6_tradicional():
    '''
    Esta funcion se utiliza cuando el usuario quiere apostar al Quini 6 Tradicional,
    primero pide el dni del usuario, luego le pregunta si desea generar los numeros
    al azar o si los desea ingresar el mismo. Por ultimo le solicita el monto y llama
    a las funciones para imprimir el comprobante y para guardar la apuesta en el archivo
    '''
    print("Has seleccionado: Quini 6 tradicional")
    while True:
        dni_apostador = input("Ingresa tu DNI: ")
        if validar_dni(dni_apostador):  #si retorna true, sale del while, dando como indicio de que el numero ingresado cumple con un dni argentino (7 u 8 digitos y solo numerico)
            break
        else:
            print("El DNI no es válido. Por favor, intenta nuevamente.")

    while True:
        opcion = input("Deseas generar los números al azar? (Sí: S / No: N): ")
        if opcion.upper() == 'S': # se utiliza el metodo upper para comprobar solamente si es mayusculas, ya que el usuario puede ingresar en minusculas pero esto no dificultara nada
            cifras_apostadas = generar_numeros_aleatorios(6)  #llama a la funcion se le pasa 6 como parametro, el resultado se almacena en cifras apostadas (lista)
            break
        elif opcion.upper() == 'N':
            cifras_apostadas = [] #la declara vacia ya que se almacenaran los numeros ingresados por el usuario
            print("Ingresa 6 números entre 0 y 45 (inclusive) para apostar:")
            print("Presiona ENTER por cada numero ingresado")
            while len(cifras_apostadas) < 6: #ejecutara este while siempre que cifras apostadas sea menor a 6, ya que solo debe contener 6 numeros, partiendo del 0 al 5
                cifra = input()
                if cifra.isdigit() and 0 <= int(cifra) <= 45 and int(cifra) not in cifras_apostadas:
                    #primero se comprueba si cifra es digito mediante isdigit() cuyo resultado es true si lo es,
                    #luego se verifica si cifra esta entre 0 y 45 convirtiendolo primero a entero.
                    #Por ultimo se verifica que el valor de cifra no este dentro de la lista ya que no puede haber numeros repetidos
                    cifras_apostadas.append(int(cifra)) #agrega la cifra a la lista, convirtiendola a entero
                else:
                    print("Número inválido o repetido. Ingrese un número válido entre 0 y 45.")
            break
        else:
            print("Ingrese el caracter correcto (S/N)")

    while True: #while 1
        # Se utiliza manejo de excepciones debido a que si el usuario ingresa un dato que no es tipo numerico, se provoca un error, por lo que se imprimira en pantalla un mensaje en vez del error
        try:
            while True: #while 2
                monto = input("Ingrese el monto a apostar: ")
                if int(monto) == 0: # No se puede apostar 0 pesos
                    print(f"Debes apostar un monto superior a {monto} pesos")
                elif int(monto) < 0: # Tampoco se puede apostar numeros negativos
                    print("No se permite apostar numeros inferiores a 0")
                else:
                    break # Sale del while 2
            break #sale del while 1
        except ValueError:
            print("Debes ingresar solo numeros")
    imprimir_comprobante("Quini 6 tradicional", dni_apostador, cifras_apostadas, monto)
    guardar_apuesta_en_archivo("Quini 6 tradicional", dni_apostador, cifras_apostadas, monto)

def comprobar_apuesta_ganadora():
    '''
    Esta funcion se encarga de comprobar si la apuesta ingresada por el usuario, es decir, la apuesta
    ganadora, coincide con alguna de las apuestas que se encuentren en el archivo. Primero se le pregunta
    al usuario si desea buscar en las apuestas de Quiniela o de Quini 6 Tradicional. Una vez elegido el modo,
    el usuario ingresara el o los numeros correspondientes al modo elegido y se comprobara si la apuesta ganadora
    existe en el archivo y en base a eso se mostrara en pantalla si el usuario gano o no.
    '''
    print("Has seleccionado: Comprobar Apuesta")
    print("1. Quiniela")
    print("2. Quini 6 tradicional")
    while True:
        opcion = input("Selecciona una opción (1 o 2): ")

        if opcion == '1':
            while True:
                apuesta_str = input("Ingresa el número apostado para Quiniela: ")
                if apuesta_str.isdigit(): #si la apuesta ingresada es un dijito, lo pasa a entero. Si no es entero, se le pide al usuario que ingrese un numero valido
                    apuesta = int(apuesta_str)
                    break  # Si se ingresó un número válido, salimos del ciclo y continuamos con la validación
                print("Error: Debes ingresar un número válido. Intenta nuevamente.")

            apuestas_quiniela = cargar_apuestas_quiniela()  #Esta variable va almacenar el dni del usuario y la apuesta que dicho dni hizo, en formato de lista

            for apostador, numero_apostado in apuestas_quiniela: #Se utiliza un for para recorrer los elementos de la lista y almacenar el dni en apostador y la apuesta en numero_apostado
                if apuesta == numero_apostado:  #Si la apuesta ingresada por el usuario coincide con el numero_apostado en la iteracion, el dni apostador es ganador
                    print(f"¡Felicidades! El numero {numero_apostado} apostado para Quiniela coincide con la apuesta ganadora.")
                    print(f"DNI del ganador: {apostador}")
                    return   #Salir de la funcion y no buscar más coincidencias una vez que se ha encontrado una.
            print("Lo siento, la apuesta para Quiniela no coincide con ninguna apuesta ganadora.")
            break

        elif opcion == '2':
            '''
            Para determinar si una apuesta es ganadora, el orden de los numeros ingresados debe ser el mismo
            que el de los numeros guardados en el archivo. De lo contrario se tomara como apuesta no ganadora.
            '''
            while True:
                apuesta_str = input("Ingresa los 6 números apostados para Quini 6 separados por comas: ")

                try:
                    numeros_apostados = [int(numero) for numero in apuesta_str.split(",")]  #comprensión de lista que recorre cada elemento de la lista generada en el paso anterior y convierte cada elemento a un número entero utilizando int(numero)
                    if len(numeros_apostados) == 6 and len(set(numeros_apostados)) == 6:  #Verificar si se ingresaron 6 numeros y tambien si los mismos no estan repetidos
                        break  # Si se ingresaron 6 números, salimos del ciclo y continuamos con la validación. Esta verificacion se hace debido a que en Quini 6 Tradicional se apuestan 6 numeros, ni mas ni menos
                    else:
                        print("Error: Debes ingresar exactamente 6 números. Intenta nuevamente.")
                except ValueError:
                     print("Error: Ingresa solo NÚMEROS separados por comas. Intenta nuevamente.")

            apuestas_quini_6 = cargar_apuestas_quini_6()  #Cargar en apuestas_quini_6 la lista que contiene elementos con el dni del apostador y los 6 numeros que dicho apostador uso

            for apostador, numeros_apostados_guardados in apuestas_quini_6:
                if numeros_apostados == numeros_apostados_guardados:  #Verificar si los numeros apostados coinciden con los numeros apostados guardados
                    numeros_apostados_str = str(numeros_apostados).replace("[", "").replace("]", "")  # Convertir la lista en una cadena y eliminar los corchetes
                    print(f"¡Felicidades! Los números {numeros_apostados_str} apostados para Quini 6 coinciden con la apuesta ganadora.")
                    print(f"DNI del ganador: {apostador}")
                    return  #Salir de la funcion y no buscar más coincidencias una vez que se ha encontrado una.

            print("Lo siento, los números apostados para Quini 6 no coinciden con ninguna apuesta ganadora.")
            break

        else:
            print("Opción inválida. Por favor, selecciona una opción válida (1 o 2).")

def cargar_apuestas_desde_archivo():
    '''
    Funcion que se encarga de leer las apuestas en el archivo y en base a los ';'
    va separando los datos para almacenarlos en una lista y retornarlos
    '''
    apuestas = []
    try:
        with open("ProyectoFinal\\apuestas.txt", "r") as archivo:
            for linea in archivo:  #Leer cada linea en el archivo
                datos = linea.strip().split(";") #datos es una lista que contendra la linea en el archivo en cada iteracion y cada elemento de datos sera un dato relacionado a la apuesta leida en la linea, ya que se separa cada dato por los ';'
                quiniela = datos[1] #quiniela va a contener el modo (Quiniela o Quini 6 Tradicional). Es importante para cuando se llame a la funcion cargar_apuestas_quiniela() o cargar_apuestas_quini_6()
                dni_apostador = datos[3] #almacenar el dni del apostador
                monto = float(datos[4]) #almacenar el monto apostado en la linea leida
                cifras_apostadas = datos[5] # lista que contiene los numeros apostados. En caso de que datos[5] sea una lista (6 numeros para quini 6), la lista tendra 6 elementos, mientras que para Quiniela, solo tendra un elemento (no habra comas)
                apuestas.append([quiniela, dni_apostador, cifras_apostadas, monto])
    except FileNotFoundError:
        pass
    return apuestas

def calcular_recaudacion_diaria():
    '''
    Esta funcion se encarga de llamar a la funcion cargar_apuestas_desde_archivo(), la cual
    retorna una lista cuyos elementos contienen los datos de cada apuesta. Esta funcion solamente
    toma el monto de cada apuesta y los agrega a una lista llamada 'montos', luego recorre esta lista
    y cada monto convertido a su tipo entero se va sumando y almacenando en la variable recaudacion_diaria.
    Por ultimo se retorna la recaudacion diaria, para ser utilizada en arqueo_de_caja()
    '''
    apuestas = cargar_apuestas_desde_archivo()
    recaudacion_diaria = 0
    montos = []
    for apuesta in apuestas:
        montos.append(apuesta[3])  #Almacenar los montos en la lista
    for monto in montos:
        recaudacion_diaria += int(monto) #Sumar los montos
    return recaudacion_diaria

def arqueo_de_caja():
    '''
    Esta funcion se encarga de realizar el arqueo de caja, para ello se obtiene la recaudacion
    diaria y a la misma se le calcula la retencion del estado. Luego, se calcula la ganancia
    neta siendo la diferencia entre las dos. Y por ultimo, se muestran los datos en pantalla
    '''
    recaudacion_diaria = calcular_recaudacion_diaria()
    retencion_estado = recaudacion_diaria * 0.47
    ganancia_neta = recaudacion_diaria - retencion_estado
    print("Has seleccionado: Arqueo de caja")
    print("Recaudación diaria en apuestas:", recaudacion_diaria)
    print("Retención del Estado (47%):", round(retencion_estado, 2)) #Redondear a 2 cifras decimales
    print("Ganancia neta para el quinielero:", round(ganancia_neta, 2)) #Redondear a 2 cifras decimales
    print("************************************************\n")

def salir_menu():
    '''
    Funcion que al ser llamada, mostrara un mensaje de despedida en pantalla
    '''
    print("Gracias por usar nuestro sistema de quiniela. ¡Hasta pronto!")

def mostrar_menu():
    print("************************************************")
    print("-------- Quiniela 'Los Pollos Hermanos' --------")
    print("************************************************")
    print("1. Quiniela")
    print("2. Quini 6 tradicional")
    print("3. Comprobar apuesta")
    print("4. Arqueo de caja")
    print("5. Salir")

while True:
    mostrar_menu()
    opcion = input("Selecciona una opción (1-5): ")

    if opcion == '1':
        quiniela()
    elif opcion == '2':
        quini_6_tradicional()
    elif opcion == '3':
        comprobar_apuesta_ganadora()
    elif opcion == '4':
        arqueo_de_caja()
    elif opcion == '5':
        salir_menu()
        break
    else:
        print("Opción inválida. Por favor, selecciona una opción válida (1-5).")


__author__ = 'MAWood'
import os

import Globals
import pygameui as ui

b1 = "Suspend server"
b2 = "Awaken server"
b3 = "Restart server"
b4 = "Shutdown server"

class Action(ui.Scene):
    def __init__(self):
        self.b1_button = ui.Button(ui.Rect(Globals.MARGIN, Globals.MARGIN, 130, 90), b1)
        self.b1_button.on_clicked.connect(self.button_handler)
        self.add_child(self.b1_button)

        self.b2_button = ui.Button(ui.Rect(170, Globals.MARGIN, 130, 90), b2)
        self.b2_button.on_clicked.connect(self.button_handler)
        self.add_child(self.b2_button)

        self.b3_button = ui.Button(ui.Rect(Globals.MARGIN, 130, 130, 90), b3)
        self.b3_button.on_clicked.connect(self.button_handler)
        self.add_child(self.b3_button)

        self.b4_button = ui.Button(ui.Rect(170, 130, 130, 90), b4)
        self.b4_button.on_clicked.connect(self.button_handler)
        self.add_child(self.b4_button)

    def button_handler(self, btn, mbtn):
        if btn.text == b1:
            os.popen('sudo pm-suspend')
        elif btn.text == b2:
            os.popen('')
        elif btn.text == b3:
            os.popen('sudo reboot')
        elif btn.text == b4:
            os.popen('')


from __future__ import absolute_import

import os.path
import sys
from datetime import timedelta

from dns.name import from_text
from dns.rdataclass import IN
from dns.rdatatype import A, AAAA

from dns.resolver import Cache

from reconfigure.configs import HostsConfig

from .dnspython import create_answer, create_simple_rrset
from .expiration import NoExpirationCacheBase
from .persistence import _DeserializeOnGetCacheBase

_year_in_seconds = timedelta(days=365).total_seconds()

# References to https://en.wikipedia.org/wiki/Hosts_(file)
_WINDOWS_PATHS = [
    # Microsoft Windows NT, 2000, XP,[5] 2003, Vista, 2008, 7, 2012, 8, 10
    r"${SystemRoot}\System32\drivers\etc\hosts",
    # Microsoft Windows 95, 98, ME
    r"${WinDir}\hosts",
    # Microsoft Windows 3.1
    r"${WinDir}\HOSTS",
    # Symbian OS 6.1-9.0
    r"C:\system\data\hosts",
    # Symbian OS 9.1+
    r"C:\private\10000882\hosts",
]
_UNIX_PATHS = [
    # Unix, Unix-like, POSIX, Apple Macintosh Mac OS X 10.2 and newer,
    # Android, iOS 2.0 and newer
    "/etc/hosts",
    # openSUSE
    "/usr/etc/hosts",
]
_OTHER_PARTS = [
    # Plan 9
    "/lib/ndb/hosts",
    # BeOS
    "/boot/beos/etc/hosts",
    # Haiku
    "/boot/common/settings/network/hosts",
]

def guess_hosts_path():
    if sys.platform == "win32":
        possible_paths = _WINDOWS_PATHS + _UNIX_PATHS + _OTHER_PARTS
    else:
        possible_paths = _UNIX_PATHS + _OTHER_PARTS + _WINDOWS_PATHS

    for path in possible_paths:
        path = os.path.expandvars(os.path.expanduser(path))
        if os.path.exists(path):
            return path

    raise RuntimeError()

def _convert_entries(entries, expiration=None):
    out_data = []

    for entry in entries:
        if ":" in entry.address:
            rdtype = AAAA
        elif "." in entry.address:
            rdtype = A
        else:
            continue

        names = [entry.name] + [
            alias.name for alias in entry.aliases
        ]
        for name in names:
            name = from_text(name)

            ip = entry.address
            rrset = create_simple_rrset(name, ip, rdtype, rdclass=IN)
            rrset.ttl = _year_in_seconds
            out_entry = create_answer(name, rrset)
            if expiration:
                out_entry.expiration = expiration

            out_data.append(out_entry)

    return out_data

def loads(filename=None):
    if not filename:
        filename = guess_hosts_path()

    mtime = os.path.getmtime(filename)

    config = HostsConfig(path=filename)
    config.load()

    expiration = mtime + _year_in_seconds
    dnspython_data = _convert_entries(config.tree.hosts, expiration)

    return dnspython_data

class HostsCacheBase(_DeserializeOnGetCacheBase, NoExpirationCacheBase):
    def __init__(
        self,
        filename,
        *args,
        **kwargs
    ):
        super(HostsCacheBase, self).__init__(
            *args,
            filename=filename,
            deserializer=loads,
            **kwargs)

class HostsCache(HostsCacheBase, Cache):
    def __init__(self, *args, **kwargs):
        super(HostsCache, self).__init__(*args, **kwargs)


# exercise 1.20

symbols = 'HPQ,AAPL,IBM,MSFT,YHOO,DOA,GOOG'

symlist = symbols.split(',')

for s in symlist:
    print('s =', s)

# s = HPQ
# s = AAPL
# s = IBM
# s = MSFT
# s = YHOO
# s = DOA
# s = GOOG


from django.shortcuts import render
from django.http import HttpResponse

# from models import Question
from django.template import loader
import json
import logging
import sys
import os
from models.predict_model import Modelpredictor
from .models import Question
import models.train_model as tm
import storage.database as db

logging.basicConfig(level="DEBUG")

# sys.path.append(r"/src")

logging.debug(f"current directory : {os.getcwd()}")
# os.chdir(r"/src ")
# from __init__ import *

# os.chdir(r"../../../")

# os.chdir(r"/src\src")
# import data.database

def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    output = ", ".join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

def index2(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    template = loader.get_template("templates/index.html")
    context = {
        "latest_question_list": latest_question_list,
    }
    return HttpResponse(template.render(context, request))

def crazyfrog(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    print(latest_question_list)
    context = {"latest_question_list": latest_question_list}
    return render(request, r"index.html", context)

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

def healthapp_intro(
    request,
):
    print("")
    context = {"linkactivate": "lala"}
    return render(request, r"healtapp.html", context)

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def calc(request):
    # print(os.environ["PYTHONPATH"])
    print(os.getcwd())
    # print(databasepath)

    json_body = json.loads(request.body)
    print(json_body)
    # print(type(request.body))
    # print(request.POST)
    # alc = json_body["alcohol"]
    # print(alc)
    # xx

    # databasepath = "projects\healthapp\data\external"
    databasepath = "/mnt/data/external"
    # Hier moet nog iets voor gevonden worden.
    # Een configuratiefile met paden e.d.?

    df = db.dbdata2df(databasepath, "healthapp", "health")
    inputparam = ["genetic", "exercise", "smoking", "alcohol", "sugar", "bmi"]
    outputparam = "lifespan"
    trainer = tm.Modeltrainer(inputparam, outputparam)

    appmodel = trainer.linearregr(df)
    predictor = Modelpredictor(appmodel)
    # inputparam = predictor.return_featuresin()
    input_val = []
    for par in inputparam:
        input_val.append(float(json_body[par]))
    # args = [6 * request.body]
    # args[0] =
    val = predictor.predict([input_val])

    val = "{:.3}".format(val[0])
    # print(val)
    # print(predictor.return_featuresin())
    # print(args)
    # val = int(predictor.predict(args))

    # print(db.databasepath)
    # print(request.body)
    # result = int(request.body) * 2
    # print(result)
    return HttpResponse(val)

# Create your views here.


import Tkinter as tk

class Room(object):
    def __init__(self, parent, shape, text):
        self.parent = parent
        self.shape = shape
        self.frame = tk.Frame(parent, borderwidth=1, relief=tk.GROOVE, background="green")
        self.lbl = tk.Label(self.frame, text=text)
        self.lbl.place(relx=.5, rely=.5, anchor="center")

    def grid(self, **kwargs):
        self.frame.grid(row=kwargs["row"], column=kwargs["column"], rowspan=self.shape[0], columnspan=self.shape[1], sticky="nesw")


import argparse

def left_factoring2(rules, n):
    counter = 0
    max_alpha = {}
    for key in rules:
        max_alpha[key] = 1

    while(True):
        if(counter == n):
            break
        keys = list(rules)
        rule = keys[counter]
        rule_without_stricks = rule.split('\'')[0]
        gram = rules[rule]
        rec = {}
        not_rec = {}
        not_rec[rule] = []
        len_grammeer = len(gram)

        visited = [False] * len_grammeer
        alpha = max_alpha[rule_without_stricks]
        for i in range(len_grammeer):
            if(visited[i]):
                continue
            visited[i] = True
            comp1 = gram[i].split(" ")

            flag = False
            for j in range(i + 1, len(gram)):
                comp2 = gram[j].split(" ")
                if(comp1[0] == comp2[0]):
                    if(not(flag)):
                        n_rule = rule_without_stricks + "\'" * alpha
                        alpha += 1
                        rec[n_rule] = []
                        if(len(comp1[1:]) > 0):
                            rec[n_rule].append(' '.join(comp1[1:]))
                        else:
                            rec[n_rule].append(' '.join(['epsilon']))
                        not_rec[rule].append(comp1[0] + " " + n_rule)
                    visited[j] = True
                    flag = True
                    if(len(comp2[1:]) == 0):
                        rec[n_rule].append(' '.join(["epsilon"]))
                    else:
                        rec[n_rule].append(' '.join(comp2[1:]))
            if(not(flag)):
                not_rec[rule].append(gram[i])

        rules = {**rules, **not_rec, **rec}
        n = len(rules)

        max_alpha[rule_without_stricks] = alpha

        counter += 1
    return rules

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        add_help=True, description='Sample Commandline')

    parser.add_argument('--file', action="store",
                        help="path of file to take as input", nargs="?", metavar="file")

    args = parser.parse_args()

    input_file = open(args.file, 'r')
    lines = input_file.read()
    input_file.close()
    tokens = (lines.strip()).split("\n")

    rules = {}

    # Split the input files into key and value where each determine a rule.
    for token in tokens:
        token_splitted = token.split(" : ")
        items = token_splitted[1].split(" | ")
        rules[token_splitted[0]] = items

    n = len(rules.keys())

    original_rules = rules.copy()
    rules_factored = left_factoring2(rules, n)

    final_text = ""
    for rule in original_rules:
        dash = 0
        while(True):
            out_rule = rule + ("\'"*dash)
            if(not(out_rule in rules_factored)):
                break
            gram = rules_factored[out_rule]
            final_text = final_text + out_rule + \
                " : " + ' | '.join(gram) + "\n"
            dash += 1

    output = open('task_4_2_result.txt', 'w')
    output.write(final_text[:-1])
    output.close()


""".. Ignore pydocstyle D400.

=====
State
=====

Constants used by the dispatcher.
.. autofunction:: resolwe.flow.managers.state.update_constants
"""

# This module should not import anything local, or there will be circular
# dependencies, since the constants are needed in various sub-modules inside
# resolwe.flow.managers.

from collections import namedtuple

from django.conf import settings

ManagerChannelPair = namedtuple("ManagerChannelPair", ["queue", "queue_response"])

MANAGER_CONTROL_CHANNEL = "DUMMY.control"
LISTENER_CONTROL_CHANNEL = "DUMMY.listener"

def update_constants():
    """Recreate channel name constants with changed settings.

    This kludge is mostly needed due to the way Django settings are
    patched for testing and how modules need to be imported throughout
    the project. On import time, settings are not patched yet, but some
    of the code needs static values immediately. Updating functions such
    as this one are then needed to fix dummy values.
    """
    global MANAGER_CONTROL_CHANNEL, LISTENER_CONTROL_CHANNEL
    redis_prefix = getattr(settings, "FLOW_MANAGER", {}).get("REDIS_PREFIX", "")

    MANAGER_CONTROL_CHANNEL = "{}.control".format(redis_prefix)
    LISTENER_CONTROL_CHANNEL = "{}.listener".format(redis_prefix)

update_constants()


from flask import (Flask, render_template, request, jsonify)

app = Flask(__name__)

@app.route('/')
def index():
    print('request for index page received')
    return render_template('index.html')

@app.route('/convert', methods=['POST'])
def convert():
    data = request.get_json()
    celsius = data.get('celsius')
    if celsius is None:
        return jsonify({'error': 'Missing "celsius" value'}), 400
    fahrenheit = (celsius * 9/5) + 32
    return jsonify({'celsius': celsius, 'fahrenheit': fahrenheit})

if __name__ == '__main__':
    app.run(debug=True)


# _*_coding:utf-8_*_
# __author: a123456

class UF:
    def __init__(self):
        self.parent = []  # 记录每个点的前导是什么
        self.rank = []  # rank[i]表示跟节点为i的的树的高度

    def add(self, e):
        self.parent.append(e)
        self.rank.append(1)

    def find(self, p):
        """查找p这个节点的根节点"""
        while p != self.parent[p]:
            self.parent[p] = self.parent[self.parent[p]]  # 路径压缩，使这个节点指向父节点的父节点
            p = self.parent[p]

        return p

    def union(self, p, q):
        """连接两个点"""
        p_root = self.find(p)
        q_root = self.find(q)

        if p_root == q_root:
            return

        if self.rank[p_root] < self.rank[q_root]:
            # 此时根为q_root，q_root高度比p_root高，所以不需维护rank数组
            self.parent[p_root] = q_root
        elif self.rank[p_root] > self.rank[q_root]:
            self.parent[q_root] = p_root
        else:
            self.parent[p_root] = q_root
            self.rank[q_root] += 1

    def is_connected(self, p, q):
        return self.find(p) == self.find(q)

    @classmethod
    def test(cls):
        c = cls()

        for i in range(10):
            c.add(i)

        for i in range(8):
            c.union(1, i)

        print(c.parent)
        print(c.is_connected(0, 5))

if __name__ == '__main__':
    UF.test()


from flask import Blueprint, render_template, send_from_directory, url_for, current_app, request, Response, send_file, jsonify
from flask_login import login_required, current_user
from flask_uploads import UploadSet, IMAGES
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired, FileAllowed
from wtforms import SubmitField
from . import db
from . import imgedit
from . import videoedit
from flask_cors import CORS
from moviepy.editor import VideoFileClip

import base64
import os

main = Blueprint('main', __name__)

@main.route('/')
def index():
    return render_template('index.html')

@main.route('/profile')
@login_required
def profile():
    return render_template('profile.html', name=current_user.name)

photos = UploadSet('photos', IMAGES)

class UploadForm(FlaskForm):
    photo = FileField(
        validators=[
            FileAllowed(photos, 'Only images are allowed.'),
            FileRequired('File field should not be empty.')
        ]
    )
    submit = SubmitField('Upload')

@main.route('/uploads/<filename>')
def get_file(filename):
    return send_from_directory(current_app.config['UPLOADED_PHOTOS_DEST'], filename)

@main.route('/takeimage', methods=['POST'])
def takeimage():
    # Nhận dữ liệu hình ảnh từ yêu cầu POST
    data_url = request.json['image']

    # Loại bỏ phần đầu của chuỗi dữ liệu URL (prefix "data:image/jpeg;base64,")
    img_data = data_url.split(',')[1]

    # Chuyển đổi dữ liệu hình ảnh từ base64 sang binary
    binary_data = base64.b64decode(img_data)

    # Lưu hình ảnh vào thư mục static/images với tên duy nhất và định dạng jpg
    image_name = f'image.jpg'
    image_path = os.path.join(image_name)
    with open(image_path, 'wb') as f:
        f.write(binary_data)

    print(f'Image saved at: {image_path}')

    return Response(status=200)

@main.route('/RGBtoGray', methods = ['GET', 'POST'])
@login_required
def RGBtoGray():
    form = UploadForm()
    if form.validate_on_submit():
        filename = photos.save(form.photo.data)
        file_url = url_for('main.get_file', filename=filename)
        gray_filename = imgedit.RGBtoGray(filename, file_url)
        gray_file_url = url_for('main.get_file', filename=gray_filename)
    else:
        file_url = None
        gray_file_url = None
    return render_template('imgedit_RGBtoGray.html', form = form, file_url = file_url, gray_file_url = gray_file_url)

@main.route('/face_detection', methods = ['GET', 'POST'])
@login_required
def face_detection():
    form = UploadForm()
    if form.validate_on_submit():
        filename = photos.save(form.photo.data)
        file_url = url_for('main.get_file', filename=filename)
        face_filename = imgedit.face_detection(filename, file_url)
        face_file_url = url_for('main.get_file', filename=face_filename)
    else:
        file_url = None
        face_file_url = None
    return render_template('imgedit_face_detection.html', form = form, file_url = file_url, face_file_url = face_file_url)

@main.route('/crop', methods = ['GET', 'POST'])
@login_required
def crop():
    form = UploadForm()
    if form.validate_on_submit():
        x1 = int(request.form.get('x1'))
        x2 = int(request.form.get('x2'))
        y1 = int(request.form.get('y1'))
        y2 = int(request.form.get('y2'))
        filename = photos.save(form.photo.data)
        file_url = url_for('main.get_file', filename=filename)
        face_filename = imgedit.crop(filename, file_url, x1, x2, y1, y2)
        face_file_url = url_for('main.get_file', filename=face_filename)
    else:
        file_url = None
        face_file_url = None
    print(file_url)
    return render_template('imgedit_crop.html', form = form, file_url = file_url, crop_file_url = face_file_url)

@main.route('/rotate', methods = ['GET', 'POST'])
@login_required
def rotate():
    form = UploadForm()
    if form.validate_on_submit():
        degree = int(request.form.get('degree'))
        x = int(request.form.get('x'))
        y = int(request.form.get('y'))
        filename = photos.save(form.photo.data)
        file_url = url_for('main.get_file', filename=filename)
        face_filename = imgedit.rotate(filename, file_url, degree, x, y)
        face_file_url = url_for('main.get_file', filename=face_filename)
    else:
        file_url = None
        face_file_url = None
    return render_template('imgedit_rotate.html', form = form, file_url = file_url, rotate_file_url = face_file_url)

@main.route('/resize', methods = ['GET', 'POST'])
@login_required
def resize():
    form = UploadForm()
    if form.validate_on_submit():
        size = int(request.form.get('size'))
        filename = photos.save(form.photo.data)
        file_url = url_for('main.get_file', filename=filename)
        face_filename = imgedit.resize(filename, file_url, size)
        face_file_url = url_for('main.get_file', filename=face_filename)
    else:
        file_url = None
        face_file_url = None
    return render_template('imgedit_resize.html', form = form, file_url = file_url, resize_file_url = face_file_url)

@main.route('/hsv', methods = ['GET', 'POST'])
@login_required
def hsv():
    form = UploadForm()
    if form.validate_on_submit():
        h = int(request.form.get('hue'))
        s = int(request.form.get('saturation'))
        l = int(request.form.get('light'))
        filename = photos.save(form.photo.data)
        file_url = url_for('main.get_file', filename=filename)
        face_filename = imgedit.hsv(filename, file_url, h, s, l)
        face_file_url = url_for('main.get_file', filename=face_filename)
    else:
        file_url = None
        face_file_url = None
    return render_template('imgedit_resize.html', form = form, file_url = file_url, face_file_url = face_file_url)

@main.route('/trim_video', methods = ['GET', 'POST'])
def trim_video():
    return render_template('trim_video.html')

@main.route('/uploads', methods=['GET'])
def get_uploaded_videos():
    uploaded_videos = [filename for filename in os.listdir(current_app.config["UPLOAD_FOLDER"])]
    return jsonify(uploaded_videos)

@main.route('/clips/<filename>')
def render_clip(filename):
    return send_file(os.path.join(current_app.config["UPLOAD_FOLDER"], filename))

@main.route('/playback/<filename>')
def playback(filename):
    video_path = os.path.join(current_app.config["UPLOAD_FOLDER"], filename)

    # You may want to add additional security checks here before sending the file
    # For example, check user authentication, permissions, etc.

    return send_file(video_path)

@main.route('/upload_video', methods=['POST'])
def upload_video():
    if not os.path.exists(current_app.config["UPLOAD_FOLDER"]):
        os.makedirs(current_app.config["UPLOAD_FOLDER"], exist_ok=True)

    try:
        videofile = request.files['videofile']
        filepath = os.path.join(current_app.config["UPLOAD_FOLDER"], videofile.filename)
        videofile.save(filepath)
        return {"filename": videofile.filename}
    except FileNotFoundError:
        return {"error": "File not found"}

@main.route('/edit_video/trim', methods=['POST'])
def trim_video_edit():
    try:
        videofile = request.json['videofile']
        trim_start = int(request.json['trim_start'])
        trim_end = int(request.json['trim_end'])
        trimmed_filename = request.json['trimmed_filename']

        # Append the video extension (e.g., ".mp4") to the filename
        trimmed_filename_with_extension = f"{trimmed_filename}.mp4"

        edited_videopath = trim_video_function(videofile, trim_start, trim_end, trimmed_filename_with_extension)
        return {
            "status": "success",
            "message": "video edit success",
            "edited_videopath": edited_videopath
        }
    except Exception as e:
        return {
            "status": "error",
            "message": "video edit failure: " + str(e),
        }

def trim_video_function(videofile, start_time, end_time, trimmed_filename):
    clip = VideoFileClip(os.path.join(current_app.config["UPLOAD_FOLDER"], videofile))
    trimmed_filename_with_extension = f"{trimmed_filename}"
    trimpath = os.path.join(current_app.config["UPLOAD_FOLDER"], trimmed_filename_with_extension)
    trimmed_clip = clip.subclip(start_time, end_time)
    trimmed_clip.write_videofile(trimpath)
    return trimpath


import logging
import argparse
import os
import sqlite3

from datetime import datetime
from bs4 import BeautifulSoup

def main():
    parser = argparse.ArgumentParser(
        description="Parse saved webpages for user and route data."
    )
    parser.add_argument("input",
                        help="The folder with saved .html files. "
                             "This program expects user profiles to be saved "
                             "with filenames in the format <uid>_user.html "
                             "and the corresponding scorecards to be saved in "
                             "the format <uid>_boulders.html")
    parser.add_argument("db_path",
                        help="A path to an existing sqlite3 database. See "
                             "create_tables.sql for the expected schema.")
    parser.add_argument("-l", "--log_level", default="info")
    args = parser.parse_args()

    levels = {
        'debug': logging.DEBUG,
        'info': logging.INFO,
        'warning': logging.WARNING,
        'error': logging.ERROR,
        'critical': logging.CRITICAL,
    }
    level = levels.get(args.log_level, logging.NOTSET)
    logging.basicConfig(
        level=level,
        format="%(asctime)s:%(levelname)s:%(name)s:%(message)s"
    )

    conn = sqlite3.connect(args.db_path)
    cursor = conn.cursor()

    def get_text_if_exists(element):
        return "" if element is None else element.text

    def parse_user(uid, text):
        soup = BeautifulSoup(text)
        name = soup.find(id="LabelUserName").text
        height = soup.find(id="LabelUserDataHeight").text
        weight = soup.find(id="LabelUserDataWeight").text
        country = soup.find(id="LabelUserCountry").text.strip(", ")
        city = soup.find(id="LabelUserCity").text
        date_string = soup.find(id="LabelUserDataBirth").text
        started_climbing = soup.find(id="LabelUserDataStartedClimbing").text
        occupation = soup.find(id="LabelUserDataOccupation").text
        interests = soup.find(id="LabelUserDataInterrests").text
        best_result = soup.find(id="LabelUserDataBestResult").text
        best_area = soup.find(id="LabelUserDataBestClimbingArea").text
        guide_areas = soup.find(id="LabelUserDataGuide").text
        sponsor = soup.find(id="LabelUserDataLinks").text

        visits_obj = soup.find(id="LabelUserUpdatesVisits")
        # noinspection PyPep8
        presentation_visits = list(visits_obj.children)[0].split(":")[-1].replace(" ", "")
        # noinspection PyPep8
        routes_visits = list(visits_obj.children)[2].split(":")[-1].replace(" ", "")
        # noinspection PyPep8
        boulders_visits = list(visits_obj.children)[4].split(":")[-1].replace(" ", "")
        # noinspection PyPep8
        blog_visits = list(visits_obj.children)[6].split(":")[-1].replace(" ", "")
        # noinspection PyPep8
        total_visits = list(visits_obj.children)[8].text.split(":")[-1].replace(" ", "")

        # Routes rankings
        r_country_score = get_text_if_exists(
            soup.find(id="LabelUserCountryScoreR")
        )
        r_country_ranking = get_text_if_exists(
            soup.find(id="LabelUserCountryRankingR")
        )
        r_world_ranking = get_text_if_exists(
            soup.find(id="LabelUserWorldRankingR")
        )

        r_all_time_country_score = get_text_if_exists(
            soup.find(id="LabelAllTimeUserCountryScoreR")
        )
        r_all_time_country_ranking = get_text_if_exists(
            soup.find(id="LabelAllTimeUserCountryRankingR")
        )
        r_all_time_world_ranking = get_text_if_exists(
            soup.find(id="LabelAllTimeUserWorldRankingR")
        )

        # Boulder rankings
        b_country_score = get_text_if_exists(
            soup.find(id="LabelUserCountryScoreB")
        )
        b_country_ranking = get_text_if_exists(
            soup.find(id="LabelUserCountryRankingB")
        )
        b_world_ranking = get_text_if_exists(
            soup.find(id="LabelUserWorldRankingB")
        )

        b_all_time_country_score = get_text_if_exists(
            soup.find(id="LabelAllTimeUserCountryScoreB")
        )
        b_all_time_country_ranking = get_text_if_exists(
            soup.find(id="LabelAllTimeUserCountryRankingB")
        )
        b_all_time_world_ranking = get_text_if_exists(
            soup.find(id="LabelAllTimeUserWorldRankingB")
        )

        date = datetime.strptime(
            date_string, '%Y-%m-%d'
        ) if date_string != "" else None
        # noinspection PyPep8,PyPep8,PyPep8
        cursor.execute(
            "INSERT INTO Users VALUES "
            "(:uid, :name, :height, :weight, :country, :city, :date,"
            ":started_climbing, :occupation, :other_interests, "
            ":best_comp_result, :best_climbing_area, :guide_areas, :sponsor,"
            ":presentation_visits, :routes_visits, :boulders_visits, "
            ":blog_visits, :total_visits,"
            ":r_country_score, :r_country_ranking_string, "
            ":r_world_ranking_string, :r_all_time_country_score, "
            ":r_all_time_country_ranking_string, "
            ":r_all_time_world_ranking_string,"
            ":b_country_score, :b_country_ranking_string, "
            ":b_world_ranking_string, :b_all_time_country_score,"
            ":b_all_time_country_ranking_string, "
            ":b_all_time_world_ranking_string)",
            {
                "uid": uid, "name": name, "height": height, "weight": weight,
                "country": country, "city": city, "date": date,
                "started_climbing": started_climbing, "occupation": occupation,
                "other_interests": interests, "best_comp_result": best_result,
                "best_climbing_area": best_area, "guide_areas": guide_areas,
                "sponsor": sponsor, "presentation_visits": presentation_visits,
                "routes_visits": routes_visits,
                "boulders_visits": boulders_visits, "blog_visits": blog_visits,
                "total_visits": total_visits,
                "r_country_score": r_country_score,
                "r_country_ranking_string": r_country_ranking,
                "r_world_ranking_string": r_world_ranking,
                "r_all_time_country_score": r_all_time_country_score,
                "r_all_time_country_ranking_string": r_all_time_country_ranking,
                "r_all_time_world_ranking_string": r_all_time_world_ranking,
                "b_country_score": b_country_score,
                "b_country_ranking_string": b_country_ranking,
                "b_world_ranking_string": b_world_ranking,
                "b_all_time_country_score": b_all_time_country_score,
                "b_all_time_country_ranking_string": b_all_time_country_ranking,
                "b_all_time_world_ranking_string": b_all_time_world_ranking
            }
        )
        conn.commit()

    def parse_boulders(uid, text):
        soup = BeautifulSoup(text)
        grade_rows = soup.find_all(class_="AscentListHeadRow")

        for grade_row in grade_rows:
            grade = list(grade_row.find("b").children)[1]
            climbs = grade_row.parent.next_siblings

            for c in climbs:
                if hasattr(c, "children") and len(list(c.children)) == 19:
                    date_obj = list(list(c.children)[1].children)[1]
                    date = datetime.strptime(
                        date_obj.text
                        if hasattr(date_obj, "text")
                        else date_obj,
                        '%y-%m-%d'
                    )

                    type_image = list(c.children)[3].find("img").attrs["src"]
                    # noinspection PyPep8,PyPep8
                    if type_image == "images/56f871c6548ae32aaa78672c1996df7f.gif":
                        type_string = "Flash"
                    elif type_image == "images/979607b133a6622a1fc3443e564d9577.gif":
                        type_string = "Redpoint"
                    else:
                        type_string = "Onsight"

                    climb_name = list(c.children)[5].find("a").text

                    rec_string = list(c.children)[7].find("img").attrs["src"]
                    rec = rec_string == "images/UserRecommended_1.gif"

                    area = list(c.children)[9].text

                    tags = list(c.children)[11].text.strip()

                    comment_elements = list(list(c.children)[13].children)
                    # noinspection PyPep8
                    comment_obj = comment_elements[-1] if len(comment_elements) > 1 else ""
                    # noinspection PyPep8
                    comment = comment_obj.text if hasattr(comment_obj, "text") else comment_obj

                    stars = list(c.children)[15].text.strip().count("*")

                    # noinspection PyPep8
                    cursor.execute(
                        "INSERT INTO Boulders VALUES "
                        "(:uid, :name, :grade, :date, :type, :recommend, :area,"
                        ":tags, :comment, :stars)",
                        {
                            "uid": uid, "name": climb_name, "grade": grade,
                            "date": date, "type": type_string,
                            "recommend": rec, "area": area, "tags": tags,
                            "comment": comment, "stars": stars
                        }
                    )
                    conn.commit()
                elif c == "\n":
                    pass
                else:
                    break

    # Get a list of all the users in the DB, so we don't parse them again.
    cursor.execute("SELECT user_id FROM Users")
    user_ids = set(zip(*cursor.fetchall())[0])

    # Sort them in reverse so all user files are parsed first.
    # TODO: Fix this if necessary.
    for filename in sorted(os.listdir(args.input), reverse=True):
        try:
            if filename.endswith("user.html"):
                u = int(filename.split("_")[0])

                if u in user_ids:
                    logging.info(
                        "Skipping user file {} (uid {})".format(filename, u)
                    )
                else:
                    logging.info(
                        "Parsing user file {} (uid {})".format(filename, u)
                    )

                    with open(os.path.join(args.input, filename)) as f:
                        parse_user(u, f.read())

            elif filename.endswith("boulders.html"):
                u = int(filename.split("_")[0])

                if u in user_ids:
                    logging.info(
                        "Skipping user file {} (uid {})".format(filename, u)
                    )
                else:
                    logging.info(
                        "Parsing boulder file {} (uid {})".format(filename, u)
                    )

                    with open(os.path.join(args.input, filename)) as f:
                        parse_boulders(u, f.read())
            else:
                logging.error("Unrecognized file {}".format(filename))
        except Exception as ex:
            logging.error("Caught exception {}".format(ex))

    conn.close()

if __name__ == "__main__":
    main()


def throughput(log, start, end):
    cnt = 0
    for i in log:
        if i[0] < end and i[1] >= start:
            cnt += 1
    return cnt

def solution(lines):
    ans = 0
    work = []                            # work에[시작시간, 끝시간] 저장
    for line in lines:
        date, s, t = line.split()       # 날짜, 응답완료시간, 처리시간
        s = s.split(':')
        t = t.replace('s', '')

        end = (int(s[0])*3600 + int(s[1])*60 + float(s[2])) * 1000 # 끝난 시간을 ms 단위로 저장
        start = end - float(t)*1000 + 1 # 시작 시간을 ms 단위로 저장
        work.append([start, end])
    for i in work:                       # 최대 초당 처리량 구하기
        ans = max(ans, throughput(work, i[0], i[0] + 1000), throughput(work, i[1], i[1] + 1000))

    return ans


# https://algorithmtutor.com/Data-Structures/Tree/Splay-Trees/
# https://tjkendev.github.io/procon-library/python/binary_search_tree/splay-tree.html
class Node(object):
    def __init__(self, value):
        self.left = None
        self.right = None
        self.value = value
        self.size = 1
        self.data = None

class Array(object):
    def __init__(self, A, dot=None):
        self._dot = dot
        node = None
        for a in A:
            parent = Node(a)
            parent.left = node
            node = parent
            self._update(node)
        self._root = node

    def __bool__(self):
        return bool(self._root)

    def __len__(self):
        return self._root.size if self else 0

    def __str__(self):
        def dfs(node, res=[]):
            if node is None:
                return []
            dfs(node.left, res)
            res.append(node.value)
            dfs(node.right, res)
            return res
        return str(dfs(self._root))

    def _update(self, node):
        node.size = 1
        if node.left:
            node.size += node.left.size
        if node.right:
            node.size += node.right.size
        if self._dot:
            node.data = node.value
            if node.left:
                node.data = self._dot(node.left.data, node.data)
            if node.right:
                node.data = self._dot(node.data, node.right.data)

    def _rotate(self, v, p, d):
        if d == 0:
            p.left = v.right
            v.right = p
        elif d == 1:
            p.right = v.left
            v.left = p
        self._update(p)

    def _splay(self, v, path, direction):
        while len(path) >= 2:
            p, g = path.pop(), path.pop()
            dp, dg = direction.pop(), direction.pop()
            if dp == dg:
                self._rotate(p, g, dg)
                self._rotate(v, p, dp)
            else:
                self._rotate(v, p, dp)
                self._rotate(v, g, dg)
        if path:
            p = path.pop()
            d = direction.pop()
            self._rotate(v, p, d)
        self._update(v)
        self._root = v

    def __getitem__(self, i):
        if i < 0:
            i += len(self)
        assert 0 <= i < len(self)
        path, direction = [], []
        v = self._root
        while True:
            lsize = v.left.size if v.left else 0
            if i == lsize:
                break
            path.append(v)
            if i < lsize:
                v = v.left
                direction.append(0)
            else:
                i -= lsize + 1
                v = v.right
                direction.append(1)
        self._splay(v, path, direction)
        return self._root.value

    def __setitem__(self, i, v):
        self[i]
        self._root.value = v
        self._update(self._root)

    def _split(self, i):
        assert 0 <= i <= len(self)
        if i == len(self):
            return None
        self[i]
        self._root, right = self._root.left, self._root
        right.left = None
        self._update(right)
        return right

    def _merge(self, node):
        if not self:
            self._root = node
        else:
            self[-1]
            self._root.right = node
            self._update(self._root)

    def insert(self, i, v):
        right = self._split(i)
        node = Node(v)
        node.left, node.right = self._root, right
        self._root = node
        self._update(self._root)

    def pop(self, i=-1):
        res = self[i]
        self._root, right = self._root.left, self._root.right
        self._merge(right)
        return res

    def sum(self, l, r):
        right = self._split(r)
        mid = self._split(l)
        res = mid.data
        self._merge(mid)
        self._merge(right)
        return res


# import datetime
#
# def regularMonths(currMonth):
#     mm, yy = map(int, currMonth.split("-"))
#     while True:
#         if mm < 12:
#             mm += 1
#         else:
#             mm = 1
#             yy +=1
#         m = datetime.date(yy, mm, 1)
#         if m.weekday() == 0:
#             return m.strftime("%m-%Y")

from datetime import datetime  # импортирует класс datetime.datetime!

def regularMonths(currMonth):
    d = datetime.strptime(currMonth, '%m-%Y')
    m = d.month
    y = d.year

    while True:
        if m % 12 == 0:
            y += 1
        m = (m % 12) + 1
        d = datetime(y, m, 1)
        print(d)
        if d.weekday() == 0:
            # Today is a Monday.
            return datetime.strftime(d, '%m-%Y')

currMonth = "2-2016"
print(regularMonths(currMonth))

'''
In an effort to be more innovative, your boss introduced a strange new tradition:
the first day of every month you're allowed to work from home.
You like this rule when the day falls on a Monday, because any excuse to skip rush hour traffic is great!

You and your colleagues have started calling these months regular months.
Since you're a fan of working from home, you decide to find out how far away the nearest regular month is,
given that the currMonth has just started.

For your convenience, here is a list of month lengths (from January to December, respectively):

    Month lengths: 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31.

Please, note that in leap years February has 29 days.

Example

For currMonth = "02-2016", the output should be
regularMonths(currMonth) = "08-2016".

February of 2016 year is regular, but it doesn't count since it has started already,
so the next regular month is August of 2016 - which is the answer.

'''