feat: add debug isbn validator lab (#61273)

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
Co-authored-by: Dario <105294544+Dario-DC@users.noreply.github.com>
This commit is contained in:
Ilenia
2025-08-04 15:40:00 +02:00
committed by GitHub
parent 9cee50eb38
commit dd561eba23
4 changed files with 712 additions and 2 deletions

View File

@@ -4281,8 +4281,10 @@
"intro": ["Learn about Understanding Error Handling in these lectures."]
},
"lab-isbn-validator": {
"title": "Debug a ISBN Validator",
"intro": [""]
"title": "Debug an ISBN Validator",
"intro": [
"In this lab, you will start with a bugged app, and you will need to debug and fix the bugs until it is working properly."
]
},
"review-error-handling": {
"title": "Error Handling Review",

View File

@@ -0,0 +1,9 @@
---
title: Introduction to the Debug an ISBN Validator
block: lab-isbn-validator
superBlock: full-stack-developer
---
## Introduction to the Debug an ISBN Validator
In this lab, you will start with a bugged app, and you will need to debug and fix the bugs until it is working properly.

View File

@@ -0,0 +1,10 @@
{
"name": "Debug an ISBN Validator",
"isUpcomingChange": true,
"dashedName": "lab-isbn-validator",
"superBlock": "full-stack-developer",
"helpCategory": "Python",
"challengeOrder": [{ "id": "686b9720ee1d032bd77a480a", "title": "Debug an ISBN Validator" }],
"blockLayout": "link",
"blockType": "lab"
}

View File

@@ -0,0 +1,689 @@
---
id: 686b9720ee1d032bd77a480a
title: Debug an ISBN Validator
challengeType: 27
dashedName: debug-an-isbn-validator
---
# --description--
The ISBN (International Standard Book Number) is a unique identifier assigned to commercial books. It can be either 10 or 13 digits long, and the last digit is a check digit calculated from the other digits.
Camperbot has tried to build their own ISBN validator. However, they have made a few mistakes along the way.
In this lab, you will fix the existing code and make it function properly.
Expected behavior:
When the user runs the program, it will show the prompt `Enter ISBN and length: `. The user can enter the ISBN code they want to validate in `ISBN,length` format. The ISBN code should not contain hyphens, followed by its length (`10` or `13`), separated by a comma.
Example inputs:
`1530051126,10` for ISBN-10 codes.
`9781530051120,13` for ISBN-13 codes.
How to find the check digit:
- You don't have to know the detailed calculation logic in this lab. The functions `calculate_check_digit_10` and `calculate_check_digit_13` will take care of the calculation and return the expected check digit in string.
- The check digit for ISBN-10 codes can be a number from `0` to `9` or an uppercase letter `X`.
- The check digit for ISBN-13 codes can be a number from `0` to `9`.
Fulfill the user stories below and get all the tests to pass to complete the lab.
**User Stories:**
1. You should fix the `IndentationError` in the current code.
1. Even if the user does not enter a comma separated value, the program should handle the `IndexError` without crashing.
1. When the user does not enter a comma separated value, they should see the message `Enter comma-separated values.` in the console, and the program should terminate.
1. Even if the user enters a non-numeric value for the length, the program should handle the `ValueError` without crashing.
1. When the user enters a non-numeric value for the length, they should see the message `Length must be a number.` in the console, and the program should terminate.
1. You should fix the off-by-one error in the `validate_isbn` function.
1. You should fix the `TypeError` in the current code that occurs when the user enters a valid ISBN code.
1. You should fix the `IndexError` in the current code when the user enters a valid ISBN code.
1. Even if the user enters an incorrect ISBN code with characters other than numbers, the program should handle the `ValueError` that occurs without crashing.
1. When the user enters an incorrect ISBN code with characters other than numbers, they should see the message `Invalid character was found.` in the console.
1. When the user enters `1530051126,10`, they should see the message `Valid ISBN Code.`
1. When the user enters `9781530051120,13`, they should see the message `Valid ISBN Code.`
**Important**: you will need to comment out the `main()` call in the global space for the tests to run properly.
When you complete the project, the user should see the following messages depending on the values they enter.
| ISBN Code | Length | Message | Example input |
|-----------|--------|---------| ------------- |
| Valid | Valid | `Valid ISBN Code.` | `1530051126,10` |
| Invalid Number | Valid | `Invalid ISBN Code.` | `1530051125,10` |
| Does not match specified length or left blank | Valid | `ISBN-10 code should be 10 digits long.` or `ISBN-13 code should be 13 digits long.`, depending on the length they entered. | `9781530051120,10` or `1530051126,13` |
| Contains non-numeric characters (except for the check digit) | Valid | `Invalid character was found.` | `15-0051126,10` |
| Any | Invalid Number | `Length should be 10 or 13.` | `1530051126,9` |
| Any | Contains non-numeric characters or left blank | `Length must be a number.` | `1530051125,A` |
| Not comma-separated | Not comma-separated | `Enter comma-separated values.` | `1530051125` |
You can use the following values to test your code manually if you would like.
Example inputs for valid ISBN-10 codes:
`1530051126,10`
`9971502100,10`
`080442957X,10`
Example inputs for valid ISBN-13 codes:
`9781530051120,13`
`9781947172104,13`
# --hints--
You should comment out the call to the `main` function to allow for the rest of the tests to work properly.
```js
({
test: () => runPython('assert not _Node(_code).has_call("main()")')
})
```
You should have a `validate_isbn` function.
```js
({
test: () => runPython('assert _Node(_code).has_function("validate_isbn")')
})
```
You should have a `calculate_check_digit_10` function.
```js
({
test: () => runPython('assert _Node(_code).has_function("calculate_check_digit_10")')
})
```
You should have a `calculate_check_digit_13` function.
```js
({
test: () => runPython('assert _Node(_code).has_function("calculate_check_digit_13")')
})
```
You should have a `main` function.
```js
({
test: () => runPython('assert _Node(_code).has_function("main")')
})
```
When the user inputs a value that is not a comma separated value, you should see the message `Enter comma-separated values.` in the console.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "9781530051120"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Enter comma-separated values." in out
`)
}})
```
When the user inputs a non-numeric value for the length, you should see the message `Length must be a number.` in the console.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "9781530051120,a"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Length must be a number." in out
`)
}})
```
When the user enters an incorrect ISBN code with characters other than numbers, you should see the message `Invalid character was found.` in the console.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "978IS3OOSII2O,13"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Invalid character was found." in out
`)
}})
```
When the user enters `1530051126,10`, you should see the message `Valid ISBN Code.` in the console.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "1530051126,10"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Valid ISBN Code." in out
`)
}})
```
When the user enters `9781530051120,13`, you should see the message `Valid ISBN Code.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "9781530051120,13"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Valid ISBN Code." in out
`)
}})
```
When the user enters `1530051125,10`, you should see the message `Invalid ISBN Code.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "1530051125,10"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Invalid ISBN Code." in out
`)
}})
```
When the user enters `9781530051120,10`, you should see the message `ISBN-10 code should be 10 digits long.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "9781530051120,10"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "ISBN-10 code should be 10 digits long." in out
`)
}})
```
When the user enters `1530051126,13`, you should see the message `ISBN-13 code should be 13 digits long.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "1530051126,13"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "ISBN-13 code should be 13 digits long." in out
`)
}})
```
When the user enters `15-0051126,10`, you should see the message `Invalid character was found.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "15-0051126,10"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Invalid character was found." in out
`)
}})
```
When the user enters `1530051126,9`, you should see the message `Length should be 10 or 13.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "1530051126,9"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Length should be 10 or 13." in out
`)
}})
```
When the user enters `1530051125,A`, you should see the message `Length must be a number.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "1530051125,A"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Length must be a number." in out
`)
}})
```
When the user enters `1530051125`, you should see the message `Enter comma-separated values.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "1530051125"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Enter comma-separated values." in out
`)
}})
```
When the user enters `9971502100,10`, you should see the message `Valid ISBN Code.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "9971502100,10"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Valid ISBN Code." in out
`)
}})
```
When the user enters `080442957X,10`, you should see the message `Valid ISBN Code.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "080442957X,10"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Valid ISBN Code." in out
`)
}})
```
When the user enters `9781947172104,13`, you should see the message `Valid ISBN Code.`.
```js
({test: () => {runPython(`
built_in_print = print
out = []
def input(*args):
return "9781947172104,13"
def custom_print(*args, **kwargs):
call_args = [arg for arg in args]
out.extend(call_args)
print = custom_print
try:
main()
except:
pass
finally:
print = built_in_print
assert "Valid ISBN Code." in out
`)
}})
```
# --seed--
## --seed-contents--
```py
def validate_isbn(isbn, length):
if len(isbn, length) != length:
print(f'ISBN-{length} code should be {length} digits long.')
return
main_digits = isbn[0:length]
given_check_digit = isbn[length]
main_digits_list = [int(digit) for digit in main_digits]
# Calculate the check digit from other digits
if length == 10:
expected_check_digit = calculate_check_digit_10(main_digits_list)
else:
expected_check_digit = calculate_check_digit_13(main_digits_list)
# Check if the given check digit matches with the calculated check digit
if given_check_digit == expected_check_digit:
print('Valid ISBN Code.')
else:
print('Invalid ISBN Code.')
def calculate_check_digit_10(main_digits_list):
# Note: You don't have to fully understand the logic in this function.
digits_sum = 0
# Multiply each of the first 9 digits by its corresponding weight (10 to 2) and sum up the results
for index, digit in enumerate(main_digits_list):
digits_sum += digit * (10 - index)
# Find the remainder of dividing the sum by 11, then subtract it from 11
result = 11 - digits_sum % 11
# The calculation result can range from 1 to 11.
# If the result is 11, use 0.
# If the result is 10, use upper case X.
# Use the value as it is for other numbers.
if result == 11:
expected_check_digit = '0'
elif result == 10:
expected_check_digit = 'X'
else:
expected_check_digit = str(result)
return expected_check_digit
def calculate_check_digit_13(main_digits_list):
# Note: You don't have to fully understand the logic in this function.
digits_sum = 0
# Multiply each of the first 12 digits by 1 and 3 alternately (starting with 1), and sum up the results
for index, digit in enumerate(main_digits_list):
if index % 2 == 0:
digits_sum += digit * 1
else:
digits_sum += digit * 3
# Find the remainder of dividing the sum by 10, then subtract it from 10
result = 10 - digits_sum % 10
# The calculation result can range from 1 to 10.
# If the result is 10, use 0.
# Use the value as it is for other numbers.
if result == 10:
expected_check_digit = '0'
else:
expected_check_digit = str(result)
return expected_check_digit
def main():
user_input = input('Enter ISBN and length: ')
values = user_input.split(',')
isbn = values[0]
length = int(values[1])
if length == 10 or length == 13:
validate_isbn(isbn, length)
else:
print('Length should be 10 or 13.')
main()
```
# --solutions--
```py
def validate_isbn(isbn, length):
if len(isbn) != length:
print(f'ISBN-{length} code should be {length} digits long.')
return
check_digit_index = length - 1
main_digits = isbn[0:check_digit_index]
given_check_digit = isbn[check_digit_index]
try:
main_digits_list = [int(digit) for digit in main_digits]
except ValueError as e:
print('Invalid character was found.')
return
# Calculate the check digit from other digits
if length == 10:
expected_check_digit = calculate_check_digit_10(main_digits_list)
else:
expected_check_digit = calculate_check_digit_13(main_digits_list)
# Check if the given check digit matches with the calculated check digit
if given_check_digit == expected_check_digit:
print('Valid ISBN Code.')
else:
print('Invalid ISBN Code.')
def calculate_check_digit_10(main_digits_list):
# Note: You don't have to fully understand the logic in this function.
digits_sum = 0
# Multiply each of the first 9 digits by its corresponding weight (10 to 2) and sum up the results
for index, digit in enumerate(main_digits_list):
digits_sum += digit * (10 - index)
# Find the remainder of dividing the sum by 11, then subtract it from 11
result = 11 - digits_sum % 11
# The calculation result can range from 1 to 11.
# If the result is 11, use 0.
# If the result is 10, use upper case X.
# Use the value as it is for other numbers.
if result == 11:
expected_check_digit = '0'
elif result == 10:
expected_check_digit = 'X'
else:
expected_check_digit = str(result)
return expected_check_digit
def calculate_check_digit_13(main_digits_list):
# Note: You don't have to fully understand the logic in this function.
digits_sum = 0
# Multiply each of the first 12 digits by 1 and 3 alternately (starting with 1), and sum up the results
for index, digit in enumerate(main_digits_list):
if index % 2 == 0:
digits_sum += digit * 1
else:
digits_sum += digit * 3
# Find the remainder of dividing the sum by 10, then subtract it from 10
result = 10 - digits_sum % 10
# The calculation result can range from 1 to 10.
# If the result is 10, use 0.
# Use the value as it is for other numbers.
if result == 10:
expected_check_digit = '0'
else:
expected_check_digit = str(result)
return expected_check_digit
def main():
user_input = input('Enter ISBN and length: ')
values = user_input.split(',')
isbn = values[0]
try:
length = int(values[1])
except ValueError as e:
print('Length must be a number.')
return
except IndexError as e:
print('Enter comma-separated values.')
return
if length == 10 or length == 13:
validate_isbn(isbn, length)
else:
print('Length should be 10 or 13.')
# main()
```