DerDu/SPHERE-Framework

View on GitHub
Application/Platform/Roadmap/Roadmap.php

Summary

Maintainability
F
1 wk
Test Coverage
<?php
namespace SPHERE\Application\Platform\Roadmap;

use SPHERE\Application\IApplicationInterface;
use SPHERE\Application\IModuleInterface;
use SPHERE\Application\IServiceInterface;
use SPHERE\Application\Platform\Gatekeeper\Authorization\Account\Account;
use SPHERE\Application\Platform\Gatekeeper\Authorization\Account\Service\Entity\TblIdentification;
use SPHERE\Application\Platform\Roadmap\Youtrack\Credentials;
use SPHERE\Application\Platform\Roadmap\Youtrack\Issue;
use SPHERE\Application\Platform\Roadmap\Youtrack\Parser;
use SPHERE\Application\Platform\Roadmap\Youtrack\Sprint;
use SPHERE\Common\Frontend\IFrontendInterface;
use SPHERE\Common\Frontend\Layout\Repository\Accordion;
use SPHERE\Common\Frontend\Layout\Repository\Label;
use SPHERE\Common\Frontend\Layout\Repository\Panel;
use SPHERE\Common\Frontend\Layout\Repository\ProgressBar;
use SPHERE\Common\Frontend\Layout\Repository\PullClear;
use SPHERE\Common\Frontend\Layout\Repository\PullLeft;
use SPHERE\Common\Frontend\Layout\Repository\PullRight;
use SPHERE\Common\Frontend\Layout\Repository\Title;
use SPHERE\Common\Frontend\Layout\Structure\Layout;
use SPHERE\Common\Frontend\Layout\Structure\LayoutColumn;
use SPHERE\Common\Frontend\Layout\Structure\LayoutGroup;
use SPHERE\Common\Frontend\Layout\Structure\LayoutRow;
use SPHERE\Common\Frontend\Link\Repository\External;
use SPHERE\Common\Frontend\Message\Repository\Success;
use SPHERE\Common\Frontend\Message\Repository\Warning;
use SPHERE\Common\Frontend\Text\Repository\Danger;
use SPHERE\Common\Frontend\Text\Repository\Italic;
use SPHERE\Common\Frontend\Text\Repository\Muted;
use SPHERE\Common\Frontend\Text\Repository\Small;
use SPHERE\Common\Main;
use SPHERE\Common\Window\Navigation\Link;
use SPHERE\Common\Window\Stage;
use SPHERE\System\Cache\Handler\MemcachedHandler;
use SPHERE\System\Extension\Extension;

/**
 * Class Roadmap
 *
 * @package SPHERE\Application\Platform\Roadmap
 */
class Roadmap extends Extension implements IApplicationInterface, IModuleInterface
{

    public static function registerApplication()
    {

        /**
         * Register Navigation
         */
        Main::getDisplay()->addApplicationNavigation(
            new Link(new Link\Route(__NAMESPACE__), new Link\Name('Roadmap'))
        );
        Main::getDispatcher()->registerRoute(Main::getDispatcher()->createRoute(
            __NAMESPACE__, __CLASS__.'::frontendDashboard'
        ));
    }

    public static function registerModule()
    {

    }

    /**
     * @return IServiceInterface
     */
    public static function useService()
    {

    }

    /**
     * @return IFrontendInterface
     */
    public static function useFrontend()
    {

    }

    public function frontendDashboard()
    {

        $Stage = new Stage('Schulsoftware','Roadmap');

        try {
            $Map = $this->getRoadmap();
            $Pool = $this->getPool();
        } catch (\Exception $Exception) {
            $Stage->setContent(new Layout(new LayoutGroup(new LayoutRow(
                new LayoutColumn(new Warning('Roadmap konnte nicht abgerufen werden'))
            ))));
            return $Stage;
        }

        $Stage->setMessage(
            'Aktuelle Versionen: <br/>'.
            new Label('Preview: '.$Map->getVersionPreview(), Label::LABEL_TYPE_WARNING)
            .' '.
            new Label('Release: '.$Map->getVersionRelease(), Label::LABEL_TYPE_PRIMARY)
        );

        $Cache = $this->getCache(new MemcachedHandler(), 'Memcached');
        if (!( $Content = $Cache->getValue('Roadmap', __METHOD__) )) {

            // Is System-Account
            $SystemLink = false;
            $tblAccount = Account::useService()->getAccountBySession();
            if ($tblAccount) {
                $tblIdentification = $tblAccount->getServiceTblIdentification();
                if ($tblIdentification) {
                    $SystemLink = ( $tblIdentification->getName() == TblIdentification::NAME_SYSTEM );
                }
            }

            $Sprints = $Map->getSprints();

            $PriorityColor = array(
                'Kritisch' => '\SPHERE\Common\Frontend\Layout\Repository\Label\Danger',
                'Hoch'     => '\SPHERE\Common\Frontend\Layout\Repository\Label\Warning',
                'Normal'   => '\SPHERE\Common\Frontend\Layout\Repository\Label\Info',
                'Niedrig'  => '\SPHERE\Common\Frontend\Layout\Repository\Label\Success',
            );

            $StateColor = array(
                'Erfasst'            => '\SPHERE\Common\Frontend\Layout\Repository\Label\Info',
                'Offen'              => '\SPHERE\Common\Frontend\Layout\Repository\Label\Danger',
                'In Bearbeitung'     => '\SPHERE\Common\Frontend\Layout\Repository\Label\Warning',
                'Behoben'            => '\SPHERE\Common\Frontend\Layout\Repository\Label\Success',
                'Integriert'         => '\SPHERE\Common\Frontend\Layout\Repository\Label\Success',
                'Zu besprechen'      => '\SPHERE\Common\Frontend\Layout\Repository\Label\Danger',
                'Wird nicht behoben' => '\SPHERE\Common\Frontend\Layout\Repository\Label\Danger'
            );

            $TypeColor = array(
                'Bug'         => '\SPHERE\Common\Frontend\Layout\Repository\Label\Danger',
                'Feature'     => '\SPHERE\Common\Frontend\Layout\Repository\Label\Success',
                'Optimierung' => '\SPHERE\Common\Frontend\Layout\Repository\Label\Warning'
            );

            $SubsystemColor = array(
                '0' => '\SPHERE\Common\Frontend\Layout\Repository\Label\Warning',
                '1' => '\SPHERE\Common\Frontend\Layout\Repository\Label\Info',
                '2' => '\SPHERE\Common\Frontend\Layout\Repository\Label\Info'
            );

            $SprintCurrent = null;
            $LayoutColumns = array();
            /** @var Sprint $Sprint */
            foreach ((array)$Sprints as $SprintIndex => $Sprint) {

                $SprintComplete = true;
                /** @var LayoutColumn[] $IssueList */
                $IssueList = array();
                /** @var LayoutColumn[] $ResolvedList */
                $ResolvedList = array();
                $Issues = $Sprint->getIssues();
                /** @var Issue $Issue */
                foreach ((array)$Issues as $Issue) {
                    $ColumnList = array();
                    if ($Issue->getState() == 'Behoben' || $Issue->getState() == 'Integriert') {

                        $Description = $this->sanitizeDescription($Issue->getDescription(), 0);
                        $Title = $this->sanitizeTitle($Issue->getTitle());

                        $ColumnList[] = new LayoutColumn(
                            new Panel(

                                new Small($Title), $Description,
                                Panel::PANEL_TYPE_SUCCESS,
                                new Label($Issue->getId()).( isset( $SubsystemColor[substr($Issue->getSubsystem(), 0,
                                        1)] )
                                    ? new $SubsystemColor[substr($Issue->getSubsystem(), 0, 1)]($Issue->getSubsystem())
                                    : $Issue->getSubsystem()
                                )
                                .( isset( $TypeColor[$Issue->getType()] )
                                    ? new $TypeColor[$Issue->getType()]($Issue->getType())
                                    : $Issue->getType()
                                )

                            ), 3);

                        $ResolvedList = array_merge($ResolvedList, $ColumnList);
                    } else {
                        $SprintComplete = false;
                        if (!$SprintCurrent) {
                            $SprintCurrent = $Sprint->getVersion();
                        }

                        if ($Issue->getState() == 'In Bearbeitung') {
                            $ProgressBar = new ProgressBar($Issue->getTimePercent(), 100 - $Issue->getTimePercent(),
                                100);
                        } else {
                            $ProgressBar = new ProgressBar($Issue->getTimePercent(), 0, 100);
                        }

                        $Title = $this->sanitizeTitle($Issue->getTitle());
                        $Description = $this->sanitizeDescription($Issue->getDescription(), 0);

                        $ColumnList[] = new LayoutColumn(array(
                            new Panel($Title.' '.$ProgressBar,
                                ( strlen($Description) == 0 ? '' : $Description ),
                                Panel::PANEL_TYPE_INFO,
                                new Layout(
                                    new LayoutGroup(
                                        new LayoutRow(array(
                                            new LayoutColumn(array(
                                                new PullClear(
                                                    new PullLeft(
                                                        new Label($Issue->getId())
                                                    ).
                                                    new PullLeft(
                                                        ( isset( $PriorityColor[$Issue->getPriority()] )
                                                            ? new $PriorityColor[$Issue->getPriority()]($Issue->getPriority())
                                                            : $Issue->getPriority()
                                                        )
                                                    )
                                                    .
                                                    new PullLeft(
                                                        ( isset( $StateColor[$Issue->getState()] )
                                                            ? new $StateColor[$Issue->getState()]($Issue->getState())
                                                            : $Issue->getState()
                                                        )
                                                    ).
                                                    new PullLeft(
                                                        ( isset( $SubsystemColor[substr($Issue->getSubsystem(), 0, 1)] )
                                                            ? new $SubsystemColor[substr($Issue->getSubsystem(), 0, 1)]
                                                            ($Issue->getSubsystem())
                                                            : $Issue->getSubsystem()
                                                        )
                                                    ).
                                                    new PullLeft(
                                                        ( isset( $TypeColor[$Issue->getType()] )
                                                            ? new $TypeColor[$Issue->getType()]($Issue->getType())
                                                            : $Issue->getType()
                                                        )
                                                    ).( $SystemLink
                                                        ? new PullRight(
                                                            new External($Issue->getId(),
                                                                'https://ticket.swe.haus-der-edv.de/issue/'.$Issue->getId()
                                                            )
                                                        )
                                                        : ''
                                                    )
                                                )
                                            ))
                                        ))
                                    )
                                )

                            )
                        ), 4);

                        $IssueList = array_merge($IssueList, $ColumnList);
                    }
                }

                $FoldSprint = false;
                // Create Sprint-Content Layout
                if ($SprintComplete) {

                    // Fold Sprint?
                    if (isset( $Sprints[( $SprintIndex + 1 )] ) && $Sprints[( $SprintIndex + 1 )]->isDone()) {
                        $FoldSprint = true;
                    }

                    $LayoutRowList = array();
                    $LayoutRowCount = 0;
                    $LayoutRow = null;
                    foreach ($ResolvedList as $LayoutColumn) {
                        if ($LayoutRowCount % 4 == 0) {
                            $LayoutRow = new LayoutRow(array());
                            $LayoutRowList[] = $LayoutRow;
                        }
                        $LayoutRow->addColumn($LayoutColumn);
                        $LayoutRowCount++;
                    }

                    $SprintList = new Layout(new LayoutGroup(array(
                        new LayoutRow(array(
                            new LayoutColumn(
                                new Layout(new LayoutGroup($LayoutRowList))
                            ),
                        )),
                    )));
                } else {
                    $LayoutRowList = array();
                    $LayoutRowCount = 0;
                    $LayoutRow = null;
                    foreach ($ResolvedList as $LayoutColumn) {
                        if ($LayoutRowCount % 4 == 0) {
                            $LayoutRow = new LayoutRow(array());
                            $LayoutRowList[] = $LayoutRow;
                        }
                        $LayoutRow->addColumn($LayoutColumn);
                        $LayoutRowCount++;
                    }
                    /** @var LayoutRow[] $ResolvedList */
                    $ResolvedList = $LayoutRowList;

                    $LayoutRowList = array();
                    $LayoutRowCount = 0;
                    $LayoutRow = null;
                    foreach ($IssueList as $LayoutColumn) {
                        if ($LayoutRowCount % 3 == 0) {
                            $LayoutRow = new LayoutRow(array());
                            $LayoutRowList[] = $LayoutRow;
                        }
                        $LayoutRow->addColumn($LayoutColumn);
                        $LayoutRowCount++;
                    }
                    /** @var LayoutRow[] $IssueList */
                    $IssueList = $LayoutRowList;

                    if (empty( $ResolvedList )) {
                        $SprintList = new Layout(new LayoutGroup(array(
                            new LayoutRow(array(
                                new LayoutColumn(
                                    new Layout(new LayoutGroup($IssueList,
                                        new Title(new Danger('Geplante Änderungen'), 'in '.$Sprint->getVersion())))
                                ),
                            ))
                        )));
                    } else {
                        $SprintList = new Layout(new LayoutGroup(array(
                            new LayoutRow(array(
                                new LayoutColumn(
                                    new Layout(new LayoutGroup($IssueList,
                                        new Title(new Danger('Geplante Änderungen'), 'in '.$Sprint->getVersion())))
                                ),
                            )),
                            new LayoutRow(array(
                                new LayoutColumn(
                                    new Layout(new LayoutGroup($ResolvedList,
                                        new Title(new \SPHERE\Common\Frontend\Text\Repository\Success('Neuerungen'),
                                            'in '.$Sprint->getVersion())))
                                ),
                            )),
                        )));
                    }
                }

                if ($SprintComplete) {
                    $VersionHeader = new Success(new PullClear(
                        new PullLeft('Änderungen in '.$Sprint->getVersion())
                        .new PullRight(
                            new Label('Freigabe Demo @ '.date('m / Y',
                                    strtotime($Sprint->getTimestampFinish().' +1 day')),
                                Label::LABEL_TYPE_WARNING)
                            .new Label('Freigabe Live @ '.date('m / Y',
                                    strtotime($Sprint->getTimestampFinish().' +2 month')),
                                Label::LABEL_TYPE_PRIMARY)
                        )
                    ));
                    if (!$Sprint->isDone()) {
                        $SprintPercent = $this->getDatePercent($Sprint->getTimestampStart(),
                            $Sprint->getTimestampFinish());
                        $VersionHeader .= new ProgressBar($SprintPercent, 100 - $SprintPercent, 100);
                    }
                    $VersionFooter = null;
                } else {
                    $SprintColor = ( $Sprint->getVersion() == $SprintCurrent
                        ? '\SPHERE\Common\Frontend\Message\Repository\Info'
                        : '\SPHERE\Common\Frontend\Message\Repository\Warning'
                    );

                    $VersionHeader = new $SprintColor(new PullClear(
                        new PullLeft('Version '.$Sprint->getVersion())
                        .new PullRight(
                            new Label('Freigabe Demo @ '.date('m / Y',
                                    strtotime($Sprint->getTimestampFinish().' +1 day')),
                                Label::LABEL_TYPE_WARNING)
                            .new Label('Freigabe Live @ '.date('m / Y',
                                    strtotime($Sprint->getTimestampFinish().' +2 month')),
                                Label::LABEL_TYPE_PRIMARY)
                        )
                    ));
                    $VersionHeader .= new ProgressBar($this->getDatePercent($Sprint->getTimestampStart(),
                        $Sprint->getTimestampFinish()), 0, 100);
                    $VersionFooter = new Muted(new Small($Sprint->getTimestampStart().' - '.$Sprint->getTimestampFinish()));
                }

                $LayoutColumns[] = new LayoutColumn(
                    new Panel(
                        $VersionHeader,
                        ( $FoldSprint ? (new Accordion())->addItem(new Small(new Italic(new Muted('[Änderungen anzeigen]'))),
                            (string)$SprintList) : (string)$SprintList ),
                        ( $SprintComplete ? Panel::PANEL_TYPE_SUCCESS : Panel::PANEL_TYPE_WARNING ),
                        $VersionFooter
                    )
                );
            }

            // Pool
            $Issues = $Pool->getPool();
            /** @var LayoutColumn[] $PoolList */
            $PoolList = array();
            /** @var Issue $Issue */
            foreach ((array)$Issues as $Issue) {
                $ColumnList = array();

                $Title = $this->sanitizeTitle($Issue->getTitle());
                $Description = $this->sanitizeDescription($Issue->getDescription(), 0);

                $ColumnList[] = new LayoutColumn(array(
                    new Panel($Title,
                        ( strlen($Description) == 0 ? '' : $Description ),
                        Panel::PANEL_TYPE_INFO,
                        new Layout(
                            new LayoutGroup(
                                new LayoutRow(array(
                                    new LayoutColumn(array(
                                        new PullClear(
                                            new PullLeft(
                                                new Label($Issue->getId())
                                            ).
                                            new PullLeft(
                                                ( isset( $PriorityColor[$Issue->getPriority()] )
                                                    ? new $PriorityColor[$Issue->getPriority()]($Issue->getPriority())
                                                    : $Issue->getPriority()
                                                )
                                            )
                                            .
                                            new PullLeft(
                                                ( isset( $StateColor[$Issue->getState()] )
                                                    ? new $StateColor[$Issue->getState()]($Issue->getState())
                                                    : $Issue->getState()
                                                )
                                            ).
                                            new PullLeft(
                                                ( isset( $SubsystemColor[substr($Issue->getSubsystem(), 0, 1)] )
                                                    ? new $SubsystemColor[substr($Issue->getSubsystem(), 0, 1)]
                                                    ($Issue->getSubsystem())
                                                    : $Issue->getSubsystem()
                                                )
                                            ).
                                            new PullLeft(
                                                ( isset( $TypeColor[$Issue->getType()] )
                                                    ? new $TypeColor[$Issue->getType()]($Issue->getType())
                                                    : $Issue->getType()
                                                )
                                            ).( $SystemLink
                                                ? new PullRight(
                                                    new External($Issue->getId(),
                                                        'https://ticket.swe.haus-der-edv.de/issue/'.$Issue->getId()
                                                    )
                                                )
                                                : ''
                                            )
                                        )
                                    ))
                                ))
                            )
                        )

                    )
                ), 4);

                $PoolList = array_merge($PoolList, $ColumnList);
            }

            $LayoutRowList = array();
            $LayoutRowCount = 0;
            $LayoutRow = null;
            foreach ($PoolList as $LayoutColumn) {
                if ($LayoutRowCount % 3 == 0) {
                    $LayoutRow = new LayoutRow(array());
                    $LayoutRowList[] = $LayoutRow;
                }
                $LayoutRow->addColumn($LayoutColumn);
                $LayoutRowCount++;
            }

            $SprintList = new Layout(new LayoutGroup(array(
                new LayoutRow(array(
                    new LayoutColumn(
                        new Layout(new LayoutGroup($LayoutRowList))
                    ),
                )),
            )));

            $LayoutColumns[] = new LayoutColumn(
                new Panel(
                    new \SPHERE\Common\Frontend\Message\Repository\Danger('Ideen & Feedback'),
                    (string)$SprintList,
                    Panel::PANEL_TYPE_DANGER
                )
            );

            $Content = (new Layout(new LayoutGroup(new LayoutRow($LayoutColumns))))->__toString();
            $Cache->setValue('Roadmap', $Content, ( 60 * 60 * 4 ), __METHOD__);
        }

        $Stage->setContent($Content);

        return $Stage;
    }

    /**
     * Get RoadMap-Object
     *
     * @return Youtrack\Map
     * @trows \Exception
     */
    public function getRoadmap()
    {

        $Parser = new Parser(
            new Credentials(),
            'Sichtbar für: {Alle Benutzer} Typ: Feature,Bug,Optimierung Teilsystem: {1*},{2*} Status: Erfasst, Offen,{In Bearbeitung},Behoben,{Zu besprechen},Integriert Beheben in: -{Nicht geplant}'
        );
        return $Parser->getMap();
    }

    public function getPool()
    {

        $Parser = new Parser(
            new Credentials(),
            'Sichtbar für: {Alle Benutzer} Typ: Feature,Bug,Optimierung Teilsystem: {1*},{2*} Status: Erfasst, Offen,{In Bearbeitung} ,{Zu besprechen} Beheben in: {Nicht geplant}'
        );
        return $Parser->getPool();
    }

    /**
     * @param string $Value
     * @param int    $MaxLineCount
     *
     * @return string
     */
    private function sanitizeDescription($Value, $MaxLineCount = 3)
    {

        $Value = explode("\n", $Value);
        array_walk($Value, function (&$Line) {

            if (empty( $Line )) {
                $Line = false;
            }
            if (strpos($Line, '@') === 0) {
                $Line = false;
            }
            if (strpos($Line, 'Line:') === 0) {
                $Line = false;
            }
            if (strpos($Line, '!') === 0) {
                $Line = false;
            }
        });
        $Value = array_filter($Value);
        $Value = preg_replace('!\s+?[\-]+\>!is', ': ', $Value);
        $Value = preg_replace('#!image.*?\..*?!#is', ' [IMAGE] ', $Value);
        $Value = preg_replace('!{html.*?}!is', ' ', $Value);

        $ShortDescription = trim(implode("\n", array_slice($Value, 0, $MaxLineCount)));
        $LongDescription = trim(implode("\n", array_slice($Value, $MaxLineCount)));

        if (strlen($ShortDescription) == 0 && $MaxLineCount > 0) {
            return new Small(new Italic(new Muted('Keine Beschreibung angegeben')));
        }

        if (strlen($LongDescription) > 0) {
            return new Small(nl2br($ShortDescription))
            .(new Accordion())->addItem(
                new Italic(new Small('[Beschreibung anzeigen]')),
                new Small(nl2br($LongDescription))
            )->getContent();
        } else {
            if (strlen($ShortDescription) == 0) {
                return new Small(new Italic(new Muted('Keine Beschreibung angegeben')));
            }
            return new Small(nl2br($ShortDescription));
        }
    }

    /**
     * @param $Value
     *
     * @return string
     */
    private function sanitizeTitle($Value)
    {

        $Value = preg_replace('!Account: [0-9]+!is', '[System Report]', $Value);
        $Value = preg_replace('!^Error!is', 'Anwendungsfehler', $Value);
        $Value = preg_replace('!^Exception!is', 'Absturz der Anwendung', $Value);
        $Value = preg_replace('!^Shutdown!is', 'Absturz der Anwendung', $Value);
        $Value = preg_replace('!\bBug\b!is', 'Fehler', $Value);
        $Value = preg_replace('!\s+?[\-]+\>!is', ': ', $Value);
        $Value = preg_replace('!\bnull\b!is', 'leer', $Value);
        return $Value;
    }

    /**
     * @param $Start
     * @param $Finish
     *
     * @return float|int
     */
    private function getDatePercent($Start, $Finish)
    {

        $Start = strtotime($Start);
        $Finish = strtotime($Finish);
        $Now = time();

        if ($Finish - $Start <= 0 || $Now >= $Finish) {
            $Percent = 100;
        } else {
            $Percent = ( $Now - $Start ) / ( $Finish - $Start ) * 100;
        }

        return number_format($Percent, 2, ',', '.');
    }
}