Coverage for blind_charging/individual.py: 98%

43 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-17 20:36 +0000

1import abc 

2from typing import Iterable, List, Optional, Type, TypeVar 

3 

4from .locale import Locale 

5 

6T = TypeVar("T", bound="Individual") 

7 

8 

9# Regular expression patterns that don't have any real meaning in terms of 

10# name matching. 

11EMPTY_PATTERNS = { 

12 r"", 

13 r"\b-\b", 

14 r"\b-", 

15 r"-\b", 

16 r"-", 

17 r"\s+", 

18 r"\s*", 

19} 

20 

21 

22class MergeDifferentPersonsError(Exception): 

23 """Error raised when merge two persons/officers who are different""" 

24 

25 

26class Individual(abc.ABC): 

27 @classmethod 

28 def clean_patterns(cls, patterns: Iterable[str]) -> List[str]: 

29 """Ensure that patterns are all meaningful for name matching. 

30 

31 :param patterns: List of patterns to check 

32 :returns: List with bad patterns filtered out. 

33 """ 

34 return [p for p in patterns if p not in EMPTY_PATTERNS] 

35 

36 @abc.abstractmethod 

37 def merge(self: T, other: T): 

38 """Merge two individual instances. 

39 

40 :param other: Other individual to merge into this one 

41 """ 

42 

43 @abc.abstractmethod 

44 def to_dict(self: T): 

45 """Serialize the instance to a dict. 

46 

47 :returns: kwargs that can be used to instantiate a new instance that is 

48 equal to the current one. 

49 """ 

50 

51 @classmethod 

52 @abc.abstractmethod 

53 def dedupe(cls: Type[T], individuals: List[T], locale: Locale) -> List[T]: 

54 """De-duplicate a list of individuals.""" 

55 persons: List[T] = [] 

56 mentions: List[Optional[T]] = list(individuals) 

57 i = 0 

58 while i < len(mentions): 

59 person_a = mentions[i] 

60 if person_a is not None: 

61 changed = False 

62 for j in range(i + 1, len(mentions)): 

63 person_b = mentions[j] 

64 if person_b is not None: 

65 if person_a == person_b: 

66 person_a.merge(person_b) 

67 mentions[j] = None 

68 changed = True 

69 if not changed: 

70 if not person_a: 

71 raise ValueError("Trying to add missing person to list") 

72 persons.append(person_a) 

73 i += 1 

74 else: 

75 i += 1 

76 return persons 

77 

78 def name_rep(self): 

79 """Get the individual's name patterns. 

80 

81 :returns: List of patterns for this individual 

82 """ 

83 patterns = self._name_rep_impl() 

84 return self.clean_patterns(patterns) 

85 

86 @abc.abstractmethod 

87 def _name_rep_impl(self: T) -> List[str]: 

88 """Create a list of patterns to match this name. 

89 

90 :returns: List of patterns for this individual 

91 """