From afa6ef61d4978063fa060daebda7f14508212196 Mon Sep 17 00:00:00 2001 From: trnq1ll0 Date: Wed, 27 May 2026 18:48:04 +0200 Subject: [PATCH] =?UTF-8?q?beide=20Datens=C3=A4tze=20verbunden=20=C3=BCber?= =?UTF-8?q?=20die=20neu=20erstellte=20Spalte=20T=C3=A4tigkeitsschl=C3=BCss?= =?UTF-8?q?el=20und=20Ausgaben=20in=20Deutsch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- input/Stellenbeschreibungen.xlsx | Bin 9846 -> 7099 bytes src/taetigkeitsnachweis/matcher.py | 60 ++++++++++++++++++++--------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/input/Stellenbeschreibungen.xlsx b/input/Stellenbeschreibungen.xlsx index 3dea5822183c90c7619988a9b15f953c647410c6..f073970425c54dad5737f953e16de68af27204e5 100644 GIT binary patch literal 7099 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 literal 9846 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 Z!BSNL3AVZbz!TUf04C-uzwLej{|7vWYO4SM diff --git a/src/taetigkeitsnachweis/matcher.py b/src/taetigkeitsnachweis/matcher.py index 9748b92..36a089a 100644 --- a/src/taetigkeitsnachweis/matcher.py +++ b/src/taetigkeitsnachweis/matcher.py @@ -48,22 +48,28 @@ def _name_keys(name: str) -> set[str]: def _disambiguate_by_project_key( candidates: list[MasterDataRecord], project_key: str, -) -> MasterDataRecord | None: - """Return the single candidate matching allocation project key via masterdata project_scope.""" - parts = [ - token.strip().casefold() - for token in re.split(r"[/,;|]", project_key) - if token.strip() - ] - project_tokens = set(parts or [project_key.strip().casefold()]) +) -> tuple[MasterDataRecord | None, str]: + """Return the single candidate matching allocation project key via masterdata taetigkeit. + + Returns a tuple of (matched_record, status) where status is one of: + - "ok": exactly one match found + - "no_match": no candidate has a matching taetigkeit + - "multiple": more than one candidate matches + """ + normalized_key = project_key.strip().casefold() matched = [] for candidate in candidates: - candidate_scope = (candidate.project_scope or "PS").strip().casefold() - if candidate_scope in project_tokens: + candidate_taetigkeit = (candidate.taetigkeit or "").strip().casefold() + if candidate_taetigkeit == normalized_key: matched.append(candidate) - return matched[0] if len(matched) == 1 else None + if len(matched) == 1: + return matched[0], "ok" + elif len(matched) == 0: + return None, "no_match" + else: + return None, "multiple" def match_assignments( @@ -103,10 +109,11 @@ def match_assignments( ValidationIssue( code="MISSING_MASTERDATA", message=( - f"No master-data match for employee '{allocation.employee_name}' " - f"(project '{allocation.project_key}')." + f"In der Stellenbeschreibung ist kein Datensatz für " + f"'{allocation.employee_name}' hinterlegt " + f"(Projekt '{allocation.project_key}'). Keine Förderung möglich." ), - severity="error", + severity="info", context=allocation.employee_name, ) ) @@ -116,17 +123,32 @@ def match_assignments( if len(candidates) == 1: selected = candidates[0] else: - # Multiple candidates: disambiguate by explicit project scope column. - selected = _disambiguate_by_project_key(candidates, allocation.project_key) - if selected is None: + # Multiple candidates: disambiguate by Tätigkeitsschlüssel. + selected, match_status = _disambiguate_by_project_key(candidates, allocation.project_key) + if match_status == "no_match": + issues.append( + ValidationIssue( + code="NO_MATCHING_TAETIGKEIT", + message=( + f"No Tätigkeitsschlüssel '{allocation.project_key}' found in " + f"master-data for '{allocation.employee_name}'. " + f"Available: {[c.taetigkeit for c in candidates]}. " + f"Keine Förderung möglich." + ), + severity="warning", + context=allocation.employee_name, + ) + ) + continue + elif selected is None: issues.append( ValidationIssue( code="AMBIGUOUS_MASTERDATA", message=( f"Ambiguous master-data for '{allocation.employee_name}' / " f"'{allocation.project_key}': {len(candidates)} rows, " - f"none or multiple match project_scope. " - f"project_scope values: {[c.project_scope for c in candidates]}" + f"multiple match taetigkeit. " + f"taetigkeit values: {[c.taetigkeit for c in candidates]}" ), severity="error", context=allocation.employee_name,