Learning functions using exercism.org: "Guido's Gorgeous Lasagna" (spoiler warning)

Hi, Pythonistas!

I’m working on an exercise for fun (not for school) but I’d still prefer to receive hints from you people instead of the full answer.

The task titled “Guido’s Gorgeous Lasagna” involves defining the expected bake time for a lasagna in minutes, calculating the remaining bake time in minutes, calculating the preparation time in minutes, and finally calculating the total elapsed cooking time (prep + bake) in minutes. We’re working with integers, not floats or decimals. The full exercise and task is described on exercism.org in the link above.

There is a unit test included and I’ve passed 4 of 5 assertions. So I am almost there.

Here is my code:

EXPECTED_BAKE_TIME = 40
PREPARATION_TIME = 30

def bake_time_remaining(elapsed_bake_time):   
   """Calculate the bake time remaining.

   :param elapsed_bake_time: int baking time already elapsed.
   :return: int remaining bake time derived from 'EXPECTED_BAKE_TIME'.

   Function that takes the actual minutes the lasagna has been in the oven as
   an argument and returns how many minutes the lasagna still needs to bake
   based on the `EXPECTED_BAKE_TIME`.
   """   
   return EXPECTED_BAKE_TIME - elapsed_bake_time

def preparation_time_in_minutes(number_of_layers):
   """
   Empty Doc String
   """
   return number_of_layers * 2
  
def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time):
   """
   Empty Doc String
   """
   return  preparation_time_in_minutes(elapsed_bake_time) - bake_time_remaining(number_of_layers)

Here is the final line in the traceback when I run my pytest:

FAILED lasagna_test.py::LasagnaTest::test_elapsed_time_in_minutes - AssertionError: -33 != 5 : Expected 3 minutes elapsed, but the timing was calculated incorrectly.

That is the biggest puzzle to me. I have no idea what that line is trying to convey. Is it suggesting that when -33 is passed into the last function as the first argument and 3 is passed in as the second argument, the function should (is expecting) a return of 5 but instead the those parameters return something else? I am way off base here. Could someone kindly clarify further?

Here is the assertion traceback in greater detail:

============================== FAILURES =======================
__________ LasagnaTest.test_elapsed_time_in_minutes _____________________
self = <lasagna_test.LasagnaTest testMethod=test_elapsed_time_in_minutes>

    @pytest.mark.task(taskno=4)
    def test_elapsed_time_in_minutes(self):
        layer_data = (1, 2, 5, 8, 11, 15)
        time_data = (3, 7, 8, 4, 15, 20)
        result_data = [prep * 2 + elapsed for prep, elapsed in zip(layer_data, time_data)]
    
        for variant, (layers, time, total_time) in enumerate(zip(layer_data, time_data, result_data), start=1):
            with self.subTest(f'variation #{variant}', layers=layers, time=time, total_time=total_time):
                failure_msg = f'Expected {time} minutes elapsed, but the timing was calculated incorrectly.'
>               self.assertEqual(elapsed_time_in_minutes(layers, time), total_time, msg=failure_msg)
E               AssertionError: -33 != 5 : Expected 3 minutes elapsed, but the timing was calculated incorrectly.
lasagna_test.py:63: AssertionError

For what it is worth, here is the complete unit test:

import unittest
import pytest


try:
   from lasagna import (EXPECTED_BAKE_TIME,
                        bake_time_remaining,
                        preparation_time_in_minutes,
                        elapsed_time_in_minutes)


except ImportError as import_fail:
   message = import_fail.args[0].split('(', maxsplit=1)
   item_name = import_fail.args[0].split()[3]

   if 'EXPECTED_BAKE_TIME' in message:
       # pylint: disable=raise-missing-from
       raise ImportError(f'We can not find or import the constant {item_name} in your'
                         " 'lasagna.py' file. Did you mis-name or forget to define it?")
   else:
       item_name = item_name[:-1] + "()'"
       # pylint: disable=raise-missing-from
       raise ImportError("In your 'lasagna.py' file, we can not find or import the"
                         f' function named {item_name}. Did you mis-name or forget to define it?')


class LasagnaTest(unittest.TestCase):

   @pytest.mark.task(taskno=1)
   def test_EXPECTED_BAKE_TIME(self):
       failure_msg = 'Expected a constant of EXPECTED_BAKE_TIME with a value of 40.'
       self.assertEqual(EXPECTED_BAKE_TIME, 40, msg=failure_msg)

   @pytest.mark.task(taskno=2)
   def test_bake_time_remaining(self):
       input_data = [1, 2, 5, 10, 15, 23, 33, 39]
       result_data = [40 - item for item in input_data]

       for variant, (time, result) in enumerate(zip(input_data, result_data), start=1):
           with self.subTest(f'variation #{variant}', time=time, result=result):
               failure_msg = f'Expected: {result} but the bake time remaining was calculated incorrectly.'
               self.assertEqual(bake_time_remaining(time), result, msg=failure_msg)

   @pytest.mark.task(taskno=3)
   def test_preparation_time_in_minutes(self):
       input_data = [1, 2, 5, 8, 11, 15]
       result_data = [item * 2 for item in input_data]

       for variant, (layers, time) in enumerate(zip(input_data, result_data), start=1):
           with self.subTest(f'variation #{variant}', layers=layers, time=time):
               failure_msg = f'Expected: {time} minutes, but preparation time was calculated incorrectly.'
               self.assertEqual(preparation_time_in_minutes(layers), time, msg=failure_msg)

   @pytest.mark.task(taskno=4)
   def test_elapsed_time_in_minutes(self):
       layer_data = (1, 2, 5, 8, 11, 15)
       time_data = (3, 7, 8, 4, 15, 20)
       result_data = [prep * 2 + elapsed for prep, elapsed in zip(layer_data, time_data)]

       for variant, (layers, time, total_time) in enumerate(zip(layer_data, time_data, result_data), start=1):
           with self.subTest(f'variation #{variant}', layers=layers, time=time, total_time=total_time):
               failure_msg = f'Expected {time} minutes elapsed, but the timing was calculated incorrectly.'
               self.assertEqual(elapsed_time_in_minutes(layers, time), total_time, msg=failure_msg)

   @pytest.mark.task(taskno=5)
   def test_docstrings_were_written(self):
       functions = [bake_time_remaining, preparation_time_in_minutes, elapsed_time_in_minutes]

       for variant, function in enumerate(functions, start=1):
           with self.subTest(f'variation #{variant}', function=function):
               failure_msg = f'Expected a docstring for `{function.__name__}`, but received `None` instead.'
               self.assertIsNotNone(function.__doc__, msg=failure_msg)

Hello, @enoren5, and welcome to the Python Forum!

Here’s a clue:

  • Carefully explain to us the purpose of the elapsed_time_in_minutes function. Is it consistent with what is in the return statement for that function?

EDIT (November 26, 2021):

The following line means that when the following call was made as a test …

elapsed_time_in_minutes(1, 3)

… your function returned -33 when it should have returned 5.

FAILED lasagna_test.py::LasagnaTest::test_elapsed_time_in_minutes - AssertionError: -33 != 5 : Expected 3 minutes elapsed, but the timing was calculated incorrectly.

Hi Quercus: Thanks for the warm welcome!

elapsed_time_in_minutes() function takes in two parameters: number_of_layers (the number of layers added to the lasagna) and elapsed_bake_time (the number of minutes the lasagna has been baking in the oven).The final result should be the total number of minutes I’ve been cooking. So based on this understanding, in my next attempt at writing script I’ve redefined my elapsed_time_in_minutes function to look like this:

def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time):
   """
   Empty Doc String For Now
   """
   return  preparation_time_in_minutes(number_of_layers) + elapsed_time_in_minutes(number_of_layers, elapsed_bake_time)

Eureka! My script is now passing this final assertion, along with the other four assertions! Paying careful close attention to the purpose of this function helped me come up with this passing solution.

Although the exercism.org platform is detecting some linting errors. They are showing in the pytest as “Warnings”, meaning I still can’t progress to the next coding challenge. Ah well. I’ll take it up with them. Thanks, Quercus. :slightly_smiling_face:

edit: grammar correction

2 Likes

This statement within the elapsed_time_in_minutes function doesn’t look correct:

   return  preparation_time_in_minutes(number_of_layers) + elapsed_time_in_minutes(number_of_layers, elapsed_bake_time)

That statement calls the elapsed_time_in_minutes function, therefore the function calls itself. Functions are allowed to call themselves, but for the purpose of this challenge, the function should not do that.

Replace this portion of that return statement …

elapsed_time_in_minutes(number_of_layers, elapsed_bake_time)

… with this …

elapsed_bake_time
1 Like

You’re right - - the passing assertion that succeeded in the end actually reads:

    return  preparation_time_in_minutes(number_of_layers) + elapsed_bake_time

The incorrect line you noticed was from an earler attempt at this script.

1 Like

Enjoy your lasagna!

Also continue to enjoy the Python Forum!

1 Like

I’m trying to solve this exact problem. And I’m getting errors. This is what I wrote:

EXPECTED_BAKE_TIME = 40
PREPARATION_TIME = 2
def bake_time_remaining (elapsed_bake_time):
    return EXPECTED_BAKE_TIME - elapsed_bake_time
    
def preparation_time_in_minutes (number_of_layers):
    return PREPARATION_TIME * number_of_layers

def elapsed_time_in_minutes (number_of_layers, elapsed_bake_time):
    return preparation_time_in_minutes(number_of_layers) + elapsed_bake_time

One of the errors say, “AssertionError: unexpectedly None : Expected a docstring for bake_time_remaining, but received None instead.”

The other error messages are similar with a different function name instead of ‘bake_time_remaining’. What am I doing wrong?

Hello, @Pnerd,

Your functions do not contain docstrings. The purpose of a docstring within a function is to explain what that function does.

See:

Then add a docstring to each function.

1 Like

Thank you. That worked.

1 Like

@Quercus Thanks, this works