Aktuelle Corona-Inzidenz

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ein weiterer Effekt: help() zeigt Namen mit vorangestelltem Unterstrich nicht in der Übersicht an. Somit sind sie auch nochmal ein bisschen "private". Ein richtiges Verstecken vor dem User ist das natürlich nicht. Mir geht es da wie gesagt auch mehr um die Abgrenzung als um eine Art von Geheimhaltung.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich habe das mal um eine Sortierung nach Namen ergänzt, damit das nicht mehr davon abhängig ist in welcher Reihenfolge die API die Ergebnisse liefert.

Code: Alles auswählen

#!/usr/bin/env python3
import sys
from operator import itemgetter
from types import MappingProxyType

import requests
import rich
from requests.exceptions import RequestException
from rich.table import Table
from rich.text import Text

API_URL = "https://api.corona-zahlen.org/districts/"

QUERY = "berlin+hamburg+köln"


def _format_incidence(value):
    for limit, color in [
        (35, "bright_green"),
        (50, "bright_yellow"),
        (100, "orange3"),
    ]:
        if value < limit:
            break
    else:
        color = "bright_red"

    return Text(f"{value:.01f}", color)


COLUMN_SPECS = MappingProxyType(
    {
        "Landkreis": ("name", str),
        "Inzidenz": ("weekIncidence", _format_incidence),
        "Gesamtfälle": ("cases", str),
        "Verstorbene": ("deaths", str),
    }
)


def get_json(url):
    response = requests.get(url)
    response.raise_for_status()
    return response.json()


def get_district_records(query):
    terms = [term.strip().casefold() for term in query.split("+")]
    data = get_json(API_URL)["data"]
    return {
        key: district
        for key, district in data.items()
        for term in terms
        if term in district["name"].casefold()
    }


def get_table(query, specs=COLUMN_SPECS):
    districts = sorted(
        get_district_records(query).values(), key=itemgetter("name")
    )
    if not districts:
        raise ValueError(query)
    table = Table(*specs.keys())
    for district in districts:
        table.add_row(
            *(formatter(district[key]) for key, formatter in specs.values())
        )
    return table


_fail = sys.exit


def main():
    query = sys.argv[1] if len(sys.argv) > 1 else QUERY
    try:
        rich.print(get_table(query))
    except RequestException as error:
        _fail(f"Verbindung fehlgeschlagen: {error}")
    except ValueError as error:
        _fail(f"Keine Treffer: {error}")


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Nun werden auch Trennzeichen bei Werten >= 1000 angezeigt. Ich habe in die Funktion viel mehr Möglichkeiten eingebaut als für diesen Anwendungsfall nötig gewesen wären. Auch hätte man locale.format() benutzen können. Ein Verändern und sauberes Zurücksetzen der Locale-Settings wäre aber auch nicht sooo viel weniger Code gewesen. Insofern habe ich mich für den Nachbau entschieden.

Code: Alles auswählen

#!/usr/bin/env python3
import sys
from operator import itemgetter
from types import MappingProxyType
from functools import partial

import requests
import rich
from requests.exceptions import RequestException
from rich.table import Table
from rich.text import Text

API_URL = "https://api.corona-zahlen.org/districts/"

QUERY = "berlin+hamburg+köln"


def format_incidence(value):
    for limit, color in [
        (35, "bright_green"),
        (50, "bright_yellow"),
        (100, "orange3"),
    ]:
        if value < limit:
            break
    else:
        color = "bright_red"

    return Text(f"{value:.01f}", color)


def format_integer(value, sep, grouplen=3):
    s = str(int(value))
    if len(s) <= grouplen:
        return s
    offset = s.startswith("-") + (len(s) % grouplen)
    groups = [s[:offset]]
    groups.extend(
        s[i:(i + grouplen)] for i in
        range(offset, len(s), grouplen)
    )
    return sep.join(groups)


COLUMN_SPECS = MappingProxyType(
    {
        "Landkreis": ("name", str),
        "Inzidenz": ("weekIncidence", format_incidence),
        "Gesamtfälle": ("cases", partial(format_integer, sep=".")),
        "Verstorbene": ("deaths", partial(format_integer, sep=".")),
    }
)


def get_json(url):
    response = requests.get(url)
    response.raise_for_status()
    return response.json()


def get_district_records(query):
    terms = [term.strip().casefold() for term in query.split("+")]
    data = get_json(API_URL)["data"]
    return {
        key: district
        for key, district in data.items()
        for term in terms
        if term in district["name"].casefold()
    }


def get_table(query, specs=COLUMN_SPECS):
    districts = sorted(
        get_district_records(query).values(), key=itemgetter("name")
    )
    if not districts:
        raise ValueError(query)
    table = Table(*specs.keys())
    for district in districts:
        table.add_row(
            *(formatter(district[key]) for key, formatter in specs.values())
        )
    return table


_fail = sys.exit


def main():
    query = sys.argv[1] if len(sys.argv) > 1 else QUERY
    try:
        rich.print(get_table(query))
    except RequestException as error:
        _fail(f"Verbindung fehlgeschlagen: {error}")
    except ValueError as error:
        _fail(f"Keine Treffer: {error}")


if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Warum zurücksetzen der Locale?
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

__blackjack__ hat geschrieben: Samstag 26. Juni 2021, 00:09 @snafu: Warum zurücksetzen der Locale?
Na, wenn man das Locale nicht ändert, bekommt man Kommas als Tausender-Trenner. Wie würdest du es denn machen, um stattdessen an Punkte zu kommen? Jetzt mal abgesehen von einem text.replace().
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Nun mit Nutzung des Locale-Moduls für den Tausender-Trenner:
https://gist.github.com/seblin/11bfb9fd ... 859fe36a19
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Die Antwort war jetzt für's *setzen* der Locale, aber Du hast ja geschrieben die müsste man auch sauber wieder *zurücksetzen*. Üblich ist doch einfach nur am Anfang die Locale passend zu setzen und das dann so zu lassen bis zum Prozessende.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten