Procházet zdrojové kódy

Добавлен график

Vladimir Tkachev před 8 roky
rodič
revize
edb3b5960f

+ 12 - 3
README.md

@@ -5,10 +5,11 @@
 
 ### Зависимости
  - \>PHP 7.1
- - MongoDB расширение из PECL
  - Composer
  - MongoDB
+ - MongoDB расширение из PECL
  - ClickHouse
+ - gnuplot
 
 > В своей конфигурации я использую MongoDB 3.6.2,
 > MongoDB extension 1.3.4
@@ -38,15 +39,23 @@
    > При помощи опции --count также можно указать количество событий,
    > генерируемых для каждой компании. По-умолчанию это 256
    
-4. И наконец получить сводку о количествах событий за текущий месяц
+4. Получить сводку о количествах событий за текущий месяц
    для конкретной компании:
    
-   **`bin/kitchen.php generate-random-events --verbose`**
+   **`bin/kitchen.php events-report --company-id=5a71d90f32cc8e5d6a09f8bb`**
    
    ![Result](https://raw.githubusercontent.com/avolver/company-events-kitchen/master/events-kitchen.png)
    
    > Месяц и год можно изменить опциями --month и --year соответственно
    
+5. И наконец — генерируем простенький, но график:
+   
+   **`bin/kitchen.php events-report --company-id=5a71d90f32cc8e5d6a09f8bb --format=graph > plot.png`**
+   
+   ![Plot](https://raw.githubusercontent.com/avolver/company-events-kitchen/master/plot.png)
+   
+   > Рендер графика реализован исключительно в ознакомительных целях и не претендует быть наглядным
+   
 ### Пояснения
 
  - Доступные типы событий хранятся в перечислении `CompanyEvent`

+ 1 - 0
composer.json

@@ -11,6 +11,7 @@
         "fzaninotto/faker":          "1.7.1",
         "smi2/phpclickhouse":        "0.17.12.1",
         "myclabs/php-enum":          "1.5.2",
+        "gregwar/gnuplot":           "1.0.0",
         "roave/security-advisories": "dev-master"
     },
     "license": "BEERWARE",

+ 60 - 13
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "content-hash": "f639da40c5d2d04164fbfc01ff9faf9f",
+    "content-hash": "fa9c619fc63bf5f0afe1c8e830ba0ca1",
     "packages": [
         {
             "name": "fzaninotto/faker",
@@ -56,6 +56,53 @@
             ],
             "time": "2017-08-15T16:48:10+00:00"
         },
+        {
+            "name": "gregwar/gnuplot",
+            "version": "v1.0.0",
+            "target-dir": "Gregwar/GnuPlot",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Gregwar/GnuPlot.git",
+                "reference": "6277ca7dc0457ea118958fa149f091f14e23c1cb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Gregwar/GnuPlot/zipball/6277ca7dc0457ea118958fa149f091f14e23c1cb",
+                "reference": "6277ca7dc0457ea118958fa149f091f14e23c1cb",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Gregwar\\GnuPlot": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Grégoire Passault",
+                    "email": "g.passault@gmail.com",
+                    "homepage": "http://www.gregwar.com/"
+                }
+            ],
+            "description": "GnuPlot library",
+            "homepage": "https://github.com/Gregwar/GnuPlot",
+            "keywords": [
+                "gnuplot",
+                "graph",
+                "image",
+                "plot",
+                "png",
+                "stats"
+            ],
+            "time": "2015-02-20T23:48:35+00:00"
+        },
         {
             "name": "myclabs/php-enum",
             "version": "1.5.2",
@@ -106,12 +153,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/Roave/SecurityAdvisories.git",
-                "reference": "0be5f7071b9007938a360d01da654b13d9caf12d"
+                "reference": "a5ff7ff05264c8796ffb1b7b6fdde43a9c6b0776"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0be5f7071b9007938a360d01da654b13d9caf12d",
-                "reference": "0be5f7071b9007938a360d01da654b13d9caf12d",
+                "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a5ff7ff05264c8796ffb1b7b6fdde43a9c6b0776",
+                "reference": "a5ff7ff05264c8796ffb1b7b6fdde43a9c6b0776",
                 "shasum": ""
             },
             "conflict": {
@@ -171,8 +218,8 @@
                 "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3",
                 "silverstripe/framework": ">=3,<3.3",
                 "silverstripe/userforms": "<3",
-                "simplesamlphp/saml2": "<1.8.1|>=1.9,<1.9.1|>=1.10,<1.10.3|>=2,<2.3.3",
-                "simplesamlphp/simplesamlphp": "<1.14.16",
+                "simplesamlphp/saml2": "<1.10.4|>=2,<2.3.5|>=3,<3.1.1",
+                "simplesamlphp/simplesamlphp": "<1.15.2",
                 "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1",
                 "socalnick/scn-social-auth": "<1.15.2",
                 "squizlabs/php_codesniffer": ">=1,<2.8.1",
@@ -244,7 +291,7 @@
                 }
             ],
             "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it",
-            "time": "2018-01-31T06:22:20+00:00"
+            "time": "2018-01-31T19:36:12+00:00"
         },
         {
             "name": "smi2/phpclickhouse",
@@ -367,16 +414,16 @@
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.6.0",
+            "version": "v1.7.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296"
+                "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
-                "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b",
+                "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b",
                 "shasum": ""
             },
             "require": {
@@ -388,7 +435,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.6-dev"
+                    "dev-master": "1.7-dev"
                 }
             },
             "autoload": {
@@ -422,7 +469,7 @@
                 "portable",
                 "shim"
             ],
-            "time": "2017-10-11T12:05:26+00:00"
+            "time": "2018-01-30T19:27:44+00:00"
         }
     ],
     "packages-dev": [],

binární
plot.png


+ 61 - 4
src/Command/EventsReportCommand.php

@@ -6,8 +6,9 @@ namespace Avolver\CompanyEventsKitchen\Command;
 use Avolver\CompanyEventsKitchen\Application;
 use Avolver\CompanyEventsKitchen\Event\CompanyEvent;
 use Avolver\CompanyEventsKitchen\Repository\EventsRepository;
-use Avolver\CompanyEventsKitchen\Transformer\EventReportTransformer;
+use Avolver\CompanyEventsKitchen\Transformer\MonthEventsReportTransformer;
 use MongoDB\BSON\ObjectId;
+use Gregwar\GnuPlot\GnuPlot;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Helper\Table;
 use Symfony\Component\Console\Input\InputInterface;
@@ -23,17 +24,24 @@ use Symfony\Component\Console\Output\OutputInterface;
  */
 class EventsReportCommand extends Command
 {
+
+    private const FORMAT_TABLE = 'table';
+    private const FORMAT_GRAPH = 'graph';
+
     /**
      * {@inheritdoc}
      */
     protected function configure(): void
     {
+        $formats = implode(', ', [self::FORMAT_TABLE, self::FORMAT_GRAPH]);
+
         $this
             ->setName('events-report')
             ->setDescription('Создание случайных событий для каждой компании')
             ->addOption('company-id', null, InputOption::VALUE_REQUIRED, 'ID компании')
             ->addOption('month', null, InputOption::VALUE_OPTIONAL, 'Месяц')
-            ->addOption('year', null, InputOption::VALUE_REQUIRED, 'Год');
+            ->addOption('year', null, InputOption::VALUE_REQUIRED, 'Год')
+            ->addOption('format', null, InputOption::VALUE_OPTIONAL, 'Тип вывода: ' . $formats, self::FORMAT_TABLE);
     }
 
     /**
@@ -59,10 +67,32 @@ class EventsReportCommand extends Command
             $year
         );
 
+        $result = MonthEventsReportTransformer::transform($result);
+
+        switch ($input->getOption('format')) {
+            case self::FORMAT_TABLE:
+                $this->renderTable($output, $result);
+                break;
+            case self::FORMAT_GRAPH:
+                $this->renderGraph($result);
+                break;
+            default:
+                throw new \InvalidArgumentException('Некорректный формат вывода');
+        }
+    }
+
+    /**
+     * Вывод таблицы с событиями
+     *
+     * @param OutputInterface $output
+     * @param array $result
+     */
+    private function renderTable(OutputInterface $output, array $result): void
+    {
         $eventTypes = CompanyEvent::keys();
-        $rows       = [];
+        $rows = [];
 
-        foreach (EventReportTransformer::transform($result) as $date => $counts) {
+        foreach ($result as $date => $counts) {
             $row = [$date];
             foreach ($eventTypes as $eventType) {
                 $row[] = $counts[$eventType] ?? '0';
@@ -76,4 +106,31 @@ class EventsReportCommand extends Command
             ->setRows($rows);
         $table->render();
     }
+
+    /**
+     * Сохранение графика
+     *
+     * @param array $result
+     */
+    public function renderGraph(array $result)
+    {
+        $eventTypes = CompanyEvent::keys();
+        $plot = (new GnuPlot())
+            ->setXTimeFormat('%Y-%m-%d')
+            ->setXLabel('Дата')
+            ->setYLabel('Количество')
+            ->setWidth(640);
+
+        foreach ($eventTypes as $eventIndex => $eventType) {
+            $plot->setTitle($eventIndex, $eventType);
+        }
+
+        foreach ($result as $date => $counts) {
+            foreach ($eventTypes as $eventIndex => $eventType) {
+                $plot->push($date, $counts[$eventType] ?? '0', $eventIndex);
+            }
+        }
+
+        echo $plot->get();
+    }
 }

+ 0 - 33
src/Transformer/EventReportTransformer.php

@@ -1,33 +0,0 @@
-<?php
-declare(strict_types = 1);
-
-namespace Avolver\CompanyEventsKitchen\Transformer;
-
-/**
- * Преобразователь сырых данных из EventsRepository::findEventsByCompanyId в массив, пригодный для отчёта
- *
- * @package Avolver\CompanyEventsKitchen\Transformer
- */
-class EventReportTransformer
-{
-    /**
-     * Трансформация данных
-     *
-     * @param array $data
-     *
-     * @return array
-     */
-    public static function transform(array $data): array
-    {
-        $result = [];
-
-        foreach ($data as $row) {
-            if (!array_key_exists($row['date'], $result)) {
-                $result[$row['date']] = [];
-            }
-            $result[$row['date']][$row['type']] = $row['count'];
-        }
-
-        return $result;
-    }
-}

+ 59 - 0
src/Transformer/MonthEventsReportTransformer.php

@@ -0,0 +1,59 @@
+<?php
+declare(strict_types = 1);
+
+namespace Avolver\CompanyEventsKitchen\Transformer;
+
+/**
+ * Преобразователь сырых данных из EventsRepository::findEventsByCompanyId за месяц в массив, пригодный для отчёта
+ *
+ * @package Avolver\CompanyEventsKitchen\Transformer
+ */
+class MonthEventsReportTransformer
+{
+    /**
+     * Трансформация данных
+     *
+     * @param array $data
+     *
+     * @return array
+     * @throws \Exception
+     */
+    public static function transform(array $data): array
+    {
+        $result = [];
+
+
+
+        foreach ($data as $row) {
+            if (!\count($result)) {
+                $result = self::initResultByEmptyDays($row, $result);
+            }
+            $result[$row['date']][$row['type']] = $row['count'];
+        }
+
+        return $result;
+    }
+
+    /**
+     * Заполнение результата пустыми днями месяца
+     *
+     * @param $row
+     * @param $result
+     * @return mixed
+     * @throws \Exception
+     */
+    private static function initResultByEmptyDays($row, $result)
+    {
+        $start = (new \DateTime($row['date']))->modify('first day of this month');
+        $interval = new \DateInterval('P1D');
+        $end = (clone $start)->modify('last day of this month')->modify('+1 day');
+        $period = new \DatePeriod($start, $interval, $end);
+
+        /** @var \DateTime $dt */
+        foreach ($period as $dt) {
+            $result[$dt->format('Y-m-d')] = [];
+        }
+
+        return $result;
+    }
+}