codeformunich/Muenchen-Transparent

View on GitHub
protected/RISParser/CalendarAgendaItem.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

declare(strict_types=1);

class CalendarAgendaItem
{
    public int $position;
    public ?int $id;
    public bool $public;
    public bool $isHeading;
    public string $topNr;
    public string $title;
    public bool $hasDisclosure = false;
    public ?string $disclosure = null; // Only for non-public agenda items
    public bool $hasDecision = false;
    public ?string $decision = null; // For public agenda items
    public ?DokumentLink $decisionDocument = null;

    /** @var int[] */
    public array $antraegeIds = [];

    /** @var int[] */
    public array $vorlagenIds = [];

    /**
     * @return CalendarAgendaItem[]
     */
    public static function parseHtmlList(string $html, bool $public): array
    {
        $parts = explode('d-table w-100 tops', $html);
        $entries = preg_split('/<div class="d-table-row (odd|even)/siu', $parts[1]);
        array_shift($entries);

        $parsedObjects = [];
        $topContext = '';
        $pos = 0;
        foreach ($entries as $entry) {
            $parsed = static::parseFromHtml($entry, $pos, $public, $topContext);
            if ($parsed) {
                $parsedObjects[] = $parsed;
                $pos++;

                if ($parsed->isHeading) {
                    $topContext = $parsed->topNr;
                }
            }
        }

        return $parsedObjects;
    }

    public static function parseFromHtml(string $html, int $pos, bool $public, string $lastHeadingTopNr): ?self
    {
        $entry = new self();
        $entry->position = $pos;
        $entry->public = $public;
        $entry->isHeading = (str_contains($html, 'topabschnitt') || str_contains($html, 'topueberschrift'));

        if (!preg_match('/<div class="d-table-cell px-1 py-2 text-center">\s*<span>(?<topNr>[^<]*)<\/span>/siu', $html, $match)) {
            throw new ParsingException('Not found: topNr');
        }
        if ($entry->isHeading || $lastHeadingTopNr === '') {
            $entry->topNr = $match['topNr'];
        } else {
            $entry->topNr = $lastHeadingTopNr . (str_ends_with($lastHeadingTopNr, '.') ? '' : '.') . $match['topNr'];
        }

        if (preg_match('/topdownload\?risid=(?<id>\d+)[^\d]/siu', $html, $match)) {
            $entry->id = intval($match['id']);
        } elseif (preg_match('/\.\.\/top\/(?<id>\d+)\//siu', $html, $match)) {
            $entry->id = intval($match['id']);
        } else {
            $entry->id = null;
        }

        if (!preg_match('/<div class="d-flex justify-content-between">\s*<div><span class="text-keepwhitespace">(?<title>[^<]*)<\/span>/siu', $html, $match)) {
            throw new ParsingException('Not found: title');
        }
        $entry->title = html_entity_decode($match['title'], ENT_COMPAT, 'UTF-8');

        if (preg_match('/top\/' . $entry->id . '\/entscheidung/siu', $html)) {
            $entry->hasDecision = true;
        }
        if (preg_match('/top\/' . $entry->id . '\/veroeffentlichung/siu', $html)) {
            $entry->hasDisclosure = true;
        }

        if (preg_match('/<a class="downloadlink" href="[.\/]*(?<url>\/dokument\/v\/(?<id>\d+))"[^>]*>(?<filename>(?<title>[^<]*)\.[^<.]*)</siuU', $html, $match)) {
            $entry->decisionDocument = new DokumentLink();
            $entry->decisionDocument->filename = $match['filename'];
            $entry->decisionDocument->title = $match['title'];
            $entry->decisionDocument->id = intval($match['id']);
            $entry->decisionDocument->url = $match['url'];
        }

        preg_match_all('/antrag\/detail\/(?<id>\d+)[^\d]/siu', $html, $matches);
        $entry->antraegeIds = array_map('intval', $matches['id'] ?? []);

        preg_match_all('/sitzungsvorlage\/detail\/(?<id>\d+)[^\d]/siu', $html, $matches);
        $entry->vorlagenIds = array_map('intval', $matches['id'] ?? []);

        return $entry;
    }

    public function parseDecision(string $html): void
    {
        if (!preg_match('/Entscheidung:<\/div>\s*<div class="keyvalue-value"><span>(?<str>[^<]*)<\/span>/siu', $html, $matches)) {
            throw new ParsingException('Not found: decision');
        }
        $this->decision = $matches['str'];
    }

    public function parseDisclosure(string $html): void
    {
        if (!preg_match('/<h2>Bekanntgabe.*<div class="card-body">\s<span>(?<str>[^<]*)<\/span>/siu', $html, $matches)) {
            throw new ParsingException('Not found: disclosure');
        }
        $this->disclosure = $matches['str'];
    }
}