From bdd151222fb1f8f369321b595646ce3a506b71f2 Mon Sep 17 00:00:00 2001 From: Mathias G Date: Mon, 3 Aug 2026 10:17:28 +0200 Subject: [PATCH 1/4] Moved get_grid_value to common --- itk_dev_shared_components/boliglaan/common.py | 7 ++++- .../boliglaan/search/search_advis.py | 28 ++++++------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/itk_dev_shared_components/boliglaan/common.py b/itk_dev_shared_components/boliglaan/common.py index 3711fb3..8233ce0 100644 --- a/itk_dev_shared_components/boliglaan/common.py +++ b/itk_dev_shared_components/boliglaan/common.py @@ -1,7 +1,7 @@ """This module contains functions to perform common actions in Boliglån.""" import time -from typing import Literal +from typing import Any, Literal import uiautomation @@ -80,3 +80,8 @@ def save_case(): # Handle popup popup = boliglaan.WindowControl(Name="Kvittering", searchDepth=1) popup.ButtonControl(Name="Gem/Send breve", searchDepth=1).GetInvokePattern().Invoke() + + +def get_grid_value(grid_pattern: uiautomation.GridPattern, row: int, column: int) -> Any: + """Helper function to get the value of a grid item.""" + return grid_pattern.GetItem(row, column).GetPattern(uiautomation.PatternId.ValuePattern).Value diff --git a/itk_dev_shared_components/boliglaan/search/search_advis.py b/itk_dev_shared_components/boliglaan/search/search_advis.py index 1fd3428..02a188c 100644 --- a/itk_dev_shared_components/boliglaan/search/search_advis.py +++ b/itk_dev_shared_components/boliglaan/search/search_advis.py @@ -4,9 +4,7 @@ from dataclasses import dataclass from datetime import date -from typing import Any, Literal - -import uiautomation +from typing import Literal from itk_dev_shared_components.boliglaan import common @@ -44,9 +42,6 @@ def search_advis(cpr: str | None = None, date_from: date | None = None, date_to: date_from: The date to search from. date_to: The date to search to. types: The types of advis to search for. If None all types are chosen. - - Raises: - ValueError: If 'All' is not alone in the types list. """ boliglaan = common.get_boliglaan() boliglaan.SendKeys("{Ctrl}T") @@ -80,8 +75,8 @@ def select_advis(cpr: str, case_number: str): grid_pattern = grid.GetGridPattern() for row in range(grid_pattern.RowCount): - row_case_number = _get_grid_value(grid_pattern, row, 1) - case_cpr = _get_grid_value(grid_pattern, row, 2) + row_case_number = common.get_grid_value(grid_pattern, row, 1) + case_cpr = common.get_grid_value(grid_pattern, row, 2) if case_cpr == cpr and row_case_number == case_number: grid_pattern.GetItem(row, 0).DoubleClick(simulateMove=False) @@ -100,12 +95,12 @@ def get_advis_list() -> list[Advis]: result = [] for row in range(grid_pattern.RowCount): - date_str: str = _get_grid_value(grid_pattern, row, 0) - case_number = _get_grid_value(grid_pattern, row, 1) - cpr = _get_grid_value(grid_pattern, row, 2) - type_ = _get_grid_value(grid_pattern, row, 3) - state = _get_grid_value(grid_pattern, row, 4) - case_type = _get_grid_value(grid_pattern, row, 6) + date_str: str = common.get_grid_value(grid_pattern, row, 0) + case_number = common.get_grid_value(grid_pattern, row, 1) + cpr = common.get_grid_value(grid_pattern, row, 2) + type_ = common.get_grid_value(grid_pattern, row, 3) + state = common.get_grid_value(grid_pattern, row, 4) + case_type = common.get_grid_value(grid_pattern, row, 6) day, month, year = (int(part) for part in date_str.split("-")) date_ = date(year, month, day) @@ -113,8 +108,3 @@ def get_advis_list() -> list[Advis]: result.append(Advis(date=date_, cpr=cpr, case_number=case_number, type=type_, state=state, case_type=case_type)) return result - - -def _get_grid_value(grid_pattern: uiautomation.GridPattern, row: int, column: int) -> Any: - """Helper function to get the value of a grid item.""" - return grid_pattern.GetItem(row, column).GetPattern(uiautomation.PatternId.ValuePattern).Value From 1ce55e456c048b4483c97f1d89c829831f4706e5 Mon Sep 17 00:00:00 2001 From: Mathias G Date: Mon, 3 Aug 2026 10:17:43 +0200 Subject: [PATCH 2/4] Implemented case search functionality --- .../boliglaan/search/search_cases.py | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 itk_dev_shared_components/boliglaan/search/search_cases.py diff --git a/itk_dev_shared_components/boliglaan/search/search_cases.py b/itk_dev_shared_components/boliglaan/search/search_cases.py new file mode 100644 index 0000000..cb15ed5 --- /dev/null +++ b/itk_dev_shared_components/boliglaan/search/search_cases.py @@ -0,0 +1,146 @@ +"""This module contains functions to search for cases +and handling the cases search results. +""" + +from dataclasses import dataclass +from typing import Literal + +from itk_dev_shared_components.boliglaan import common + + +CaseState = Literal[ + "Afslået", + "Afventer bevilling", + "Bevilget", + "Udbetalt", + "Afvikling - frivillig", + "Afvikling - misligholdt afdrag kautionslån", + "Afvikling - misligholdt kautionslån", + "Afvikling - 5 års løbetid", + "Afvikling - overskridelse af indkomstgrænse", + "Afvikling - fraflyttet", + "Afvikling - rentefri", + "Gældssaneret", + "Afskrevet - gældssaneret", + "Afskrevet - lån uerholdeligt", + "Afskrevet - dødsfald", + "Afskrevet - RIM (Restance Inddrivelses Myndighed)", + "Overført til anden person", + "Overført/Annulleret", + "Tilbageført", + "Indfriet", + "Annulleret lån", + "Annulleret ansøgning" +] + + +CaseType = Literal[ + "Pligtlån 5 års løbetid §56, §57", + "Pligtlån sanering §54", + "Pligtlån pensionister §54", + "Pligtlån enkeltværelse §54", + "Pligtlån genhusning §54", + "Pligtlån ældrebolig §54", + "Flygtningelån 100% 5 års løbetid §66, §67", + "Flygtningelån 100% sanering §66, §67", + "Flygtningelån 100% pensionister §66, §67", + "Flygtninglån 50% 5 års løbetid §68", + "Kautionslån kaution §59", + "Kautionslån flygtninge §68", + "Kommune lån §59", + "Garanti for istandsættelse" +] + + +@dataclass +class Case: + """A dataclass representing a case from the case search result.""" + case_number: str + cpr: str + person_name: str + address: str + case_type: str + case_state: str + + +def search_cases(cpr: str | None = None, street: str | None = None, types: tuple[CaseType] | None = None, states: tuple[CaseState] | None = None): + """Search for cases. + + Args: + cpr: The CPR number to look for. + street: The street name to look for. + types: The case types to look for. + states: The case states to look for. + """ + boliglaan = common.get_boliglaan() + boliglaan.SendKeys("{Ctrl}B") + + popup = boliglaan.WindowControl(Name="Udsøg lånesager", searchDepth=1) + + if cpr: + popup.TextControl(Name="Personnummer:").GetNextSiblingControl().GetValuePattern().SetValue(cpr) + + if street: + popup.EditControl(AutomationId="vejSearchEdit").EditControl().GetValuePattern().SetValue(street) + + group = popup.GroupControl(Name="Lånetype", searchDepth=3) + if types is None: + group.CheckBoxControl(Name='Alle valgt').GetTogglePattern().SetToggleState(1) + else: + group.CheckBoxControl(Name='Alle valgt').GetTogglePattern().SetToggleState(0) + + for type_ in types: + group.CheckBoxControl(Name=type_).GetTogglePattern().SetToggleState(1, waitTime=0) + + group = popup.GroupControl(Name="Lånestatus", searchDepth=3) + if states is None: + group.CheckBoxControl(Name='Alle valgt').GetTogglePattern().SetToggleState(1) + else: + group.CheckBoxControl(Name='Alle valgt').GetTogglePattern().SetToggleState(0) + + for state in states: + group.CheckBoxControl(Name=state).GetTogglePattern().SetToggleState(1, waitTime=0) + + popup.ButtonControl(Name="Søg").Click(simulateMove=False) + + +def get_case_list() -> list[Case]: + """Get the case search results as a list of Case objects.""" + boliglaan = common.get_boliglaan() + + grid = boliglaan.TabControl(Name="TabbedGroup", searchDepth=4).GroupControl(Name="Sager").DataGridControl(AutomationId="LaanesagerGridControl") + grid_pattern = grid.GetGridPattern() + + result = [] + + for row in range(grid_pattern.RowCount): + new_case = Case( + case_number=common.get_grid_value(grid_pattern, row, 0), + cpr=common.get_grid_value(grid_pattern, row, 1), + person_name=common.get_grid_value(grid_pattern, row, 2), + case_state=common.get_grid_value(grid_pattern, row, 3), + case_type=common.get_grid_value(grid_pattern, row, 4), + address=common.get_grid_value(grid_pattern, row, 5) + ) + + result.append(new_case) + + return result + + +def select_case(cpr: str, case_number: str): + """Select the case from the case search result with the given cpr and case number.""" + boliglaan = common.get_boliglaan() + + grid = boliglaan.TabControl(Name="TabbedGroup", searchDepth=4).GroupControl(Name="Sager").DataGridControl(AutomationId="LaanesagerGridControl") + grid_pattern = grid.GetGridPattern() + + for row in range(grid_pattern.RowCount): + row_case_number = common.get_grid_value(grid_pattern, row, 0) + case_cpr = common.get_grid_value(grid_pattern, row, 1) + + if case_cpr == cpr and row_case_number == case_number: + grid_pattern.GetItem(row, 0).DoubleClick(simulateMove=False) + return + + raise LookupError(f"Couldn't find advis: {cpr} - {case_number}") From 3d03bd9eb1d8918c49eacd47192f6ae804ffe387 Mon Sep 17 00:00:00 2001 From: Mathias G Date: Mon, 3 Aug 2026 10:28:32 +0200 Subject: [PATCH 3/4] Fixed error text --- itk_dev_shared_components/boliglaan/search/search_cases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itk_dev_shared_components/boliglaan/search/search_cases.py b/itk_dev_shared_components/boliglaan/search/search_cases.py index cb15ed5..e800c54 100644 --- a/itk_dev_shared_components/boliglaan/search/search_cases.py +++ b/itk_dev_shared_components/boliglaan/search/search_cases.py @@ -143,4 +143,4 @@ def select_case(cpr: str, case_number: str): grid_pattern.GetItem(row, 0).DoubleClick(simulateMove=False) return - raise LookupError(f"Couldn't find advis: {cpr} - {case_number}") + raise LookupError(f"Couldn't find case: {cpr} - {case_number}") From 7a536c7cd7449eb7d0ee38fa70ce869691f7bb0e Mon Sep 17 00:00:00 2001 From: Mathias G Date: Mon, 3 Aug 2026 10:30:23 +0200 Subject: [PATCH 4/4] Bumped version --- changelog.md | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index bd9a586..31c743e 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.18.0] - 2026-08-03 + +### Added + +- Boliglån: Functionality to search for cases. + ## [2.17.4] - 2026-07-08 ### Fixed diff --git a/pyproject.toml b/pyproject.toml index d64aadd..7327484 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "itk_dev_shared_components" -version = "2.17.4" +version = "2.18.0" authors = [ { name="ITK Development", email="itk-rpa@mkb.aarhus.dk" }, ]