diff --git a/config.json b/config.json index c94203fd60e..f8012f219b6 100644 --- a/config.json +++ b/config.json @@ -901,6 +901,21 @@ "stacks" ] }, + { + "uuid": "7961c852-c87a-44b0-b152-efea3ac8555c", + "slug": "isbn-verifier", + "core": false, + "unlocked_by": null, + "difficulty": 3, + "topics": [ + "type_conversion", + "conditionals", + "strings", + "arrays", + "integers", + "parsing" + ] + }, { "uuid": "e7351e8e-d3ff-4621-b818-cd55cf05bffd", "slug": "accumulate", diff --git a/exercises/isbn-verifier/README.md b/exercises/isbn-verifier/README.md new file mode 100644 index 00000000000..4da3a15ffde --- /dev/null +++ b/exercises/isbn-verifier/README.md @@ -0,0 +1,43 @@ +# Isbn Verifier + +Check if a given ISBN-10 is valid. + +## Functionality + +Given an unkown string the program should check if the provided string is a valid ISBN-10. +Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN. + +The program should allow for ISBN-10 without the separating dashes to be verified as well. + +## ISBN + +Let's take a random ISBN-10 number, say `3-598-21508-8` for this. +The first digit block indicates the group where the ISBN belongs. Groups can consist of shared languages, geographic regions or countries. The leading '3' signals this ISBN is from a german speaking country. +The following number block is to identify the publisher. Since this is a three digit publisher number there is a 5 digit title number for this book. +The last digit in the ISBN is the check digit which is used to detect read errors. + +The first 9 digits in the ISBN have to be between 0 and 9. +The check digit can additionally be an 'X' to allow 10 to be a valid check digit as well. + +A valid ISBN-10 is calculated with this formula `(x1 * 10 + x2 * 9 + x3 * 8 + x4 * 7 + x5 * 6 + x6 * 5 + x7 * 4 + x8 * 3 + x9 * 2 + x10 * 1) mod 11 == 0` +So for our example ISBN this means: +(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 = 0 + +Which proves that the ISBN is valid. + +### Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/` directory. + +For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit /python/bob/bob.py`. + + +For more detailed information about running tests, code style and linting, +please see the [help page](http://exercism.io/languages/python). + +## Source + +Converting a string into a number and some basic processing utilizing a relatable real world example. [https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation](https://en.wikipedia.org/wiki/International_Standard_Book_Number) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/exercises/isbn-verifier/exemple.py b/exercises/isbn-verifier/exemple.py new file mode 100644 index 00000000000..153de2436e6 --- /dev/null +++ b/exercises/isbn-verifier/exemple.py @@ -0,0 +1,46 @@ +def verify(isbn): + clear_isbn = remove_non_alphanumeric(isbn) + if len(clear_isbn) != 10: + return False + + isbn_main_part = get_isbn_main_part(clear_isbn) + if not isbn_main_part.isdigit(): + return False + + check_digit = get_isbn_check_digit(clear_isbn) + if not check_digit.isdigit() and check_digit.upper() != 'X': + return False + + if calculate_isbn_check_digit(isbn_main_part) != check_digit: + return False + + return True + + +def remove_non_alphanumeric(value): + return ''.join([x for x in str(value) if x.isalnum()]) + + +def get_isbn_main_part(isbn_value): + return isbn_value[:-1] + + +def get_isbn_check_digit(isbn_value): + return isbn_value[-1:] + + +def calculate_isbn_check_digit(isbn): + check_digit = 11 - (int(isbn[0:1]) * 10 + + int(isbn[1:2]) * 9 + + int(isbn[2:3]) * 8 + + int(isbn[3:4]) * 7 + + int(isbn[4:5]) * 6 + + int(isbn[5:6]) * 5 + + int(isbn[6:7]) * 4 + + int(isbn[7:8]) * 3 + + int(isbn[8:9]) * 2) % 11 + + if check_digit == 10: + return 'X' + + return str(check_digit) diff --git a/exercises/isbn-verifier/isbn_verifier.py b/exercises/isbn-verifier/isbn_verifier.py new file mode 100644 index 00000000000..b00fcc6a45e --- /dev/null +++ b/exercises/isbn-verifier/isbn_verifier.py @@ -0,0 +1,2 @@ +def verify(isbn): + pass diff --git a/exercises/isbn-verifier/isbn_verifier_test.py b/exercises/isbn-verifier/isbn_verifier_test.py new file mode 100644 index 00000000000..38c6c40c5b6 --- /dev/null +++ b/exercises/isbn-verifier/isbn_verifier_test.py @@ -0,0 +1,51 @@ +import unittest + +from isbn_verifier import verify + + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0 + +class IsbnVerifierTests(unittest.TestCase): + + def test_valid_isbn(self): + self.assertIs(verify('3-598-21508-8'), True) + + def test_invalid_check_digit(self): + self.assertIs(verify('3-598-21508-9'), False) + + def test_valid_with_X_check_digit(self): + self.assertIs(verify('3-598-21507-X'), True) + + def test_invalid_check_digit_other_than_X(self): + self.assertIs(verify('3-598-21507-A'), False) + + def test_invalid_character_in_isbn(self): + self.assertIs(verify('3-598-2K507-0'), False) + + def test_invalid_X_other_than_check_digit(self): + self.assertIs(verify('3-598-2X507-0'), False) + + def test_valid_isbn_without_separating_dashes(self): + self.assertIs(verify('3598215088'), True) + + def test_valid_isbn_without_separating_dashes_with_X_check_digit(self): + self.assertIs(verify('359821507X'), True) + + def test_invalid_isbn_without_check_digit_and_dashes(self): + self.assertIs(verify('359821507'), False) + + def test_invalid_too_long_isbn_with_no_dashes(self): + self.assertIs(verify('3598215078X'), False) + + def test_invalid_isbn_without_check_digit(self): + self.assertIs(verify('3-598-21507'), False) + + def test_invalid_too_long_isbn(self): + self.assertIs(verify('3-598-21507-XA'), False) + + def test_invalid_check_digit_X_used_for_0(self): + self.assertIs(verify('3-598-21515-X'), False) + + +if __name__ == '__main__': + unittest.main()