Loading poolpay/fints/to_compatibility_regex.py 0 → 100644 +33 −0 Original line number Diff line number Diff line from functools import reduce REPLACEMENTS = { "ä": "(ä|ae|a)", "ö": "(ö|oe|o)", "ü": "(ü|ue|u)", "ß": "(ß|ss)", "Ä": "(Ä|Ae|A)", "Ö": "(Ö|Oe|O)", "Ü": "(Ü|Ue|U)", "ẞ": "(ẞ|SS)", } def to_compatibility_regex(evil_unicode_string: str) -> str: """ > Schon unsere Großväter wussten, was gut ist: Ein selbstbereiteter String mit irgendwelchen Zeichen drin. > Doch sie wussten auch ein Lied davon zu singen, wie schwer es ist, so einen String zu verarbeiten. > Ja, so war es damals. > > Und heute? Converts a normal string, which might contain evil characters, such as ä, ö, ü, or ß, to a regular expression matching variants of those characters which you can use if you are a German bank and have never heard of Unicode before. :see: https://www.youtube.com/watch?v=P56pk5mDewU """ return reduce( lambda string, replacement: string.replace(*replacement), REPLACEMENTS.items(), evil_unicode_string, ) poolpay/model/Person.py +22 −3 Original line number Diff line number Diff line Loading @@ -4,9 +4,10 @@ from typing import TYPE_CHECKING, Literal, Self from babel.numbers import format_currency from sqlalchemy import select from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, QueryableAttribute, mapped_column, relationship from poolpay import db from poolpay.fints.to_compatibility_regex import to_compatibility_regex from poolpay.model.Base import Base if TYPE_CHECKING: Loading Loading @@ -133,9 +134,19 @@ class Person(Base): If no persons can be found or there are too many plausible matches, None is returned. """ # TODO Improve heuristics # We currently ignore middle names first_name, *_, last_name = full_name.split(" ") # We try to deal with umlauts etc. first_name_regex = to_compatibility_regex(first_name) last_name_regex = to_compatibility_regex(last_name) def _try_get_single_person( field: QueryableAttribute, regex: str ) -> Self | None: persons = ( db.session.execute(select(cls).where(cls.name == full_name.split(" ")[0])) db.session.execute(select(cls).where(field.regexp_match(regex))) .scalars() .all() ) Loading @@ -144,3 +155,11 @@ class Person(Base): return None return persons[0] potential_last_name_match = _try_get_single_person( cls.cl_account_name, last_name_regex.lower() ) if potential_last_name_match: return potential_last_name_match return _try_get_single_person(cls.name, first_name_regex) Loading
poolpay/fints/to_compatibility_regex.py 0 → 100644 +33 −0 Original line number Diff line number Diff line from functools import reduce REPLACEMENTS = { "ä": "(ä|ae|a)", "ö": "(ö|oe|o)", "ü": "(ü|ue|u)", "ß": "(ß|ss)", "Ä": "(Ä|Ae|A)", "Ö": "(Ö|Oe|O)", "Ü": "(Ü|Ue|U)", "ẞ": "(ẞ|SS)", } def to_compatibility_regex(evil_unicode_string: str) -> str: """ > Schon unsere Großväter wussten, was gut ist: Ein selbstbereiteter String mit irgendwelchen Zeichen drin. > Doch sie wussten auch ein Lied davon zu singen, wie schwer es ist, so einen String zu verarbeiten. > Ja, so war es damals. > > Und heute? Converts a normal string, which might contain evil characters, such as ä, ö, ü, or ß, to a regular expression matching variants of those characters which you can use if you are a German bank and have never heard of Unicode before. :see: https://www.youtube.com/watch?v=P56pk5mDewU """ return reduce( lambda string, replacement: string.replace(*replacement), REPLACEMENTS.items(), evil_unicode_string, )
poolpay/model/Person.py +22 −3 Original line number Diff line number Diff line Loading @@ -4,9 +4,10 @@ from typing import TYPE_CHECKING, Literal, Self from babel.numbers import format_currency from sqlalchemy import select from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, QueryableAttribute, mapped_column, relationship from poolpay import db from poolpay.fints.to_compatibility_regex import to_compatibility_regex from poolpay.model.Base import Base if TYPE_CHECKING: Loading Loading @@ -133,9 +134,19 @@ class Person(Base): If no persons can be found or there are too many plausible matches, None is returned. """ # TODO Improve heuristics # We currently ignore middle names first_name, *_, last_name = full_name.split(" ") # We try to deal with umlauts etc. first_name_regex = to_compatibility_regex(first_name) last_name_regex = to_compatibility_regex(last_name) def _try_get_single_person( field: QueryableAttribute, regex: str ) -> Self | None: persons = ( db.session.execute(select(cls).where(cls.name == full_name.split(" ")[0])) db.session.execute(select(cls).where(field.regexp_match(regex))) .scalars() .all() ) Loading @@ -144,3 +155,11 @@ class Person(Base): return None return persons[0] potential_last_name_match = _try_get_single_person( cls.cl_account_name, last_name_regex.lower() ) if potential_last_name_match: return potential_last_name_match return _try_get_single_person(cls.name, first_name_regex)