import torch

from conf import agents_cfgs
from fairdiplomacy.game import sort_phase_key
from fairdiplomacy.agents.base_agent import BaseAgent
from fairdiplomacy.agents.model_wrapper import ModelWrapper
from fairdiplomacy.models.consts import POWERS
from fairdiplomacy.utils.thread_pool_encoding import FeatureEncoder

_DEFAULT_DEVICE = "cuda" if torch.cuda.is_available() else "cpu"

class ModelSampledAgent(BaseAgent):
    def __init__(self, cfg: agents_cfgs.ModelSampledAgent, device=_DEFAULT_DEVICE):
        self.model = ModelWrapper(
            cfg.model_path, device=device, half_precision=cfg.half_precision
        )
        self.temperature = cfg.temperature
        self.top_p = cfg.top_p
        self.device = device
        self.input_encoder = FeatureEncoder()

    def get_orders(self, game, power, **kwargs):
        if len(game.get_orderable_locations().get(power, [])) == 0:
            return []
        return self.get_orders_many_powers(game, [power], **kwargs)[power]

    def get_orders_many_powers(self, game, powers, *, temperature=None, top_p=None):
        temperature = temperature if temperature is not None else self.temperature
        top_p = top_p if top_p is not None else self.top_p
        encode_fn = (
            self.input_encoder.encode_inputs_all_powers
            if self.model.is_all_powers()
            else self.input_encoder.encode_inputs
        )
        inputs = encode_fn([game])

        inputs = inputs.to(self.device)
        actions, _, _ = self.model.do_model_request(inputs, temperature=temperature, top_p=top_p)
        actions = actions[0]  # batch dim
        return {p: a for p, a in zip(POWERS, actions) if p in powers}


from itertools import product
import sqlite3

# class Card:
#
#     def __init__(self, name, rules_text, color, type_, subtype, cmc, power,
#                  toughness, mana_cost, rarity):
#         self.name = name
#         self.rules_text = rules_text
#         self.color = color
#         self.type_ = type_
#         self.subtype = subtype
#         self.cmc = cmc
#         self.power = power
#         self.toughness = toughness
#         self.mana_cost = mana_cost
#         self.rarity = rarity

sqlite_file = 'db.sqlite'
conn = sqlite3.connect(sqlite_file)
c = conn.cursor()

class Card:

    def __init__(self, cost):
        self.cost = cost

class Creature(Card):

    def __init__(self, power, toughness, *args, **kwargs):
        super(Creature, self).__init__(*args, **kwargs)
        self.power = power
        self.toughness = toughness

class Deck:

    def __init__(self):
        self.cards = []

    def take_top_card(self):
        # last item is top
        card = self.cards.pop()
        return card

class Player:

    def __init__(self, deck):
        self.deck = deck
        self.hand = []

    def draw(self, size):
        for i in range(size):
            card = self.deck.take_top_card()
            self.hand.append(card)

class Engine:

    STARTING_HAND_SIZE = 7

    def __init__(self, player1, player2):
        self.player1 = player1
        self.player2 = player2
        self.player1.draw(7)
        self.player2.draw(7)
        self.turn = 1

def run_simulation():
    deck1 = Deck()
    player1 = Player(deck1)
    deck2 = Deck()
    player2 = Player(deck2)
    engine = Engine(player1, player2)

def generate_cards():
    setup_db()

    res = list(product(range(0, 6), range(1, 6)))
    print(len(res))
    print(res)

def setup_db():
    """
    INTEGER: A signed integer up to 8 bytes depending on the magnitude of the value.
    REAL: An 8-byte floating point value.
    TEXT: A text string, typically UTF-8 encoded (depending on the database encoding).
    BLOB: A blob of data (binary large object) for storing binary data.
    NULL: A NULL value, represents missing data or an empty cell.
    """
    c.execute("""
    CREATE TABLE creature (
        id INTEGER PRIMARY KEY,
        power INTEGER NOT NULL,
        toughness INTEGER NOT NULL,
        UNIQUE (power, toughness)
    )
    """)
    conn.commit()

if __name__ == '__main__':
    generate_cards()
    # run_simulation()


import sys
sys.path.append('..')
from MobileNetV2 import MobileNetV2
from torch.autograd import Variable
import torch.nn as nn
import torch
from data.config import cfg
from gcn.st_gcn import Model_G

class EventDetector(nn.Module):
    def __init__(self, pretrain, width_mult, lstm_layers, lstm_hidden, bidirectional=True, dropout=True):
        super(EventDetector, self).__init__()
        self.width_mult = width_mult
        self.lstm_layers = lstm_layers
        self.lstm_hidden = lstm_hidden
        self.bidirectional = bidirectional
        self.dropout = dropout
        ######for gcn######
        self.in_channels = 3
        self.num_class = 9
        self.edge_importance_weighting = True
        self.gcn_feature = 128
        self.graph_args = {'layout': 'customer settings','strategy': 'spatial', 'max_hop': 1, 'dilation': 1}
        self.st_gcn_net = Model_G(self.in_channels, self.num_class,self.edge_importance_weighting,self.graph_args)
        net = MobileNetV2(width_mult=width_mult)
        state_dict_mobilenet = torch.load('../mobilenet_v2.pth.tar')
        state_dict_st_gcn_net = torch.load('../gcn_spotting_net_8000.pth.tar')
        if pretrain:
            net.load_state_dict(state_dict_mobilenet)
            self.st_gcn_net.load_state_dict(state_dict_st_gcn_net['model_state_dict'])

        self.cnn = nn.Sequential(*list(net.children())[0][:19])
        self.rnn = nn.LSTM(int(1280*width_mult if width_mult > 1.0 else 1280),
                           self.lstm_hidden, self.lstm_layers,
                           batch_first=True, bidirectional=bidirectional)
        if self.bidirectional:
            if cfg.FRAME_13_OPEN:
                self.lin = nn.Linear(2*self.lstm_hidden+self.gcn_feature, 14)
            else:
                self.lin = nn.Linear(2*self.lstm_hidden+self.gcn_feature, 9)
        else:
            if cfg.FRAME_13_OPEN:
                self.lin = nn.Linear(self.lstm_hidden+self.gcn_feature, 14)
            else:
                self.lin = nn.Linear(self.lstm_hidden+self.gcn_feature, 9)
        if self.dropout:
            self.drop = nn.Dropout(0.5)

    def init_hidden(self, batch_size):
        if self.bidirectional:
            return (Variable(torch.zeros(2*self.lstm_layers, batch_size, self.lstm_hidden).cuda(), requires_grad=True),
                    Variable(torch.zeros(2*self.lstm_layers, batch_size, self.lstm_hidden).cuda(), requires_grad=True))
        else:
            return (Variable(torch.zeros(self.lstm_layers, batch_size, self.lstm_hidden).cuda(), requires_grad=True),
                    Variable(torch.zeros(self.lstm_layers, batch_size, self.lstm_hidden).cuda(), requires_grad=True))

    def forward(self, x, k,lengths=None):
        k = self.st_gcn_net.extract_feature(k)
        batch_size, timesteps, C, H, W = x.size()
        self.hidden = self.init_hidden(batch_size)

        # CNN forward
        c_in = x.view(batch_size * timesteps, C, H, W)
        c_out = self.cnn(c_in)
        c_out = c_out.mean(3).mean(2)
        if self.dropout:
            c_out = self.drop(c_out)

        # LSTM forward
        r_in = c_out.view(batch_size, timesteps, -1)
        r_out, states = self.rnn(r_in, self.hidden)
        # print("after lstm")
        # print(r_out.shape)
        # print(k.shape)
        concat_out = torch.cat((r_out, k), 2)
        out = self.lin(concat_out)
        if cfg.FRAME_13_OPEN:
            out = out.view(batch_size*timesteps, 14)
        else:
            out = out.view(batch_size*timesteps, 9)

        return out


from collections import deque
from tkinter import*
from menu import*
import random

class Board:
    def __init__(self, gamemode, tk, menu):             #konštruktor,deklaruje základné premenné a zapne funkcie create_board a create_buttons ktoré vytvoria hraciu plochu
        self.menu=menu
        self.tk=tk
        self.canvas = Canvas(height= 600, width = 500, bg="white", master=self.tk)
        self.canvas.pack()

        self.gamemode = gamemode
        self.create_board()
        self.create_buttons()

        self.win = False
        self.position=0
        self.round=0
        self.player_1 = 5*[0]
        self.player_2 = 5*[0]
        self.colors_dict = dict()
        self.colors_used = dict()
        colours= ["red","lime","orange","blue","cyan", "yellow", "magenta", "brown"]
        for i in range(1,9):
            self.colors_dict[i]=colours[i-1]
            self.colors_used[i]=6*[0]
        if gamemode == 1:
            self.evp()

    def create_board(self):
        self.p1_text = "player 1"
        self.p2_text = "player 2"
        if self.gamemode == 1:
            self.p1_text = "computer"
        elif self.gamemode == 2:
            self.p2_text = "computer"

        self.canvas.delete("all")
        for i in range (1,5):
            self.canvas.create_line(i*35+50, 50, i*35+50, 400, fill="#c8c8c8")
            self.canvas.create_line(i*35+275, 50, i*35+275 , 400, fill="#c8c8c8")
            self.canvas.create_line(i*35+50, 440, i*35+50 , 475, fill="#c8c8c8")
        for i in range (1,11):
            self.canvas.create_line(50, i*35+50, 225, i*35+50)
            self.canvas.create_line(275, i*35+50, 450, i*35+50)
            self.canvas.create_text(35, i*35+33,text=str(i),font="Times 15 bold")

        self.canvas.create_rectangle(50, 50, 225, 400)
        self.canvas.create_rectangle(275, 50, 450, 400)
        self.canvas.create_rectangle(50, 440, 225, 475)

        self.canvas.create_text(140, 420, font="Times 15 bold", text=self.p1_text)
        self.canvas.create_text(140, 30, font="Times 15 bold", text=self.p2_text)

    def create_buttons(self):
        self.b_color_1 = Button(text = "red", command=lambda:self.create_circle(1))
        self.b_color_2 = Button(text = "green", command=lambda:self.create_circle(2))
        self.b_color_3 = Button(text = "orange", command=lambda:self.create_circle(3))
        self.b_color_4 = Button(text = "blue", command=lambda:self.create_circle(4))
        self.b_color_5 = Button(text = "cyan", command=lambda:self.create_circle(5))
        self.b_color_6 = Button(text = "yellow", command=lambda:self.create_circle(6))
        self.b_color_7 = Button(text = "pink", command=lambda:self.create_circle(7))
        self.b_color_8 = Button(text = "brown", command=lambda:self.create_circle(8))

        self.b_delete = Button (text = "delete", command = lambda:self.delete_circle())
        self.b_next_round = Button(text="next round", command = lambda:self.next_round())
        self.b_new_game = Button(text="new game", command = lambda: self.menu.new_game())

        self.b_color_1.pack(side= LEFT)
        self.b_color_2.pack(side= LEFT)
        self.b_color_3.pack(side= LEFT)
        self.b_color_4.pack(side= LEFT)
        self.b_color_5.pack(side= LEFT)
        self.b_color_6.pack(side= LEFT)
        self.b_color_7.pack(side= LEFT)
        self.b_color_8.pack(side= LEFT)

        self.b_new_game.pack(side= RIGHT)
        self.b_next_round.pack(side= RIGHT)
        self.b_delete.pack(side= RIGHT)

    def create_circle(self, color):                 #vykresluje panáčikov, na základe stačeného tlačítka
        if self.position <5 and color not in self.player_1 and self.round == 0:
                self.canvas.create_oval(self.position*35+52, 442, self.position*35+83, 473, fill=self.colors_dict[color], outline= "")
                self.player_1[self.position] = color
                self.position+=1
        elif self.position <5 and color not in self.player_2 and self.round >0:
                self.canvas.create_oval(52+35*self.position,52+35*(self.round-1),83+35*self.position,83+35*(self.round-1), fill=self.colors_dict[color], outline= "")
                self.player_2[self.position] = color
                self.position+=1

    def delete_circle(self):                        #vymaže posledného vykresleného panáčika
        if self.position > 0:
            self.position-=1
            if self.round == 0:
                self.player_1[self.position] = 0
                self.canvas.create_oval(self.position*35+52, 442, self.position*35+83, 473, fill="white", outline= "")
            else:
                self.player_2[self.position] = 0
                self.canvas.create_oval(52+35*self.position,52+35*(self.round-1),83+35*self.position,83+35*(self.round-1), fill="white", outline= "")

    def next_round(self):                           #posúva hru do nového stavu, zapína funkciu na ohodnotenie stavu a zakrýva postupnosť hráča 1
        if self.position == 5:
            if self.round>0:
                self.state_rating()
                if self.gamemode == 2 and self.win == False:
                    self.pve()
            else:
                self.canvas.create_rectangle(50, 440, 225, 475, fill="black", tag="black_box")
                self.round+=1
                self.position=0
                if self.gamemode == 2 and self.win == False:
                    self.pve()

    def state_rating(self):                         #ohodnocuje postupnosť hráča 2, zaznamenáva stav do color dictu
        for i in range(5):
            if self.player_2[i] == self.player_1[i]:            #porovnáva pozíciu a farbu panáčikov, pvykresluje čierny puntík
                self.canvas.create_oval(277+35*i, 52+35*(self.round-1), 308+35*i, 83+35*(self.round-1), fill="black", outline="")
                self.colors_used[self.player_2[i]][0]=1
                self.colors_used[self.player_2[i]][i+1]=1
            elif self.player_2[i] in self.player_1:             #porovnáva či je farba puntíku v postupnosti hráča 1, vykresluje sivý puntík
                self.canvas.create_oval(277+35*i, 52+35*(self.round-1), 308+35*i, 83+35*(self.round-1), fill="grey", outline="")
                self.colors_used[self.player_2[i]][i+1]=-1
                self.colors_used[self.player_2[i]][0]=2
            else:                                               #keď nie je farba použitá, zaznamená si to do color_dictu
                self.colors_used[self.player_2[i]][0]=-1

        if self.player_1 == self.player_2:                      # podmienka pre ukončenie hry
             self.win = True
             self.end_screen()
        elif self.round == 10:
            self.end_screen()

        self.round+=1
        self.position=0
        self.player_2=5*[0]

    def pve (self):                                             #funkcia ktorá hrá za hráča 2, pokiaľ je zapnutý mód pve
        sequence = solver(self.colors_used)
        for i in sequence:
            self.create_circle(i)
        self.next_round()

    def evp (self):                                             #funkcia, ktorá hrá za hráča 1, pokiaľ je zapnutý mód evp
        for i in range(5):
            i = random.randint(1,8)
            while i in self.player_1:
                i = random.randint(1,8)
            self.create_circle(i)
        self.next_round()

    def end_screen(self):                                       #vykresľuje výhernú obrazovku, vypína tlačítka, vypisuje kto vyhral
        self.canvas.delete("black_box")
        if self.win:
            self.canvas.create_text(250,250,font="Times 50 bold", fill="DarkOrchid4", text=f"{self.p2_text} wins!")
        else:
            self.canvas.create_text(250,250,font="Times 50 bold", fill="DarkOrchid4", text=f"{self.p1_text} wins!")

        self.b_color_1["state"] = DISABLED
        self.b_color_2["state"] = DISABLED
        self.b_color_3["state"] = DISABLED
        self.b_color_4["state"] = DISABLED
        self.b_color_5["state"] = DISABLED
        self.b_color_6["state"] = DISABLED
        self.b_color_7["state"] = DISABLED
        self.b_color_8["state"] = DISABLED

        self.b_delete["state"] = DISABLED
        self.b_next_round["state"] = DISABLED

class State:                                                   #pomocná classa k funkcii solver, ukladá stavy, aby sa mohli vložiť do zásobníku
    def __init__(self, state, position) -> None:
        self.state = state.copy()
        self.position = position

def solver(colors_used):                                       #funkcia, ktorá háda postupnosť
    stack = deque()
    position = 0
    start_state = 5*[0]
    for j in range (5):                                         #najskôr doplní panáčikov, ktorý boli ohodnotený čiernym puntíkom
        for i in range(1,9):
                if i not in start_state and colors_used[i][0] == 1 and colors_used[i][j+1] == 1:
                    start_state[j] = i
    current_state = start_state.copy()
    while position < 5:                                         #iteruje pokým nenájde stav, kde sú doplnení všetci panáčikovia
        if current_state[position] == 0:                        #ak je už v postupnosti doplnení panáčik (bol ohodnotený čiernym puntíkom) tak túto pozíciu preskakuje
            for i in range(1,9):
                if i not in current_state and colors_used[i][0] == 0:           # dopĺňa panáčikov ohodnotených šedým puntíkom, na pozíciu kde ešte neboli
                    current_state[position]=i
                    stack.appendleft(State(current_state, position+1))
            for i in range(1,9):
                if i not in current_state and colors_used[i][0] == 2 and colors_used[i][position+1] == 0:           #doplňuje farbu panáčika, ktorá ešte nebola použitá
                    current_state[position]=i
                    stack.appendleft(State(current_state, position+1))
            state = stack.popleft()                          #vyberá nový state na prehladanie zo zásobníku
            current_state = state.state
            position = state.position
        else:
            position+=1

    return current_state                                          #vracia hotovú postupnosť


#-*- coding:utf-8 -*-
import json, urllib2

API_KEY    = os.environ["REKOGNITION_API_KEY"]
API_SECRET = os.environ["REKOGNITION_API_SECRET"]

jobs = 'face_age_beauty'
pic_url = 'http://pic.prepics-cdn.com/smapphoto/12756846.jpeg'
url = 'http://rekognition.com/func/api/?api_key=' + API_KEY + '&api_secret=' + API_SECRET + '&jobs=' + jobs + '&urls=' + pic_url

r = urllib2.urlopen(url)
root = json.loads(r.read())

print(root)
print(root[u'face_detection'][0][u'age'])
print(root[u'face_detection'][0][u'beauty'])


#!/usr/bin/env python

import logging
from math import floor
import time

from csvkit import CSVKitReader
from django.conf import settings
from django.utils.translation import ugettext
from livesettings import config_value

from panda import solr, utils
from panda.exceptions import DataImportError
from panda.tasks.import_file import ImportFileTask
from panda.utils.typecoercion import DataTyper

SOLR_ADD_BUFFER_SIZE = 500

class ImportCSVTask(ImportFileTask):
    """
    Task to import all data for a dataset from a CSV.
    """
    name = 'panda.tasks.import.csv'

    def _count_lines(self, filename):
        """
        Efficiently count the number of lines in a file.
        """
        with open(filename) as f:
            for i, l in enumerate(f):
                pass
        return i + 1

    def run(self, dataset_slug, upload_id, external_id_field_index=None, *args, **kwargs):
        """
        Execute import.
        """
        from panda.models import Dataset, DataUpload

        log = logging.getLogger(self.name)
        log.info('Beginning import, dataset_slug: %s' % dataset_slug)

        try:
            dataset = Dataset.objects.get(slug=dataset_slug)
        except Dataset.DoesNotExist:
            log.warning('Import failed due to Dataset being deleted, dataset_slug: %s' % dataset_slug)

            return

        upload = DataUpload.objects.get(id=upload_id)

        task_status = dataset.current_task
        task_status.begin(ugettext('Preparing to import'))

        line_count = self._count_lines(upload.get_path())

        if self.is_aborted():
            task_status.abort('Aborted during preperation')

            log.warning('Import aborted, dataset_slug: %s' % dataset_slug)

            return

        f = open(upload.get_path(), 'r')

        reader = CSVKitReader(f, encoding=upload.encoding, **upload.dialect_as_parameters())
        reader.next()

        add_buffer = []
        data_typer = DataTyper(dataset.column_schema)
        throttle = config_value('PERF', 'TASK_THROTTLE')

        i = 0

        while True:
            # The row number which is about to be read, for error handling and indexing
            i += 1

            try:
                row = reader.next()
            except StopIteration:
                i -= 1
                break
            except UnicodeDecodeError:
                raise DataImportError(ugettext('This CSV file contains characters that are not %(encoding)s encoded in or after row %(row)i. You need to re-upload this file and input the correct encoding in order to import data from this file.') % { 'encoding': upload.encoding, 'row': i })

            external_id = None

            if external_id_field_index is not None:
                external_id = row[external_id_field_index]

            data = utils.solr.make_data_row(dataset, row, data_upload=upload, external_id=external_id)
            data = data_typer(data, row)

            add_buffer.append(data)

            if i % SOLR_ADD_BUFFER_SIZE == 0:
                solr.add(settings.SOLR_DATA_CORE, add_buffer)

                add_buffer = []

                task_status.update(ugettext('%.0f%% complete (estimated)') % floor(float(i) / float(line_count) * 100))

                if self.is_aborted():
                    task_status.abort(ugettext('Aborted after importing %.0f%% (estimated)') % floor(float(i) / float(line_count) * 100))

                    log.warning('Import aborted, dataset_slug: %s' % dataset_slug)

                    return

                time.sleep(throttle)

        if add_buffer:
            solr.add(settings.SOLR_DATA_CORE, add_buffer)
            add_buffer = []

        solr.commit(settings.SOLR_DATA_CORE)

        f.close()

        task_status.update('100% complete')

        # Refresh dataset from database so there is no chance of crushing changes made since the task started
        try:
            dataset = Dataset.objects.get(slug=dataset_slug)
        except Dataset.DoesNotExist:
            log.warning('Import could not be completed due to Dataset being deleted, dataset_slug: %s' % dataset_slug)

            return

        if not dataset.row_count:
            dataset.row_count = i
        else:
            dataset.row_count += i

        dataset.column_schema = data_typer.schema

        dataset.save()

        # Refres
        upload = DataUpload.objects.get(id=upload_id)

        upload.imported = True
        upload.save()

        log.info('Finished import, dataset_slug: %s' % dataset_slug)

        return data_typer


# IMPORTING CONFIGURATIONS
import yaml

try:
    with open('config.yaml', 'r') as configfile:
        cfg = yaml.load(configfile)
except:
    with open('../config.yaml', 'r') as configfile:
        cfg = yaml.load(configfile)

# IMPORTING NECESSARY PACKAGES
from microsoftbotframework import ReplyToActivity
from helpers.db_class import DB
from helpers.dialogflow_connector import detect_intent_texts
import json
import os.path
import apiai
from numpy.random import randint
from datetime import datetime
from helpers.rephrase import check_rephrase, check_override
from helpers.log_conversations import log_response, log_message, get_conversation_status, get_participantid_invalid

db = DB()

## TO DO:
## Logging
## Check participant ID
## Create and/or update conversation
## Check conversation status
## Standard messages

def echo_response(message):
    conditionid = None
    participantid = None
    # CONTACT RELATION UPDATE: Agent is added to Skype
    if message["type"] == "contactRelationUpdate":
        if 'action' in message.keys():
            if message['action'] == 'add':
                resp_speech = '[STANDARDWELCOMEMESSAGE]'
                resp_speech = check_rephrase(resp_speech)

                log_response(db, message, resp_speech, resp_speech)
                if 'channelData' in message.keys():
                    del message['channelData']['clientActivityID']
                ReplyToActivity(fill=message,
                        text=resp_speech).send()
                return

    # MESSAGE: Default type of conversation
    if message["type"] == "message":
        if 'text' not in message.keys():
            message['text'] = '-'

        message['text'] = str(message['text']).replace("'", '‘')
        conversationid = message['conversation']['id']
        conversationid_trunc = conversationid[0:35]

        resp_speech = None

        resp = detect_intent_texts(conversationid_trunc, str(message['text']))

        try:
            # resp_speech = resp_dialogflow.query_result.fulfillment_text
            resp_speech = resp['fulfillmentText']
        except:
            resp_speech = 'ERROR: ' + str(resp_dialogflow)

        if not resp_speech:
            try:
                resp_speech  = resp['result']['fulfillment']['speech']
            except:
                resp_speech = resp['result']['fulfillment']['messages'][0]['speech']

        conversation_status, conversation_code, conditionid, participantid  = get_conversation_status(db, conversationid, message, resp)
        print(conversation_code, conditionid, conversation_status)

        message = log_message(db, message, participantid=participantid)
        override = check_override(message['text'])

        ## Performing manual connection if intents in DialogFlow
        connect_intent = None
        # If the conversation_status is considered invalid (due to invalid participant id)
        if conversation_status == 'conditionid_invalid':
            override_invalid_participantid = get_participantid_invalid()
            if override_invalid_participantid:
                connect_intent = '[' + str(override_invalid_participantid) + ']'
                log_response(db, message, connect_intent, conversation_status, participantid = participantid)

        # If the response in DialogFlow needs to connect manually to a new intent
        elif 'connect_intents' in cfg.keys():
            for source_intent, target_intent in cfg['connect_intents'].items():
                if resp_speech == '[' + str(source_intent) + ']':
                    print('check 1:', source_intent)
                    connect_intent = '[' + str(target_intent) + ']'
                    log_response(db, message, connect_intent, conversation_status, participantid = participantid)

        if connect_intent:
            ai = apiai.ApiAI(cfg['other']['dialogflow_access_token'])
            request = ai.text_request()
            request.lang = 'en'  # optional, default value equal 'en'
            request.session_id = conversationid_trunc
            request.query = connect_intent

            respAPI = request.getresponse()
            resp = respAPI.read().decode('utf-8')
            resp = json.loads(resp)
            try:
                resp_speech  = resp['result']['fulfillment']['speech']
            except:
                resp_speech = resp['result']['fulfillment']['messages'][0]['speech']

            resp_speech = check_rephrase(resp_speech, conversation_code = conversation_code, conditionid=conditionid)
            log_response(db, message, resp_speech, resp, participantid=participantid)
            if 'channelData' in message.keys():
                del message['channelData']['clientActivityID']
            ReplyToActivity(fill=message, text=resp_speech).send()

        elif override['override'] == True:
            resp_speech = check_rephrase(override['resp_speech'], conversation_code = conversation_code)
            reason_override = override['reason']
            log_response(db, message, resp_speech, reason_override, participantid=participantid)
            ReplyToActivity(fill=message,
                        text=resp_speech).send()

        else:
            resp_speech = check_rephrase(resp_speech, conversation_code = conversation_code, conditionid=conditionid)
            message['textFormat'] = 'markdown'
            log_response(db, message, resp_speech, resp, participantid=participantid)
            #override for disappearing messages
            if 'channelData' in message.keys():
                del message['channelData']['clientActivityID']
            ReplyToActivity(fill=message, text=resp_speech).send()


import functools
import weakref

import numpy as np

from tensorflow.python.util import nest

def get_source_inputs(tensor, layer=None, node_index=None):
  """Returns the list of input tensors necessary to compute `tensor`.

  Output will always be a list of tensors
  (potentially with 1 element).

  Args:
      tensor: The tensor to start from.
      layer: Origin layer of the tensor. Will be
          determined via tensor._keras_history if not provided.
      node_index: Origin node index of the tensor.

  Returns:
      List of input tensors.
  """
  if not hasattr(tensor, '_keras_history'):
    return tensor

  if layer is None or node_index:
    layer, node_index, _ = tensor._keras_history
  if not layer._inbound_nodes:
    return [tensor]
  else:
    node = layer._inbound_nodes[node_index]
    if node.is_input:
      # Reached an Input layer, stop recursion.
      return nest.flatten(node.input_tensors)
    else:
      source_tensors = []
      for layer, node_index, _, tensor in node.iterate_inbound():
        previous_sources = get_source_inputs(tensor, layer, node_index)
        # Avoid input redundancy.
        for x in previous_sources:
          if all(x is not t for t in source_tensors):
            source_tensors.append(x)
      return source_tensors

def validate_string_arg(input_data,
                        allowable_strings,
                        layer_name,
                        arg_name,
                        allow_none=False,
                        allow_callables=False):
  """Validates the correctness of a string-based arg."""
  if allow_none and input_data is None:
    return
  elif allow_callables and callable(input_data):
    return
  elif isinstance(input_data, str) and input_data in allowable_strings:
    return
  else:
    allowed_args = '`None`, ' if allow_none else ''
    allowed_args += 'a `Callable`, ' if allow_callables else ''
    allowed_args += 'or one of the following values: %s' % (allowable_strings,)
    raise ValueError(('The %s argument of layer %s received an invalid '
                      'value %s. Allowed values are: %s.') %
                     (arg_name, layer_name, input_data, allowed_args))

def count_params(weights):
  """Count the total number of scalars composing the weights.

  Args:
      weights: An iterable containing the weights on which to compute params

  Returns:
      The total number of scalars composing the weights
  """
  unique_weights = {id(w): w for w in weights}.values()
  weight_shapes = [w.shape.as_list() for w in unique_weights]
  standardized_weight_shapes = [
      [0 if w_i is None else w_i for w_i in w] for w in weight_shapes
  ]
  return int(sum(np.prod(p) for p in standardized_weight_shapes))

def print_summary(model, line_length=None, positions=None, print_fn=None):
  """Prints a summary of a model.

  Args:
      model: Keras model instance.
      line_length: Total length of printed lines
          (e.g. set this to adapt the display to different
          terminal window sizes).
      positions: Relative or absolute positions of log elements in each line.
          If not provided, defaults to `[.33, .55, .67, 1.]`.
      print_fn: Print function to use.
          It will be called on each line of the summary.
          You can set it to a custom function
          in order to capture the string summary.
          It defaults to `print` (prints to stdout).
  """
  if print_fn is None:
    print_fn = print

  if model.__class__.__name__ == 'Sequential':
    sequential_like = True
  elif not model._is_graph_network:
    # We treat subclassed models as a simple sequence of layers, for logging
    # purposes.
    sequential_like = True
  else:
    sequential_like = True
    nodes_by_depth = model._nodes_by_depth.values()
    nodes = []
    for v in nodes_by_depth:
      if (len(v) > 1) or (len(v) == 1 and
                          len(nest.flatten(v[0].keras_inputs)) > 1):
        # if the model has multiple nodes
        # or if the nodes have multiple inbound_layers
        # the model is no longer sequential
        sequential_like = False
        break
      nodes += v
    if sequential_like:
      # search for shared layers
      for layer in model.layers:
        flag = False
        for node in layer._inbound_nodes:
          if node in nodes:
            if flag:
              sequential_like = False
              break
            else:
              flag = True
        if not sequential_like:
          break

  if sequential_like:
    line_length = line_length or 65
    positions = positions or [.45, .85, 1.]
    if positions[-1] <= 1:
      positions = [int(line_length * p) for p in positions]
    # header names for the different log elements
    to_display = ['Layer (type)', 'Output Shape', 'Param #']
  else:
    line_length = line_length or 98
    positions = positions or [.33, .55, .67, 1.]
    if positions[-1] <= 1:
      positions = [int(line_length * p) for p in positions]
    # header names for the different log elements
    to_display = ['Layer (type)', 'Output Shape', 'Param #', 'Connected to']
    relevant_nodes = []
    for v in model._nodes_by_depth.values():
      relevant_nodes += v

  def print_row(fields, positions):
    line = ''
    for i in range(len(fields)):
      if i > 0:
        line = line[:-1] + ' '
      line += str(fields[i])
      line = line[:positions[i]]
      line += ' ' * (positions[i] - len(line))
    print_fn(line)

  print_fn('Model: "{}"'.format(model.name))
  print_fn('_' * line_length)
  print_row(to_display, positions)
  print_fn('=' * line_length)

  def print_layer_summary(layer):
    """Prints a summary for a single layer.

    Args:
        layer: target layer.
    """
    try:
      output_shape = layer.output_shape
    except AttributeError:
      output_shape = 'multiple'
    except RuntimeError:  # output_shape unknown in Eager mode.
      output_shape = '?'
    name = layer.name
    cls_name = layer.__class__.__name__
    if not layer.built and not getattr(layer, '_is_graph_network', False):
      # If a subclassed model has a layer that is not called in Model.call, the
      # layer will not be built and we cannot call layer.count_params().
      params = '0 (unused)'
    else:
      params = layer.count_params()
    fields = [name + ' (' + cls_name + ')', output_shape, params]
    print_row(fields, positions)

  def print_layer_summary_with_connections(layer):
    """Prints a summary for a single layer (including topological connections).

    Args:
        layer: target layer.
    """
    try:
      output_shape = layer.output_shape
    except AttributeError:
      output_shape = 'multiple'
    connections = []
    for node in layer._inbound_nodes:
      if relevant_nodes and node not in relevant_nodes:
        # node is not part of the current network
        continue

      for inbound_layer, node_index, tensor_index, _ in node.iterate_inbound():
        connections.append('{}[{}][{}]'.format(inbound_layer.name, node_index,
                                               tensor_index))

    name = layer.name
    cls_name = layer.__class__.__name__
    if not connections:
      first_connection = ''
    else:
      first_connection = connections[0]
    fields = [
        name + ' (' + cls_name + ')', output_shape,
        layer.count_params(), first_connection
    ]
    print_row(fields, positions)
    if len(connections) > 1:
      for i in range(1, len(connections)):
        fields = ['', '', '', connections[i]]
        print_row(fields, positions)

  layers = model.layers
  for i in range(len(layers)):
    if sequential_like:
      print_layer_summary(layers[i])
    else:
      print_layer_summary_with_connections(layers[i])
    if i == len(layers) - 1:
      print_fn('=' * line_length)
    else:
      print_fn('_' * line_length)

  if hasattr(model, '_collected_trainable_weights'):
    trainable_count = count_params(model._collected_trainable_weights)
  else:
    trainable_count = count_params(model.trainable_weights)

  non_trainable_count = count_params(model.non_trainable_weights)

  print_fn('Total params: {:,}'.format(trainable_count + non_trainable_count))
  print_fn('Trainable params: {:,}'.format(trainable_count))
  print_fn('Non-trainable params: {:,}'.format(non_trainable_count))
  print_fn('_' * line_length)

def convert_dense_weights_data_format(dense,
                                      previous_feature_map_shape,
                                      target_data_format='channels_first'):
  """Utility useful when changing a convnet's `data_format`.

  When porting the weights of a convnet from one data format to the other,
  if the convnet includes a `Flatten` layer
  (applied to the last convolutional feature map)
  followed by a `Dense` layer, the weights of that `Dense` layer
  should be updated to reflect the new dimension ordering.

  Args:
      dense: The target `Dense` layer.
      previous_feature_map_shape: A shape tuple of 3 integers,
          e.g. `(512, 7, 7)`. The shape of the convolutional
          feature map right before the `Flatten` layer that
          came before the target `Dense` layer.
      target_data_format: One of "channels_last", "channels_first".
          Set it "channels_last"
          if converting a "channels_first" model to "channels_last",
          or reciprocally.
  """
  assert target_data_format in {'channels_last', 'channels_first'}
  kernel, bias = dense.get_weights()
  for i in range(kernel.shape[1]):
    if target_data_format == 'channels_first':
      c, h, w = previous_feature_map_shape
      original_fm_shape = (h, w, c)
      ki = kernel[:, i].reshape(original_fm_shape)
      ki = np.transpose(ki, (2, 0, 1))  # last -> first
    else:
      h, w, c = previous_feature_map_shape
      original_fm_shape = (c, h, w)
      ki = kernel[:, i].reshape(original_fm_shape)
      ki = np.transpose(ki, (1, 2, 0))  # first -> last
    kernel[:, i] = np.reshape(ki, (np.prod(previous_feature_map_shape),))
  dense.set_weights([kernel, bias])

def is_builtin_layer(layer):
  if not getattr(layer, '_keras_api_names', None):
    return False

  # Subclasses of `Layer` that are not exported inherit the export name
  # of the base layer class.
  return (layer._keras_api_names != ('keras.layers.Layer',) and
          layer._keras_api_names_v1 != ('keras.layers.Layer',))

def cached_per_instance(f):
  """Lightweight decorator for caching lazily constructed properties.

  When to use:
  This decorator provides simple caching with minimal overhead. It is designed
  for properties which are expensive to compute and static over the life of a
  class instance, and provides no mechanism for cache invalidation. Thus it is
  best suited for lazily exposing derived properties of other static data.

  For classes with custom getattr / setattr behavior (such as trackable
  objects), storing cache results as object attributes is not performant.
  Instead, a specialized cache can significantly reduce property lookup
  overhead. (While still allowing the decorated property to be lazily computed.)
  Consider the following class:

  ```
  class MyClass(object):
    def __setattr__(self, key, value):
      # Some expensive class specific code
      # ...
      # ...

      super(MyClass, self).__setattr__(key, value)

    @property
    def thing(self):
      # `thing` is expensive to compute (and may not even be requested), so we
      # want to lazily compute it and then cache it.
      output = getattr(self, '_thing', None)
      if output is None:
        self._thing = output = compute_thing(self)
      return output
  ```

  It's also worth noting that ANY overriding of __setattr__, even something as
  simple as:
  ```
    def __setattr__(self, key, value):
      super(MyClass, self).__setattr__(key, value)
  ```

  Slows down attribute assignment by nearly 10x.

  By contrast, replacing the definition of `thing` with the following sidesteps
  the expensive __setattr__ altogether:

  '''
  @property
  @tracking.cached_per_instance
  def thing(self):
    # `thing` is expensive to compute (and may not even be requested), so we
    # want to lazily compute it and then cache it.
    return compute_thing(self)
  '''

  Performance:
  The overhead for this decorator is ~0.4 us / call. A much lower overhead
  implementation (~0.085 us / call) can be achieved by using a custom dict type:

  ```
  def dict_based_cache(f):
    class Cache(dict):
      __slots__ = ()
      def __missing__(self, key):
        self[key] = output = f(key)
        return output

    return property(Cache().__getitem__)
  ```

  However, that implementation holds class instances as keys, and as a result
  blocks garbage collection. (And modifying it to use weakref's as keys raises
  the lookup overhead to ~0.4 us) As a result, the WeakKeyDictionary
  implementation below turns out to be more prudent.

  Args:
    f: The function to cache.

  Returns:
    f decorated with simple caching behavior.
  """

  cache = weakref.WeakKeyDictionary()

  @functools.wraps(f)
  def wrapped(item):
    output = cache.get(item)
    if output is None:
      cache[item] = output = f(item)
    return output

  wrapped.cache = cache
  return wrapped

def filter_empty_layer_containers(layer_list):
  """Filter out empty Layer-like containers and uniquify."""
  # TODO(b/130381733): Make this an attribute in base_layer.Layer.
  existing = set()
  to_visit = layer_list[::-1]
  while to_visit:
    obj = to_visit.pop()
    if id(obj) in existing:
      continue
    existing.add(id(obj))
    if hasattr(obj, '_is_layer') and not isinstance(obj, type):
      yield obj
    else:
      sub_layers = getattr(obj, 'layers', None) or []

      # Trackable data structures will not show up in ".layers" lists, but
      # the layers they contain will.
      to_visit.extend(sub_layers[::-1])


from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from bs4 import BeautifulSoup
import time
import json
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException
import re
# Set up the Selenium WebDriver
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

main_page_url = 'https://www.immobilier.ch/fr/louer/appartement-maison/geneve/geneve/page-1?t=rent&c=1;2&p=c10377&pn=8000&nb=false'

# Navigate to the main page
driver.get(main_page_url)

# Find an apartment listing in the "Location" category and click on it
apartment_links = driver.find_elements_by_css_selector('a[category="Location"]')
if apartment_links:
    apartment_links[0].click()  # Click on the first apartment in the list
    time.sleep(5)  # Wait for the apartment page to load
# URL of the individual apartment listing
url = 'https://www.immobilier.ch/fr/louer/appartement/geneve/geneve/nsr-group-1607/magnifique-appartement-vue-parc-bertrand-999481'

# Open the URL
driver.get(url)

# Wait for the dynamic content to load
time.sleep(5)  # Adjust this time based on your internet speed

# Get the page source and parse it
soup = BeautifulSoup(driver.page_source, 'html.parser')

# Extract details
address_elements = driver.find_elements(By.CLASS_NAME, 'object-address')
price_element = driver.find_element(By.CLASS_NAME, 'im__postDetails__price')
price_text = price_element.text
price_numeric = int(''.join(filter(str.isdigit, price_text)))

addresses = [element.text for element in address_elements]

match = re.search(r'(\d+)$', url)
if match:
    apartment_id = match.group(1)
else:
    apartment_id = None
apartment_id = apartment_id

# Construct the XPath for the "Read More" button using the extracted apartment ID
button_xpath = f'//a[contains(@href, "javascript:sDetail.readMore({apartment_id})")]'

# Wait for the "Read More" button to become clickable
try:
    button = WebDriverWait(driver, 10).until(
        EC.element_to_be_clickable((By.XPATH, button_xpath))
    )
    button.click()

    # Explicit wait for the table to load
    # You can adjust the timeout as needed

    # Find the specific list items using the refined CSS selector
    elements = WebDriverWait(driver, 10).until(
        EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".im__table.im__table--responsive.im__row .im__table__row.js-last-col .im__assets__title.im__assets__title--big"))
    )
except NoSuchElementException as e:
    print(f"Element not found: {e}")

# Explicit wait for the table to load
# You can adjust the timeout as needed

# Find the specific list items using the refined CSS selector
elements = driver.find_elements(By.CSS_SELECTOR, ".im__table.im__table--responsive.im__row .im__table__row.js-last-col .im__assets__title.im__assets__title--big")

# Get the textual content of the element without HTML tags
description_element = driver.find_element(By.CLASS_NAME, 'im__postContent__body')
description_text = description_element.text

# Create the apartment_data dictionary

# Convert the dictionary to a JSON string

# Print the JSON data

image_elements = driver.find_elements(By.CSS_SELECTOR, '.im__banner__slide img')

# URL to exclude
exclude_url = "https://www.immobilier.ch/Images/Loading/Detail.jpg?v=2"

# Extract URLs, ensuring uniqueness and excluding the specific URL
image_urls_set = set()
for img in image_elements:
    # Check if 'data-lazy' attribute exists
    lazy_load_url = img.get_attribute('data-lazy')
    if lazy_load_url:
        if lazy_load_url != exclude_url and lazy_load_url not in image_urls_set:
            image_urls_set.add(lazy_load_url)
    else:
        src_url = img.get_attribute('src')
        if src_url and src_url != exclude_url and src_url not in image_urls_set:
            image_urls_set.add(src_url)

# Convert the set to a list for further processing if necessary
image_urls = list(image_urls_set)

# Print or process the image URLs
apartment_details = {
    'price': price_numeric,
    'addresses': addresses,
    'description': description_text,
    'elements': [element.text for element in elements],
    'url': image_urls
}

# Nesting the apartment_details under 'apartment'
apartment_data = {'apartment': apartment_details}

# Convert the dictionary to a JSON string
json_data = json.dumps(apartment_data, indent=4)

# Print the JSON data
print(json_data)

# Write JSON data to a file named 'estate.json'
with open('estate.json', 'w', encoding='utf-8') as file:
    file.write(json_data)

print("JSON data has been saved to estate.json")

# Close the browser
driver.quit()


# -*- coding: utf-8 -*-
# This file is part of Viper - https://github.com/viper-framework/viper
# See the file 'LICENSE' for copying permission.

import os
from os.path import expanduser
import sys
import glob
import atexit
import logging
import readline
import traceback

from viper.common.out import print_error
# from viper.common.out import print_output  # currently not used
from viper.common.colors import cyan, magenta, white, bold, blue, red
from viper.common.version import __version__
from viper.core.session import __sessions__
from viper.core.plugins import __modules__
from viper.core.project import __project__, get_project_list
from viper.core.ui.commands import Commands
from viper.core.database import Database
from viper.core.config import __config__, console_output

log = logging.getLogger("viper")

cfg = __config__
cfg.parse_http_client()

def logo():
    print("""         _
        (_)
   _   _ _ ____  _____  ____
  | | | | |  _ \\| ___ |/ ___)
   \\ V /| | |_| | ____| |
    \\_/ |_|  __/|_____)_| v{}
          |_|
    """.format(__version__))

    db = Database()
    count = db.get_sample_count()

    try:
        db.find("all")
    except Exception:
        sys.exit()

    if __project__.name:
        name = __project__.name
    else:
        name = "default"

    print(magenta("You have " + bold(count)) +
          magenta(" files in your " + bold(name)) +
          magenta(" repository."))

    modules_count = len(__modules__)
    if modules_count == 0:
        print("")
        print(red(bold("You do not have any modules installed!")))
        print(red("If you wish to download community modules from GitHub run:"))
        print(red(bold("    update-modules")))
    else:
        print(magenta("You have " + bold(modules_count)) +
              magenta(" modules installed."))

class Console(object):

    def __init__(self):
        # This will keep the main loop active as long as it's set to True.
        self.active = True
        self.cmd = Commands()

    def parse(self, data):
        root = ""
        args = []

        # Split words by white space.
        words = data.split()
        # First word is the root command.
        root = words[0]

        # If there are more words, populate the arguments list.
        if len(words) > 1:
            args = words[1:]

        return (root, args)

    def keywords(self, data):
        # Check if $self is in the user input data.
        if "$self" in data:
            # Check if there is an open session.
            if __sessions__.is_set():
                # If a session is opened, replace $self with the path to
                # the file which is currently being analyzed.
                data = data.replace("$self", __sessions__.current.file.path)
            else:
                print("No open session")
                return None

        return data

    def stop(self):
        # Stop main loop.
        self.active = False

    def start(self):
        # log start
        log.info("Starting viper")

        # Logo.
        logo()

        # Setup shell auto-complete.
        def complete(text, state):
            # filesystem path completion only makes sense for a few commands/modules
            fs_path_completion = False

            # clean up user input so far (no leading/trailing/duplicate spaces)
            line = " ".join(readline.get_line_buffer().split())
            words = line.split(" ")  # split words; e.g. store -f /tmp -> ["store", "-f", "/tmp"]

            if words[0] in [i for i in self.cmd.commands]:
                # handle completion for commands

                # enable filesystem path completion for certain commands (e.g. export, store)
                if words[0] in [x for x in self.cmd.commands if self.cmd.commands[x]["fs_path_completion"]]:
                    fs_path_completion = True

                options = [key for key in self.cmd.commands[words[0]]["parser_args"]]

                # enable tab completion for projects --switch
                if words[0] == "projects":
                    if "--switch" in words or "-s" in words:
                        options += get_project_list()

                        # enable tab completion for copy (list projects)
                if words[0] == "copy":
                    options += get_project_list()

                completions = [i for i in options if i.startswith(text) and i not in words]

            elif words[0] in [i for i in __modules__]:
                # handle completion for modules
                if len(words) == 1:
                    # only the module name is give so far - present all args and the subparsers (if any)
                    options = [key for key in __modules__[words[0]]["parser_args"]]
                    options += [key for key in __modules__[words[0]]["subparser_args"]]

                elif len(words) == 2:
                    # 1 complete word and one either complete or incomplete that specifies the subparser or an arg
                    if words[1] in list(__modules__[words[0]]["parser_args"]):
                        # full arg for a module is given
                        options = [key for key in __modules__[words[0]]["parser_args"]]

                    elif words[1] in list(__modules__[words[0]]["subparser_args"]):
                        # subparser is specified - get all subparser args
                        options = [key for key in __modules__[words[0]]["subparser_args"][words[1]]]

                    else:
                        options = [key for key in __modules__[words[0]]["parser_args"]]
                        options += [key for key in __modules__[words[0]]["subparser_args"]]

                else:  # more that 2 words
                    if words[1] in list(__modules__[words[0]]["subparser_args"]):
                        # subparser is specified - get all subparser args
                        options = [key for key in __modules__[words[0]]["subparser_args"][words[1]]]
                    else:
                        options = [key for key in __modules__[words[0]]["parser_args"]]

                completions = [i for i in options if i.startswith(text) and i not in words]

            else:
                # initial completion for both commands and modules
                completions = [i for i in self.cmd.commands if i.startswith(text)]
                completions += [i for i in __modules__ if i.startswith(text)]

            if state < len(completions):
                return completions[state]

            if fs_path_completion:
                # completion for paths only if it makes sense
                if text.startswith("~"):
                    text = "{0}{1}".format(expanduser("~"), text[1:])
                return (glob.glob(text + "*") + [None])[state]

            return

        # Auto-complete on tabs.
        readline.set_completer_delims(" \t\n;")
        readline.parse_and_bind("tab: complete")
        readline.set_completer(complete)

        # Save commands in history file.
        def save_history(path):
            readline.write_history_file(path)

        # If there is an history file, read from it and load the history
        # so that they can be loaded in the shell.
        # Now we are storing the history file in the local project folder
        history_path = os.path.join(__project__.path, "history")

        if os.path.exists(history_path):
            readline.read_history_file(history_path)

        readline.set_history_length(10000)

        # Register the save history at program"s exit.
        atexit.register(save_history, path=history_path)

        # Main loop.
        while self.active:
            # If there is an open session, we include the path to the opened
            # file in the shell prompt.
            # TODO: perhaps this block should be moved into the session so that
            # the generation of the prompt is done only when the session"s
            # status changes.
            prefix = ""
            if __project__.name:
                prefix = bold(cyan(__project__.name)) + " "

            if __sessions__.is_set():
                stored = ""
                filename = ""
                if __sessions__.current.file:
                    filename = __sessions__.current.file.name
                    if not Database().find(key="sha256", value=__sessions__.current.file.sha256):
                        stored = magenta(" [not stored]", True)

                misp = ""
                if __sessions__.current.misp_event:
                    misp = " [MISP"
                    if __sessions__.current.misp_event.event.id:
                        misp += " {}".format(__sessions__.current.misp_event.event.id)
                    else:
                        misp += " New Event"
                    if __sessions__.current.misp_event.off:
                        misp += " (Offline)"
                    misp += "]"

                prompt = (prefix + cyan("viper ", True) +
                          white(filename, True) + blue(misp, True) + stored + cyan(" > ", True))
            # Otherwise display the basic prompt.
            else:
                prompt = prefix + cyan("viper > ", True)

            # force str (Py3) / unicode (Py2) for prompt
            if sys.version_info <= (3, 0):
                prompt = prompt.encode("utf-8")
            else:
                prompt = str(prompt)

            # Wait for input from the user.
            try:
                data = input(prompt).strip()
            except KeyboardInterrupt:
                print("")
            # Terminate on EOF.
            except EOFError:
                self.stop()
                print("")
                continue
            # Parse the input if the user provided any.
            else:
                # If there are recognized keywords, we replace them with
                # their respective value.
                data = self.keywords(data)
                # Skip if the input is empty.
                if not data:
                    continue

                # Check for output redirection
                # If there is a > in the string, we assume the user wants to output to file.
                if ">" in data:
                    data, console_output["filename"] = data.split(">", 1)
                    if ";" in console_output["filename"]:
                        console_output["filename"], more_commands = console_output["filename"].split(";", 1)
                        data = "{};{}".format(data, more_commands)
                    print("Writing output to {0}".format(console_output["filename"].strip()))

                # If the input starts with an exclamation mark, we treat the
                # input as a bash command and execute it.
                # At this point the keywords should be replaced.
                if data.startswith("!"):
                    os.system(data[1:])
                    continue

                # Try to split commands by ; so that you can sequence multiple
                # commands at once.
                # For example:
                # viper > find name *.pdf; open --last 1; pdf id
                # This will automatically search for all PDF files, open the first entry
                # and run the pdf module against it.
                split_commands = data.split(";")
                for split_command in split_commands:
                    split_command = split_command.strip()
                    if not split_command:
                        continue

                    # If it's an internal command, we parse the input and split it
                    # between root command and arguments.
                    root, args = self.parse(split_command)

                    # Check if the command instructs to terminate.
                    if root in ("exit", "quit"):
                        self.stop()
                        continue

                    try:
                        # If the root command is part of the embedded commands list we
                        # execute it.
                        if root in self.cmd.commands:
                            self.cmd.commands[root]["obj"](*args)
                            del(self.cmd.output[:])
                        # If the root command is part of loaded modules, we initialize
                        # the module and execute it.
                        elif root in __modules__:
                            module = __modules__[root]["obj"]()
                            module.set_commandline(args)
                            module.run()

                            if cfg.modules.store_output and __sessions__.is_set():
                                try:
                                    Database().add_analysis(__sessions__.current.file.sha256, split_command, module.output)
                                except Exception:
                                    pass
                            del(module.output[:])
                        else:
                            print("Command not recognized.")
                    except KeyboardInterrupt:
                        pass
                    except Exception:
                        print_error("The command {0} raised an exception:".format(bold(root)))
                        traceback.print_exc()

                console_output["filename"] = None   # reset output to stdout


import unittest
from typing import List, Optional

import pandas as pd

from kbc_pul.data_structures.pandas_kb import PandasKnowledgeBaseWrapper

from kbc_pul.data_structures.rule_wrapper import RuleWrapper

from kbc_pul.rule_metrics.prediction_cache_rule_metrics.rule_ipw_and_ipw_pca_confidences_from_cached_predictions import  \
    calculate_rule_ipw_and_ipw_pca_confidences_from_df_cached_predictions
from kbc_pul.test.rule_wrapper_testing_utils import get_rule_wrapper_from_str_repr

from pylo.language.lp import (Clause as PyloClause, global_context as pylo_global_context)

class TestingRegularConfidenceMetrics(unittest.TestCase):
    def setUp(self):
        data: List[List[str]] = [
            ["'adam'", "livesin", "'paris'"],
            ["'adam'", "livesin", "'rome'"],
            ["'bob'", "livesin", "'zurich'"],

            ["'adam'", "wasbornin", "'paris'"],
            ["'carl'", "wasbornin", "'rome'"],

            ["'dennis'", "wasbornin", "'zurich'"]  # added to have a different PCA conf in both directions
        ]
        columns = ["Subject", "Rel", "Object"]
        self.df: pd.DataFrame = pd.DataFrame(data=data, columns=columns)

        rule_string: str = "wasbornin(X,Y) :- livesin(X,Y)"
        self.rule_wrapper: RuleWrapper = get_rule_wrapper_from_str_repr(rule_string)

        self.correct_relative_pu_confidence_uniased: float = 1 / 3
        self.correct_pca_pu_confidence_subject_to_object: float = 0.5
        self.correct_pca_pu_confidence_object_to_subject: float = 1/3

    def test_regular_confidence_metrics(self):
        kb_wrapper: PandasKnowledgeBaseWrapper = PandasKnowledgeBaseWrapper.create_from_full_data(
            df_full_data=self.df
        )
        df_prediction_cache: Optional[pd.DataFrame] = kb_wrapper.calculate_prediction_cache_for_rule(
            rule=self.rule_wrapper.rule)

        calculate_rule_ipw_and_ipw_pca_confidences_from_df_cached_predictions(
            rule_wrapper=self.rule_wrapper,
            df_cached_predictions=df_prediction_cache,
            pylo_context=pylo_global_context,
            propensity_score_controller=true_set_inclusion_prop_score_controller,
            verbose=False
        )

        # self.assertAlmostEqual(self.rule_wrapper.o_relative_pu_confidence_unbiased,
        #                        self.correct_relative_pu_confidence_uniased, places=4
        #                        )
        # self.assertAlmostEqual(self.rule_wrapper.o_relative_pu_confidence_pca_subject_to_object,
        #                        self.correct_pca_pu_confidence_subject_to_object, places=4)
        # self.assertAlmostEqual(self.rule_wrapper.o_relative_pu_confidence_pca_object_to_subject,
        #                        self.correct_pca_pu_confidence_object_to_subject, places=4)


import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
from tqdm import tqdm

def uar(userId, train_pt_df):
    l = [x for x in list(train_pt_df.loc[userId]) if x != 0]
    if len(l)==0:
        return 2.5
    else:
        return sum(l)/len(l)

def iar(itemId, train_pt_df):
    l = [x for x in list(train_pt_df[itemId]) if x != 0]
    if len(l)==0:
        return 2.5
    else:
        return sum(l)/len(l)

def func(train_pt_df):
    train_pt_df = train_pt_df.T

    user_average_rating = {userId:uar(userId, train_pt_df) for userId in train_pt_df.index}
    item_average_rating = {itemId:iar(itemId, train_pt_df) for itemId in train_pt_df.columns}

    git = {}
    for item in (train_pt_df.columns):
        l = train_pt_df[item].tolist()
        M = 3
        rp = [i for i in l if i >= M and i != 0]
        rq = [i for i in l if i < M and i != 0]
        left = 0
        right = 0
        for i in rp:
            left += i-M
        for i in rq:
            right += i-M
        git[item] = left - right

    gitd = {}
    for item in (train_pt_df.columns):
        if git[item] > 0:
            l = train_pt_df[item].tolist()
            M = 3
            rp = [i for i in l if i >= M and i != 0]
            if len(rp) == 0:
                gitd[item] = 0
            else:
                gitd[item] = sum(rp)/len(rp)
        elif git[item] == 0:
            gitd[item] = 3
        else:
            l = train_pt_df[item].tolist()
            M = 3
            rq = [i for i in l if i < M and i != 0]
            if len(rq) == 0:
                gitd[item] = 0
            else:
                gitd[item] = sum(rq)/len(rq)

    new_train_pt_df = train_pt_df

    for item in tqdm(train_pt_df.columns):
        for user in train_pt_df.index:
            if train_pt_df[item][user] == 0:
    #             r = [x for x in list(train_pt_df[item]) if x != 0]
    #             ri_bar = item_average_rating[item]
    #             r = [i-ri_bar for i in r]
    #             ru = user_average_rating[user] + (sum(r)/len(r))

    #             u = [x for x in list(train_pt_df.iloc[user-1]) if x != 0]
    #             ui_bar = user_average_rating[user]
    #             u = [i-ui_bar for i in u]
    #             ri = item_average_rating[item] + (sum(u)/len(u))
                one = user_average_rating[user]
                two = item_average_rating[item]
                three = gitd[item]
                new_train_pt_df[item][user] = three #math.sqrt(three)
    return new_train_pt_df.T

def predict_rating_IB(userId, itemId, train_pt_df, sm_df, K):
    try:
        # users_ratings = train_pt_df[userId]  # ratings of all items for user 'userId'
        items_similarities = sm_df[itemId]  # cosine similarities of item 'itemId' with all other items.
    except:
        return []

    # Consider only highest K item similarities. (Here, similarity of the same item will also not considered).
    d = dict(items_similarities) # {itemId : item_similarity}
    d_sorted = {k: v for k, v in sorted(d.items(), key=lambda item: item[1], reverse=True)}
    drop_rest_users = list(d_sorted.keys())
    drop_rest_users = drop_rest_users[K:]
    # users_ratings = users_ratings.drop(drop_rest_users)
    items_similarities = items_similarities.drop(drop_rest_users)

    # Not consider items whose ratings are NA.
    # drop_empty_ratings = users_ratings[users_ratings == 0].index
    # users_ratings = users_ratings.drop(drop_empty_ratings)
    # items_similarities = items_similarities.drop(drop_empty_ratings)

    return list(dict(items_similarities).keys())

# This function takes the training & testing dataframes and return the MAE.
def IB_MAE(userId, itemId, train_df, K):
    # pivot the training dataframe and Transpose it.
    train_pt_df = pd.pivot_table(train_df, values='Rating', index='userId', columns='itemId', aggfunc=np.sum).T
    # Replace the NA values with 0   (Note: I observed -> No user in the whole dataset have rated 0 to any movie)
    train_pt_df = train_pt_df.fillna(0)

    # train_pt_df = func(train_pt_df)

    # Calculate the cosine similarities of item-item.
    sm_df = pd.DataFrame(cosine_similarity(train_pt_df), index=train_pt_df.index, columns=train_pt_df.index)
    # Not consider the similarity of same item while predicting the rating. eg: similarity of item 1 with item 1.
    np.fill_diagonal(sm_df.values, 0)

    return predict_rating_IB(userId, itemId, train_pt_df, sm_df, K)


from telebot.handler_backends import State, StatesGroup
from telebot.storage import StateMemoryStorage
from keyboards.inline.inline_button import Inline
from keyboards.reply.reply_button import Reply
from telebot.types import  Message, CallbackQuery
from database.model_functions.user import UserMethods
from database.model_functions.hotel_photos import HotelPhotos_methods
from loader import bot
from re import search
from pprint import pprint
from .process_data import ProcessData
from api_parse.parse_api_and_unpack import API
from api_parse.api_utils import API_UTILS
from loader import logger

state_storage = StateMemoryStorage()
API_UTILS = API_UTILS()

class UserData(StatesGroup):
    city = State()
    date = State()
    adults_children = State()
    price = State()
    distance = State()
    photos = State()
    photos_amount = State()
    api_response = State()
    finalize_results = State()
@bot.message_handler(state="*", commands=["cancel"])
def cancel_state(message: Message) -> None:
    bot.send_message(message.from_user.id, "You can enter the command again.")
    bot.set_state(message.from_user.id, None, message.chat.id)

@bot.message_handler(state=UserData.city)
def city_get(message: Message) -> None:
    logger.info("City Function")
    city, chat_id, from_user_id = message.text, message.chat.id, message.from_user.id

    url, headers = API_UTILS.location_url, API_UTILS.headers
    query_string = {"q": city}
    location_response = API.request_to_api(url=url, headers=headers, query_string=query_string)
    city_data = API.unpack_locationv3_json(json_str=location_response.text, city_group=city)
    if city_data:
        markup = Inline().add_buttons(text=city_data["fullName"], callback=city_data["gaiaId"])
        bot.send_message(chat_id, "Please be a bit more precise!", reply_markup=markup)

    bot.set_state(from_user_id, UserData.date, chat_id)
    with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
        data["city_chosen"] = city

@bot.message_handler(state=UserData.date)
def get_date(message: Message) -> None:
    logger.info("Date Function")
    dates = message.text.split()
    check_date = ProcessData.check_dates_validity(dates=dates)
    if not check_date:
        bot.send_message(message.from_user.id, "Wrong format of date. Try again.")
        bot.set_state(message.from_user.id, UserData.date, message.chat.id)

    # with bot.retrieve_data(message.from_user.id, message.chat.id) as user_data:
    #     user_data["date"] = dates
    else:
        ProcessData.save_data(user_id=message.from_user.id, chat_id=message.chat.id, dict_key="date", data=check_date)
        bot.send_message(message.chat.id,
                         "Now, enter how many adults and children are needed."
                         "\nFormat: Adults Children\n"
                         "If there are more than two children please split the age by a comma.й\nExample 1: 2 5,6\n"
                         "Example 2:  2 (No children)")
        bot.set_state(message.from_user.id, UserData.adults_children, message.chat.id)
    # TODO add calendar

@bot.message_handler(state=UserData.adults_children)
def adults_children(message: Message) -> None:
    logger.info("Getting adults")
    adults_children_object = message.text.split()
    pattern = r'[A-Za-z]'
    find = search(pattern, message.text)
    if find:
        bot.send_message(message.from_user.id, "This parameter only takes digits and commas.\nTry again.")
        bot.set_state(message.from_user.id, UserData.adults_children, message.chat.id)
    else:
        if len(adults_children_object) == 1:
            adults = adults_children_object[0]
            children_object = list()
        else:
            adults = adults_children_object[0]
            children_object = adults_children_object[1].split(",")
        rooms_object = ProcessData.create_rooms_object(adults=adults, children_ages=children_object)
        with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
            command  = data["command"]
        if command == "/bestdeal":
            bot.set_state(message.from_user.id, UserData.price, message.chat.id)
            bot.send_message(message.from_user.id, "Please enter the minimum and maximum price of the hotel.\nFormat: Min Max(50 100)\nSend 0 for setting the price automatically.")
        else:
            markup = Inline().add_buttons(text=["Yes", "No"], callback=["photo_yes", "photo_no"])
            bot.send_message(message.from_user.id, "Would you like to upload photos?", reply_markup=markup)
            bot.set_state(message.from_user.id, UserData.photos, message.chat.id)
        ProcessData.save_data(user_id=message.from_user.id, chat_id=message.chat.id,
                              dict_key="adults_children", data=rooms_object)

@bot.message_handler(state=UserData.price)
def get_price(message: Message) -> None:

    logger.info("Getting price")
    pattern = r'[A-Za-z]'
    find = search(pattern, message.text)
    if find:
        bot.send_message(message.from_user.id,
                         "This parameter only takes digits.\nTry again.")
        bot.set_state(message.from_user.id, UserData.adults_children, message.chat.id)
    else:
        with bot.retrieve_data(message.from_user.id, message.chat.id) as data:

            if data["filter"] == "PRICE_HIGH_TO_LOW" and message.text == "0":
                min_price, max_price = 100, 1500
            elif data["command"] == "/bestdeal" and message.text == "0":
                min_price, max_price = 1, 500
            elif message.text == "0":
                min_price, max_price = 100, 150
            else:
                min_price, max_price = message.text.split()
        price_object = {"price": {"max": max_price, "min": min_price}}
        bot.send_message(chat_id=message.chat.id, text="Please enter the distance from center in miles.")
        bot.set_state(message.from_user.id, UserData.distance, message.chat.id)
        ProcessData.save_data(user_id=message.from_user.id, chat_id=message.chat.id, dict_key="price", data=price_object)

@bot.message_handler(state=UserData.distance)
def get_distance(message: Message) -> None:
    with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
        data["filter"] = f"DISTANCE ({message.text})"

    markup = Inline().add_buttons(text=["Yes", "No"], callback=["photo_yes", "photo_no"])
    bot.send_message(message.from_user.id, "Would you like to upload photos?", reply_markup=markup)
@bot.message_handler(state=UserData.photos_amount)
def photos_amount(message: Message) -> None:
    logger.info("Photos Amount")
    if not message.text.isdigit():
        bot.send_message(message.from_user.id, "This parameter only takes digits.\nTry again.")
        bot.set_state(message.from_user.id, UserData.photos_amount, message.chat.id)
    else:
        with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
            data["photos_amount"] = int(message.text)

    markup = Reply.add_buttons(text="Start searching")
    bot.send_message(message.chat.id, text="Start?", reply_markup=markup)
    bot.set_state(message.from_user.id, UserData.api_response, message.chat.id)

@bot.message_handler(state=UserData.api_response)
def process_data(message: Message) -> None:
    logger.info("Processing data")
    url, headers = API_UTILS.properties_url, API_UTILS.headers
    headers["content-type"] = "application/json"
    with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
        photos_upload = data.get("photos")
        results_number = 500
        sort, city_name, command, date_of_command = data["filter"], data["city_chosen"], data["command"], data["date_of_command"]
        payload = ProcessData.finalize_payload(data=data, sort=sort, result_size=results_number)
        results = API.post_request(url=url, payload=payload, headers=headers)

    if results:
        hotels_founds = API.unpack_propertiesv2_json(response=results, results_amount=results_number,
                                                    city=city_name, photo_upload=photos_upload, user_id=message.from_user.id)
        if hotels_founds:
            UserMethods.create_record(user_id=message.from_user.id, user_name=message.from_user.username,
                               command=command, date_of_command=date_of_command, hotel_ids=hotels_founds)
            bot.send_message(message.chat.id, "The search has finished.")
        # if not detailed_results:
        #     bot.send_message(message.from_user.id, "Похоже, что у сервера сейчас проблема. Пожалуйста, напишите автору бота.")
    else:
        bot.send_message(message.from_user.id, "We were not able to find results for your parameters. || This message can happen due to server errors as well.")
    bot.set_state(message.from_user.id, None, message.chat.id)

""" BUTTON CALLBACKS"""
@bot.callback_query_handler(func=lambda call: call.data.isdigit())
def date_callback(call: CallbackQuery) -> None:

    logger.info("Extra city callback")
    if call.data.isdigit():
        with bot.retrieve_data(call.from_user.id, call.from_user.id) as data:
            data["city"] = call.data
        bot.send_message(call.from_user.id,
                         'Please enter the date of checking in and checking out to your hotel.\nFormat:  DD/MM/YY DD/MM/YY\nExample: 22/10/22 30/10/22')

@bot.callback_query_handler(func=lambda call: call.data == "photo_yes")
def photos_yes(call: CallbackQuery) -> None:
    logger.info("Photos Yes callback")
    photos = True
    bot.send_message(call.from_user.id,
                     "Please enter the amount of photos you would like to enter.\nMaximum 9 photos.")
    bot.set_state(call.from_user.id, UserData.photos_amount, call.from_user.id)
    with bot.retrieve_data(call.from_user.id, call.from_user.id) as data:
        data["photos"] = photos

@bot.callback_query_handler(func= lambda call: call.data == "photo_no")
def photos_no(call: CallbackQuery) -> None:
    logger.info("NO PHOTOS CALLBACK")
    photos = False
    markup = Reply.add_buttons(text="Start searching")
    bot.send_message(call.from_user.id, text="Start?", reply_markup=markup)
    bot.set_state(call.from_user.id, UserData.api_response, call.from_user.id)
    ProcessData.save_data(user_id=call.from_user.id, dict_key="photos_amount", chat_id=call.from_user.id, data=photos)

@bot.callback_query_handler(func= lambda call: call.data.startswith("show_photo"))
def show_extra_photos(call: CallbackQuery) -> None:
    logger.info("SHOWING EXTRA PHOTOS")
    property_id = call.data.split("_")[2]
    with bot.retrieve_data(call.from_user.id, call.from_user.id) as data:
        photos_amount = data["photos_amount"]

    photos = HotelPhotos_methods.select_photo_links(hotel_id=property_id, photo_amount=photos_amount)
    bot.send_media_group(call.from_user.id, photos)


#Anthony Guzman             CIS2348                 #1503239

#--Importing the required libraries-------

# import pandas as pd so we can leverage the pd function to read the csv files
import pandas as pd
pd.options.mode.chained_assignment = None  # default='warn'

import datetime

#-----------Import the data into their dataframes-------

# Used panda read csv() function to read the data from each file
# load the data from each of the  files into corresponding  DataFrames

#SInce the csv files don't have any headers, we had to define the headers
#Defined headers in names
ManufacturerList=pd.read_csv('ManufacturerList.csv ' ,header=None,names=["ID","Manufacturer Name","Item Type","Damaged"])

PriceList=pd.read_csv('PriceList.csv',header=None,names=["ID","Price"])

ServiceDatesList=pd.read_csv('ServiceDatesList.csv',header=None,names=["ID","Service Date"])

#------------  Part 1  ------------------------------
#--------------Processed Inventory Reports-----------

#------------Part a (Full Inventory)-----------------

#Merge the first two files together on base of ID
dataAll=pd.merge(ManufacturerList,PriceList,how='inner',left_on='ID',right_on='ID')

#Merge the previous file and last file together
FullInventory=pd.merge(dataAll,ServiceDatesList,how='inner',left_on='ID',right_on='ID')

#Rearrange the coloumns in the the order mentioned in the assignment
FullInventory=FullInventory[["ID","Manufacturer Name","Item Type","Price","Service Date","Damaged"]]
#Sort file alphabetically by manufacturer
FullInventory=FullInventory.sort_values('Manufacturer Name')

#Export the Full inventory to a CSV file
FullInventory.to_csv(r'FullInventory.csv',index = False,header=False)

#------Part b(Item Type Inventory)--------

#Obtain items that fall in laptop category
LaptopInventory=FullInventory.loc[FullInventory['Item Type'] == "laptop"]
#Obtain items that fall in phone category
PhoneInventory=FullInventory.loc[FullInventory['Item Type'] == "phone"]
#Obtain items that fall in tower category
TowerInventory=FullInventory.loc[FullInventory['Item Type'] == "tower"]

#Sort items according to their item ID
LaptopInventory=LaptopInventory.sort_values('ID')
PhoneInventory=PhoneInventory.sort_values('ID')
TowerInventory=TowerInventory.sort_values('ID')

LaptopInventory.to_csv(r'LaptopInventory.csv',index=False,header=False)
PhoneInventory.to_csv(r'PhoneInventory.csv',index=False,header=False)
TowerInventory.to_csv(r'TowerInventory.csv',index=False,header=False)

#----------Part c(Past Service Date Inventory)-------------

#Get the today's date
today=datetime.date.today()
today=pd.to_datetime('today')

#Converting the Dataset Service Date column to convert to DateTime to compare with today's date
FullInventory['Service Date']=pd.to_datetime(FullInventory['Service Date'])
#Checking the items that are past the service date on today
PastServiceDateInventory= FullInventory[FullInventory['Service Date']<today]

#Sort the items in the order of service date from oldest to most recent
PastServiceDateInventory=PastServiceDateInventory.sort_values('Service Date')

#Export the data to a PastServiceDateInventory.csv
PastServiceDateInventory.to_csv(r'PastServiceDateInventory.csv ',index=False,header=False)

#----Part d(Damaged Items Inventory)--------

#Check which items are damaged
Damaged=FullInventory.loc[FullInventory['Damaged'] == "damaged"]
#Just to del the damaged columns
del Damaged['Damaged']
#Sorted items in order of most expensive to least expensive
Damaged=Damaged.sort_values('Price',ascending=False)

#Export the Damaged products data to the DamagedInventory.csv
Damaged.to_csv(r'DamagedInventory.csv ',index=False,header=False)

#---------------  Part 2  -------------------------
#-------Interactive Inventory Query ---------------

choice='y'
while(choice!='q'):
    # prompt for user input for the manufacturer
    print('Enter the Manufacturer:')
    manufacturer = input()
    print('Enter the Item Type:')
    item_type = input()
    results=FullInventory.loc[(FullInventory['Manufacturer Name'] == manufacturer) & (FullInventory['Item Type'] == item_type)]
    # Move out items that have passed their service date
    results = results[results['Service Date'] > today]
    # MOve out items that are damaged
    results = results[results['Damaged'] != 'damaged']
    del results['Damaged']
    del results['Service Date']
    if results.empty:
    # If there is no item ony user defined item type show an error message
        print('No such Item in Inventory')
    else:
        # Show the item type which has max price
        print('Your item is:')
        results=results.to_string(index=False)

        print(results)

    #Ask the user if he wants to search again
    print('Do you want to search again (q for quit and y for yes)?')
    choice=input()[0]
    #Check if the user input is valid or not
    while(choice!='q' and choice !='y'):
        print('Invalid Input')
        print('Do you want to search again (q for quit and y for yes)?')
        choice=input()[0]


from django.shortcuts import render
import datetime
# Create your views here.

def calc(request):
    if request.method == "POST":
        hidden_val = request.POST["val"]
        period = request.POST["period"]
        rate = request.POST["rate"]
        interest_amount = request.POST["interest_amount"]
        investment_amount = request.POST["investment_amount"]
        maturity_amount = request.POST["maturity_amount"]
        due_date = request.POST["due_date"]
        investment_date = request.POST["investment_date"]
        date_format = "%Y-%m-%d"

        if investment_date and due_date and not period:
            due_date = datetime.datetime.strptime(due_date, date_format)
            investment_date = datetime.datetime.strptime(investment_date, date_format)
            period = abs((due_date-investment_date).days)

        elif investment_date and period and not due_date:
            investment_date = datetime.datetime.strptime(investment_date, date_format)
            period = datetime.timedelta(days=int(period))
            due_date = investment_date + period
            period = period.days

        elif due_date and period and not investment_date:
            due_date = datetime.datetime.strptime(due_date, date_format)
            period = datetime.timedelta(days=int(period))
            investment_date = due_date - period
            period = period.days

        elif investment_date and due_date and period:
            investment_date = datetime.datetime.strptime(investment_date, date_format)
            period = datetime.timedelta(days=int(period))
            due_date = investment_date + period
            period = period.days

        if hidden_val =="1":
            # interest_amount = (float(investment_amount)*float(rate)*float(period))/36500
            # maturity_amount = float(investment_amount)+float(interest_amount)
            try:
                if rate == "" :

                    t = (float(investment_amount))*int(period)
                    rate = (float(interest_amount)*36500)/t
                else:
                    interest_amount1 = (float(investment_amount)*float(rate)*float(period))/36500
                    maturity_amount1 = float(investment_amount)+float(interest_amount1)
                    interest_amount = (float(maturity_amount1)*float(rate)*float(period))/36500
                    maturity_amount =  float(investment_amount) + float(interest_amount)
            except Exception as msg:
                print("error", msg)

        if hidden_val == "2":
            # investment_amount = (float(maturity_amount)*36500)/(float(rate)*float(period)+36500)
            # interest_amount = (float(investment_amount)*float(rate)*float(period))/36500
            try:
                    a = float(rate)**2*float(period)**2+36500*float(rate)*float(period)+36500**2
                    investment_amount = (float(maturity_amount)*36500*36500)/a
                    interest_amount = float(maturity_amount)-float(investment_amount)
            except Exception as msg:
                print(msg)

        context = {"investment_amount":investment_amount, "maturity_amount":maturity_amount, "interest_amount":interest_amount, "period":period, "rate":rate, "due_date":due_date, "investment_date":investment_date}

    elif request.method== "GET":
        maturity_amount = 0.0
        interest_amount = 0.0
        rate = 0.0
        period = 0.0

        context = {"maturity_amount":maturity_amount, "interest_amount":interest_amount}
    return render(request, 'base.html', context)

def calc1(request):
    if request.method == "POST":
        period = request.POST["period"]
        rate = request.POST["rate"]
        investment_amount = request.POST["investment_amount"]
        interest_amount1 = (float(investment_amount)*float(rate)*float(period))/36500
        maturity_amount1 = float(investment_amount)+float(interest_amount1)
        interest_amount = (float(maturity_amount1)*float(rate)*float(period))/36500
        maturity_amount =  float(investment_amount) + float(interest_amount)
        context = {"investment_amount1":investment_amount, "maturity_amount1":maturity_amount, "interest_amount1":interest_amount, "period1":period, "rate1":rate}
    else:
        context = {}
    return render(request,'base.html', context )