diff --git a/config.json b/config.json index f2be5794644..68caabad3f1 100644 --- a/config.json +++ b/config.json @@ -80,6 +80,20 @@ "transforming" ] }, + { + "uuid": "7961c852-c87a-44b0-b152-efea3ac8555c", + "slug": "isbn-verifier", + "core": false, + "unlocked_by": null, + "difficulty": 1, + "topics": [ + "type_conversion", + "conditionals", + "strings", + "integers", + "parsing" + ] + }, { "uuid": "8648fa0c-d85f-471b-a3ae-0f8c05222c89", "slug": "hamming", 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/example.py b/exercises/isbn-verifier/example.py new file mode 100644 index 00000000000..f3cb3f49deb --- /dev/null +++ b/exercises/isbn-verifier/example.py @@ -0,0 +1,8 @@ +def verify(isbn): + chars = list(isbn.replace('-', '')) + if chars and chars[-1] == 'X': + chars[-1] = '10' + if not len(chars) == 10 or not all(c.isdigit() for c in chars): + return False + indices = list(range(10, 0, -1)) + return sum(int(c) * i for c, i in zip(chars, indices)) % 11 == 0 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..30470b9ac9b --- /dev/null +++ b/exercises/isbn-verifier/isbn_verifier_test.py @@ -0,0 +1,54 @@ +import unittest + +from isbn_verifier import verify + + +# Tests adapted from `problem-specifications//canonical-data.json` @ v2.2.0 + +class IsbnVerifierTests(unittest.TestCase): + + def test_valid_isbn_number(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-9'), 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-XX'), False) + + def test_invalid_check_digit_X_used_for_0(self): + self.assertIs(verify('3-598-21515-X'), False) + + def test_valid_empty_isbn(self): + self.assertIs(verify(''), False) + + +if __name__ == '__main__': + unittest.main()