diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..b4d2f71 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..430fbd2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.idea +/.vscode +/env \ No newline at end of file diff --git a/Capture.png b/Capture.png new file mode 100644 index 0000000..668a6dd Binary files /dev/null and b/Capture.png differ diff --git a/Class 12.2 _ 989878474.ipynb b/Class 12.2 _ 989878474.ipynb new file mode 100644 index 0000000..fe71182 --- /dev/null +++ b/Class 12.2 _ 989878474.ipynb @@ -0,0 +1,21 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "collapsed": false + }, + "outputs": [ + ], + "source": [ + ] + } + ], + "metadata": { + "kernelspec": { + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/Engine.pl b/Engine.pl new file mode 100644 index 0000000..bdaba9c --- /dev/null +++ b/Engine.pl @@ -0,0 +1,48 @@ +KB = """ +% Tell prolog that known/3 will be added later by asserta +:- dynamic known/3. + +% Enter your KB below this line: + +problem(battery) :- \+engine(turning_over), battery(bad). +problem(notbattery) :- \+engine(turning_over), battery(bad). +battery(bad) :- lights(weak). +battery(bad) :- radio(weak). +problem(out_of_gas) :- engine(turning_over), gas_gauge(empty). +problem(engine_flooded) :- engine(turning_over), smell(gas). + +% The code below implements the prompting to ask the user: + +gas_gauge(X) :- ask(gas_gauge, X). +engine(X) :- ask(engine, X). +lights(X) :- ask(lights, X). +radio(X) :- ask(radio, X). +smell(X) :- ask(smell, X). + + +% Asking clauses +multivalued(none). % We don't have any multivalued attributes + +ask(A, V):- +known(yes, A, V), % succeed if true +!. % stop looking + +ask(A, V):- +known(_, A, V), % fail if false +!, fail. + +% If not multivalued, and already known, don't ask again for a different value. +ask(A, V):- +\+multivalued(A), +known(yes, A, V2), +V \== V2, +!. + +ask(A, V):- +read_py(A,V,Y), % get the answer +asserta(known(Y, A, V)), % remember it +Y == yes. % succeed or fail + +ask(A, V) + +""" \ No newline at end of file diff --git a/code.zip b/code.zip new file mode 100644 index 0000000..12ab63d Binary files /dev/null and b/code.zip differ diff --git a/code/fullcode.py b/code/fullcode.py new file mode 100644 index 0000000..c8df0df --- /dev/null +++ b/code/fullcode.py @@ -0,0 +1,308 @@ +KB = ''' +%%KB + + +%RULES + + +%rules for I HAVE SYMPTOMS + +answer('You need to contact Geo Blue') :- intention('I am currently experiencing COVID symptoms, I want to know what to do'), \+reside(res), insured('GeoBlue'). +answer('You need to contact your local provider') :- intention('I am currently experiencing COVID symptoms, I want to know what to do'), \+reside(res), \+insured('GeoBlue'). + + +answer('You need to test at City Test SF') :- intention('I am currently experiencing COVID symptoms, I want to know what to do'), reside(res), res_hall(turk). +answer('You need to test at chinatown') :- intention('I am currently experiencing COVID symptoms, I want to know what to do'), reside(res), res_hall('851'). + + + +%rules for I HAVE TRAVELLED/ WILL BE TRAVELLING + +answer('Visit the City Test SF test centera') :- + intention('I want to travel around'), result(urgent), res_hall(turk). +answer('Visit the glide test center') :- + intention('I want to travel around'), result(urgent), res_hall(turk). +answer('Visit the chinatown test center') :- + intention('I want to travel around'), result(urgent), res_hall('851'). +answer('Test privately') :- + intention('I want to travel around'), \+result(urgent), testing(at_home). +answer('Visit the glide test center') :- + intention('I want to travel around'), \+result(urgent), testing(at_testing_center), reside(res), res_hall(turk). +answer('Visit the carbon health market test center') :- + intention('I want to travel around'), \+result(urgent), testing(at_testing_center), reside(res), res_hall(turk). +answer('Visit the carbon health market test center') :- + intention('I want to travel around'), \+result(urgent), testing(at_testing_center), reside(res), res_hall('851'). +answer('Visit the carbon health market test center'):- + intention('I want to travel around'), \+result(urgent), testing(at_testing_center), reside(res), res_hall('851'). + + + +%rules for HAVE A CLOSE CONTACT WITH COVID + +answer('You should start quarantine immediately'):- + intention('I had a close contact with a COVID patient. What next?'), exposure(known), \+quarantine_status(qurantine). +answer('You should continue your quarantine'):- + intention('I had a close contact with a COVID patient. What next?'), exposure(known), quarantine_status(quarantine). + +answer('Visit City Test SF'):- + intention('I had a close contact with a COVID patient. What next?'), \+exposure(known), \+symptoms(felt). +answer('Visit the local provider'):- + intention('I had a close contact with a COVID patient. What next?'), \+exposure(known), symptoms(felt), \+reside(res), \+insured('GeoBlue'). +answer('Visit Geo Blue'):- + intention('I had a close contact with a COVID patient. What next?'), \+exposure(known), symptoms(felt), \+reside(res),\+insured('GeoBlue'). +answer('Visit City Test SF'):- + intention('I had a close contact with a COVID patient. What next?'), \+exposure(known), symptoms(felt), reside(res), res_hall(turk). +answer('Visit chinatown center'):- + intention('I had a close contact with a COVID patient. What next?'), \+exposure(known), symptoms(felt), reside(res), res_hall('851'). + + +% rules for A CASUAL CHECK +%The 4 lines below I am not sure, as there are two options glide and carbon for given askables +answer('Visit the glide test center') :- + intention('I just wanted to know about some casual stuff'), testing(at_testing_center), res_hall(turk). +answer('Visit the carbon health market test center') :- + intention('I just wanted to know about some casual stuff'), testing(at_testing_center), res_hall(turk). +answer('Visit the Concentera Downtown Center') :- + intention('I just wanted to know about some casual stuff'), testing(at_testing_center), res_hall('851'). +answer('Visit the carbon health fin test center') :- + intention('I just wanted to know about some casual stuff'), testing(at_testing_center), res_hall('851'). +answer('Test privately') :- + intention('I just wanted to know about some casual stuff'), testing(at_home). + + + +%FACTS +reside(X) :- + ask('Do you live in the', X). + +res_hall(X) :- + menuask('Which res hall do you reside in?', X, [turk, '851']). + +testing(X) :- + menuask('Where do you want to test?', X, [at_home, at_testing_center]). + + + +exposure(X) :- + ask('Is your source of exposure', X). + +result(X) :- ask('Is your need for testing', X). + +insured(X) :- ask('Are you insured by', X). + + +intention(X) :- + menuask('What is your intention for taking the COVID test?', X, ['I am currently experiencing COVID symptoms, I want to know what to do', 'I want to travel around', 'I had a close contact with a COVID patient. What next?', 'I just wanted to know about some casual stuff']). + + +quarantine_status(X) :- + ask('Are you currently in', X). + + +symptoms(X) :- + ask('Are your symptoms', X). + + +% Asking clauses +multivalued(none). % We don't have any multivalued attributes + +ask(A, V):- +known(yes, A, V), % succeed if true + +!. % stop looking + +ask(A, V):- +known(_, A, V), % fail if false +!, fail. + +% If not multivalued, and already known, don't ask again for a different value. +ask(A, V):- +\+multivalued(A), +known(yes, A, V2), +V \== V2, +!. + +ask(A, V):- +read_py(A,V,Y), % get the answer +asserta(known(Y, A, V)), % remember it +write_py(known(Y, A, V)), +Y == yes. % succeed or fail + + +menuask(A, V, _):- +known(yes, A, V), % succeed if true +!. % stop looking + +menuask(A, V, _):- +known(yes, A, V2), % If already known, don't ask again for a different value. +V \== V2, +!, +fail. + +menuask(A, V, MenuList) :- + read_py_menu(A, X, MenuList), + check_val(X, A, V, MenuList), + asserta( known(yes, A, X) ), + X == V. +check_val(X, _, _, MenuList) :- + member(X, MenuList), + !. +check_val(X, A, V, MenuList) :- + write_py(X), write_py(' is not a legal value, try again.'), nl, + menuask(A, V, MenuList). +''' + + +# The code here will ask the user for input based on the askables +# It will check if the answer is known first + +import tempfile +import os +import pylcs +import numpy as np +import requests + +# Check if pyswip package needs to be installed + + +from pyswip.prolog import Prolog +from pyswip.easy import * + + +prolog = Prolog() # Global handle to interpreter + +retractall = Functor("retractall") +known = Functor("known",3) + +#Define foreign functions for getting user input and writing to the screen + + +def write_py(X): + """Prints the input X to the console and returns True to Prolog""" + print(f'\t{X}') + return True + + +def read_py(A: Atom, V: Atom, Y: Variable) -> bool: + """ + Asks a question to the user and sends the response to Prolog. + It is used to get Yes, No questions. + Yes is normally evaluated by Prolog as True and any other input as False. + + :param A: The question (askable) that the usere should be prompted with. + :param V: The value that the user needs to agree (yes) or disagree (any other input) with. + :param Y: The value that Prolog will match with as True. Normally it's 'Yes'. + + :returns True if Y is a Prolog Variable and False otherwise. + """ + if isinstance(Y, Variable): + response = input(str(A) + " " + str(V) + "?\n--> ") + Y.unify(response) + return True + else: + return False + +def read_py_menu(A: Atom, Y: Variable, MenuList: list) -> bool: + """ + Asks the user for input based on a menu. Choosing the index of the option + as well as the exact text of the option would work. When the response + has the best LCS match with an option above 10% it is the default response. + + :param A: The prompt (askable) the user needs to answer. + :param V: The answer that the user would let Prolog know about. + :param MenuList: The options that the user has to choose from. + + :returns True if Y is a Prolog variable and False otherwise. + """ + if isinstance(Y, Variable): + print(A) + list_for_lcs = [] + for i, x in enumerate(MenuList): + print(i, ". " ,x) + list_for_lcs.append(str(x)) + + response = get_menu_input(MenuList, list_for_lcs) + print('\tYou chose', response) + Y.unify(response) + return True + else: + return False + + +def get_menu_input(MenuList: list, lst_lcs: list) -> str: + """ + Carries out the logic of identifying the choice of the user from the menu. + A user can either choose a number or write some text. + If they choose a number it has to be a valid number amoung the count of options given. + If they provide a string it has to have the best match (among the options) be more than 10%. + + :param MenuList: The options that the user needs to choose from. They are stored as Prolog Atoms. + :param lst_lcs: The options stored in a list of strings that Python can interprate. + + :returns a string of the option that the user chose. + """ + from_user = input('--> ') + response_int = float('inf') + try: + response_int = int(from_user) + response = str(MenuList[response_int]) + except: + response = from_user.lower() + response = most_appropriate(response, lst_lcs) + return response + +def most_appropriate(response: str, lst_lcs: list) -> str: + """ + Choose the most appropriate option given the response string. + It matches the choice by finding out the percentage LCS (Least Common Subsequence match). + If the best match is above 30%, it is taken as the choice of the user. + Otherwise the user will be reprompted for another option. + + :parm response: The input from the user. + :param lst_lcs: The options stored in a list of strings that Python can interprate. + + :returns a string of the option that the user chose. + """ + lcs = pylcs.lcs_of_list(response, lst_lcs) + + lengths = [] + for option in lst_lcs: + lengths.append(len(option)) + + # Calculate the percentage match of the LCS + similarities = np.array(lcs)/np.array(lengths) + + # Identify the index of the highest match + option_idx = np.argmax(similarities) + + # If the highest match is less than 10% return original response + # so that the user can be reprompted + # otherwise return the best option + if similarities[option_idx] < 0.1: + return response + return lst_lcs[option_idx] + +write_py.arity = 1 +read_py.arity = 3 + +read_py_menu.arity = 3 + +registerForeign(read_py) +registerForeign(write_py) +registerForeign(read_py_menu) + +# Create a temporary file with the KB in it +(FD, name) = tempfile.mkstemp(suffix='.pl', text = "True") +with os.fdopen(FD, "w") as text_file: + text_file.write(KB) +prolog.consult(name) # open the KB for consulting +os.unlink(name) # Remove the temporary file + +call(retractall(known)) + +#Using covidtracking API to give uptodate information +response = requests.get("https://api.covidtracking.com/v1/states/ca/current.json") +print("Here is a quick update on COVID situation in San Francisco: \nThe death toll as of",response.json()['checkTimeEt'], "is", response.json()['death']) +print("Today, the number of cases increased by",response.json()['positiveIncrease'],",while death cases increased by",response.json()['deathIncrease'],"\n") +answer = [s for s in prolog.query("answer(X).", maxresult=1)] +print((answer[0]['X'] +"." if answer else "unknown.")) \ No newline at end of file diff --git a/code/requirements.txt b/code/requirements.txt new file mode 100644 index 0000000..55a47e4 --- /dev/null +++ b/code/requirements.txt @@ -0,0 +1,4 @@ +pylcs == 0.0.6 +requests +pyswip +numpy \ No newline at end of file diff --git a/fullcode.py b/fullcode.py new file mode 100644 index 0000000..c8df0df --- /dev/null +++ b/fullcode.py @@ -0,0 +1,308 @@ +KB = ''' +%%KB + + +%RULES + + +%rules for I HAVE SYMPTOMS + +answer('You need to contact Geo Blue') :- intention('I am currently experiencing COVID symptoms, I want to know what to do'), \+reside(res), insured('GeoBlue'). +answer('You need to contact your local provider') :- intention('I am currently experiencing COVID symptoms, I want to know what to do'), \+reside(res), \+insured('GeoBlue'). + + +answer('You need to test at City Test SF') :- intention('I am currently experiencing COVID symptoms, I want to know what to do'), reside(res), res_hall(turk). +answer('You need to test at chinatown') :- intention('I am currently experiencing COVID symptoms, I want to know what to do'), reside(res), res_hall('851'). + + + +%rules for I HAVE TRAVELLED/ WILL BE TRAVELLING + +answer('Visit the City Test SF test centera') :- + intention('I want to travel around'), result(urgent), res_hall(turk). +answer('Visit the glide test center') :- + intention('I want to travel around'), result(urgent), res_hall(turk). +answer('Visit the chinatown test center') :- + intention('I want to travel around'), result(urgent), res_hall('851'). +answer('Test privately') :- + intention('I want to travel around'), \+result(urgent), testing(at_home). +answer('Visit the glide test center') :- + intention('I want to travel around'), \+result(urgent), testing(at_testing_center), reside(res), res_hall(turk). +answer('Visit the carbon health market test center') :- + intention('I want to travel around'), \+result(urgent), testing(at_testing_center), reside(res), res_hall(turk). +answer('Visit the carbon health market test center') :- + intention('I want to travel around'), \+result(urgent), testing(at_testing_center), reside(res), res_hall('851'). +answer('Visit the carbon health market test center'):- + intention('I want to travel around'), \+result(urgent), testing(at_testing_center), reside(res), res_hall('851'). + + + +%rules for HAVE A CLOSE CONTACT WITH COVID + +answer('You should start quarantine immediately'):- + intention('I had a close contact with a COVID patient. What next?'), exposure(known), \+quarantine_status(qurantine). +answer('You should continue your quarantine'):- + intention('I had a close contact with a COVID patient. What next?'), exposure(known), quarantine_status(quarantine). + +answer('Visit City Test SF'):- + intention('I had a close contact with a COVID patient. What next?'), \+exposure(known), \+symptoms(felt). +answer('Visit the local provider'):- + intention('I had a close contact with a COVID patient. What next?'), \+exposure(known), symptoms(felt), \+reside(res), \+insured('GeoBlue'). +answer('Visit Geo Blue'):- + intention('I had a close contact with a COVID patient. What next?'), \+exposure(known), symptoms(felt), \+reside(res),\+insured('GeoBlue'). +answer('Visit City Test SF'):- + intention('I had a close contact with a COVID patient. What next?'), \+exposure(known), symptoms(felt), reside(res), res_hall(turk). +answer('Visit chinatown center'):- + intention('I had a close contact with a COVID patient. What next?'), \+exposure(known), symptoms(felt), reside(res), res_hall('851'). + + +% rules for A CASUAL CHECK +%The 4 lines below I am not sure, as there are two options glide and carbon for given askables +answer('Visit the glide test center') :- + intention('I just wanted to know about some casual stuff'), testing(at_testing_center), res_hall(turk). +answer('Visit the carbon health market test center') :- + intention('I just wanted to know about some casual stuff'), testing(at_testing_center), res_hall(turk). +answer('Visit the Concentera Downtown Center') :- + intention('I just wanted to know about some casual stuff'), testing(at_testing_center), res_hall('851'). +answer('Visit the carbon health fin test center') :- + intention('I just wanted to know about some casual stuff'), testing(at_testing_center), res_hall('851'). +answer('Test privately') :- + intention('I just wanted to know about some casual stuff'), testing(at_home). + + + +%FACTS +reside(X) :- + ask('Do you live in the', X). + +res_hall(X) :- + menuask('Which res hall do you reside in?', X, [turk, '851']). + +testing(X) :- + menuask('Where do you want to test?', X, [at_home, at_testing_center]). + + + +exposure(X) :- + ask('Is your source of exposure', X). + +result(X) :- ask('Is your need for testing', X). + +insured(X) :- ask('Are you insured by', X). + + +intention(X) :- + menuask('What is your intention for taking the COVID test?', X, ['I am currently experiencing COVID symptoms, I want to know what to do', 'I want to travel around', 'I had a close contact with a COVID patient. What next?', 'I just wanted to know about some casual stuff']). + + +quarantine_status(X) :- + ask('Are you currently in', X). + + +symptoms(X) :- + ask('Are your symptoms', X). + + +% Asking clauses +multivalued(none). % We don't have any multivalued attributes + +ask(A, V):- +known(yes, A, V), % succeed if true + +!. % stop looking + +ask(A, V):- +known(_, A, V), % fail if false +!, fail. + +% If not multivalued, and already known, don't ask again for a different value. +ask(A, V):- +\+multivalued(A), +known(yes, A, V2), +V \== V2, +!. + +ask(A, V):- +read_py(A,V,Y), % get the answer +asserta(known(Y, A, V)), % remember it +write_py(known(Y, A, V)), +Y == yes. % succeed or fail + + +menuask(A, V, _):- +known(yes, A, V), % succeed if true +!. % stop looking + +menuask(A, V, _):- +known(yes, A, V2), % If already known, don't ask again for a different value. +V \== V2, +!, +fail. + +menuask(A, V, MenuList) :- + read_py_menu(A, X, MenuList), + check_val(X, A, V, MenuList), + asserta( known(yes, A, X) ), + X == V. +check_val(X, _, _, MenuList) :- + member(X, MenuList), + !. +check_val(X, A, V, MenuList) :- + write_py(X), write_py(' is not a legal value, try again.'), nl, + menuask(A, V, MenuList). +''' + + +# The code here will ask the user for input based on the askables +# It will check if the answer is known first + +import tempfile +import os +import pylcs +import numpy as np +import requests + +# Check if pyswip package needs to be installed + + +from pyswip.prolog import Prolog +from pyswip.easy import * + + +prolog = Prolog() # Global handle to interpreter + +retractall = Functor("retractall") +known = Functor("known",3) + +#Define foreign functions for getting user input and writing to the screen + + +def write_py(X): + """Prints the input X to the console and returns True to Prolog""" + print(f'\t{X}') + return True + + +def read_py(A: Atom, V: Atom, Y: Variable) -> bool: + """ + Asks a question to the user and sends the response to Prolog. + It is used to get Yes, No questions. + Yes is normally evaluated by Prolog as True and any other input as False. + + :param A: The question (askable) that the usere should be prompted with. + :param V: The value that the user needs to agree (yes) or disagree (any other input) with. + :param Y: The value that Prolog will match with as True. Normally it's 'Yes'. + + :returns True if Y is a Prolog Variable and False otherwise. + """ + if isinstance(Y, Variable): + response = input(str(A) + " " + str(V) + "?\n--> ") + Y.unify(response) + return True + else: + return False + +def read_py_menu(A: Atom, Y: Variable, MenuList: list) -> bool: + """ + Asks the user for input based on a menu. Choosing the index of the option + as well as the exact text of the option would work. When the response + has the best LCS match with an option above 10% it is the default response. + + :param A: The prompt (askable) the user needs to answer. + :param V: The answer that the user would let Prolog know about. + :param MenuList: The options that the user has to choose from. + + :returns True if Y is a Prolog variable and False otherwise. + """ + if isinstance(Y, Variable): + print(A) + list_for_lcs = [] + for i, x in enumerate(MenuList): + print(i, ". " ,x) + list_for_lcs.append(str(x)) + + response = get_menu_input(MenuList, list_for_lcs) + print('\tYou chose', response) + Y.unify(response) + return True + else: + return False + + +def get_menu_input(MenuList: list, lst_lcs: list) -> str: + """ + Carries out the logic of identifying the choice of the user from the menu. + A user can either choose a number or write some text. + If they choose a number it has to be a valid number amoung the count of options given. + If they provide a string it has to have the best match (among the options) be more than 10%. + + :param MenuList: The options that the user needs to choose from. They are stored as Prolog Atoms. + :param lst_lcs: The options stored in a list of strings that Python can interprate. + + :returns a string of the option that the user chose. + """ + from_user = input('--> ') + response_int = float('inf') + try: + response_int = int(from_user) + response = str(MenuList[response_int]) + except: + response = from_user.lower() + response = most_appropriate(response, lst_lcs) + return response + +def most_appropriate(response: str, lst_lcs: list) -> str: + """ + Choose the most appropriate option given the response string. + It matches the choice by finding out the percentage LCS (Least Common Subsequence match). + If the best match is above 30%, it is taken as the choice of the user. + Otherwise the user will be reprompted for another option. + + :parm response: The input from the user. + :param lst_lcs: The options stored in a list of strings that Python can interprate. + + :returns a string of the option that the user chose. + """ + lcs = pylcs.lcs_of_list(response, lst_lcs) + + lengths = [] + for option in lst_lcs: + lengths.append(len(option)) + + # Calculate the percentage match of the LCS + similarities = np.array(lcs)/np.array(lengths) + + # Identify the index of the highest match + option_idx = np.argmax(similarities) + + # If the highest match is less than 10% return original response + # so that the user can be reprompted + # otherwise return the best option + if similarities[option_idx] < 0.1: + return response + return lst_lcs[option_idx] + +write_py.arity = 1 +read_py.arity = 3 + +read_py_menu.arity = 3 + +registerForeign(read_py) +registerForeign(write_py) +registerForeign(read_py_menu) + +# Create a temporary file with the KB in it +(FD, name) = tempfile.mkstemp(suffix='.pl', text = "True") +with os.fdopen(FD, "w") as text_file: + text_file.write(KB) +prolog.consult(name) # open the KB for consulting +os.unlink(name) # Remove the temporary file + +call(retractall(known)) + +#Using covidtracking API to give uptodate information +response = requests.get("https://api.covidtracking.com/v1/states/ca/current.json") +print("Here is a quick update on COVID situation in San Francisco: \nThe death toll as of",response.json()['checkTimeEt'], "is", response.json()['death']) +print("Today, the number of cases increased by",response.json()['positiveIncrease'],",while death cases increased by",response.json()['deathIncrease'],"\n") +answer = [s for s in prolog.query("answer(X).", maxresult=1)] +print((answer[0]['X'] +"." if answer else "unknown.")) \ No newline at end of file diff --git a/gui_code.py b/gui_code.py new file mode 100644 index 0000000..e932298 --- /dev/null +++ b/gui_code.py @@ -0,0 +1,107 @@ +import time +from tkinter import * +import tkinter.messagebox +from itertools import chain, repeat +import requests + +window_size = "550x450" + + +def chat(user_response): + problem = [s for s in prolog.query("answer(X).", maxresult=1)] + return((problem[0]['X'] + "." if problem else "unknown.")) + +# showing the user interface + + +class chat_UI(Frame): + + def __init__(self, master=None): + Frame.__init__(self, master) + self.master = master + + # attributes for colors + self.tl_bg = "#EEEEEE" + self.tl_bg2 = "#EEEEEE" + self.tl_fg = "black" + self.font = "Verdana 10" + + menu = Menu(self.master) + + self.text_frame = Frame(self.master, bd=6) + self.text_frame.pack(expand=True, fill=BOTH) + + # adding scroll bar + self.text_box_scrollbar = Scrollbar(self.text_frame, bd=0) + self.text_box_scrollbar.pack(fill=Y, side=RIGHT) + + # Adding text box to show the conversation + + self.text_box = Text(self.text_frame, yscrollcommand=self.text_box_scrollbar.set, state=DISABLED, + bd=1, padx=6, pady=6, spacing3=8, wrap=WORD, bg=None, font="Verdana 10", relief=GROOVE, + width=10, height=1) + self.text_box.pack(expand=True, fill=BOTH) + self.text_box_scrollbar.config(command=self.text_box.yview) + + # Adding a frame that wraps user entry + self.entry_frame = Frame(self.master, bd=1) + self.entry_frame.pack(side=LEFT, fill=BOTH, expand=True) + + # Adding a box that takes user input + self.entry_field = Entry(self.entry_frame, bd=1, justify=LEFT) + self.entry_field.pack(fill=X, padx=6, pady=6, ipady=3) + # self.users_message = self.entry_field.get() + + # Frame containing send button + self.send_button_frame = Frame(self.master, bd=0) + self.send_button_frame.pack(fill=BOTH) + + # Send button + self.send_button = Button(self.send_button_frame, text="Send", width=5, relief=GROOVE, bg='white', + bd=1, command=lambda: self.send_message_insert(None), activebackground="#FFFFFF", + activeforeground="#000000") + self.send_button.pack(side=LEFT, ipady=8) + self.master.bind("", self.send_message_insert) + + self.last_sent_label(date="No messages sent.") + + # to show the time/date of last seen message + + def last_sent_label(self, date): + + try: + self.sent_label.destroy() + except AttributeError: + pass + + self.sent_label = Label( + self.entry_frame, font="Verdana 7", text=date, bg=self.tl_bg2, fg=self.tl_fg) + self.sent_label.pack(side=LEFT, fill=X, padx=3) + + # facilitating the input and output + + def send_message_insert(self, message): + user_input = self.entry_field.get() # get input from user + pr1 = "You : " + user_input + "\n" + self.text_box.configure(state=NORMAL) + self.text_box.insert(END, pr1) + self.text_box.configure(state=DISABLED) + self.text_box.see(END) + ob = chat(user_input) # show response to ser + pr = "Covid Buddy : " + ob + "\n" + self.text_box.configure(state=NORMAL) + self.text_box.insert(END, pr) + self.text_box.configure(state=DISABLED) + self.text_box.see(END) + self.last_sent_label( + str(time.strftime("Last message sent: " + '%B %d, %Y' + ' at ' + '%I:%M %p'))) + self.entry_field.delete(0, END) + time.sleep(0) + + +root = Tk() + +a = chat_UI(root) +root.geometry(window_size) +root.title("COVID Buddy") +root.mainloop() diff --git a/kb.pl b/kb.pl new file mode 100644 index 0000000..c96055e --- /dev/null +++ b/kb.pl @@ -0,0 +1,7 @@ +telldisease(malaria). + +telldisease(malaria,sweating,sweating,sweating,sweating). + +telldisease(fever,X,sweating,sweating,sweating). + +telldisease(fever,X,sweating,sweating,sweating). \ No newline at end of file diff --git a/lcs_code.py b/lcs_code.py new file mode 100644 index 0000000..0d221b3 --- /dev/null +++ b/lcs_code.py @@ -0,0 +1,30 @@ +def most_appropriate(response: str, lst_lcs: list) -> str: + """ + Choose the most appropriate option given the response string. + It matches the choice by finding out the percentage LCS (Least Common Subsequence match). + If the best match is above 30%, it is taken as the choice of the user. + Otherwise the user will be reprompted for another option. + + :parm response: The input from the user. + :param lst_lcs: The options stored in a list of strings that Python can interprate. + + :returns a string of the option that the user chose. + """ + lcs = pylcs.lcs_of_list(response, lst_lcs) + + lengths = [] + for option in lst_lcs: + lengths.append(len(option)) + + # Calculate the percentage match of the LCS + similarities = np.array(lcs)/np.array(lengths) + + # Identify the index of the highest match + option_idx = np.argmax(similarities) + + # If the highest match is less than 10% return original response + # so that the user can be reprompted + # otherwise return the best option + if similarities[option_idx] < 0.1: + return response + return lst_lcs[option_idx] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..01b2fff --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pylcs == 0.0.6 +requests \ No newline at end of file diff --git a/script.py b/script.py new file mode 100644 index 0000000..d38a522 --- /dev/null +++ b/script.py @@ -0,0 +1,71 @@ +# The code here will ask the user for input based on the askables +# It will check if the answer is known first + +import os + +# Check if pyswip package needs to be installed + + +from pyswip.prolog import Prolog +from pyswip.easy import * + + +prolog = Prolog() # Global handle to interpreter + +retractall = Functor("retractall") +known = Functor("known",3) + +#Define foreign functions for getting user input and writing to the screen + + +def write_py(X): + print(X) + return True + + +def read_py(A,V,Y): + if isinstance(Y, Variable): + response = input(str(A) + " is " + str(V) + "? ") + Y.unify(response) + return True + else: + return False + +def read_py_menu(A, V, Y, MenuList): + if isinstance(Y, Variable): + print(A) + for i, x in enumerate(MenuList): + print(i, ". " ,x) + + response = get_menu_input(MenuList) + print('\tYou chose', response) + Y.unify(response) + return True + else: + return False + + +def get_menu_input(MenuList): + from_user = input() + response_int = -1 + try: + response_int = int(from_user) + response = str(MenuList[response_int]) + except: + response = from_user + return response + +write_py.arity = 1 +read_py.arity = 3 + +read_py_menu.arity = 4 + +registerForeign(read_py) +registerForeign(write_py) +registerForeign(read_py_menu) + +prolog.consult('KB.pl') # open the KB for consulting + +call(retractall(known)) +problem = [s for s in prolog.query("answer(X).", maxresult=1)] +print((problem[0]['X'] +"." if problem else "unknown.")) \ No newline at end of file diff --git a/stuff.py b/stuff.py new file mode 100644 index 0000000..62ba8bb --- /dev/null +++ b/stuff.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""Script for Tkinter GUI chat client.""" +import tkinter + + +def receive(msg="test"): + """Handles receiving of messages.""" + while True: + try: + msg_list.insert(tkinter.END, msg) + except OSError: # Possibly client has left the chat. + break + + +def send(event=None): # event is passed by binders. + """Handles sending of messages.""" + msg = my_msg.get() + my_msg.set("") # Clears input field. + if msg == "{quit}": + top.quit() + + +def on_closing(event=None): + """This function is to be called when the window is closed.""" + my_msg.set("{quit}") + send() + +top = tkinter.Tk() +top.title("Chatter") + +messages_frame = tkinter.Frame(top) +my_msg = tkinter.StringVar() # For the messages to be sent. +my_msg.set("Type your messages here.") +scrollbar = tkinter.Scrollbar(messages_frame) # To navigate through past messages. +# Following will contain the messages. +msg_list = tkinter.Listbox(messages_frame, height=15, width=50, yscrollcommand=scrollbar.set) +scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y) +msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH) +msg_list.pack() +messages_frame.pack() + +entry_field = tkinter.Entry(top, textvariable=my_msg) +entry_field.bind("", send) +entry_field.pack() +send_button = tkinter.Button(top, text="Send", command=send) +send_button.pack() + +top.protocol("WM_DELETE_WINDOW", on_closing) + +tkinter.mainloop() # Starts GUI execution. \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..08adf1c --- /dev/null +++ b/test.py @@ -0,0 +1,106 @@ +from tkinter import * +import tkinter +import tkinter.messagebox +from PIL import ImageTk, Image +from pyswip import * + + +sympList = ['--Select Symptom--', 'headache', 'sneezing', 'runny_nose', 'sore_throat', 'fever', 'chills', 'bodyache', + 'abdominal_pain', 'loss_of_appetite', 'skin_rash', 'conjunctivitus', 'sweating', 'vomitting', 'diarrhea'] + + +class DPDP: + + def __init__(self, master): + + frame = Frame(master) + frame.grid() + + # ---------medical symbol pic------------------------ + path1 = "Capture.png" + img = ImageTk.PhotoImage(Image.open(path1)) + panel = Label(root, image=img) + panel.photo = img + panel.place(x=20, y=20, width=140, height=130) + + # ----------page title----------------------------------- + mainHeading = Label(master, text="Disease Prediction & Drug Prescribtion ", font=( + 'Verdana 20'), bg='#44689E') + mainHeading.grid(padx=200, pady=60) + + # ----------symptoms selection--------------------------------------------------- + + # -------Symptom1------------------------- + symp1 = Label(root, text="1st Symptom", + font=('Verdana 15'), bg='#44689E') + symp1.place(x=20, y=200) + + self.selSymp1 = StringVar() + self.selSymp1.set(sympList[0]) + + sympDropDown1 = OptionMenu(root, self.selSymp1, *sympList) + sympDropDown1.place(x=180, y=200) + + # -------Symptom2------------------------- + + self.symp2 = Label(root, text="2nd Symptom", + font=('Verdana 15'), bg='#44689E') + self.symp2.place(x=20, y=300) + + self.selSymp2 = StringVar() + self.selSymp2.set(sympList[0]) + + sympDropDown2 = OptionMenu(root, self.selSymp2, *sympList) + sympDropDown2.place(x=180, y=300) + + # -------Symptom3------------------------- + + self.symp3 = Label(root, text="3rd Symptom", + font=('Verdana 15'), bg='#44689E') + self.symp3.place(x=20, y=400) + + self.selSymp3 = StringVar() + self.selSymp3.set(sympList[0]) + + sympDropDown3 = OptionMenu(root, self.selSymp3, *sympList) + sympDropDown3.place(x=180, y=400) + + # -------Symptom4------------------------- + + symp4 = Label(root, text="4th Symptom", + font=('Verdana 15'), bg='#44689E') + symp4.place(x=20, y=500) + + self.selSymp4 = StringVar() + self.selSymp4.set(sympList[0]) + + sympDropDown4 = OptionMenu(root, self.selSymp4, *sympList) + sympDropDown4.place(x=180, y=500) + + bt = Button(frame, text="click", width=5, + command=lambda: queryGenerator(self.selSymp1.get(), self.selSymp2.get(), self.selSymp3.get(), + self.selSymp4.get())) + bt.grid(row=4, column=5) + + +def queryGenerator(s1, s2, s3, s4): + + print(s1, s2, s3, s4) # this prints the values that are chosen,correctly + prolog = Prolog() + prolog.consult('kb.pl') + + q = list(prolog.query("telldisease(X,%s,%s,%s,%s)." % (s1, s2, s3, s4))) # prolog query + # for e in q[0].values(): + # print("You have " + e) + # break + print(q) + tkinter.messagebox.showinfo(title=str(q), message=str(q)) + + +root = Tk() +root.geometry("820x600") +root.resizable(0, 0) +root.config(bg='#44689E') +app = DPDP(root) +root.title("DPDP") +root.mainloop() diff --git a/ver.py b/ver.py new file mode 100644 index 0000000..28c3db8 --- /dev/null +++ b/ver.py @@ -0,0 +1,198 @@ +import time +from tkinter import * +import tkinter.messagebox +from itertools import chain, repeat +from tkinter import simpledialog + + + +from pyswip.prolog import Prolog +from pyswip.easy import * + + +prolog = Prolog() # Global handle to interpreter + +retractall = Functor("retractall") +known = Functor("known",3) + +#Define foreign functions for getting user input and writing to the screen + + +def write_py(X): + print(X) + return True + + +def read_py(A,V,Y): + if isinstance(Y, Variable): + response = simpledialog.askstring("Input", str(A) + " is " + str(V) + "? ", + parent=root) + Y.unify(response) + return True + else: + return False + +def read_py_menu(A, V, Y, MenuList): + if isinstance(Y, Variable): + print(A) + for i, x in enumerate(MenuList): + print(i, ". " ,x) + + response = get_menu_input(MenuList) + print('\tYou chose', response) + Y.unify(response) + return True + else: + return False + + +def get_menu_input(MenuList): + from_user = input() + response_int = -1 + try: + response_int = int(from_user) + response = str(MenuList[response_int]) + except: + response = from_user + return response + +write_py.arity = 1 +read_py.arity = 3 + +read_py_menu.arity = 4 + +registerForeign(read_py) +registerForeign(write_py) +registerForeign(read_py_menu) + +prolog.consult('Engine.pl') # open the KB for consulting + +call(retractall(known)) +# problem = [s for s in prolog.query("answer(X).", maxresult=1)] +# print((problem[0]['X'] +"." if problem else "unknown.")) + +window_size="550x450" + +#getting response from user +def chat(user_response): + problem = [s for s in prolog.query("problem(X).", maxresult=1)] + return((problem[0]['X'] +"." if problem else "unknown.")) + +#showing the user interface +class chat_UI(Frame): + + def __init__(self, master=None): + Frame.__init__(self, master) + self.master = master + + #attributes for colors + self.tl_bg = "#EEEEEE" + self.tl_bg2 = "#EEEEEE" + self.tl_fg = "black" + self.font = "Verdana 10" + + menu = Menu(self.master) + + + self.text_frame = Frame(self.master, bd=6) + self.text_frame.pack(expand=True, fill=BOTH) + + #adding scroll bar + self.text_box_scrollbar = Scrollbar(self.text_frame, bd=0) + self.text_box_scrollbar.pack(fill=Y, side=RIGHT) + + #Adding text box to show the conversation + + self.text_box = Text(self.text_frame, yscrollcommand=self.text_box_scrollbar.set, state=DISABLED, + bd=1, padx=6, pady=6, spacing3=8, wrap=WORD, bg=None, font="Verdana 10", relief=GROOVE, + width=10, height=1) + self.text_box.pack(expand=True, fill=BOTH) + self.text_box_scrollbar.config(command=self.text_box.yview) + + #Adding a frame that wraps user entry + self.entry_frame = Frame(self.master, bd=1) + self.entry_frame.pack(side=LEFT, fill=BOTH, expand=True) + + #Adding a box that takes user input + self.entry_field = Entry(self.entry_frame, bd=1, justify=LEFT) + self.entry_field.pack(fill=X, padx=6, pady=6, ipady=3) + # self.users_message = self.entry_field.get() + + #Frame containing send button + self.send_button_frame = Frame(self.master, bd=0) + self.send_button_frame.pack(fill=BOTH) + + #Send button + self.send_button = Button(self.send_button_frame, text="Send", width=5, relief=GROOVE, bg='white', + bd=1, command=lambda: self.send_message_insert(None), activebackground="#FFFFFF", + activeforeground="#000000") + self.send_button.pack(side=LEFT, ipady=8) + self.master.bind("", self.send_message_insert) + + self.last_sent_label(date="No messages sent.") + + + #to show the time/date of last seen message + def last_sent_label(self, date): + + try: + self.sent_label.destroy() + except AttributeError: + pass + + self.sent_label = Label(self.entry_frame, font="Verdana 7", text=date, bg=self.tl_bg2, fg=self.tl_fg) + self.sent_label.pack(side=LEFT, fill=X, padx=3) + + + def chatexit(self): + exit() + def send_message_insert(self, message): + user_input = self.entry_field.get() #enter + pr1 = "Me : " + user_input + "\n" + self.text_box.configure(state=NORMAL) + self.text_box.insert(END, pr1) + self.text_box.configure(state=DISABLED) + self.text_box.see(END) + ob=chat(user_input) #give response + pr="Covid Buddy : " + ob + "\n" + self.text_box.configure(state=NORMAL) + self.text_box.insert(END, pr) + self.text_box.configure(state=DISABLED) + self.text_box.see(END) + self.last_sent_label(str(time.strftime( "Last message sent: " + '%B %d, %Y' + ' at ' + '%I:%M %p'))) + self.entry_field.delete(0,END) + time.sleep(0) + #t2 = threading.Thread(target=self.playResponce, args=(ob,)) + #t2.start() + #return ob + + def font_change_default(self): + self.text_box.config(font="Verdana 10") + self.entry_field.config(font="Verdana 10") + self.font = "Verdana 10" + + def color_theme_default(self): + self.master.config(bg="#EEEEEE") + self.text_frame.config(bg="#EEEEEE") + self.entry_frame.config(bg="#EEEEEE") + self.text_box.config(bg="#FFFFFF", fg="#000000") + self.entry_field.config(bg="#FFFFFF", fg="#000000", insertbackground="#000000") + self.send_button_frame.config(bg="#EEEEEE") + self.send_button.config(bg="#FFFFFF", fg="#000000", activebackground="#FFFFFF", activeforeground="#000000") + self.sent_label.config(bg="#EEEEEE", fg="#000000") + + self.tl_bg = "#FFFFFF" + self.tl_bg2 = "#EEEEEE" + self.tl_fg = "#000000" + + # Default font and color theme + def default_format(self): + self.font_change_default() + self.color_theme_default() + +root=Tk() + +a =chat_UI(root) +root.geometry(window_size) +root.title("COVID Buddy") +root.mainloop() \ No newline at end of file