Last active
October 8, 2015 20:14
-
-
Save martianboy/5850da55cffd68a39edd to your computer and use it in GitHub Desktop.
Iranian ISBN Validator and Converter.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var isbn_groups = { | |
'964': [ | |
[0, 14, 2], | |
[150, 249, 3], | |
[300, 549, 3], | |
[970, 989, 3], | |
[2500, 2999, 4], | |
[5500, 8999, 4], | |
[9900, 9999, 4], | |
[90000, 96999, 5] | |
], | |
'600': [ | |
[0, 9, 2], // 00-09 | |
[100, 499, 3], | |
[5000, 8999, 4], | |
[90000, 99999, 5] | |
] | |
}; | |
function reverse(s) { | |
var o = ''; | |
for (var i = s.length - 1; i >= 0; i--) | |
o += s[i]; | |
return o; | |
} | |
function parseIsbnDigit(c) { | |
if (c === 'X' || c === 'x') | |
return 10; | |
else | |
return parseInt(c); | |
} | |
function validate_isbn(isbn) { | |
/* Validates a given 10- or 13-digit isbn. */ | |
let weighted_sum = 0; | |
if (isbn.length === 13) { | |
if (!isbn.startsWith('978600') && !isbn.startsWith('978964')) | |
return false; | |
for (let i = 13; i > 0; i--) | |
weighted_sum += (i % 2 === 0 ? 3 : 1) * parseIsbnDigit(isbn[13 - i]); | |
return weighted_sum % 10 === 0; | |
} | |
if (isbn.length === 10) { | |
if (!isbn.startsWith('600') && !isbn.startsWith('964')) | |
return false; | |
for (let i = 10; i > 0; i--) | |
weighted_sum += i * parseIsbnDigit(isbn[10 - i]); | |
return weighted_sum % 11 === 0; | |
} | |
return false; | |
} | |
function preprocess_isbn(isbn) { | |
/* | |
* 1. Check if there's 10 or 13 numbers used in the isbn | |
* 2. Check if it starts with 964 or 600 for 10-digit isbn | |
* 3. If not, check if it ends with 964 or 600 for 10-digit isbn. If so, reverse the isbn | |
* 4. Check if it starts with 978 followed by 964 or 600 for 13-digit isbn | |
* 5. If not, check if it ends with 964978 or 600978 for 13-digit. If so, reverse the isbn | |
* 6. Check if the resulting isbn is valid using the parity check. | |
*/ | |
let numerical_isbn = isbn.replace(/[^0-9xX]/g, ''); | |
if (!validate_isbn(numerical_isbn)) { | |
// Assume reversed ISBN | |
const is_isbn_13 = numerical_isbn.length === 13; | |
if (is_isbn_13) | |
numerical_isbn = numerical_isbn.substr(0, 10); | |
if (!numerical_isbn.endsWith('964') && !numerical_isbn.endsWith('600')) | |
throw new RangeError('Not a valid ISBN!'); | |
const publisher_code = get_reverse_publisher_code(numerical_isbn); | |
numerical_isbn = (is_isbn_13 ? '978' : '') + publisher_code + | |
numerical_isbn.substr(1, 10 - publisher_code.length - 1) + | |
numerical_isbn[0]; | |
if (!validate_isbn(numerical_isbn)) | |
throw new RangeError('Invalid ISBN!'); | |
} | |
return convert_to_isbn_10(numerical_isbn); | |
} | |
function convert_to_isbn_13(isbn_10) { | |
if (isbn_10.length === 13) | |
return isbn_10; | |
if (isbn_10.length !== 10) | |
throw new RangeError('Not a ISBN-10 code!'); | |
let isbn_13 = '978' + isbn_10; | |
let weighted_sum = 0; | |
for (let i = 13; i > 1; i--) | |
weighted_sum += (i % 2 === 0 ? 3 : 1) * parseIsbnDigit(isbn_13[13 - i]); | |
let parity_digit = 10 - weighted_sum % 10; | |
return isbn_13.substr(0, 12) + parity_digit.toString(); | |
} | |
function convert_to_isbn_10(isbn_13) { | |
if (isbn_13.length === 10) | |
return isbn_13; | |
if (isbn_13.length !== 13) | |
throw new RangeError('Not a ISBN-13 code!'); | |
let isbn_10 = isbn_13.substr(3); | |
let weighted_sum = 0; | |
for (let i = 10; i > 1; i--) | |
weighted_sum += i * parseIsbnDigit(isbn_10[10 - i]); | |
let parity_digit = (11 - weighted_sum % 11) % 11; | |
return isbn_10.substr(0, 9) + parity_digit.toString(); | |
} | |
function get_reverse_publisher_code(reverse_isbn) { | |
const code = reverse_isbn.substr(7); | |
const bounds = isbn_groups[code]; | |
for (let bound of bounds) { | |
let publisher_code = reverse_isbn.substr(10 - bound[2] - 3, bound[2]); | |
// console.log('Testing publisher_code: ' + publisher_code + ' must be in [' + bound[0] + ', ' + bound[1] + '];'); | |
if (parseInt(publisher_code) >= bound[0] && parseInt(publisher_code) <= bound[1]) | |
return code + publisher_code; | |
} | |
} | |
function get_publisher_code(isbn) { | |
const code = isbn.substr(0, 3); | |
const bounds = isbn_groups[code]; | |
for (let bound of bounds) { | |
let publisher_code = isbn.substr(3, bound[2]); | |
// console.log('Testing publisher_code: ' + publisher_code + ' must be in [' + bound[0] + ', ' + bound[1] + '];'); | |
if (parseInt(publisher_code) >= bound[0] && parseInt(publisher_code) <= bound[1]) | |
return code + publisher_code; | |
} | |
} | |
function test_convert_13_to_10() { | |
console.assert(convert_to_isbn_10('9789643342661') === '9643342662') | |
} | |
function test_convert_10_to_13() { | |
console.assert(convert_to_isbn_13('9643342662') === '9789643342661') | |
} | |
function test_get_publisher_code() { | |
console.assert(get_publisher_code('9643342662') === '964334'); | |
console.assert(get_publisher_code('6009298433') === '60092984'); | |
} | |
function test_get_reverse_publisher_code() { | |
console.assert(get_reverse_publisher_code('2266334964') === '964334'); | |
console.assert(get_reverse_publisher_code('3392984600') === '60092984'); | |
} | |
function test_validate_isbn() { | |
console.assert(validate_isbn('9789643058722') === true); | |
console.assert(validate_isbn('9789643342662') === false); | |
console.assert(validate_isbn('9643342662') === true); | |
console.assert(validate_isbn('9786009298433') === true); | |
console.assert(validate_isbn('6009298433') === false); | |
} | |
function test_preprocess_isbn() { | |
console.assert(preprocess_isbn('978-964-305-872-2') === '9643058727'); | |
console.assert(preprocess_isbn('964-334-266-2') === '9643342662'); | |
console.assert(preprocess_isbn('2-266-334-964') === '9643342662'); | |
console.assert(preprocess_isbn('3-3-92984-600-978') === '6009298431'); | |
} | |
test_convert_13_to_10(); | |
test_convert_10_to_13(); | |
test_get_reverse_publisher_code(); | |
test_get_publisher_code(); | |
test_validate_isbn(); | |
test_preprocess_isbn(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
ISBN factory, functions about isbn like checking text if is isbn or not, formatting isbn... | |
""" | |
import re | |
from persian import persian_num_to_english | |
from strings import get_digits, insert | |
isbn_groups = { | |
'964': [ | |
(0, 14, 2), | |
(150, 249, 3), | |
(300, 549, 3), | |
(970, 989, 3), | |
(2500, 2999, 4), | |
(5500, 8999, 4), | |
(9900, 9999, 4), | |
(90000, 96999, 5) | |
], | |
'600': [ | |
(0, 9, 2), # 00-09 | |
(100, 499, 3), | |
(5000, 8999, 4), | |
(90000, 99999, 5) | |
] | |
} | |
def parseIsbnDigit(c): | |
if c == 'X' or c == 'x': | |
return 10 | |
else: | |
return int(c) | |
def validate_isbn(isbn): | |
""" Validates a given 10- or 13-digit isbn. """ | |
# print 'Validating ISBN code', isbn | |
weighted_sum = 0 | |
if len(isbn) == 13: | |
if not isbn.startswith('978600') and not isbn.startswith('978964'): | |
return False | |
for i in xrange(13, 0, -1): | |
weighted_sum += (3 if i % 2 == 0 else 1) * parseIsbnDigit(isbn[13 - i]) | |
# print weighted_sum | |
return weighted_sum % 10 == 0 | |
if len(isbn) == 10: | |
if not isbn.startswith('600') and not isbn.startswith('964'): | |
return False; | |
for i in xrange(10, 0, -1): | |
weighted_sum += i * parseIsbnDigit(isbn[10 - i]) | |
# print weighted_sum | |
return weighted_sum % 11 == 0 | |
return False | |
def preprocess_isbn(isbn): | |
""" | |
1. Check if there's 10 or 13 numbers used in the isbn | |
2. Check if it starts with 964 or 600 for 10-digit isbn | |
3. If not, check if it ends with 964 or 600 for 10-digit isbn. If so, reverse the isbn | |
4. Check if it starts with 978 followed by 964 or 600 for 13-digit isbn | |
5. If not, check if it ends with 964978 or 600978 for 13-digit. If so, reverse the isbn | |
6. Check if the resulting isbn is valid using the parity check. | |
""" | |
numerical_isbn = re.sub(r'[^0-9xX]', '', isbn) | |
if not validate_isbn(numerical_isbn): | |
# Assume reversed ISBN | |
is_isbn_13 = len(numerical_isbn) == 13 | |
if is_isbn_13: | |
numerical_isbn = numerical_isbn[0:10] | |
if not numerical_isbn.endswith('964') and not numerical_isbn.endswith('600'): | |
raise ValueError('Not a valid ISBN!') | |
publisher_code = get_reverse_publisher_code(numerical_isbn) | |
numerical_isbn = ('978' if is_isbn_13 else '') + publisher_code + \ | |
numerical_isbn[1:10 - len(publisher_code)] + \ | |
numerical_isbn[0] | |
if not validate_isbn(numerical_isbn): | |
raise ValueError('Invalid ISBN! ({})'.format(numerical_isbn)) | |
return numerical_isbn | |
def convert_to_isbn_13(isbn_10): | |
if len(isbn_10) == 13: | |
return isbn_10 | |
if len(isbn_10) != 10: | |
raise ValueError('Not a ISBN-10 code!') | |
isbn_13 = '978' + isbn_10 | |
weighted_sum = 0 | |
for i in xrange(13, 1, -1): | |
weighted_sum += (3 if i % 2 == 0 else 1) * parseIsbnDigit(isbn_13[13 - i]) | |
parity_digit = 10 - weighted_sum % 10 | |
return isbn_13[0:12] + str(parity_digit) | |
def convert_to_isbn_10(isbn_13): | |
if len(isbn_13) == 10: | |
return isbn_13 | |
if len(isbn_13) != 13: | |
raise ValueError('Not a ISBN-13 code!') | |
isbn_10 = isbn_13[3:] | |
weighted_sum = 0 | |
for i in xrange(10, 1, -1): | |
weighted_sum += i * parseIsbnDigit(isbn_10[10 - i]) | |
parity_digit = (11 - weighted_sum % 11) % 11 | |
return isbn_10[0:9] + str(parity_digit) | |
def get_reverse_publisher_code(reverse_isbn): | |
# print 'Get reverse publisher code for ', reverse_isbn | |
code = reverse_isbn[7:] | |
bounds = isbn_groups[code] | |
for bound in bounds: | |
publisher_code = reverse_isbn[10 - bound[2] - 3:10 - 3] | |
# print('Testing publisher_code: ' + publisher_code + ' must be in [' + str(bound[0]) + ', ' + str(bound[1]) + ']') | |
if int(publisher_code) >= bound[0] and int(publisher_code) <= bound[1]: | |
return code + publisher_code | |
def get_publisher_code(isbn): | |
code = isbn[0:3] | |
bounds = isbn_groups[code] | |
for bound in bounds: | |
publisher_code = isbn[3:3 + bound[2]] | |
# print('Testing publisher_code: ' + publisher_code + ' must be in [' + str(bound[0]) + ', ' + str(bound[1]) + ']') | |
if int(publisher_code) >= bound[0] and int(publisher_code) <= bound[1]: | |
return code + publisher_code | |
def format_isbn(isbn): | |
""" | |
format isbn, push dashes between isbn numbers in correct isbn format | |
for example: convert the isbn: 9646409334 to isbn: 964-6409-33-4 | |
:param isbn: | |
:type: str | |
:return: formatted isbn | |
:rtype: str | |
""" | |
isbn0 = re.sub(r'[^0-9xX]', '', isbn) | |
publisher_code = get_publisher_code(isbn0[-10:]) | |
formatted_isbn = insert(publisher_code, '-', 3) + '-' | |
is_isbn_13 = len(isbn0) == 13 | |
if is_isbn_13: | |
formatted_isbn = '978-' + formatted_isbn | |
formatted_isbn += isbn0[(3 if is_isbn_13 else 0) + len(publisher_code):-1] + '-' + isbn0[-1] | |
# print formatted_isbn | |
return formatted_isbn | |
def test_convert_13_to_10(): | |
assert convert_to_isbn_10('9789643342661') == '9643342662' | |
def test_convert_10_to_13(): | |
assert(convert_to_isbn_13('9643342662') == '9789643342661') | |
def test_get_publisher_code(): | |
assert(get_publisher_code('9643342662') == '964334') | |
assert(get_publisher_code('6009298433') == '60092984') | |
def test_get_reverse_publisher_code(): | |
assert(get_reverse_publisher_code('2266334964') == '964334') | |
assert(get_reverse_publisher_code('3392984600') == '60092984') | |
def test_validate_isbn(): | |
assert(validate_isbn('9789643058722') == True) | |
assert(validate_isbn('9789643342662') == False) | |
assert(validate_isbn('9643342662') == True) | |
assert(validate_isbn('9786009298433') == True) | |
assert(validate_isbn('6009298433') == False) | |
def test_preprocess_isbn(): | |
assert(preprocess_isbn('978-964-305-872-2') == '9789643058722') | |
assert(preprocess_isbn('964-334-266-2') == '9643342662') | |
assert(preprocess_isbn('2-266-334-964') == '9643342662') | |
assert(preprocess_isbn('3-3-92984-600-978') == '9786009298433') | |
def test_format_isbn(): | |
assert(format_isbn('9643342662') == '964-334-266-2') | |
assert(format_isbn('9789643342661') == '978-964-334-266-1') | |
if __name__ == '__main__': | |
test_convert_13_to_10() | |
test_convert_10_to_13() | |
test_get_reverse_publisher_code() | |
test_get_publisher_code() | |
test_validate_isbn() | |
test_preprocess_isbn() | |
test_format_isbn() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment