import logging
import argparse
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torch.autograd import Variable
import copy
import numpy as np
import os
from torch.utils.data import DataLoader
from torch.utils.data.sampler import SubsetRandomSampler
#import matplotlib.pyplot as plt
import math
import time
#from scipy.io import savemat
import collections
import shutil
import networkFiles as NF
import torchvision
import torchvision.transforms as transforms

##################################################################
# Parameters:
# 		* input_dist: Sets distribution of inputs {'gaussian', 'non-planted', 'natural_scences'}
# 		* data_gen: Specifies method for generating outputs
#		* out_file: Path to save results
#		* use_gpu: Boolean flag for using GPU
##################################################################

parser = argparse.ArgumentParser()
parser.add_argument('--data_gen', default='identity', type=str, metavar='PATH',
					help='method for generating outpus')
parser.add_argument('--input_dist', default='guassian', type=str, metavar='PATH',
					help='sets the input distribution')
parser.add_argument('--out_file', default='Results/test.pth.tar', type=str, metavar='PATH',
					help='path to save result block')
parser.add_argument('--use_gpu', action='store_true')

def main(args):
	##################################################################
	# Generate and train a one layer quadratic NN
	# Goal is to see if GD finds global minimum
	#
	#
	##################################################################

	# Load in arguments
	data_gen = args.data_gen
	input_dist = args.input_dist
	out_file = args.out_file

	nsamp = 1500 # Sample number
	ntrial = 20
	lower_hidden = 0
	upper_hidden = 20 # Upper bound on the
	input_dim = 10 # Input dimension

	training_epochs = 30000
	training_epochs_lsq = 700
	batch = 2000
	learning = 1e-3

	dtype = torch.FloatTensor
	if args.use_gpu:
		print('GPU is used')
		dtype = torch.cuda.FloatTensor

	if ((input_dist=='natural_scenes') or (input_dist=='non_planted_natural')):
		print('Using natural scenes')
		# Import cifar
		transform = transforms.Compose(
		[transforms.ToTensor(),
		 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

		cifarset = torchvision.datasets.CIFAR10(root='./data', train=True,
												download=True, transform=transform)
		cifarloader = torch.utils.data.DataLoader(cifarset, batch_size=5,
												  shuffle=True)

	# Save out metadata about the experiments
	results = {}
	results['Meta'] = {}
	results['Meta']['Data Generation'] = data_gen
	results['Meta']['Input Distribution'] = input_dist

	results['Meta']['Sample Number'] = nsamp
	results['Meta']['Lower Hidden'] = upper_hidden
	results['Meta']['Upper Hidden'] = upper_hidden
	results['Meta']['Input Dimension'] = input_dim
	############################################
	# Step 1: generate A's and training examples
	#
	############################################

	# Set the data_generation
	if (data_gen == 'identity'):
		print('Generating data with identity matrix')
		W = np.eye(input_dim)
		v = np.ones((1, input_dim))
	elif(data_gen == 'identity_mixed_signs'):
		print('Generating data with identity matrix, mixed signs')
		W = np.eye(input_dim)
		v = np.random.binomial(1, 0.5, (1, input_dim))
		v[v==0] = -1
	elif(data_gen == 'gaussian_matrix'):
		print('Generating data with random Gaussian matrix')
		W = np.random.normal(size = (input_dim, input_dim))/np.sqrt(input_dim)
		v = np.random.binomial(1, 0.5, (1, input_dim))
		v[v==0] = -1
	elif(data_gen == 'identity_lowrank'):
		print('Generating data with low-rank identity matrix')
		Id = np.eye(input_dim)
		W = Id[0:rank, :]
		v = np.ones((1, rank))
	elif(data_gen == 'identity_lowrank_mixed_signs'):
		print('Generating data with low-rank identity matrix, mixed signs')
		Id = np.eye(input_dim)
		W = Id[0:rank, :]
		v = np.random.binomial(1, 0.5, (1, rank))
		v[v==0] = -1
	elif(data_gen == 'gaussian_lowrank'):
		print('Generating data with random Gaussian matrix')
		W = np.random.normal(size = (rank, input_dim))/np.sqrt(input_dim)
		v = np.random.binomial(1, 0.5, (1, rank))
		v[v==0] = -1
	else:
		print('The specified data generation method not recognized; using identity.')
		W = np.eye(input_dim)
		v = np.ones((1, input_dim))

	print(W)
	print(v)

	trainFeatures = np.zeros((nsamp, input_dim))
	trainLabels = np.zeros((nsamp, 1))

	for j in range(nsamp):

		if (input_dist == 'gaussian'):
			x = np.random.normal(size = (input_dim, 1))/(np.sqrt(input_dim))
			x[0] = 1
			y = v.dot(np.square(W.dot(x)))
		elif (input_dist == 'non_planted'):
			x = np.random.normal(size = (input_dim, 1))/(np.sqrt(input_dim))
			x[0] = 1
			y = 2*np.random.normal(size = (1))
		elif (input_dist == 'natural_scenes'):
			data, labels = iter(cifarloader).next()

			image = np.random.choice(5, input_dim)
			i_pixel = np.random.choice(32, input_dim)
			j_pixel = np.random.choice(32, input_dim)

			x = data[image, 1, i_pixel, j_pixel]

			y = v.dot(np.square(W.dot(x)))
		elif (input_dist == 'non_planted_natural'):
			data, labels = iter(cifarloader).next()

			image = np.random.choice(5, input_dim)
			i_pixel = np.random.choice(32, input_dim)
			j_pixel = np.random.choice(32, input_dim)

			x = data[image, 1, i_pixel, j_pixel]

			y = 2*np.random.normal(size = (1))
		else:
			x = np.random.normal(size = (input_dim, 1))
			y = v.dot(np.square(W.dot(x)))

		trainFeatures[j, :] = np.transpose(x)
		trainLabels[j, :] = y

	trainFeaturesTorch = torch.from_numpy(trainFeatures).type(dtype)
	trainLabelsTorch = torch.from_numpy(trainLabels).type(dtype)

	trainLoader = [trainLabelsTorch]

	# Solve the least squares problem via the QR factorization
	X = Veronese(trainFeaturesTorch,input_dim,2,nsamp).numpy()

	m, c, r, s  = np.linalg.lstsq(X,trainLabels,rcond = None)
	rel_error = ((trainLabels-np.matmul(X,m))**2).mean()/(np.transpose(trainLabels)**2).mean()
	# Q, R = torch.qr(tensorFeaturesTorch)
	# temp = torch.mm(Q.t(), trainLabelsTorch)

	# # beta is the least squares solution
	# beta = torch.mm(torch.inverse(R), temp)
	# #q, _ = torch.lstsq(trainLabelsTorch, tensorFeaturesTorch)
	# #beta = q[0:100, :]

	# print((torch.mm(tensorFeaturesTorch, beta) - trainLabelsTorch)**2)

	# residual_all = ((torch.mm(tensorFeaturesTorch, beta) - trainLabelsTorch)**2).mean()
	# residual_all_lsq = residual_all*(1/(trainLabelsTorch**2).mean())

	results['Meta']['Q'] = W
	results['Meta']['lambda'] = v
	results['Meta']['Features'] = trainFeaturesTorch
	results['Meta']['Labels'] = trainLabelsTorch
	results['Meta']['lsq_weights'] = m
	results['Meta']['lsq_residual'] = rel_error

	for trial in range(ntrial):

		print(rel_error)
		results[trial] = {}

		############################################
		# Set up the quadratic network in Pytorch and train on the data set using GD

		for hidden_iter in range(lower_hidden, upper_hidden):

			hidden = hidden_iter + 1

			print('Trial %i: %i Hidden Units \n' % (trial, hidden))

			results[trial][hidden_iter] = {}

			######## Solve with regular network
			print('Regular QNN \n')
			model = NF.QuadraticNet(input_dim, hidden, 1)
			model.type(dtype)

			eta = torch.norm(trainLabelsTorch)**2

			loss_fn = nn.MSELoss()
			optimizer = optim.Adam(model.parameters(), lr = learning)

			I = torch.eye(input_dim).type(dtype)

			epoch_trace = []
			residual_trace = []

			results[trial][hidden_iter]['Quadratic'] = {}

			for epoch in range(training_epochs):
				#loader = DataLoader(trainset, batch_size=batch, shuffle=True)
				model.train()
				for y in trainLoader:
					# Run the model forward to compute scores and loss.
					output = model(trainFeaturesTorch, dtype).type(dtype)
					loss = loss_fn(output, y) + eta*loss_fn(torch.mm(model.hiddenLayer.weight.t(), model.hiddenLayer.weight), I)

					# Run the model backward and take a step using the optimizer.
					optimizer.zero_grad()
					loss.backward()
					optimizer.step()

				if (epoch % 2000 == 0):

					model.eval()

					output = model(trainFeaturesTorch, dtype).detach().cpu().numpy()
					residual_all = np.sum((trainLabels - output)**2)

					residual_all_nn = np.asscalar(residual_all*(1/(np.linalg.norm(trainLabels)**2)))

					print('Epoch %i: Current residual: %.6f' % (epoch, residual_all_nn))
					epoch_trace = np.append(epoch_trace, epoch)
					residual_trace = np.append(residual_trace, residual_all_nn)

			model.eval()

			output = model(trainFeaturesTorch, dtype).detach().cpu().numpy()
			residual_all = np.sum((trainLabels - output)**2)

			residual_all_nn = np.asscalar(residual_all*(1/(np.linalg.norm(trainLabels)**2)))

			trace = np.vstack((epoch_trace, residual_trace))

			results[trial][hidden_iter]['Quadratic']['Model'] = model.state_dict()
			results[trial][hidden_iter]['Quadratic']['Residual'] = residual_all_nn
			results[trial][hidden_iter]['Quadratic']['Trace'] = trace

		torch.save(results, out_file)

def Veronese(x,d,k,T):
	# This makes an unrolled version of the kth order symetric tensor generated by x
	#	d: input dimension (dimension of x)
	#	k: order of tensor
	#	T: number of samples

		X = torch.Tensor(T,d**k)
		for t in range(T):
			temp = x[t,:].squeeze()
			len = d
			# print(temp)
			for i in range(k-1):
				len = len*d
				temp = torch.ger(temp,x[t,:].squeeze()).view(len)
				# print(temp)
			X[t,:] = temp
		return X

if __name__ == '__main__':
	args = parser.parse_args()
	main(args)


from typing import Any, Dict, Iterable

from odd_collector_sdk.domain.adapter import AbstractAdapter
from odd_models.models import DataEntityList
from oddrn_generator.generators import DmsGenerator

from odd_collector_aws.domain.fetch_paginator import fetch_paginator
from odd_collector_aws.domain.paginator_config import PaginatorConfig
from odd_collector_aws.domain.plugin import DmsPlugin

from .client import DMSClient
from .mappers.tasks import map_dms_task

MAX_RESULTS_FOR_PAGE = 100

class Adapter(AbstractAdapter):
    def __init__(self, config: DmsPlugin) -> None:
        self._dms_client = DMSClient(config)
        self._oddrn_generator = DmsGenerator(
            cloud_settings={
                "region": config.aws_region,
                "account": self._dms_client.account_id,
            }
        )

    def get_data_source_oddrn(self) -> str:
        return self._oddrn_generator.get_data_source_oddrn()

    def get_data_entity_list(self) -> DataEntityList:
        endpoints_nodes = self._get_endpoints_nodes_arn_dict()
        tasks = list(self._get_tasks())
        tasks_entities = [
            map_dms_task(
                task,
                {
                    "oddrn_generator": self._oddrn_generator,
                    "endpoints_arn_dict": endpoints_nodes,
                },
            )
            for task in tasks
        ]

        return DataEntityList(
            data_source_oddrn=self.get_data_source_oddrn(),
            items=[*tasks_entities],
        )

    def _get_tasks(self) -> Iterable:
        return fetch_paginator(
            PaginatorConfig(
                op_name="describe_replication_tasks",
                parameters={},
                page_size=MAX_RESULTS_FOR_PAGE,
                list_fetch_key="ReplicationTasks",
            ),
            self._dms_client.dms,
        )

    def _get_endpoints_nodes(self) -> Iterable:
        paginator = fetch_paginator(
            PaginatorConfig(
                op_name="describe_endpoints",
                parameters={},
                page_size=MAX_RESULTS_FOR_PAGE,
                list_fetch_key="Endpoints",
            ),
            self._dms_client.dms,
        )
        return paginator

    def _get_endpoints_nodes_arn_dict(self) -> Dict[str, Any]:
        endpoint_nodes = self._get_endpoints_nodes()
        return {
            endpoint_node.get("EndpointArn"): endpoint_node
            for endpoint_node in endpoint_nodes
        }


from collections import UserDict

from typing import List

class Abundances(UserDict):
    """
    Dict-like class to store the abundances

    Usage:

    >>> Abundances.from_3abunds_as_lines(
    ... '''# initial abundances, n(X)/n(H), 'frc(1:nfrc)
    ... 15                ! "Low" metals
    ... G0         1.000D-00
    ... HD         1.550D-05
    ... oH2        3.750D-01
    ... pH2        1.250D-01
    ... HE         0.975D-01
    ... C          0.786D-04'''.splitlines()
    ... ) ==  {'G0': 1.0, 'HD': 1.55e-05, 'oH2': 0.375, 'pH2': 0.125, 'HE': 0.0975, 'C': 7.86e-05}
    True
    """

    @classmethod
    def from_3abunds(cls, file: str):
        """Read 3abunds.inp ALCHEMIC abundances file"""
        with open(file) as fff:
            lines = fff.readlines()
        return cls.from_3abunds_as_lines(lines)

    @classmethod
    def from_3abunds_as_lines(cls, lines: List):
        """Process the string containing the 3abunds.inp ALCHEMIC abundances file"""
        species = {}
        for line in lines[2:]:
            _species, _abundance_to_hydrogen = line.split()
            species[_species] = float(_abundance_to_hydrogen.replace("D", "E"))
        return cls(species)

    def __init__(self, *args, **kwargs):
        if not (bool(args) or bool(kwargs)):
            super().__init__({"H2": 0.5, "CO": 0.5e-4})
        else:
            super().__init__(*args, **kwargs)


import json
import ecdsa

class Transaction:
    def __init__(self, sender_address, sender_private_key, recipient_address, amount):
        self.sender_address = sender_address
        self.sender_private_key = sender_private_key
        self.recipient_address = recipient_address
        self.amount = amount

    def to_dict(self):
        return {
            "sender_address": self.sender_address,
            "recipient_address": self.recipient_address,
            "amount": self.amount
        }

    def sign_transaction(self):
        # Create a digital signature using sender's private key
        print(self.sender_private_key)
        private_key = ecdsa.SigningKey.from_pem(self.sender_private_key)
        message = json.dumps(self.to_dict()).encode()
        signature = private_key.sign(message)

        # Add the digital signature to the transaction
        self.signature = signature.hex()


from typing import List

import numpy

from collatable.collator import Collator
from collatable.extras.indexer import LabelIndexer, TokenIndexer
from collatable.fields import LabelField, MetadataField, TextField
from collatable.instance import Instance

def test_instance() -> None:
    dataset = [
        ("this is awesome", "positive"),
        ("this is a bad movie", "negative"),
        ("this movie is an awesome movie", "positive"),
        ("this movie is too bad to watch", "negative"),
    ]

    token_indexer = TokenIndexer[str]()
    label_indexer = LabelIndexer[str]()

    instances: List[Instance] = []
    with token_indexer.context(train=True), label_indexer.context(train=True):
        for id_, (text, label) in enumerate(dataset):
            tokens = text.split()
            instance = Instance(
                text=TextField(tokens, indexer=token_indexer),
                label=LabelField(label, indexer=label_indexer),
                metadata=MetadataField({"id": id_}),
            )
            instances.append(instance)

    collator = Collator()
    output = collator(instances)

    assert isinstance(output, dict)
    assert set(output.keys()) == {"text", "label", "metadata"}
    assert isinstance(output["text"], dict)
    assert isinstance(output["label"], numpy.ndarray)
    assert isinstance(output["metadata"], list)
    assert output["metadata"] == [{"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}]


# Definition for singly-linked list.
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        if not head or not head.next:
            return True
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        def reversenode(head):
            pre = None
            while head:
                temp = head.next
                head.next = pre
                pre = head
                head = temp
            return pre
        slow = reversenode(slow)
        while head and slow:
            if head.val != slow.val:
                return False
            head = head.next
            slow = slow.next
        return True


#CS496 Mobile and Web development HW3
# Cord Meados 2017

#-----------------------------------
#Slip Class and handler for main.py
#-----------------------------------

# [START imports]
from google.appengine.ext import ndb
import webapp2
import json

#slips
class Slip(ndb.Model):
	number = ndb.IntegerProperty(required=True)
	current_boat = ndb.StringProperty()
	arrival_date = ndb.StringProperty()
	departure_history = ndb.StringProperty(repeated=True)

class SlipHandler(webapp2.RequestHandler):
	def get(self, id=None):
		#if there is an id, respond with slip info corresponding to that id.
		#else, give a body response saying your're at the sliphandler page
		if id:
			s = ndb.Key(urlsafe=id).get() #get the object from the database
			s_d = s.to_dict() #turn s object (slip) into a dictionary
			s_d['self'] = "/slip/" + id
			self.response.write(json.dumps(s_d))
		else:
			slip_dict = [] #dictionary to store slips in
			for s in Slip.query().fetch(): #fetch all slips from the database
				s_d = s.to_dict() #turn each slip instance into a dictionary
				s_d['self'] = '/slip/' + s.key.urlsafe() #add a "self" link to each slip dictionary
				slip_dict.append(s_d) #save slip dictionary in larger dictionary
			self.response.write(json.dumps(slip_dict)) #return slips with links to themselves.

	def post(self):
		#Create parent key so we can have a slip tree
		parent_key = ndb.Key(Slip, "parent_slip")
		#get json data from post, then use "loads" to turn json into object. assign to slip_data.
		slip_data = json.loads(self.request.body)

		#check if required data is good. If not throw bad data error or don't input data.
		#if good add data to new_slip
		if (isinstance(slip_data['number'], int) == False):
			webapp2.abort(400,"Bad user input")
		new_slip = Slip(number=slip_data['number'], parent=parent_key) #add required data

		if str(Slip.query(Slip.number == new_slip.number).fetch()) == "[]":	 #check if number already exists
			new_slip.put() #put slip object on database
		else:
			webapp2.abort(400,"That slip number is already in use.")

		#provide link to new slip object and return data for error testing
		slip_dict = new_slip.to_dict()
		slip_dict['self'] = '/slip/' + new_slip.key.urlsafe()
		self.response.write(json.dumps(slip_dict))

	def patch(self, id=None):
		if id:
			s = ndb.Key(urlsafe=id).get() #get the object instance from the database
			if self.request.body: #check if user sent data. If not, abort error 400
				new_data = json.loads(self.request.body)
			else:
				webapp2.abort(400,"Bad user input")

			#replace data if it is there.
			if isinstance(new_data.get('number', None), int):
				new_slip_number = Slip(number=new_data['number'])
				if str(Slip.query(Slip.number == new_slip_number.number).fetch()) == "[]":
					s.number = new_slip_number.number
				else:
					webapp2.abort(400,"That slip number is already in use.")

			s.put() #put new info on database

		else:
			self.response.write("patch to SlipHandler") #debugging

	def put(self, id=None):
		if id:
			s = ndb.Key(urlsafe=id).get() #get the object instance from the database
			if self.request.body: #check if user sent data. If not, abort error 400
				new_data = json.loads(self.request.body)
			else:
				webapp2.abort(400,"Bad user input")

			#replace data if it is there.
			if isinstance(new_data.get('number', None), int):
				new_slip_number = Slip(number=new_data['number'])
				if str(Slip.query(Slip.number == new_slip_number.number).fetch()) == "[]":
					s.number = new_slip_number.number
				else:
					webapp2.abort(400,"That slip number is already in use.")

			s.put() #put new info on database

		else:
			elf.response.write("put to SlipHandler") #debugging

	def delete(self, id=None):
		#if there is an id, delete it
		if id:
			s = ndb.Key(urlsafe=id).get()
			if s.current_boat != None:
				b = ndb.Key(urlsafe=s.current_boat).get() #get boat that is in the slip
				b.at_sea = True
				b.put()
			ndb.Key(urlsafe=id).delete()


class FormatDataSet():
    def __init__(self, output_dir, output_file, input_dir, is_comment=True):
        self.output_dir = output_dir
        self.output_file = output_file
        self.input_dir = input_dir
        self.is_comment = is_comment
        # Bohr -> ang
        self.CONVERT_ANG = 0.529177;
        # Hartree -> eV
        self.CONVERT_EV = 27.2114;
        # Hartree/Bohr -> eV/ang
        self.CONVERT_FORCE = self.CONVERT_EV / self.CONVERT_ANG;

    def qmas_to_n2p2(self):
        # データのファイル名
        PATH = [
            'data_alpha-critobalite',
            'data_alpha-quartz',
            'data_beta-quartz',
            'data_beta-trydymite',
            'data_coesite',
            'data_Fdd2-beta-critobalite',
            'data_hex-trydymite',
            'data_stishovite'
        ]

        # 出力ファイル
        export_file = open(f'{self.output_dir}/{self.output_file}', 'a')
        structure_idx = 0
        for raw_data_file in PATH:
            structure = raw_data_file.split('_')[-1]
            with open(f'{self.input_dir}/{raw_data_file}') as file:
                # 入力ファイル一行ずつリストで取得する
                l_strip = [line.strip() for line in file.readlines()]

                # ブロックに分割する
                number_of_atom = int(l_strip[5].split()[1])
                all_lines = len(l_strip)
                lines_in_block = number_of_atom * 3 + 10
                number_of_block = int(all_lines / lines_in_block)

                # ブロックに分けた連想配列
                block_list = []
                for i in range(number_of_block):
                    block_list.append(l_strip[lines_in_block * i:lines_in_block * (i + 1)])

                # index取得
                lattice_index = 2
                position_abs_index = 7
                position_frac_index = 8 + (number_of_atom)
                energy_index = 8 + number_of_atom * 2
                force_index = 10 + number_of_atom * 2

                # n2p2のフォーマットに変更
                for block in block_list:
                    # begin出力
                    export_file.write('begin\n')

                    # comment
                    export_file.write(f'comment structure:{structure} structure_idx:{structure_idx}.\n')
                    structure_idx += 1

                    # lattice
                    lattice_block = block[lattice_index:lattice_index + 3]

                    for lattice in lattice_block:
                        # convert
                        # lattice = ' '.join([str(float(i) * self.CONVERT_ANG) for i in lattice.split(' ')])
                        export_file.write(f'lattice {lattice}\n')

                    # atom
                    # position_block = block[position_abs_index:position_abs_index + number_of_atom]
                    position_block = block[position_frac_index:position_frac_index + number_of_atom]
                    force_block = block[force_index:force_index + number_of_atom]
                    atom_block = []
                    dummy_data = [0.0, 0.0]
                    for position, force in zip(position_block, force_block):
                        position = position.split()[2:]
                        force = force.split()[1:]
                        # convert
                        # position = [str(float(i) * self.CONVERT_ANG) for i in position]
                        atom = force[0]
                        force = [str(float(i) * self.CONVERT_FORCE) for i in force[1:]]
                        force.insert(0, atom)

                        force.insert(1, '0.0')
                        force.insert(1, '0.0')
                        row = position + force
                        atom_block.append(row)

                    for atom in atom_block:
                        atom_str = ' '.join(atom)
                        export_file.write(f'atom {atom_str}\n')

                    # energy
                    energy = float(block[energy_index].split()[1]) * self.CONVERT_EV
                    export_file.write(f'energy {energy}\n')

                    # charge
                    charge = 0.0
                    export_file.write(f'charge {charge}\n')

                    # end出力
                    export_file.write('end\n')

        export_file.close()

if __name__ == '__main__':
    obj = FormatDataSet('/Users/y1u0d2/desktop/Lab/data/qmas_frac', 'frac.data',
                        '/Users/y1u0d2/desktop/Lab/data/qmas_data')
    obj.qmas_to_n2p2()


"""
    开闭和礼帽运算
"""
import cv2
import cv2 as cv
import numpy as np
import random

# 开运算 先侵蚀后膨胀
def my_opening():
    element = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3), (1, 1))
    img_opening = cv.morphologyEx(img_bgr, cv.MORPH_OPEN, element)
    img_show = cv2.hconcat([img_bgr, img_opening])
    cv.imshow("img_opening", img_show)
    cv.waitKey()

# 闭运算 先膨胀后侵蚀
def my_closing():
    element = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3), (1, 1))
    img_closing = cv.morphologyEx(img_bgr, cv.MORPH_CLOSE, element)
    img_show = cv2.hconcat([img_bgr, img_closing])
    cv.imshow("img_closing", img_show)
    cv.waitKey()

# 梯度 轮廓
def my_gradient():
    element = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3), (1, 1))
    img_gradient = cv.morphologyEx(img_bgr, cv.MORPH_GRADIENT, element)
    img_show = cv2.hconcat([img_bgr, img_gradient])
    cv.imshow("img_gradient", img_show)
    cv.waitKey()

# input - opening
def my_tophat():
    element = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3), (1, 1))
    img_tophat = cv.morphologyEx(img_bgr, cv.MORPH_TOPHAT, element)
    img_show = cv2.hconcat([img_bgr, img_tophat])
    cv.imshow("img_tophat", img_show)
    cv.waitKey()

# input - closing
def my_blackhat():
    element = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3), (1, 1))
    img_blackhat = cv.morphologyEx(img_bgr, cv.MORPH_BLACKHAT, element)
    img_show = cv2.hconcat([img_bgr, img_blackhat])
    cv.imshow("img_tophat", img_show)
    cv.waitKey()

def morphology_operations(val):
    morph_operator = cv.getTrackbarPos(title_trackbar_operator_type, title_window)
    morph_size = cv.getTrackbarPos(title_trackbar_kernel_size, title_window)
    morph_elem = 0
    val_type = cv.getTrackbarPos(title_trackbar_element_type, title_window)
    if val_type == 0:
        morph_elem = cv.MORPH_RECT
    elif val_type == 1:
        morph_elem = cv.MORPH_CROSS
    elif val_type == 2:
        morph_elem = cv.MORPH_ELLIPSE

    element = cv.getStructuringElement(morph_elem, (2*morph_size + 1, 2*morph_size+1), (morph_size, morph_size))
    operation = morph_op_dic[morph_operator]
    dst = cv.morphologyEx(src, operation, element)
    cv.imshow(title_window, dst)

def main():
    cv.namedWindow(title_window)
    cv.createTrackbar(title_trackbar_operator_type, title_window, 0, max_operator, morphology_operations)
    cv.createTrackbar(title_trackbar_element_type, title_window, 0, max_elem, morphology_operations)
    cv.createTrackbar(title_trackbar_kernel_size, title_window, 0, max_kernel_size, morphology_operations)

    morphology_operations(0)
    cv.waitKey()

if __name__ == "__main__":
    morph_size = 0
    max_operator = 4
    max_elem = 2
    max_kernel_size = 21
    title_trackbar_operator_type = 'Operator:\n 0: Opening - 1: Closing  \n 2: Gradient - 3: Top Hat \n 4: Black Hat'
    title_trackbar_element_type = 'Element:\n 0: Rect - 1: Cross - 2: Ellipse'
    title_trackbar_kernel_size = 'Kernel size:\n 2n + 1'
    title_window = 'Morphology Transformations Demo'
    morph_op_dic = {0: cv.MORPH_OPEN, 1: cv.MORPH_CLOSE, 2: cv.MORPH_GRADIENT, 3: cv.MORPH_TOPHAT, 4: cv.MORPH_BLACKHAT}

    dir_root = r"D:\00myGitHub\opencv\samples\data/"
    filename = dir_root + 'LinuxLogo.jpg'
    img_bgr = cv.imread(filename)
    src = img_bgr.copy()
    my_opening()


from __future__ import annotations

import copy
import logging
from typing import Any, ClassVar, Dict, List, Optional, TYPE_CHECKING, Union

import numpy as np
import pandas as pd
from flask_babel import _
from pandas import DateOffset
from typing_extensions import TypedDict

from superset import app
from superset.annotation_layers.dao import AnnotationLayerDAO
from superset.charts.dao import ChartDAO
from superset.common.chart_data import ChartDataResultFormat
from superset.common.db_query_status import QueryStatus
from superset.common.query_actions import get_query_results
from superset.common.utils import dataframe_utils as df_utils
from superset.common.utils.query_cache_manager import QueryCacheManager
from superset.connectors.base.models import BaseDatasource
from superset.constants import CacheRegion
from superset.exceptions import (
    InvalidPostProcessingError,
    QueryObjectValidationError,
    SupersetException,
)
from superset.extensions import cache_manager, security_manager
from superset.models.helpers import QueryResult
from superset.utils import csv
from superset.utils.cache import generate_cache_key, set_and_log_cache
from superset.utils.core import (
    DTTM_ALIAS,
    error_msg_from_exception,
    get_column_names_from_columns,
    get_column_names_from_metrics,
    get_metric_names,
    normalize_dttm_col,
    TIME_COMPARISON,
)
from superset.utils.date_parser import get_past_or_future, normalize_time_delta
from superset.views.utils import get_viz

if TYPE_CHECKING:
    from superset.common.query_context import QueryContext
    from superset.common.query_object import QueryObject
    from superset.stats_logger import BaseStatsLogger

config = app.config
stats_logger: BaseStatsLogger = config["STATS_LOGGER"]
logger = logging.getLogger(__name__)

class CachedTimeOffset(TypedDict):
    df: pd.DataFrame
    queries: List[str]
    cache_keys: List[Optional[str]]

class QueryContextProcessor:
    """
    The query context contains the query object and additional fields necessary
    to retrieve the data payload for a given viz.
    """

    _query_context: QueryContext
    _qc_datasource: BaseDatasource
    """
    The query context contains the query object and additional fields necessary
    to retrieve the data payload for a given viz.
    """

    def __init__(self, query_context: QueryContext):
        self._query_context = query_context
        self._qc_datasource = query_context.datasource

    cache_type: ClassVar[str] = "df"
    enforce_numerical_metrics: ClassVar[bool] = True

    def get_df_payload(
        self, query_obj: QueryObject, force_cached: Optional[bool] = False
    ) -> Dict[str, Any]:
        """Handles caching around the df payload retrieval"""
        cache_key = self.query_cache_key(query_obj)
        cache = QueryCacheManager.get(
            cache_key,
            CacheRegion.DATA,
            self._query_context.force,
            force_cached,
        )

        if query_obj and cache_key and not cache.is_loaded:
            try:
                invalid_columns = [
                    col
                    for col in get_column_names_from_columns(query_obj.columns)
                    + get_column_names_from_metrics(query_obj.metrics or [])
                    if (
                        col not in self._qc_datasource.column_names
                        and col != DTTM_ALIAS
                    )
                ]
                if invalid_columns:
                    raise QueryObjectValidationError(
                        _(
                            "Columns missing in datasource: %(invalid_columns)s",
                            invalid_columns=invalid_columns,
                        )
                    )
                query_result = self.get_query_result(query_obj)
                annotation_data = self.get_annotation_data(query_obj)
                cache.set_query_result(
                    key=cache_key,
                    query_result=query_result,
                    annotation_data=annotation_data,
                    force_query=self._query_context.force,
                    timeout=self.get_cache_timeout(),
                    datasource_uid=self._qc_datasource.uid,
                    region=CacheRegion.DATA,
                )
            except QueryObjectValidationError as ex:
                cache.error_message = str(ex)
                cache.status = QueryStatus.FAILED

        return {
            "cache_key": cache_key,
            "cached_dttm": cache.cache_dttm,
            "cache_timeout": self.get_cache_timeout(),
            "df": cache.df,
            "applied_template_filters": cache.applied_template_filters,
            "annotation_data": cache.annotation_data,
            "error": cache.error_message,
            "is_cached": cache.is_cached,
            "query": cache.query,
            "status": cache.status,
            "stacktrace": cache.stacktrace,
            "rowcount": len(cache.df.index),
            "from_dttm": query_obj.from_dttm,
            "to_dttm": query_obj.to_dttm,
        }

    def query_cache_key(self, query_obj: QueryObject, **kwargs: Any) -> Optional[str]:
        """
        Returns a QueryObject cache key for objects in self.queries
        """
        datasource = self._qc_datasource
        extra_cache_keys = datasource.get_extra_cache_keys(query_obj.to_dict())

        cache_key = (
            query_obj.cache_key(
                datasource=datasource.uid,
                extra_cache_keys=extra_cache_keys,
                rls=security_manager.get_rls_cache_key(datasource),
                changed_on=datasource.changed_on,
                **kwargs,
            )
            if query_obj
            else None
        )
        return cache_key

    def get_query_result(self, query_object: QueryObject) -> QueryResult:
        """Returns a pandas dataframe based on the query object"""
        query_context = self._query_context
        # Here, we assume that all the queries will use the same datasource, which is
        # a valid assumption for current setting. In the long term, we may
        # support multiple queries from different data sources.

        # The datasource here can be different backend but the interface is common
        result = query_context.datasource.query(query_object.to_dict())
        query = result.query + ";\n\n"

        df = result.df
        # Transform the timestamp we received from database to pandas supported
        # datetime format. If no python_date_format is specified, the pattern will
        # be considered as the default ISO date format
        # If the datetime format is unix, the parse will use the corresponding
        # parsing logic
        if not df.empty:
            df = self.normalize_df(df, query_object)

            if query_object.time_offsets:
                time_offsets = self.processing_time_offsets(df, query_object)
                df = time_offsets["df"]
                queries = time_offsets["queries"]

                query += ";\n\n".join(queries)
                query += ";\n\n"

            # Re-raising QueryObjectValidationError
            try:
                df = query_object.exec_post_processing(df)
            except InvalidPostProcessingError as ex:
                raise QueryObjectValidationError from ex

        result.df = df
        result.query = query
        result.from_dttm = query_object.from_dttm
        result.to_dttm = query_object.to_dttm
        return result

    def normalize_df(self, df: pd.DataFrame, query_object: QueryObject) -> pd.DataFrame:
        datasource = self._qc_datasource
        timestamp_format = None
        if datasource.type == "table":
            dttm_col = datasource.get_column(query_object.granularity)
            if dttm_col:
                timestamp_format = dttm_col.python_date_format

        normalize_dttm_col(
            df=df,
            timestamp_format=timestamp_format,
            offset=datasource.offset,
            time_shift=query_object.time_shift,
        )

        if self.enforce_numerical_metrics:
            df_utils.df_metrics_to_num(df, query_object)

        df.replace([np.inf, -np.inf], np.nan, inplace=True)

        return df

    def processing_time_offsets(  # pylint: disable=too-many-locals
        self,
        df: pd.DataFrame,
        query_object: QueryObject,
    ) -> CachedTimeOffset:
        query_context = self._query_context
        # ensure query_object is immutable
        query_object_clone = copy.copy(query_object)
        queries: List[str] = []
        cache_keys: List[Optional[str]] = []
        rv_dfs: List[pd.DataFrame] = [df]

        time_offsets = query_object.time_offsets
        outer_from_dttm = query_object.from_dttm
        outer_to_dttm = query_object.to_dttm
        for offset in time_offsets:
            try:
                query_object_clone.from_dttm = get_past_or_future(
                    offset,
                    outer_from_dttm,
                )
                query_object_clone.to_dttm = get_past_or_future(offset, outer_to_dttm)
            except ValueError as ex:
                raise QueryObjectValidationError(str(ex)) from ex
            # make sure subquery use main query where clause
            query_object_clone.inner_from_dttm = outer_from_dttm
            query_object_clone.inner_to_dttm = outer_to_dttm
            query_object_clone.time_offsets = []
            query_object_clone.post_processing = []

            if not query_object.from_dttm or not query_object.to_dttm:
                raise QueryObjectValidationError(
                    _(
                        "An enclosed time range (both start and end) must be specified "
                        "when using a Time Comparison."
                    )
                )
            # `offset` is added to the hash function
            cache_key = self.query_cache_key(query_object_clone, time_offset=offset)
            cache = QueryCacheManager.get(
                cache_key, CacheRegion.DATA, query_context.force
            )
            # whether hit on the cache
            if cache.is_loaded:
                rv_dfs.append(cache.df)
                queries.append(cache.query)
                cache_keys.append(cache_key)
                continue

            query_object_clone_dct = query_object_clone.to_dict()
            # rename metrics: SUM(value) => SUM(value) 1 year ago
            metrics_mapping = {
                metric: TIME_COMPARISON.join([metric, offset])
                for metric in get_metric_names(
                    query_object_clone_dct.get("metrics", [])
                )
            }
            join_keys = [col for col in df.columns if col not in metrics_mapping.keys()]

            result = self._qc_datasource.query(query_object_clone_dct)
            queries.append(result.query)
            cache_keys.append(None)

            offset_metrics_df = result.df
            if offset_metrics_df.empty:
                offset_metrics_df = pd.DataFrame(
                    {
                        col: [np.NaN]
                        for col in join_keys + list(metrics_mapping.values())
                    }
                )
            else:
                # 1. normalize df, set dttm column
                offset_metrics_df = self.normalize_df(
                    offset_metrics_df, query_object_clone
                )

                # 2. rename extra query columns
                offset_metrics_df = offset_metrics_df.rename(columns=metrics_mapping)

                # 3. set time offset for index
                # TODO: add x-axis to QueryObject, potentially as an array for
                #  multi-dimensional charts
                granularity = query_object.granularity
                index = granularity if granularity in df.columns else DTTM_ALIAS
                if not pd.api.types.is_datetime64_any_dtype(
                    offset_metrics_df.get(index)
                ):
                    raise QueryObjectValidationError(
                        _(
                            "A time column must be specified "
                            "when using a Time Comparison."
                        )
                    )

                offset_metrics_df[index] = offset_metrics_df[index] - DateOffset(
                    **normalize_time_delta(offset)
                )

            # df left join `offset_metrics_df`
            offset_df = df_utils.left_join_df(
                left_df=df,
                right_df=offset_metrics_df,
                join_keys=join_keys,
            )
            offset_slice = offset_df[metrics_mapping.values()]

            # set offset_slice to cache and stack.
            value = {
                "df": offset_slice,
                "query": result.query,
            }
            cache.set(
                key=cache_key,
                value=value,
                timeout=self.get_cache_timeout(),
                datasource_uid=query_context.datasource.uid,
                region=CacheRegion.DATA,
            )
            rv_dfs.append(offset_slice)

        rv_df = pd.concat(rv_dfs, axis=1, copy=False) if time_offsets else df
        return CachedTimeOffset(df=rv_df, queries=queries, cache_keys=cache_keys)

    def get_data(self, df: pd.DataFrame) -> Union[str, List[Dict[str, Any]]]:
        if self._query_context.result_format == ChartDataResultFormat.CSV:
            include_index = not isinstance(df.index, pd.RangeIndex)
            columns = list(df.columns)
            verbose_map = self._qc_datasource.data.get("verbose_map", {})
            if verbose_map:
                df.columns = [verbose_map.get(column, column) for column in columns]
            result = csv.df_to_escaped_csv(
                df, index=include_index, **config["CSV_EXPORT"]
            )
            return result or ""

        return df.to_dict(orient="records")

    def get_payload(
        self,
        cache_query_context: Optional[bool] = False,
        force_cached: bool = False,
    ) -> Dict[str, Any]:
        """Returns the query results with both metadata and data"""

        # Get all the payloads from the QueryObjects
        query_results = [
            get_query_results(
                query_obj.result_type or self._query_context.result_type,
                self._query_context,
                query_obj,
                force_cached,
            )
            for query_obj in self._query_context.queries
        ]
        return_value = {"queries": query_results}

        if cache_query_context:
            cache_key = self.cache_key()
            set_and_log_cache(
                cache_manager.cache,
                cache_key,
                {"data": self._query_context.cache_values},
                self.get_cache_timeout(),
            )
            return_value["cache_key"] = cache_key  # type: ignore

        return return_value

    def get_cache_timeout(self) -> int:
        cache_timeout_rv = self._query_context.get_cache_timeout()
        if cache_timeout_rv:
            return cache_timeout_rv
        return config["CACHE_DEFAULT_TIMEOUT"]

    def cache_key(self, **extra: Any) -> str:
        """
        The QueryContext cache key is made out of the key/values from
        self.cached_values, plus any other key/values in `extra`. It includes only data
        required to rehydrate a QueryContext object.
        """
        key_prefix = "qc-"
        cache_dict = self._query_context.cache_values.copy()
        cache_dict.update(extra)

        return generate_cache_key(cache_dict, key_prefix)

    def get_annotation_data(self, query_obj: QueryObject) -> Dict[str, Any]:
        """
        :param query_context:
        :param query_obj:
        :return:
        """
        annotation_data: Dict[str, Any] = self.get_native_annotation_data(query_obj)
        for annotation_layer in [
            layer
            for layer in query_obj.annotation_layers
            if layer["sourceType"] in ("line", "table")
        ]:
            name = annotation_layer["name"]
            annotation_data[name] = self.get_viz_annotation_data(
                annotation_layer, self._query_context.force
            )
        return annotation_data

    @staticmethod
    def get_native_annotation_data(query_obj: QueryObject) -> Dict[str, Any]:
        annotation_data = {}
        annotation_layers = [
            layer
            for layer in query_obj.annotation_layers
            if layer["sourceType"] == "NATIVE"
        ]
        layer_ids = [layer["value"] for layer in annotation_layers]
        layer_objects = {
            layer_object.id: layer_object
            for layer_object in AnnotationLayerDAO.find_by_ids(layer_ids)
        }

        # annotations
        for layer in annotation_layers:
            layer_id = layer["value"]
            layer_name = layer["name"]
            columns = [
                "start_dttm",
                "end_dttm",
                "short_descr",
                "long_descr",
                "json_metadata",
            ]
            layer_object = layer_objects[layer_id]
            records = [
                {column: getattr(annotation, column) for column in columns}
                for annotation in layer_object.annotation
            ]
            result = {"columns": columns, "records": records}
            annotation_data[layer_name] = result
        return annotation_data

    @staticmethod
    def get_viz_annotation_data(
        annotation_layer: Dict[str, Any], force: bool
    ) -> Dict[str, Any]:
        chart = ChartDAO.find_by_id(annotation_layer["value"])
        if not chart:
            raise QueryObjectValidationError(_("The chart does not exist"))
        form_data = chart.form_data.copy()
        try:
            viz_obj = get_viz(
                datasource_type=chart.datasource.type,
                datasource_id=chart.datasource.id,
                form_data=form_data,
                force=force,
            )
            payload = viz_obj.get_payload()
            return payload["data"]
        except SupersetException as ex:
            raise QueryObjectValidationError(error_msg_from_exception(ex)) from ex

    def raise_for_access(self) -> None:
        """
        Raise an exception if the user cannot access the resource.

        :raises SupersetSecurityException: If the user cannot access the resource
        """
        for query in self._query_context.queries:
            query.validate()
        security_manager.raise_for_access(query_context=self._query_context)


from requests_html import HTMLSession
from dotenv import load_dotenv
import os
import requests
import schedule
import time

s = HTMLSession()
load_dotenv() #Load .env variable

def send_msg(text):
    token = os.getenv("TOKEN")
    chat_id = os.getenv("CHAT_ID")

    url_req = "https://api.telegram.org/bot" + token + "/sendMessage" + "?chat_id=" + chat_id + "&text=" + text

    #Send the message in telegram
    result = requests.get(url_req)

    #Print on terminal the result
    print(result.json())

def scraping():
    query = 'italy'
    url = f'https://www.google.com/search?q=weather+{query}'
    user_agent = os.getenv("USER_AGENT")

    r = s.get(url, headers={'User-Agent': user_agent})

    # Request
    temp = r.html.find('span#wob_tm', first=True).text
    unit = r.html.find('div.vk_bk.wob-unit span.wob_t', first=True).text
    desc = r.html.find('div.VQF4g', first=True).find('span#wob_dc', first=True).text

    msg = query.capitalize() + ": " + temp + unit + " " + desc + "\n" + url
    send_msg(msg)

# Start every 10 seconds
schedule.every(10).seconds.do(scraping)
# schedule.every(24).hours.do(scraping)

while True:
    schedule.run_pending()
    time.sleep(1)


import sys

sys.path.insert(1, "/home/ubuntu/cust2vec")

import h5py
import pandas as pd
import boto3
import pickle
import io
from pyspark.ml.feature import VectorAssembler
from pyspark.sql import SparkSession
from utils.logging_framework import log
from src.models.kmeans import *

if __name__ == "__main__":

    task = sys.argv[1]
    bucket = sys.argv[2]
    create_cost_plot = bool(sys.argv[3])
    scored_kmeans_path = sys.argv[4]
    saved_kmeans_model_path = sys.argv[5]
    num_clusts = int(sys.argv[6])

    spark = SparkSession.builder.appName("cust2vec").getOrCreate()

    # ========== Load the customer embeddings ==========
    log.info("Loading customer embeddings")
    s3 = boto3.client("s3")
    with open("cust_embeddings.hdf5", "wb") as data:
        s3.download_fileobj(bucket, "cust_embeddings.hdf5", data)

    with h5py.File("cust_embeddings.hdf5", "r") as f:
        # List all groups
        print("Keys: %s" % f.keys())
        a_group_key = list(f.keys())[0]

        # Get the data
        cust_embeddings = list(f[a_group_key])

    log.info("Example of customer embedding...")
    print(cust_embeddings[0])

    # ========== Map embeddings to CUST_CODE ==========

    log.info("Loading customer index list")
    s3 = boto3.client("s3")

    # Customer index list (lists of customers in same order as all_cust_data)
    with open("cust_index_list.txt", "wb") as data:
        s3.download_fileobj(bucket, "cust_index_list.txt", data)
    with open("cust_index_list.txt", "rb") as data:
        cust_index_list = pickle.load(data)
    cust_index_pd = pd.DataFrame(cust_index_list, columns=["index"])

    log.info("Loading index to customer ID mapping dictionary")

    s3 = boto3.resource("s3")
    with open("reversed_cust_dictionary.pkl", "wb") as data:
        s3.Bucket(bucket).download_fileobj("reversed_cust_dictionary.pkl", data)
    with open("reversed_cust_dictionary.pkl", "rb") as data:
        reversed_dictionary = pickle.load(data)

    cust_embeddings_pd = pd.DataFrame(cust_embeddings)
    # create column headers
    cols = ["embedd_" + str(sub) for sub in cust_embeddings_pd.columns]
    cust_embeddings_pd.columns = cols
    cust_embeddings_pd.loc[:, "index"] = cust_index_pd["index"]
    cust_embeddings_pd.loc[:, "CUST_CODE"] = cust_index_pd["index"].map(
        reversed_dictionary
    )

    # Convert to Spark DataFrame
    cust_embeddings_df = spark.createDataFrame(cust_embeddings_pd)

    # ========== Prep data for k-means ==========
    feature_cols = [elem for elem in cust_embeddings_df.columns if "embedd" in elem]
    assembler = VectorAssembler(inputCols=feature_cols, outputCol="features")

    kmeans_input = assembler.transform(cust_embeddings_df)
    kmeans_input = kmeans_input.select("CUST_CODE", "features")

    # ========== Create cost plot ==========
    if create_cost_plot:
        log.info("Creating k-means cost plot")
        cost_plot = kmeans_compute_cost(
            df=kmeans_input, num_clusters=25, feature_col="features"
        )

        # Upload plot to s3
        img_data = io.BytesIO()
        cost_plot.savefig(img_data, format="png")
        img_data.seek(0)

        bucket = s3.Bucket(bucket)
        bucket.put_object(Body=img_data, ContentType="image/png", Key="cost_plot.png")

    # ========== Fit K-Means model ==========
    model = fit_kmeans(
        df=kmeans_input,
        num_clusters=num_clusts,
        feature_column="features",
        save_path=saved_kmeans_model_path,
    )

    scored_kmeans_df = score_kmeans(
        kmeans_input, model=model, save_path=scored_kmeans_path
    )

    scored_kmeans_df.show()


import os
import time

import tornado.ioloop
import tornado.web
import tornado.websocket

port = int(os.getenv('PORT', 80))
WIDTH = 80
HEIGHT = 45
mx = [['#000000' for j in range(HEIGHT)] for i in range(WIDTH)]
PRD = 3600 # seconds

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write(open("static/index.html", 'rb').read())

class StaticHandler(tornado.web.RequestHandler):
    def get(self):
        try:
            self.write(open(self.request.uri[1:], 'rb').read())
        except BaseException as ex:
            self.write('404 File not found\n' + str(ex))

class WebSocket(tornado.websocket.WebSocketHandler):
    connections = []
    tmr = time.time()

    def open(self):
        self.connections.append(self)

    def on_message(self, message):
        if self.is_clear_time():
            self.tmr = time.time()
            [client.write_message('clear all') for client in self.connections]
        if message == 'initial update':
            self.write_message('IU' + str(mx))
            return
        try:
            spl = message.split(',')
            mx[int(spl[1])][int(spl[2])] = spl[0]
            if len(self.connections) > 1:
                for client in [i for i in self.connections if i != self]:
                    client.write_message(f"2,{spl[0]},{spl[1]},{spl[2]}")
        except:
            return

    def on_close(self):
        self.connections.remove(self)

    def is_clear_time(self):
        return time.time() - self.tmr >= PRD

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
        (r"/static/.*", StaticHandler),
        (r"/websocket", WebSocket)
    ])

if __name__ == "__main__":
    app = make_app()
    print('http://127.0.0.1:' + str(port) + '/')
    app.listen(port=port, address='0.0.0.0')
    tornado.ioloop.IOLoop.current().start()


import sys

from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
from PyQt5.QtWidgets import QWidget, QApplication
from addEditCoffeeForm import AddEdit_Form
from coffeeInfoTable import CoffeeTable_Form

class EditCoffeeTable(QWidget, AddEdit_Form):
    def __init__(self, parent, model):
        super().__init__()
        self.setupUi(self)
        self.model = model
        self.initUI()

    def initUI(self):
        self.add_btn.clicked.connect(self.addRow)
        self.save_btn.clicked.connect(self.save)

        self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
        self.model.select()
        self.view.setModel(self.model)

    def addRow(self):
        self.model.insertRow(self.model.rowCount())

    def save(self):
        self.model.submitAll()

class CoffeeTable(QWidget, CoffeeTable_Form):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.db = QSqlDatabase.addDatabase('QSQLITE')
        self.model = QSqlTableModel(self, self.db)
        self.initUI()

    def initUI(self):
        self.edit_btn.clicked.connect(self.edit)

        self.db.setDatabaseName('coffee.db')
        self.db.open()

        self.model.setTable('Coffee_info')
        self.model.select()

        self.view.setModel(self.model)

    def edit(self):
        self.edit_from = EditCoffeeTable(self, self.model)
        self.edit_from.show()

    def closeEvent(self, event):
        self.db.close()

def except_hook(cls, exception, traceback):
    sys.__excepthook__(cls, exception, traceback)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    form = CoffeeTable()
    form.show()
    sys.excepthook = except_hook
    sys.exit(app.exec())


#!/usr/bin/env python

import os
import sys
import numpy as np
import argparse
from kaffe import KaffeError, print_stderr
from kaffe.tensorflow import TensorFlowTransformer

def fatal_error(msg):
    print_stderr(msg)
    exit(-1)

def validate_arguments(args):
    if (args.data_output_path is not None) and (args.caffemodel is None):
        fatal_error('No input data path provided.')
    if (args.caffemodel is not None) and (args.data_output_path is None):
        fatal_error('No output data path provided.')
    if (args.code_output_path is None) and (args.data_output_path is None):
        fatal_error('No output path specified.')

def convert(def_path, caffemodel_path, data_output_path, code_output_path, phase):
    try:
        transformer = TensorFlowTransformer(def_path, caffemodel_path, phase=phase)
        print_stderr('Converting data...')
        if caffemodel_path is not None:
            data = transformer.transform_data()
            print_stderr('Saving data...')
            with open(data_output_path, 'wb') as data_out:
                np.save(data_out, data)
        if code_output_path:
            print_stderr('Saving source...')
            with open(code_output_path, 'wb') as src_out:
                b = bytes(transformer.transform_source(), encoding='utf-8')
                src_out.write(b)
        print_stderr('Done.')
    except KaffeError as err:
        fatal_error('Error encountered: {}'.format(err))

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('def_path', help='Model definition (.prototxt) path')
    parser.add_argument('--caffemodel', help='Model data (.caffemodel) path')
    parser.add_argument('--data-output-path', help='Converted data output path')
    parser.add_argument('--code-output-path', help='Save generated source to this path')
    parser.add_argument('-p',
                        '--phase',
                        default='test',
                        help='The phase to convert: test (default) or train')
    args = parser.parse_args()
    validate_arguments(args)
    convert(args.def_path, args.caffemodel, args.data_output_path, args.code_output_path,
            args.phase)

def my_main():
    def_path = 'models/reference_model/deploy_nodist.prototxt'
    caffe_model_path = 'models/reference_model/model.caffemodel'
    data_output_path = 'userguide_reference.npy'
    code_output_path = 'userguide_reference.py'

    convert(def_path, caffe_model_path, data_output_path, code_output_path, 'test')

if __name__ == '__main__':
    # main()
    my_main()


# -*- coding: utf-8 -*-
from __future__ import absolute_import
import logging

import pytest
import docopt

from pymorphy2 import cli

def run_pymorphy2(args=(), stdin=None):
    cli.main(args)

def test_show_usage():
    with pytest.raises(docopt.DocoptExit) as e:
        run_pymorphy2([])
    assert 'Usage:' in str(e.value)

def test_show_memory_usage(capsys):
    pytest.importorskip("psutil")

    run_pymorphy2(['dict', 'mem_usage'])
    out = ' '.join(capsys.readouterr())
    assert 'Memory usage:' in out

def test_show_dict_meta(capsys, morph):
    meta = morph.dictionary.meta
    run_pymorphy2(['dict', 'meta'])
    out = ' '.join(capsys.readouterr())
    assert meta['compiled_at'] in out

def test_parse_basic(tmpdir, capsys):
    logging.raiseExceptions = False
    try:
        p = tmpdir.join('words.txt')
        p.write_text(u"""
        крот пришел
        """, encoding='utf8')
        run_pymorphy2(["parse", str(p)])
        out, err = capsys.readouterr()
        print(out)
        print(err)
        assert out.strip() == u"""
крот{крот:1.000=NOUN,anim,masc sing,nomn}
пришел{прийти:1.000=VERB,perf,intr masc,sing,past,indc}
        """.strip()
    finally:
        logging.raiseExceptions = True


import base64
import math
from .varint import get_varint, pop_varint
from cardcodedb import carddb as cdb
import binascii

CARDCODE_LENGTH = 7
MAX_KNOWN_VERSION = 4
FORMAT = 1
INITIAL_VERSION = 1

faction_library_version = {
    "DE" : 1,
    "FR" : 1,
    "IO" : 1,
    "NX" : 1,
    "PZ" : 1,
    "SI" : 1,
    "BW" : 2,
    "MT" : 2,
    "SH" : 3,
    "BC" : 4
}

def encode_deck (decklist : list) -> str:
    result = base64.b32encode(get_deck_bytes(decklist)).decode('utf-8').strip("=")
    return result

def decode_deck (code : str) -> list:
    deck = []

    pad_length = math.ceil(len(code) / 8) * 8 - len(code)
    padcode = code + '=' * pad_length
    try:
        deckbytes = base64.b32decode(padcode)
    except binascii.Error as error:
        raise ValueError("Invalid deck code")

    bytelist = list(deckbytes)

    if len(bytelist) == 0:
        raise ValueError("Empty deck code")

    # grab format and version
    format = bytelist[0] >> 4
    version = bytelist[0] & 0xF
    del bytelist[0]

    if version > MAX_KNOWN_VERSION:
        raise ValueError("The provided code requires a higher version of this library; please update.")

    for i in range(3):
        idx = 3-i
        num_group_ofs = pop_varint(bytelist)

        for j in range(num_group_ofs):
            num_ofs_in_this_group = pop_varint(bytelist)
            set = pop_varint(bytelist)
            faction = pop_varint(bytelist)

            for k in range(num_ofs_in_this_group):
                card = pop_varint(bytelist)

                set_string = f'{set:02}'
                faction_string =  cdb.get_faction_code(faction)
                card_string = f'{card:03}'

                deck += [ (f"{set_string}{faction_string}{card_string}", idx)]

    # the remainder of the deck code is comprised of entries for cards with counts >= 4
    # this will only happen in Limited and special game modes.
    # the encoding is simply [count] [cardcode]
    while (len(bytelist) > 0):
        four_plus_count = pop_varint(bytelist)
        four_plus_set = pop_varint(bytelist)
        four_plus_faction = pop_varint(bytelist)
        four_plus_number = pop_varint(bytelist)

        four_plus_set_string = f'{four_plus_set:02}'
        four_plus_faction_string = cdb.get_faction_code(four_plus_faction)
        four_plus_card_string = f'{four_plus_number:03}'

        deck += [ (f"{four_plus_set_string}{four_plus_faction_string}{four_plus_card_string}", four_plus_count)]

    return deck

def get_deck_bytes(decklist: list) -> list:
    deck_bytes = []

    if not are_valid_cardcodes_and_counts(decklist):
        raise ValueError("The provided deck contains invalid card codes.")

    format_and_version = FORMAT << 4 | get_min_supported_library_version(decklist) & 0xF

    deck_bytes += [format_and_version]

    of3 = []
    of2 = []
    of1 = []
    ofN = []

    for cardcode,count in decklist:
        if count == 3:
            of3 += [(cardcode,count)]
        elif count == 2:
            of2 += [(cardcode,count)]
        elif count == 1:
            of1 += [(cardcode,count)]
        elif count < 1:
            raise ValueError(f"Invalid count of {count} for card {cardcode}")
        else:
            ofN += [(cardcode,count)]

    grouped_of3s = get_grouped_ofs(of3)
    grouped_of2s = get_grouped_ofs(of2)
    grouped_of1s = get_grouped_ofs(of1)

    grouped_of3s = sort_group_of(grouped_of3s)
    grouped_of2s = sort_group_of(grouped_of2s)
    grouped_of1s = sort_group_of(grouped_of1s)

    ofN = sorted(ofN, key=lambda e: e[0])

    # encode
    encode_group_of(deck_bytes, grouped_of3s)
    encode_group_of(deck_bytes, grouped_of2s)
    encode_group_of(deck_bytes, grouped_of1s)
    encode_nof(deck_bytes, ofN)

    return bytearray(deck_bytes)

def get_decklist(code_deck : dict) -> list:
    db_deck = []
    for card in code_deck:
        cd = cdb.get_card_data(card[0])
        db_deck += [(card[1], cd)]

    db_deck.sort(key=lambda x: x[1]["cost"])
    return db_deck

def get_classified_decklist(code_deck : dict) -> dict:
    classified_deck = { "champions" : [], "units" : [], "landmarks" : [], "spells" : [] }

    for card in code_deck:
        cd = cdb.get_card_data(card[0])
        if cd["type"] == "unit" and cd["supertype"] == "champion":
            classified_deck["champions"] += [(card[1], cd)]
        if cd["type"] == "unit" and cd["supertype"] != "champion":
            classified_deck["units"] += [(card[1], cd)]
        if cd["type"] == "spell":
            classified_deck["spells"] += [(card[1], cd)]
        if cd["type"] == "landmark":
            classified_deck["landmarks"] += [(card[1], cd)]

    for k in classified_deck.keys():
        classified_deck[k].sort(key=lambda x: x[1]["cost"])
    return classified_deck

def are_valid_cardcodes_and_counts(decklist: list) -> bool:
    for cardcode,count in decklist:
        if len(cardcode) != CARDCODE_LENGTH:
            return False
        try:
            int(cardcode[:2])
            f = cardcode[2:4]
            if not f in cdb.faction_codes.keys():
                return False
            int(cardcode[4:])
        except ValueError as e:
            return False
        if count < 1:
            return False
    return True

def get_min_supported_library_version(decklist: list) -> int:
    if len(decklist) == 0:
        return INITIAL_VERSION
    try:
        return max([faction_library_version[cardcode[2:4]] for cardcode,q in decklist])
    except Exception as e:
        return MAX_KNOWN_VERSION

def get_grouped_ofs(cardcodecount : list) -> list:
    groups = {}
    while len(cardcodecount) > 0:
        current_ccc = cardcodecount.pop(0)
        current_set_faction = current_ccc[0][0:4]
        if current_set_faction not in groups.keys():
            groups[current_set_faction] = [current_ccc]
        else:
            groups[current_set_faction] += [current_ccc]

        for idx,ccc in enumerate(cardcodecount):
            set_faction = ccc[0][0:4]
            if set_faction == current_set_faction:
                groups[current_set_faction] += [ccc]
                del cardcodecount[idx]
    return list(groups.values())

def sort_group_of(groups_of : list) -> list:
    groups_of = sorted(groups_of, key=lambda e: (len(e), e[0][0]))
    for i in range(len(groups_of)):
        groups_of[i] = sorted(groups_of[i], key=lambda e: e[0])
    return groups_of

def parse_card_code(code : str) -> tuple:
    set = int(code[:2])
    faction = code[2:4]
    id = int(code[4:])
    return (set, faction, id)

def encode_group_of(bytelist : list, group_of : list):
    bytelist += get_varint(len(group_of))
    for current_list in group_of:
        # how many cards in group?
        bytelist += get_varint(len(current_list))

        # what is this group?
        current_cardcode = current_list[0][0]
        setno, faction, number = parse_card_code(current_cardcode)
        faction_no = cdb.get_faction_id(faction)
        bytelist += get_varint(setno)
        bytelist += get_varint(faction_no)

        # what are the cards?
        for cardcode,count in current_list:
            number = int(cardcode[4:])
            bytelist += get_varint(number)

def encode_nof(bytelist : list, nof : list):
    for ccc in nof:
        bytelist += get_varint(ccc[1])

        set_no, faction, number = parse_card_code(ccc[0])
        faction_no = cdb.get_faction_id(faction)

        bytelist += get_varint(set_no)
        bytelist += get_varint(faction_no)
        bytelist += get_varint(number)


from django.urls import path
from . import views
app_name='storemenu'
urlpatterns = [
    path('', views.index,name='index'),
    path('create/', views.create,name='create'),
    path('update/<int:id>', views.update,name='update'),
    path('delete/<int:id>', views.delete,name='delete'),
    path('userindex/', views.userindex,name='userindex'),
    path('search/', views.search,name='search'),
    path('cindex/<str:id>', views.cindex,name='cindex'),
]


class Reader:
    def __init__(self, input_file_name):
        self.n_intersections = 0
        self.n_streets = 0
        self.n_cars = 0

        self.cars = []
        self.intersections = []
        self.streets = []
        self.street_name_id_pairs = {}
        self.street_intersection_pairs = {}

        self.all_street_scores = {}

        self.input_path_prefix = './input/'
        self.output_path_prefix = './output/'
        self.input_file_name = input_file_name
        self.read_input()

    def read_input(self):
        with open(self.input_path_prefix + self.input_file_name) as input_file:
            header_line = input_file.readline()
            _, self.n_intersections, self.n_streets, self.n_cars, _ = [
                int(i) for i in header_line.strip().split()
            ]

            self.initialize_intersections()
            self.read_streets(input_file)
            self.read_cars(input_file)

    def initialize_intersections(self):
        for i in range(self.n_intersections):
            self.intersections.append({
                'id': i,
                'in': [],
                'in_scores': {},
                'out': [],
                'schedule': {}
            })

    def read_streets(self, input_file):
        for i in range(self.n_streets):
            street_line = input_file.readline()
            i_start, i_end, s_name, s_len = street_line.strip().split()

            self.streets.append({
                'id': i,
                'name': s_name,
                'len': int(s_len),
                'i_start': int(i_start),
                'i_end': int(i_end),
            })
            self.street_name_id_pairs[s_name] = i
            self.street_intersection_pairs[i] = int(i_end)

            self.intersections[int(i_start)]['out'].append(i)
            self.intersections[int(i_end)]['in'].append(i)

    def read_cars(self, input_file):
        for i in range(self.n_cars):
            car_line = input_file.readline()
            car_line_fragments = car_line.strip().split()

            self.cars.append({
                'i_start': int(car_line_fragments.pop(0)),
                'route_table': [self.street_name_id_pairs[s_name] for s_name in car_line_fragments]
            })

    def write_output(self):
        with open(self.output_path_prefix + self.input_file_name, 'w+') as file:
            file.write(str(self.n_intersections) + "\n")
            for i in range(self.n_intersections):
                file.write(str(i) + '\n')
                if len(self.intersections[i]['in']):
                    sorted_schedules = list(
                        sorted(self.intersections[i]['schedule'].items(), key=lambda item: item[1], reverse=True))

                    n_schedules = len(sorted_schedules)
                    file.write(str(n_schedules) + '\n')

                    for in_street_name, seconds in sorted_schedules:
                        file.write(in_street_name + ' ' + str(seconds) + '\n')

class Solver(Reader):
    def solve(self, min_scheduled_time, max_scheduled_time,
              upscale_fraction=10, upscale_factor=2, use_round_instead_of_floor=False):
        self.set_schedules(upscale_fraction, upscale_factor)
        self.normalize_schedules(min_scheduled_time, max_scheduled_time, use_round_instead_of_floor)
        self.write_output()

        print('Solved: ' + self.input_file_name)

    def set_schedules(self, upscale_fraction, upscale_factor):
        self.score_streets_in_intersections()
        self.schedule_one_way_in_intersections()
        self.schedule_round_robin()
        self.upscale_fraction_of_best_streets(upscale_fraction, upscale_factor)

    def schedule_one_way_in_intersections(self):
        for i in range(self.n_intersections):
            if len(self.intersections[i]['in']) == 1:
                in_street_id = self.intersections[i]['in'][0]
                in_street_name = self.streets[in_street_id]['name']
                self.intersections[i]['schedule'] = {
                    in_street_name: 1
                }

    def score_streets_in_intersections(self):
        for car_id in range(self.n_cars):
            streets_to_score = self.cars[car_id]['route_table'][:-1]
            n_streets_to_score = len(streets_to_score)
            if n_streets_to_score == 0:
                continue

            for loop_id, street_id in enumerate(streets_to_score):
                i_end = self.street_intersection_pairs[street_id]
                if street_id not in self.intersections[i_end]['in_scores']:
                    self.intersections[i_end]['in_scores'][street_id] = 0
                self.intersections[i_end]['in_scores'][street_id] += 1
                self.increase_street_global_score(street_id, loop_id, n_streets_to_score)

    def increase_street_global_score(self, street_id, loop_id, n_streets_to_score):
        if street_id not in self.all_street_scores:
            self.all_street_scores[street_id] = {
                'id': street_id,
                'score': 0
            }
        self.all_street_scores[street_id]['score'] += int(
            ((n_streets_to_score - loop_id) / n_streets_to_score) * 2 + 1)
        if loop_id == 0:
            self.all_street_scores[street_id]['score'] += 3

    def schedule_round_robin(self):
        for i in range(self.n_intersections):
            if len(self.intersections[i]['schedule']) == 0:
                if len(self.intersections[i]['in_scores']) > 0:
                    for in_street_id in self.intersections[i]['in_scores']:
                        in_street_name = self.streets[in_street_id]['name']
                        self.intersections[i]['schedule'][in_street_name] = self.intersections[i]['in_scores'][
                            in_street_id]
                else:
                    for in_street_id in self.intersections[i]['in']:
                        in_street_name = self.streets[in_street_id]['name']
                        self.intersections[i]['schedule'][in_street_name] = 1

    def upscale_fraction_of_best_streets(self, suggested_fraction_of_streets, upscale_factor):
        n_upscaled_streets = int(self.n_streets / suggested_fraction_of_streets)
        if n_upscaled_streets == 0:
            return

        sorted_global_street_scores = list(
            sorted(self.all_street_scores.items(), key=lambda item: item[1]['score'], reverse=True))
        streets_to_increase_score = sorted_global_street_scores[:n_upscaled_streets]

        for street_id, global_street_score in streets_to_increase_score:
            if global_street_score == 0:
                continue

            i_end = self.streets[street_id]['i_end']
            in_street_name = self.streets[street_id]['name']
            self.intersections[i_end]['schedule'][in_street_name] *= upscale_factor

    def normalize_schedules(self, min_scheduled_time, max_scheduled_time, use_round_instead_of_floor):
        for i in range(self.n_intersections):
            if len(self.intersections[i]['in']):
                min_seconds, max_seconds = self.get_min_max_scheduled_seconds(i)

                for street_name, seconds in self.intersections[i]['schedule'].items():
                    normalized_seconds = self.get_normalized_seconds(seconds, min_seconds, max_seconds,
                                                                     min_scheduled_time, max_scheduled_time)
                    normalized_seconds = self.get_rounded_seconds(normalized_seconds, use_round_instead_of_floor)
                    self.intersections[i]['schedule'][street_name] = normalized_seconds

    def get_min_max_scheduled_seconds(self, intersection_id):
        min_seconds, max_seconds = None, None

        for street_name, seconds in self.intersections[intersection_id]['schedule'].items():
            if min_seconds is None or seconds < min_seconds:
                min_seconds = seconds
            if max_seconds is None or seconds > max_seconds:
                max_seconds = seconds

        if max_seconds == min_seconds:
            min_seconds = max_seconds - 1

        return min_seconds, max_seconds

    def get_normalized_seconds(self, seconds, min_seconds, max_seconds, min_scheduled_time, max_scheduled_time):
        scale_factor = max_scheduled_time - min_scheduled_time

        normalized_seconds = ((seconds - min_seconds) / (max_seconds - min_seconds)) * scale_factor
        normalized_seconds += min_scheduled_time

        return normalized_seconds

    def get_rounded_seconds(self, seconds, use_round_instead_of_floor):
        if use_round_instead_of_floor:
            return round(seconds)
        return int(seconds)

if __name__ == '__main__':
    Solver('a.txt').solve(1, 2)
    Solver('b.txt').solve(1, 1)
    Solver('c.txt').solve(1, 2)
    Solver('d.txt').solve(1, 1)
    Solver('e.txt').solve(1, 4, use_round_instead_of_floor=True)
    Solver('f.txt').solve(1, 6, upscale_fraction=10, upscale_factor=1000)


#!/usr/bin/env python
"""Example demonstrating collision detection and shortest distance queries."""
import time

import numpy as np
import pybullet as pyb
import pybullet_data

import pyb_utils

TIMESTEP = 1.0 / 60

def load_environment(client_id):
    pyb.setTimeStep(TIMESTEP, physicsClientId=client_id)
    pyb.setAdditionalSearchPath(
        pybullet_data.getDataPath(), physicsClientId=client_id
    )

    # ground plane
    ground_id = pyb.loadURDF(
        "plane.urdf", [0, 0, 0], useFixedBase=True, physicsClientId=client_id
    )

    # KUKA iiwa robot arm
    kuka_id = pyb.loadURDF(
        "kuka_iiwa/model.urdf",
        [0, 0, 0],
        useFixedBase=True,
        physicsClientId=client_id,
    )

    # some cubes for obstacles
    cube1_id = pyb.loadURDF(
        "cube.urdf", [1, 1, 0.5], useFixedBase=True, physicsClientId=client_id
    )
    cube2_id = pyb.loadURDF(
        "cube.urdf", [-1, -1, 0.5], useFixedBase=True, physicsClientId=client_id
    )
    cube3_id = pyb.loadURDF(
        "cube.urdf", [1, -1, 0.5], useFixedBase=True, physicsClientId=client_id
    )

    # store body indices in a dict with more convenient key names
    bodies = {
        "robot": kuka_id,
        "ground": ground_id,
        "cube1": cube1_id,
        "cube2": cube2_id,
        "cube3": cube3_id,
    }
    return bodies

def create_robot_debug_params(robot_id, gui_id):
    """Create debug params to set the robot joint positions from the GUI."""
    params = {}
    for i in range(pyb.getNumJoints(robot_id, physicsClientId=gui_id)):
        joint_name = pyb_utils.getJointInfo(robot_id, i, decode="utf8").jointName
        params[joint_name] = pyb.addUserDebugParameter(
            joint_name,
            rangeMin=-2 * np.pi,
            rangeMax=2 * np.pi,
            startValue=0,
            physicsClientId=gui_id,
        )
    return params

def read_robot_configuration(robot_id, robot_params, gui_id):
    """Read robot configuration from the GUI."""
    n = pyb.getNumJoints(robot_id, physicsClientId=gui_id)
    q = np.zeros(n)

    for i in range(n):
        joint_name = pyb_utils.getJointInfo(robot_id, i, decode="utf8").jointName
        q[i] = pyb.readUserDebugParameter(
            robot_params[joint_name],
            physicsClientId=gui_id,
        )
    return q

def update_robot_configuration(robot_id, q, gui_id):
    """Set the robot configuration."""
    for i in range(pyb.getNumJoints(robot_id, physicsClientId=gui_id)):
        pyb.resetJointState(robot_id, i, q[i], physicsClientId=gui_id)

def main():

    # main simulation server, with a GUI
    gui_id = pyb.connect(pyb.GUI)

    # simulation server only used for collision detection
    col_id = pyb.connect(pyb.DIRECT)

    # add bodies to both of the environments
    bodies = load_environment(gui_id)
    collision_bodies = load_environment(col_id)

    robot_id = bodies["robot"]

    # create user debug parameters
    collision_margin_param = pyb.addUserDebugParameter(
        "collision_margin",
        rangeMin=0,
        rangeMax=0.2,
        startValue=0.01,
        physicsClientId=gui_id,
    )
    robot_params = create_robot_debug_params(robot_id, gui_id)

    # define bodies (and links) to use for shortest distance computations and
    # collision checking
    ground = pyb_utils.NamedCollisionObject("ground")
    cube1 = pyb_utils.NamedCollisionObject("cube1")
    cube2 = pyb_utils.NamedCollisionObject("cube2")
    cube3 = pyb_utils.NamedCollisionObject("cube3")
    link7 = pyb_utils.NamedCollisionObject(
        "robot", "lbr_iiwa_link_7"
    )  # last link

    col_detector = pyb_utils.CollisionDetector(
        col_id,
        collision_bodies,
        [(link7, ground), (link7, cube1), (link7, cube2), (link7, cube3)],
    )

    robot_id = bodies["robot"]

    while True:
        q = read_robot_configuration(robot_id, robot_params, gui_id)

        # compute shortest distances for user-selected configuration
        d = col_detector.compute_distances(q)
        in_col = col_detector.in_collision(
            q, margin=pyb.readUserDebugParameter(collision_margin_param)
        )

        # move to the requested configuration if it is not in collision,
        # otherwise display a warning
        # the key is that we can check collisions using the separate physics
        # client, so we don't have to set the robot to a configuration in the
        # main GUI sim to check if that configuration is in collision
        if not in_col:
            update_robot_configuration(robot_id, q, gui_id=gui_id)
        else:
            pyb.addUserDebugText(
                "Avoiding collision",
                textPosition=[0, 0, 1.5],
                textColorRGB=[1, 0, 0],
                textSize=2,
                lifeTime=0.2,
            )

        print(f"Distance to obstacles = {d}")

        pyb.stepSimulation(physicsClientId=gui_id)
        time.sleep(TIMESTEP)

if __name__ == "__main__":
    main()