Improvements (automation) upon police website

I live in Norway and have a residence card, which I have to renew every two years. The process is pretty straightforwad: fill in the application online, book an appointment at the local police station, hand in the documents and get a new card one week later.

Living in Oslo makes it a bit more challenging. Oslo is the biggest city in Norway. There are many applicants here. Normally I would schedule an appointment something like 3 months in advance. COVID-19 had it's word in this process as well. Recently, when I wanted to renew the residence card, I got a nice error message saying that there are no available appointments at the moment. Not in 3 months, not in 5 months, just none.

But the part that really got me on was the fact that police didn't provide a mechanism, which could notify me about available spots. I would think: OK, if I can not get the appointment now just let me know when I can. I do not know their reasoning for not providing it, so the outcome makes me grumpy.

Technology to the rescue! Last time I did something similar was way back in 2011, when I tweaked Google through browser extensions. Let's have a look what is available for us in 2020.

Microsoft has recently released python library playwright. It allows one to automate browser right from Python. Pretty cool! So I sat down and in 20 minutes had this script:

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.newPage()
    page.goto("https://selfservice.udi.no/")
    page.click("#ctl00_BodyRegion_PageRegion_MainRegion_LogInHeading")

    page.type("input[type=email]", config.EMAIL)
    page.type("input[type=password]", config.PWD.get_secret_value())
    page.click("#next")

    try:
        # book appointment
        page.click("#ctl00_BodyRegion_PageRegion_MainRegion_IconNavigationTile2_heading")
    except playwright.helper.TimeoutError:
        msg = "Failed to login. Check your password."
        print(msg)
        telegram_send.send(messages=[msg])
        return

    # click on the first one in the list
    page.click(
        "#ctl00_BodyRegion_PageRegion_MainRegion_ApplicationOverview_applicationOverviewListView_ctrl0_btnBookAppointment"
    )

    try:
        page.waitForSelector(
            "#ctl00_PageRegion_MainContentRegion_ViewControl_spnReceiptAndBooking_divErrorMessageForNoAvailabelAppointments",
            timeout=5000,
        )
        # No appointments
        print("No appointments")
        return
    except playwright.helper.TimeoutError:
        msg = "Looks like UDI is ready for appointments"
        telegram_send.send(messages=[msg])

        with tempfile.TemporaryFile("r+b") as fp:
            encoded_img = page.screenshot(type="png")
            fp.write(encoded_img)
            fp.seek(0, 0)
            telegram_send.send(images=[fp])

It logs in as you at the police website, then opens the very first of your applications and checks for presence of an error message (the error message saying that there are no available appointments). If there is no error message, then it sends a message and a screenshot of the page to my Telegram bot. Runing this script from a cron job I do not need to check the website manually. Sweet!

telega

This type of activity leaves me with mixed feelings. I very much enjoy that I can do something useful, and stitch together a solution, and extend something. On the other hand side, shall I spend my life fixing this type of things?! Shouldn't this functionality be there right from the beginning?

PS. Grab the code from Github repo udiinformer.