Skip to content

Instantly share code, notes, and snippets.

@martianboy
Last active October 8, 2015 20:14
Show Gist options
  • Save martianboy/5850da55cffd68a39edd to your computer and use it in GitHub Desktop.
Save martianboy/5850da55cffd68a39edd to your computer and use it in GitHub Desktop.
Iranian ISBN Validator and Converter.
'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();
"""
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