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
« 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
4from .locale import Locale
6T = TypeVar("T", bound="Individual")
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}
22class MergeDifferentPersonsError(Exception):
23 """Error raised when merge two persons/officers who are different"""
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.
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]
36 @abc.abstractmethod
37 def merge(self: T, other: T):
38 """Merge two individual instances.
40 :param other: Other individual to merge into this one
41 """
43 @abc.abstractmethod
44 def to_dict(self: T):
45 """Serialize the instance to a dict.
47 :returns: kwargs that can be used to instantiate a new instance that is
48 equal to the current one.
49 """
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
78 def name_rep(self):
79 """Get the individual's name patterns.
81 :returns: List of patterns for this individual
82 """
83 patterns = self._name_rep_impl()
84 return self.clean_patterns(patterns)
86 @abc.abstractmethod
87 def _name_rep_impl(self: T) -> List[str]:
88 """Create a list of patterns to match this name.
90 :returns: List of patterns for this individual
91 """