Coverage for oc_ds_converter / oc_idmanager / isbn.py: 88%

60 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-25 18:06 +0000

1# SPDX-FileCopyrightText: 2023 Arcangelo Massari <arcangelo.massari@unibo.it> 

2# SPDX-FileCopyrightText: 2024 Arianna Moretti <arianna.moretti4@unibo.it> 

3# 

4# SPDX-License-Identifier: ISC 

5 

6 

7import re 

8from re import match, sub 

9 

10from oc_ds_converter.oc_idmanager.base import IdentifierManager 

11 

12 

13class ISBNManager(IdentifierManager): 

14 """This class implements an identifier manager for isbn identifier""" 

15 def __init__(self, data={}): 

16 """ISBN manager constructor.""" 

17 self._p = "isbn:" 

18 self._data = data 

19 super(ISBNManager, self).__init__() 

20 

21 def is_valid(self, id_string, get_extra_info=False): 

22 isbn = self.normalise(id_string, include_prefix=True) 

23 if isbn is None: 

24 return False 

25 else: 

26 if isbn not in self._data or self._data[isbn] is None: 

27 self._data[isbn] = {"valid":self.check_digit(isbn) and self.syntax_ok(isbn)} 

28 return ( 

29 self.check_digit(isbn) 

30 and self.syntax_ok(isbn) 

31 ) 

32 return self._data[isbn].get("valid") 

33 

34 def normalise(self, id_string, include_prefix=False): 

35 try: 

36 isbn_string = sub("[^X0-9]", "", id_string.upper()) 

37 return "%s%s" % (self._p if include_prefix else "", isbn_string) 

38 except: # Any error in processing the ISBN will return None 

39 return None 

40 

41 def check_digit(self, isbn): 

42 if isbn.startswith(self._p): 

43 spl = isbn.find(self._p) + len(self._p) 

44 isbn = isbn[spl:] 

45 

46 isbn = isbn.replace("-", "") 

47 isbn = isbn.replace(" ", "") 

48 check_digit = False 

49 if len(isbn) == 13: 

50 total = 0 

51 val = 1 

52 for x in isbn: 

53 if x == "X": 

54 x = 10 

55 total += int(x)*val 

56 val = 3 if val == 1 else val == 1 

57 if (total % 10) == 0: 

58 check_digit = True 

59 elif len(isbn) == 10: 

60 total = 0 

61 val = 10 

62 for x in isbn: 

63 if x == "X": 

64 x = 10 

65 total += int(x)*val 

66 val -= 1 

67 if (total % 11) == 0: 

68 check_digit = True 

69 

70 return check_digit 

71 

72 def syntax_ok(self, id_string): 

73 id_string.replace(" ", "") 

74 id_string.replace("-", "") 

75 if not id_string.startswith(self._p): 

76 id_string = self._p+id_string 

77 if len(id_string) - len(self._p) == 13: 

78 return True if match("^isbn:97[89][0-9X]{10}$", id_string, re.IGNORECASE) else False 

79 elif len(id_string) - len(self._p) == 10: 

80 return True if match("^isbn:[0-9X]{10}$", id_string, re.IGNORECASE) else False 

81 else: 

82 return False 

83