complex iteration over a string in python - python

The aim of this code is to go through the users input check if check if any of the words match the words on the dictionary then give one response related to the first word that matches and if not reply with "I am curious tell me more". My problem is that I can't iterate over the list and print a single response.
def main():
bank = {"crashed":"Are the drivers up to date?","blue":"Ah, the blue screen of death. And then what happened?","hacked":"You should consider installing anti-virus software.","bluetooth":"Have you tried mouthwash?", "windows":"Ah, I think I see your problem. What version?","apple":"You do mean the computer kind?","spam":"You should see if your mail client can filter messages.","connection":"Contact Telkom."}
def welcome():
print('Welcome to the automated technical support system.')
print('Please describe your problem:')
def get_input():
return input().lower().split()
def mainly():
welcome()
query = get_input()
while (not query=='quit'):
for word in query:
pass
if word in bank:
print(bank[word])
elif not(word=="quit"):
print("Curious, tell me more.")
query = get_input()
mainly()
if __name__=="__main__":
main()

In your code there is few mistakes. First one, when you start the script you run main which loading a local disctionnary 'bank' which does't exist out of the function. When the function end, it runs 'mainly' but doesn't remember the dictionary.
Second one, as you use a dictionnary structure you don't need to loop thru and check all elements 1 by 1. You can instead use the function dict.get
I can propose you this solution :
def welcome():
print('Welcome to the automated technical support system.')
print('Please describe your problem:')
def get_input():
return input().lower().split()
def main():
bank = {"crashed": "Are the drivers up to date?", ...}
welcome()
query = get_input()
while query != 'quit':
if bank.get(query, None) is not None:
print(bank[query])
else:
print("doesn't exist")
query = get_input()
print("Curious, tell me more.") # will be triggered only when you are out of the loop
if __name__=="__main__":
main()
In that case bank.get(query, None) will return the sentence if the word exist, else it returns None.
You can also simplify it as :
while query != 'quit':
sentence = bank.get(query, "doesn't exist")
print(bank[query])
query = get_input()
this is because if it exist, sentence = what you want to display, if it doesn't, it displays the error message you want
I hope it helps,

Related

I am trying to update my program to have some return values to make it more testable. Why is the one named other not returned usable in main?

I receive a NameError: name 'other' is not defined. Could someone give me an example and reasoning to point me in the right direction to help me out (so i don't screw up when adding return values to the rest of my program). I thought i understood them that's why I know i need help to get a better understanding. I almost forgot to mention i am entering a number to trigger the else clause when i get the error message regarding main code.
def yes_no(y_n):
other = 'invalid response'
if y_n == 'n':
index_person(address_dict)
elif y_n == 'y':
add_person()
else:
return other
def main():
#other = 'Invalid response'
while True:
#Hmmm..would the user like to add a new person the the address book or index it?
#Loop the address program until break.
try:
user_answer = input('Would you like to add a new person to the address book? y/n \n - ')
yes_no(user_answer)
if user_answer not in ['y','n']:
print(other)

python testing, how handle 'continue' statement loop

I'm learning how to use Python. I have a function with a conditional inside of it, if an invalid input is provided, it should restart the loop until a valid input is provided.
Unfortunately, this "restarting" behavior is causing an infinite loop within my tests (it circularly provides the wrong input). How can I pause, or break, or limit the output to one instance so I can test the returned string?
function:
def confirm_user_choice(choice: str):
while True:
user_response = input(f"\nYou chose '{choice}', is this correct? y/n ")
if user_response == "y":
return True
elif user_response == "n":
return False
else:
print("\nSelect either 'y' (yes) or 'n' (no)")
test:
import unittest
from unittest import mock
from src.utils.utils import addValues, confirm_user_choice
class TestConfirmUserChoice(unittest.TestCase):
def test_yes(self):
with mock.patch("builtins.input", return_value="y"):
result = confirm_user_choice("y")
self.assertEqual(result, True)
def test_no(self):
with mock.patch("builtins.input", return_value="n"):
result = confirm_user_choice("n")
self.assertEqual(result, False)
def test_invalid_input(self):
with mock.patch("builtins.input", return_value="apple"): <-- triggers func else case
result = confirm_user_choice("apple")
self.assertEqual(result, False)
You have a partial function: on a proper input, it will return a Boolean value, but it may not return at all, and you can't test that an infinite loop is indeed infinite.
To make it more testable, allow the function to take an optional iterable value that defaults to sys.stdin, allowing you to control what the function reads (and how long it will attempt to do so.)
def confirm_user_choice(choice: str, responses: Optional[Iterable[str]] = None):
if responses is None:
# An infinite stream of calls to input()
responses = iter(lambda: input(f"\nYou chose '{choice}', is this correct? y/n "), None)
for user_response in responses:
if user_response == "y":
return True
elif user_response == "n":
return False
else:
print("\nSelect either 'y' (yes) or 'n' (no)")
else:
# Note: cannot be raised from the default value of responses
raise ValueError("Unexpected end of responses")
Now your test can simply pass canned lists of responses, and either catch the expected ValueError, or look at the returned Boolean value.
import unittest
from src.utils.utils import addValues, confirm_user_choice
class TestConfirmUserChoice(unittest.TestCase):
def test_yes(self):
result = confirm_user_choice("y", ["y"])
self.assertTrue(result)
def test_eventual_yes(self):
result = confirm_user_choice("y", ["apple", "pear", "y"])
self.assertTrue(result)
def test_no(self):
result = confirm_user_choice("y", ["n"])
self.assertFalse(result)
def test_no_valid_input(self):
with self.assertRaises(ValueError):
result = confirm_user_choice(["apple"])
continue does nothing in your code
continue alows you to ignore a part of the code for some instance of the loop.
For example :
for i in range(2):
if i < 1:
continue
print(i)
Output :
1
For what you want to do, don't forget while is suppose to end when a condition is meet. Hence bypassing the condition using while True: and then using a if to exit your loop is a bit counter productive.
Just use the while condition :
user_response = ""
while user_response not in ["y", "n"]:
user_response = input("y/n ? ")
print(user_response)
Happy programming
I'm new to Python myself but in my understanding, unit tests investigate how function handle different inputs based on the function's return value or exceptions raised (if any).
Your function only exits when the user inputs either "y" or "n" or when an error is raised (for instance, if the user provides Crtl-Z). Your while loop does not break when a user inputs 'apple.' There is no return value for pytest (or the like) to inspect.
If you really want to test this, you'd have to rewrite your function so that's a little more modular. It would have to feature at least three different return values, including one that implies that the input was invalid.

What kind of unittests make sense for a chatbot in python?

I am creating a simple chatbot at the moment and now I would like to test it using the unittests package. I am new to programming and I have only recently heard about the existence of unit tests. I am thinking that for a chatbot to be tested, it would make sense to see if it gives the right output to the right user input. So if the user writes "Hi", for example, it should output "Hey what's up?" as specified in the library (I have written a bunch of libraries for it; it is not a deep-learning-kinda-bot).
This is the code of my chatbot:
# importing regular expression operations, which we need later to access the libraries
import re
import random # later we want to pick a chatbot answer to a statement by random
from Jordan_Library import dict_smalltalk, dict_caring, dict_cursing, dict_meditating, dict_corona # import libraries
# and here we imported all the topic libraries
class JordanBot:
"""
A class that represents the abilities of Jordan the chatbot.
...
Attributes
----------
name : str
user can insert name, which will later be shown in the chat.
Methods
----------
name : str
user can insert name, which will later be shown in the chat.
"""
def __init__(self, name): # this function is always called when a new object of the class is called
"""
Constructing topics for Jordan. Takes the user's name as an argument.
"""
self.name = name
def chat(self):
"""
A function that enables chatting with Jordan after picking a topic.
Take no arguments.
"""
topics = [dict_smalltalk, dict_caring, dict_cursing, dict_meditating, dict_corona]
while True:
print("Welcome. My name is Jordan. You can now choose a topic that we can talk about.")
print("Press '0' to have some good old-fashioned smalltalk.")
print("Press '1' to tell me about your deepest sorrows and your destroyed soul.")
print("Press '2' to curse with me.")
print("Press '3' to meditate with me.")
print("Press '4' to talk about Corona.")
# execute this welcome text and tell user what to press in order to pick a topic.
choice = input(">>> ")
# initialize input
if choice == '0': # determine which key to press to initiate the specific topic
print("Alrighty, let's do some chitchatting.")# initiate welcome text for specific topic
print("Don't forget that I am sensitive to punctuation.")
elif choice == '1':
print("Please tell me about your deepest sorrows and your destroyed soul.")
print("Don't forget that I am sensitive to punctuation.")
elif choice == '2':
print("Make yourself ready. let's insult each other!")
print("Don't forget that I am sensitive to punctuation.")
elif choice == '3':
print("Ok then, let's meditate.")
print("Don't forget that I am sensitive to punctuation..")
elif choice == '4':
print("Let's talk about Corona.")
print("Don't forget that I am sensitive to punctuation..")
elif choice == 'q': # if user wants to quit
break
else: # if user pressed the wrong key.
print("Try again.")
edition = topics[int(choice)]
statement = list(map(lambda x:re.compile(x[0], re.IGNORECASE), edition))
# list(map()) applies a function to all elements of a specified object, in this case the cursing library
# lambda makes sure that re.compile is applied in a certain way to all elements of the library without being case-sensitive
# re.compile makes sure that the elemets are turned into objects that can be matched later to another item in the library
answer = list(map(lambda x:x[1], edition))
# same here, but here we pick the second argument in the list x[1], which entails Jordan's answers
while True:
userInput = input(' ' + self.name + ' >>> ') # this allows for the user to type in keys
resp = "I did not understand what you said. Also, I am sensitive to punctuation." # initalize response variable
counter = 0 # initalize counter
while resp == "I did not understand what you said. Also, I am sensitive to punctuation." and counter < len(statement): # determine when to start my loop
for i in range(0, len(statement)): # loop through the indexed library
match = statement[i].match(userInput) # look if input of the user matches with one of the words in the library
if match:
word = statement[i].split(userInput)[1] # We have to take the first element of this list
resp = random.choice(answer[i]) # if there is a match, pick a random answer from x[1]
counter += 1 # make sure that the counter is now + 1 so it does not write the initialized response from the beginning but continues with the loop
# if there is no match though, then it will write the initialized answer
if userInput == 'q':
print(random.choice(answer[i]))
print("---------------------------------")
print("Do you want to choose another topic? Pick below or press 'q' to quit for realsies.")
print("---------------------------------")
break
resp = resp.format(word)
print('____________')
print(' ')
print('Jordan >>> ' + resp) # print Jordan's answer
The unittests I am trying to create are something like:
import unittest
from Jordan_Library import dict_smalltalk, dict_caring, dict_cursing, dict_meditating, dict_corona # import dictionairies
from Jordan_Class import JordanBot
class testJordan(unittest.TestCase):
"""
A class to test several units of Jordan to make sure everything is working correctly.
"""
def test_match(self):
j = JordanBot('StringName')
j.chat()
user_input = "What are you?"
bot_output = list(map(lambda x:x[1], dict_smalltalk)) # this is how I access the right response in the right library
matched = bot_output.match(user_input)
self.assertIn("I am a chatbot, dude. What do you think?", matched)
def test_regex(self):
j = JordanBot('StringName')
j.chat()
text = 'something'
regex = {}
self.assertRegexpMatches(text, regex)
if __name__ == '__main__':
unittest.main()
The main problem is that these lines only run my code, but not really any unit tests. Advice is much appreciated!

Recursion with defined functions for simple text-based Python program

I'm writing my first program - it's an idiom generator, which combines individual elements from lists of random verbs, nouns, and pronouns (that I have entered) in Madlibs style and generates a humorous expression. This is a simplified version of my source code:
baseFunction = True
def mainFunction() :
import random
quest = input("Which language do you want it in? Type 'French' or 'English'. ")
if quest == "French" or "french":
verb =
#list of verbs I have manually entered
noun =
#list of nouns I have manually entered
pronoun =
#list of pronouns I have manually entered
morenouns =
#list of nouns I have manually entered
phrase = random.choice(verb) + random.choice(noun) + random.choice(pronoun) + random.choice(morenouns)
print(phrase)
print("Now, give it some meaning and use in the world!")
elif quest == "English" or "english":
verb =
#another list of verbs I have manually entered
noun =
#another list of nouns I have manually entered
pronoun =
#another list of pronouns I have manually entered
morenouns =
#another list of nouns I have manually entered
phrase = random.choice(verb) + random.choice(noun) + random.choice(pronoun) + random.choice(morenouns)
print(phrase)
print("Now, invent some meaning for it and use it in the world!")
f8 = input("Do you want to make another one? Say 'yes' if you do. ")
if f8 == "yes" or "Yes":
mainFunction()
else:
print("Thanks for playing!")
else:
print("Didn't quite catch that. Try again! (say yes!)")
mainFunction()
def malif() :
ques = input("Want to hear a funny idiom? Say 'yes' or 'no'. ")
if ques == "yes" or "Yes":
mainFunction()
elif ques == "no" or "No":
print("Wrong answer. Try again! (say yes)")
malif()
else:
print("Didn't quite catch that. Say 'yes' or 'no'.")
while baseFunction :
malif()
mainFunction()
Essentially, I am asking the user whether they want to make an idiom, offering them a choice of language, generating the expression for them, and then asking them if they want to repeat the process. When I run the script in PyCharm, it runs the two functions in order (meaning, malif() first and then mainFunction(), as I have it at the end) but it does not pay any attention to my input (ex. if I say 'no' it runs the mainFunction anyway and will always do it in French even if I say 'English').
I used some of the tips discussed in this entry (Python - How to make program go back to the top of the code instead of closing). I think the problem lies calling the functions in their own definitions (ex. calling malif() if I answer 'no' to input 'ques', which is defined in malif() ). Yet, I have followed the tips discussed in the question that I linked and it is still not working the way that I want it to. Am I doing something wrong in formatting the code (ex. in terms of indentation) or if it is not obvious what I am doing wrong, is there a way for me to loop functions back to the beginning that was not suggested in the original question?
Thanks!
First some tips when you work with strings as input. Python will make the difference between caps and non-caps letter, thus a good way to deal with strings is to lower() them first (or upper(), ...):
Example:
ques = input("Enter Yes or No: ")
ques = ques.lower()
if ques == "yes":
# do something
elif ques == "no":
# do something else
else:
# raise error
Now I feel like your code is build in a funny way. A good habit is to separate the import from the functions, from the main program. The 2 first will be imported if the module (file) is imported, while the last one will be played when the file is executed. To do so, you can use this:
# -*- coding: utf-8 -*-
"""
docstring of the module
"""
# Imports
import random
import os
# Functions
def f():
return "Hello world"
# Main program
if __name__ == '__main__':
# Calling the function, taking the inputs and so on
In the main program, it's rather useful to deal with the possibility that an exception is raised. Moreover, if you use the cmd to display your program, the cmd will close immediately when an error is raised. This syntax is quite useful:
if __name__ == '__main__':
try:
# Do stuff
except:
import sys
print (sys.exc_info()[0])
import traceback
print (traceback.format_exc())
os.system("pause") # for windows, else easy way is to have an empty input to freeze the cmd
Now your code. I would rework it this way:
# -*- coding: utf-8 -*-
"""
Docstring
"""
# Imports
import random
import os
# Functions
def build_a_phrase(language) :
if language == "french":
verb = ["vendre", "atterir", "attaquer", "jeter"]
#list of verbs I have manually entered
noun = ["arbre", "poisson", "chien"]
#list of nouns I have manually entered
pronoun = ["un", "les"]
#list of pronouns I have manually entered
morenouns = ["chat", "oiseau"]
#list of nouns I have manually entered
choices = [random.choice(verb), random.choice(noun), random.choice(pronoun), random.choice(morenouns)]
phrase = " ".join(choices) # Add a space between the words
return phrase
elif language == "english":
verb = ["...", "...", "..."]
#another list of verbs I have manually entered
noun = ["...", "...", "..."]
#another list of nouns I have manually entered
pronoun = ["...", "...", "..."]
#another list of pronouns I have manually entered
morenouns = ["...", "...", "..."]
#another list of nouns I have manually entered
choices = [random.choice(verb), random.choice(noun), random.choice(pronoun), random.choice(morenouns)]
phrase = " ".join(choices) # Add a space between the words
return phrase
if __name__ == '__main__':
try:
# Parameters
available_language = ["french", "english"]
available_answers = ["yes", "no"]
# Safety implementation of an input
quest = ""
i = 0
while quest.lower() not in available_answers:
quest = input("Want to hear a funny idiom? Say 'yes' or 'no'. ")
i += 1
if i == 2: # number of tries
break
if quest.lower() == "no":
print ("I'm sure you meant yes.")
language = ""
i = 0
while language.lower() not in available_language:
language = input("Which language do you want it in? Type 'French' or 'English'.\n")
i += 1
if i == 2: # number of tries
break
while True:
sentence = build_a_phrase(language)
print (sentence)
print ("Now, give it some meaning and use in the world!")
f8 = ""
i = 0
while f8.lower() not in available_answers:
f8 = input("Do you want to make another one? Say 'yes' if you do. ")
i += 1
if i == 2: # number of tries
break
if f8.lower() == "no":
print("Thanks for playing!")
break
except:
import sys
print (sys.exc_info()[0])
import traceback
print (traceback.format_exc())
os.system("pause")
Hope you'll get a few good tricks from this answer, and some good habits :)
Not complete yet, when the input is wrong, an Error should be raised rather than waiting for the error resulting in the wrong input (i.e. a raise statement should be placed instead of the breaks)

How to use a function's if statement to use info from another function?

So I'm designing a sign-in AI, and I want it to work so that the admin name is Shawn. Here is my issue:
The program starts with the interface -
def interface():
username = input('Hello traveler, what is your name? ')
lowerUsername = username.lower()
print()
print()
if lowerUsername == 'megan':
print()
print('So you are the one Shawn has told me so much about? You are looking beautiful today my dear ☺️🌷')
elif lowerUsername == 'shawn':
OfficialSignInEdit()
So you can see at the end that if the user inputs that their name is 'shawn' at sign-in, it calls on the OfficialSignInEdit function, which is the admin sign in. It looks like this:
def OfficialSignInEdit():
print()
if PossInputs('perio', 'What street did you grow up on?: ') == correct:
print()
print('Greetings Master Shawn, it is a pleasure to see you again πŸ™‚')
else:
print()
res1 = input('Incorrect password, try again? (Yes/No)')
lowres1 = res1.lower()
if lowres1 == 'yes':
print()
print()
OfficialSignIn()
elif lowres1 == 'no':
print()
print()
interface()
So I have pinpointed the source of my issue to be right here in this particular line:
if PossInputs('perio', 'What street did you grow up on?: ') == correct:
print()
print('Greetings Master Shawn, it is a pleasure to see you again πŸ™‚')
this (just for your reference) is the PossInputs function:
def PossInputs(x, y):
term = x
question = input(y)
lowQuestion = question.lower()
words = lowQuestion.split()
if term in words:
print()
print (correct)
else:
print()
print (incorrect)
So what I want to happen is, when 'shawn' is entered as a name, the program will jump to the OfficialSignInEdit Function, and ask the question 'What street did you grow up on?'. Then IF the user enters the answer 'perio', the program will print 'correct', and then print the message 'Greetings Master Shawn, it is a pleasure to see you again'. I tried to say that IF PossInputs == correct (and I did define correct = 'correct', and incorrect = 'incorrect' outside all functions) then this would happen, but instead it prints 'correct', and then 'Incorrect password, try again? (Yes/No)', so how can I make a conditional statement that says that if the user answers 'perio', then it will print the welcome message?
Just for thoroughness sake, I also tried
if PossInputs('perio', 'What street did you grow up on?: ') == True
also without success...
anyways anything you can give me is extremely appreciated, if you have any questions or you would like to to clarify something about the written code, I would be more than happy to get back with you as soon as I can.
Thanks!

Categories