Salon appointment scheduler, show same list of services again test not passing

Hello, sorry for the repeated question but a few people seem to have had issues with this test and none of the threads about it include a solution.

I have passed every test except the one which states: ‘If you pick a service that doesn’t exist, you should be shown the same list of services again’.

I did include this function in my script and it works as specified when I test it. See below my code:

> #! /bin/bash
> PSQL="psql -X --username=freecodecamp --dbname=salon --tuples-only -c"
> 
> echo -e "\n~~~~~~~~~~Hairdresser's~~~~~~~~~~\n"
> 
> MENU()
> {
>   echo -e "\nWelcome to the hairdresser's. Take a look at our services:\n"
>   # get services
>   SERVICES=$($PSQL "SELECT service_id, name FROM services ORDER BY service_id")
>   # display services
>   echo "$SERVICES" | while read SERVICE_ID BAR SERVICE
>   do
>     echo -e "$SERVICE_ID) "$SERVICE""
>   done
>   echo -e "\nWhich service would you like today?"
>   GET_DETAILS
> }
> 
> GET_DETAILS()
> {
> read SERVICE_ID_SELECTED
> # get service
> SELECTED_SERVICE=$($PSQL "SELECT service_id FROM services WHERE service_id = $SERVICE_ID_SELECTED")
> # if service doesn't exist
> if [[ -z $SELECTED_SERVICE ]]
> then
>   # send to main menu
>   echo -e "\nThat is not a valid service."
>   MENU
> else
>   # get phone number
>   echo -e "\nWhat is your phone number?"
>   read CUSTOMER_PHONE
>   # if customer doesn't exist
>   EXISTING_CUSTOMER_NAME=$($PSQL "SELECT name FROM customers WHERE phone = '$CUSTOMER_PHONE'")
>   if [[ -z $EXISTING_CUSTOMER_NAME ]]
>   then
>     # get customer name
>     echo -e "\nIt seems you have not visited us before. Welcome! What is your name?"
>     read CUSTOMER_NAME
>     # insert customer
>     INSERT_CUSTOMER_RESULT=$($PSQL "INSERT INTO customers(name, phone) VALUES('$CUSTOMER_NAME', '$CUSTOMER_PHONE')")
>   else
>   EXISTING_CUSTOMER_NAME_FORMATTED=$(echo $EXISTING_CUSTOMER_NAME | sed -r 's/^ *| *$//g')
>     echo -e "\nWelcome back, $EXISTING_CUSTOMER_NAME_FORMATTED." 
>   fi
> fi
> MAKE_APPO
> }
> 
> MAKE_APPO()
> {
> echo -e "\nWhat time would you like to come in?"
> read SERVICE_TIME
> # get customer_id
> CUST_ID=$($PSQL "SELECT customer_id FROM customers WHERE phone = '$CUSTOMER_PHONE'")
> # add appointment
> ADD_APPO_RESULT=$($PSQL "INSERT INTO appointments(customer_id, service_id, time) VALUES($CUST_ID, $SELECTED_SERVICE, '$SERVICE_TIME')")
> # get service name
> SERVICE_NAME=$($PSQL "SELECT name FROM services WHERE service_id = $SELECTED_SERVICE")
> SERVICE_NAME_FORMATTED=$(echo $SERVICE_NAME | sed -r 's/^ *| *$//g')
> # get customer name
> CUST_NAME=$($PSQL "SELECT name FROM customers WHERE customer_id = $CUST_ID")
> CUST_NAME_FORMATTED=$(echo $CUST_NAME | sed -r 's/^ *| *$//g')
> echo -e "\nI have put you down for a $SERVICE_NAME_FORMATTED at $SERVICE_TIME, $CUST_NAME_FORMATTED."
> }
> 
> MENU

Is there some other way to display the list of services each time an erroneous input is received?

Thank you in advance for your help.

I tried a different way, which as far as I can tell directly prints the list of services again rather than sending the user back to the initial menu containing the list:

#! /bin/bash
PSQL="psql -X --username=freecodecamp --dbname=salon --tuples-only -c"

echo -e "\n~~~~~~~~~~Hairdresser's~~~~~~~~~~\n"

MENU()
{
  echo -e "\nWelcome to the hairdresser's. Take a look at our services:\n"
  # get services
  SERVICES=$($PSQL "SELECT service_id, name FROM services ORDER BY service_id")
  # display services
  echo "$SERVICES" | while read SERVICE_ID BAR SERVICE
  do
    echo -e "$SERVICE_ID) "$SERVICE""
  done
  echo -e "\nWhich service would you like today?"
  GET_DETAILS
}

GET_DETAILS()
{
read SERVICE_ID_SELECTED
# get service
SELECTED_SERVICE=$($PSQL "SELECT service_id FROM services WHERE service_id = $SERVICE_ID_SELECTED")
# if service doesn't exist
if [[ -z $SELECTED_SERVICE ]]
then
  echo -e "\nThat is not a valid service. Please select a service from the list.\n"
  # get services
  echo "$SERVICES" | while read SERVICE_ID BAR SERVICE
  do
    echo -e "$SERVICE_ID) "$SERVICE""
  done
  GET_DETAILS
else
  # get phone number
  echo -e "\nWhat is your phone number?"
  read CUSTOMER_PHONE
  # if customer doesn't exist
  EXISTING_CUSTOMER_NAME=$($PSQL "SELECT name FROM customers WHERE phone = '$CUSTOMER_PHONE'")
  if [[ -z $EXISTING_CUSTOMER_NAME ]]
  then
    # get customer name
    echo -e "\nIt seems you have not visited us before. Welcome! What is your name?"
    read CUSTOMER_NAME
    # insert customer
    INSERT_CUSTOMER_RESULT=$($PSQL "INSERT INTO customers(name, phone) VALUES('$CUSTOMER_NAME', '$CUSTOMER_PHONE')")
  else
  EXISTING_CUSTOMER_NAME_FORMATTED=$(echo $EXISTING_CUSTOMER_NAME | sed -r 's/^ *| *$//g')
    echo -e "\nWelcome back, $EXISTING_CUSTOMER_NAME_FORMATTED." 
  fi
fi
MAKE_APPO
}

MAKE_APPO()
{
echo -e "\nWhat time would you like to come in?"
read SERVICE_TIME
# get customer_id
CUST_ID=$($PSQL "SELECT customer_id FROM customers WHERE phone = '$CUSTOMER_PHONE'")
# add appointment
ADD_APPO_RESULT=$($PSQL "INSERT INTO appointments(customer_id, service_id, time) VALUES($CUST_ID, $SELECTED_SERVICE, '$SERVICE_TIME')")
# get service name
SERVICE_NAME=$($PSQL "SELECT name FROM services WHERE service_id = $SELECTED_SERVICE")
SERVICE_NAME_FORMATTED=$(echo $SERVICE_NAME | sed -r 's/^ *| *$//g')
# get customer name
CUST_NAME=$($PSQL "SELECT name FROM customers WHERE customer_id = $CUST_ID")
CUST_NAME_FORMATTED=$(echo $CUST_NAME | sed -r 's/^ *| *$//g')
echo -e "\nI have put you down for a $SERVICE_NAME_FORMATTED at $SERVICE_TIME, $CUST_NAME_FORMATTED."
}

MENU

And that didn’t pass either. I’m not sure what else to try.

I played with another person’s code who was having a similar problem. Here’s the solution I found for them. I didn’t quite figure out why the test wasn’t passing with their code. I would try something like that - let us know if it works. I will take another look at the tests and see if I can see anything wrong with it.

I’m afraid it’s still not working. I changed my code so it looks like this:

MENU()
{
  echo -e "\nWelcome to the hairdresser's. Take a look at our services:\n"
  # get services
  SERVICES=$($PSQL "SELECT service_id, name FROM services ORDER BY service_id")
  # display services
  echo "$SERVICES" | while IFS="|" read SERVICE_ID NAME
  do
    echo -e "$SERVICE_ID) "$NAME""
  done
  echo -e "\nWhich service would you like today?"
  GET_DETAILS
}

GET_DETAILS()
{
read SERVICE_ID_SELECTED
# get service
SELECTED_SERVICE=$($PSQL "SELECT service_id FROM services WHERE service_id = $SERVICE_ID_SELECTED")
# if service doesn't exist
if [[ -z $SELECTED_SERVICE ]]
then
  echo -e "\nThat is not a valid service. Please select a service from the list.\n"
  echo "$SERVICES" | while IFS="|" read SERVICE_ID NAME
  do
    echo -e "$SERVICE_ID) "$NAME""
  done
  GET_DETAILS

My lines for displaying the list of services the first time and when an invalid service is selected looks like this:

echo "$SERVICES" | while IFS="|" read SERVICE_ID NAME
  do
    echo -e "$SERVICE_ID) "$NAME""
  done

When I include the IFS specifier and remove BAR from the read command, it no longer passes the initial test for ‘You should display a numbered list of the services you offer before the first prompt for input’, which seems like a mistake as well because the output looks like this:

image

Removing the IFS specifier produces this output:

image

Which also doesn’t pass that initial test about displaying a list of services. It seems like the BAR has to be there in the read command , which produces this output

image

Which passes the initial test but not the test for when an invalid service is entered. I’m not sure how you got the checks to pass with the code you linked. What would adding a ` to the NAME variable do? When I include that it just produces errors in my script, and the checks still don’t pass.

It’s frustrating that I have to mess around like this when my code does do what the tests specify.

When I include the IFS specifier and remove BAR from the read command, it no longer passes the initial test

Yea, currently the output needs to be pretty strictly formatted I think. The extra spaces before the numbers might be alright, but probably not the ones between the number and parenthesis.

What would adding a ` to the NAME variable do?

That’s just a typo - it shouldn’t be there. I removed it from that post.

Sorry, for your frustration. I will try to run your code to see if I can get it to pass with slight modifications and get back to you. I will see if we can make those tests a little more lenient, as well.

Can you share a dump of your database using the command in the instructions @chriswallacet?

--
-- PostgreSQL database dump
--

-- Dumped from database version 12.9 (Ubuntu 12.9-2.pgdg20.04+1)
-- Dumped by pg_dump version 12.9 (Ubuntu 12.9-2.pgdg20.04+1)

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;

DROP DATABASE salon;
--
-- Name: salon; Type: DATABASE; Schema: -; Owner: freecodecamp
--

CREATE DATABASE salon WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'C.UTF-8' LC_CTYPE = 'C.UTF-8';


ALTER DATABASE salon OWNER TO freecodecamp;

\connect salon

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;

SET default_tablespace = '';

SET default_table_access_method = heap;

--
-- Name: appointments; Type: TABLE; Schema: public; Owner: freecodecamp
--

CREATE TABLE public.appointments (
    appointment_id integer NOT NULL,
    customer_id integer,
    service_id integer,
    "time" character varying(10)
);


ALTER TABLE public.appointments OWNER TO freecodecamp;

--
-- Name: appointments_appointment_id_seq; Type: SEQUENCE; Schema: public; Owner: freecodecamp
--

CREATE SEQUENCE public.appointments_appointment_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


ALTER TABLE public.appointments_appointment_id_seq OWNER TO freecodecamp;

--
-- Name: appointments_appointment_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: freecodecamp
--

ALTER SEQUENCE public.appointments_appointment_id_seq OWNED BY public.appointments.appointment_id;


--
-- Name: customers; Type: TABLE; Schema: public; Owner: freecodecamp
--

CREATE TABLE public.customers (
    customer_id integer NOT NULL,
    name character varying(40),
    phone character varying(12)
);


ALTER TABLE public.customers OWNER TO freecodecamp;

--
-- Name: customers_customer_id_seq; Type: SEQUENCE; Schema: public; Owner: freecodecamp
--

CREATE SEQUENCE public.customers_customer_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


ALTER TABLE public.customers_customer_id_seq OWNER TO freecodecamp;

--
-- Name: customers_customer_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: freecodecamp
--

ALTER SEQUENCE public.customers_customer_id_seq OWNED BY public.customers.customer_id;


--
-- Name: services; Type: TABLE; Schema: public; Owner: freecodecamp
--

CREATE TABLE public.services (
    service_id integer NOT NULL,
    name character varying(30)
);


ALTER TABLE public.services OWNER TO freecodecamp;

--
-- Name: services_service_id_seq; Type: SEQUENCE; Schema: public; Owner: freecodecamp
--

CREATE SEQUENCE public.services_service_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


ALTER TABLE public.services_service_id_seq OWNER TO freecodecamp;

--
-- Name: services_service_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: freecodecamp
--

ALTER SEQUENCE public.services_service_id_seq OWNED BY public.services.service_id;


--
-- Name: appointments appointment_id; Type: DEFAULT; Schema: public; Owner: freecodecamp
--

ALTER TABLE ONLY public.appointments ALTER COLUMN appointment_id SET DEFAULT nextval('public.appointments_appointment_id_seq'::regclass);


--
-- Name: customers customer_id; Type: DEFAULT; Schema: public; Owner: freecodecamp
--

ALTER TABLE ONLY public.customers ALTER COLUMN customer_id SET DEFAULT nextval('public.customers_customer_id_seq'::regclass);


--
-- Name: services service_id; Type: DEFAULT; Schema: public; Owner: freecodecamp
--

ALTER TABLE ONLY public.services ALTER COLUMN service_id SET DEFAULT nextval('public.services_service_id_seq'::regclass);


--
-- Data for Name: appointments; Type: TABLE DATA; Schema: public; Owner: freecodecamp
--

INSERT INTO public.appointments VALUES (24, 18, 1, '10:30');
INSERT INTO public.appointments VALUES (25, 18, 2, '11am');


--
-- Data for Name: customers; Type: TABLE DATA; Schema: public; Owner: freecodecamp
--

INSERT INTO public.customers VALUES (18, 'Fabio', '555-555-5555');


--
-- Data for Name: services; Type: TABLE DATA; Schema: public; Owner: freecodecamp
--

INSERT INTO public.services VALUES (1, 'Men''s cut');
INSERT INTO public.services VALUES (2, 'Ladies'' cut');
INSERT INTO public.services VALUES (3, 'Colouring');
INSERT INTO public.services VALUES (4, 'High/lowlights');
INSERT INTO public.services VALUES (5, 'Extensions');
INSERT INTO public.services VALUES (6, 'Blow dry');
INSERT INTO public.services VALUES (7, 'Shave');


--
-- Name: appointments_appointment_id_seq; Type: SEQUENCE SET; Schema: public; Owner: freecodecamp
--

SELECT pg_catalog.setval('public.appointments_appointment_id_seq', 60, true);


--
-- Name: customers_customer_id_seq; Type: SEQUENCE SET; Schema: public; Owner: freecodecamp
--

SELECT pg_catalog.setval('public.customers_customer_id_seq', 43, true);


--
-- Name: services_service_id_seq; Type: SEQUENCE SET; Schema: public; Owner: freecodecamp
--

SELECT pg_catalog.setval('public.services_service_id_seq', 7, true);


--
-- Name: appointments appointments_pkey; Type: CONSTRAINT; Schema: public; Owner: freecodecamp
--

ALTER TABLE ONLY public.appointments
    ADD CONSTRAINT appointments_pkey PRIMARY KEY (appointment_id);


--
-- Name: customers customers_phone_key; Type: CONSTRAINT; Schema: public; Owner: freecodecamp
--

ALTER TABLE ONLY public.customers
    ADD CONSTRAINT customers_phone_key UNIQUE (phone);


--
-- Name: customers customers_pkey; Type: CONSTRAINT; Schema: public; Owner: freecodecamp
--

ALTER TABLE ONLY public.customers
    ADD CONSTRAINT customers_pkey PRIMARY KEY (customer_id);


--
-- Name: services services_name_key; Type: CONSTRAINT; Schema: public; Owner: freecodecamp
--

ALTER TABLE ONLY public.services
    ADD CONSTRAINT services_name_key UNIQUE (name);


--
-- Name: services services_pkey; Type: CONSTRAINT; Schema: public; Owner: freecodecamp
--

ALTER TABLE ONLY public.services
    ADD CONSTRAINT services_pkey PRIMARY KEY (service_id);


--
-- Name: appointments appointments_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: freecodecamp
--

ALTER TABLE ONLY public.appointments
    ADD CONSTRAINT appointments_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customers(customer_id);


--
-- Name: appointments appointments_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: freecodecamp
--

ALTER TABLE ONLY public.appointments
    ADD CONSTRAINT appointments_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(service_id);


--
-- PostgreSQL database dump complete
--

Here. Thanks for taking the time. Yeah I do think the tests could be more lenient too. Keen to hear about what you find works.

Were you able to get anywhere with this? Sorry to bug you, I don’t feel like I’ve got much to go on as I’ve got no clue about how the checks actually work behind the scenes. Thanks again.

Sorry for the delay here @chriswallacet - I just took a look. The test inputs these at each prompt: 'BadInput', '1', '555-5555', 'Test', 'FakeTime'. When I do that in your script, I see another message at the end to input the time I want the appointment and the script doesn’t finish running. It needs to finish running to pass the test - you couldn’t have known this. I used the script in your initial post and the db above - I changed the script a tiny bit and was able to pass - basically, the GET_DETAILS function is running multiple times I think and doesn’t finish. I moved first part of the GET_DETAILS function to the bottom of the MENU function - so it looks like this:

  read SERVICE_ID_SELECTED
  # get service
  SELECTED_SERVICE=$($PSQL "SELECT service_id FROM services WHERE service_id = $SERVICE_ID_SELECTED")

  # if service doesn't exist
  if [[ -z $SELECTED_SERVICE ]]
  then
    # send to main menu
    echo -e "\nThat is not a valid service."
    MENU
  else
   GET_DETAILS
  fi

I would give that a try. I could mention that I did try to make the tests a little more forgiving recently, so I am using a version of the tutorial that has those new tests, you are likely still using the previous one. I don’t think that will have any impact here.

Edit: I think I will try to clarify those user stories so you would know what the test is inputting.

Thanks a lot for this, that solved it. Hopefully I was able to highlight something that will make things easier for people in the future.

2 Likes

I completed the project today, and I noticed something weird with the 1:16 subtask. I’m not sure what it is exactly, but since it prevents the most obvious solution from working correctly, I guess it’s worth digging into.

TL;DR: I think the issue comes from a faulty SQL request that sends a string instead of an integer.

Here under is my first version of the code snippet that deals with sending the service menu again on bad input. That version worked and passed the test. The complete code can be found here, but just to give some quick clarifications, I wrote 3 functions to complete the project (SERVICE_MENU, CUSTOMER_MENU and APPOINTMENT_MENU). I wanted to use the “case” syntax from the bike shop exercise for the service menu, and I found a solution to generate it dynamically here, which is cool if you want to add/remove services.

At the end of SERVICE_MENU, you either have a valid service_id and move to the next function, or you go back to the top of the function. There is also a line that aliases SERVICE_ID to SERVICE_ID_SELECTED because I didn’t like working with that variable name.

SERVICE_MENU() {

  # if error message
  if [[ $1 ]]
  then
    # display error message
    echo -e "\n$1\n"
  fi

  # display service menu
  echo "What service do you want to book?" 
  SERVICES=$($PSQL "SELECT * FROM services")
  echo "$SERVICES" | while read S_ID BAR S_NAME
  do
    echo "$S_ID) $S_NAME"
  done

  # get service request
  read SERVICE_ID_SELECTED

  # get services id list and format it like this: 1|2|3|4
  SERVICES_ID_LIST=$($PSQL "SELECT service_id FROM services")
  FORMATTED_LIST=$(echo $SERVICES_ID_LIST | sed 's/ /|/g; s/^|//; s/|$//')

  # compare input to list
  eval "case \"$SERVICE_ID_SELECTED\" in
    $FORMATTED_LIST)
        SERVICE_ID=$SERVICE_ID_SELECTED
        CUSTOMER_MENU ;;
    *)
        SERVICE_MENU \"Please enter a valid option.\" ;;
  esac"
}

Then, I realized that I had over complicated things, and I changed my code for a simpler snippet, which still worked as expected, but didn’t pass the test 1:16. I only realized it later because I was working on other subtasks at the time and I didn’t pay enough attention.

  # get service request
  read SERVICE_ID_SELECTED
  # validity check
  SERVICE_ID=$($PSQL "SELECT service_id from services WHERE service_id=$SERVICE_ID_SELECTED")
  if [[ -z $SERVICE_ID ]]
  then
    SERVICE_MENU 'Please enter a valid option.' 
  fi
  # send to customer menu
  CUSTOMER_MENU

When I looked into why the subtask 1:16 was failing, I noticed a weird SQL error message that was coming through the terminal when I tried to enter letters instead of numbers to test a wrong input, right before the service menu was displayed again. It’s the only time an error like that appeared on the terminal over the whole project. It wasn’t caused by an “echo” command on my side.

ERROR:  column "a" does not exist
LINE 1: SELECT service_id from services WHERE service_id=a
                                                         ^

Checking the OUTPUT in the terminal, subpanel CodeRoad(Tests), I saw that the test failure on subtask 1:16 was connected to my input not being an integer. Just like ‘badinput’ from the test suite.

As you can see, my convoluted first solution doesn’t send a faulty SQL request with a string instead of an integer for service_id, therefore it doesn’t trigger that error. And that solution passes the test.

Given that both solutions trigger the correct behavior in the terminal (the service menu is displayed after the student-made error message for bad input), I think that there might be something wrong with the way the test is written.

I recently found a solution to pass this test. This is probably not what they had in mind as far as iterating through service table and designating list number with service_id but this will pass the test.

#! /bin/bash
PSQL="psql -X --username=freecodecamp --dbname=salon --tuples-only -c"

echo -e "\n~~~~ Welcome to Aura and Rousy's Wonderful Salon! ~~~~\n"

MAIN_MENU() {
    if  [[ $1 ]]
    then 
      echo "$1"
    else
      echo "How may we help you?"
    fi  

    echo -e "1) Cut\n2) Color\n3) Perm\n4) Style\n5) Trim\n6) Exit"
    read SERVICE_ID_SELECTED

    case $SERVICE_ID_SELECTED in
      1) SCHEDULE_APPOINTMENT "$SERVICE_ID_SELECTED" ;;
      2) SCHEDULE_APPOINTMENT "$SERVICE_ID_SELECTED" ;;
      3) SCHEDULE_APPOINTMENT "$SERVICE_ID_SELECTED" ;;
      4) SCHEDULE_APPOINTMENT "$SERVICE_ID_SELECTED" ;;
      5) SCHEDULE_APPOINTMENT "$SERVICE_ID_SELECTED" ;;
      6) echo "Thanks for stopping in!" ;;
      *) MAIN_MENU "Please pick a valid service." ;;
    esac  
}

SCHEDULE_APPOINTMENT() {
  SERVICE_NAME=$($PSQL "SELECT name FROM services WHERE service_id = $1")
}

MAIN_MENU