Browse Source

Allow comparing all major versions in download stats, refs #1065

Jordi Boggiano 5 years ago
parent
commit
fae1ddbd7a

+ 24 - 14
src/Packagist/WebBundle/Controller/PackageController.php

@@ -1016,8 +1016,8 @@ class PackageController extends Controller
                 }
             }
         }
-        $data['majorVersions'] = array_unique($majorVersions);
-        $data['expandedId'] = $expandedVersion ? $expandedVersion->getId() : false;
+        $data['majorVersions'] = $majorVersions ? array_merge(['all'], array_unique($majorVersions)) : [];
+        $data['expandedId'] = $majorVersions ? 'major/all' : ($expandedVersion ? $expandedVersion->getId() : false);
 
         return $data;
     }
@@ -1138,7 +1138,7 @@ class PackageController extends Controller
      * )
      * @ParamConverter("version", options={"exclude": {"name"}})
      */
-    public function overallStatsAction(Request $req, Package $package, Version $version = null, int $majorVersion = null)
+    public function overallStatsAction(Request $req, Package $package, Version $version = null, $majorVersion = null)
     {
         if ($from = $req->query->get('from')) {
             $from = new DateTimeImmutable($from);
@@ -1154,37 +1154,47 @@ class PackageController extends Controller
 
         $dlData = [];
         if ($majorVersion) {
-            $dlData = $this->getDoctrine()->getRepository('PackagistWebBundle:Download')->findDataByMajorVersion($package, $majorVersion);
+            if ($majorVersion === 'all') {
+                $dlData = $this->getDoctrine()->getRepository('PackagistWebBundle:Download')->findDataByMajorVersions($package);
+            } else {
+                if (!is_numeric($majorVersion)) {
+                    throw new BadRequestHttpException('Major version should be an int or "all"');
+                }
+                $dlData[$majorVersion] = $this->getDoctrine()->getRepository('PackagistWebBundle:Download')->findDataByMajorVersion($package, (int) $majorVersion);
+            }
         } elseif ($version) {
             $downloads = $this->getDoctrine()->getRepository('PackagistWebBundle:Download')->findOneBy(['id' => $version->getId(), 'type' => Download::TYPE_VERSION]);
-            $dlData[] = $downloads ? $downloads->getData() : [];
+            $dlData[$version->getVersion()] = [$downloads ? $downloads->getData() : []];
         } else {
             $downloads = $this->getDoctrine()->getRepository('PackagistWebBundle:Download')->findOneBy(['id' => $package->getId(), 'type' => Download::TYPE_PACKAGE]);
-            $dlData[] = $downloads ? $downloads->getData() : [];
+            $dlData[$package->getName()] = [$downloads ? $downloads->getData() : []];
         }
 
         $datePoints = $this->createDatePoints($from, $to, $average);
+        $series = [];
 
         foreach ($datePoints as $label => $values) {
-            $value = 0;
-            foreach ($values as $valueKey) {
-                foreach ($dlData as $data) {
-                    $value += $data[$valueKey] ?? 0;
+            foreach ($dlData as $seriesName => $seriesData) {
+                $value = 0;
+                foreach ($values as $valueKey) {
+                    foreach ($seriesData as $data) {
+                        $value += $data[$valueKey] ?? 0;
+                    }
                 }
+                $series[$seriesName][] = ceil($value / count($values));
             }
-            $datePoints[$label] = ceil($value / count($values));
         }
 
         $datePoints = array(
             'labels' => array_keys($datePoints),
-            'values' => array_values($datePoints),
+            'values' => $series,
         );
 
         $datePoints['average'] = $average;
 
         if (empty($datePoints['labels']) && empty($datePoints['values'])) {
             $datePoints['labels'][] = date('Y-m-d');
-            $datePoints['values'][] = 0;
+            $datePoints['values'][] = [0];
         }
 
         $response = new JsonResponse($datePoints);
@@ -1197,7 +1207,7 @@ class PackageController extends Controller
      * @Route(
      *      "/packages/{name}/stats/major/{majorVersion}.json",
      *      name="major_version_stats",
-     *      requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?", "majorVersion"="[0-9]+?"}
+     *      requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+?", "majorVersion"="(all|[0-9]+?)"}
      * )
      */
     public function majorVersionStatsAction(Request $req, Package $package, $majorVersion)

+ 27 - 0
src/Packagist/WebBundle/Entity/DownloadRepository.php

@@ -40,4 +40,31 @@ class DownloadRepository extends ServiceEntityRepository
             return $row['data'] ? json_decode($row['data'], true) : [];
         }, $result);
     }
+
+    public function findDataByMajorVersions(Package $package)
+    {
+        $sql = '
+            SELECT v.normalizedVersion, d.data
+            FROM package_version v
+            INNER JOIN download d ON d.id=v.id AND d.type = :versionType
+            WHERE v.package_id = :package AND v.development = 0 AND v.normalizedVersion REGEXP "^[0-9]+"
+            ORDER BY v.normalizedVersion DESC
+        ';
+
+        $stmt = $this->getEntityManager()->getConnection()
+            ->executeQuery(
+                $sql,
+                ['package' => $package->getId(), 'versionType' => Download::TYPE_VERSION]
+            );
+        $result = $stmt->fetchAll();
+        $stmt->closeCursor();
+
+        $series = [];
+        foreach ($result as $row) {
+            $name = preg_replace('{^(\d+)(\.|$).*}', '$1', $row['normalizedVersion']);
+            $series[$name][] = $row['data'] ? json_decode($row['data'], true) : [];
+        }
+
+        return $series;
+    }
 }

+ 44 - 9
src/Packagist/WebBundle/Resources/public/js/charts.js

@@ -87,16 +87,44 @@
     });
 
     window.initPackageStats = function (average, date, versions, statsUrl, versionStatsUrl) {
+        colors = [
+            '#f28d1a',
+            '#1765f4',
+            '#ed1f96',
+            '#ee1e23',
+            '#b817f4',
+            '#c4f516',
+            '#804040',
+            '#ff8040',
+            '#008080',
+            '#004080',
+            '#8080ff',
+            '#800040',
+            '#800000',
+        ];
+
         var match,
             hash = document.location.hash,
             versionCache = {},
             ongoingRequest = false;
 
         function initChart(type, res) {
+            var key, series = [];
+
+            for (key in res.values) {
+                if (res.values.hasOwnProperty(key)) {
+                    series.push({name: key, values: res.values[key]});
+                }
+            }
+
+            series.sort(function (a, b) {
+                return b.name.localeCompare(a.name);
+            })
+
             initPackagistChart(
                 $('.js-'+type+'-dls')[0],
                 res.labels,
-                [{name: 'Daily Downloads', values: res.values}],
+                series,
                 true
             );
         }
@@ -125,6 +153,20 @@
 
             $('.package .stats-wrapper').hide();
             $('.package .stats-wrapper[data-stats-type=' + statsType + ']').show();
+
+            initializeVersionListExpander();
+        }
+
+        function initializeVersionListExpander() {
+            var versionsList = $('.package .versions')[0];
+            if (versionsList.offsetHeight < versionsList.scrollHeight) {
+                $('.package .versions-expander').removeClass('hidden').on('click', function () {
+                    $(this).addClass('hidden');
+                    $(versionsList).css('max-height', 'inherit');
+                });
+            } else {
+                $('.package .versions-expander').addClass('hidden')
+            }
         }
 
         // initializer for #<version-id> present on page load
@@ -177,13 +219,6 @@
             $('.version-stats-chart').css('top', Math.max(0, Math.min(footerPadding, window.scrollY - basePos + headerPadding)) + 'px');
         });
 
-        // initialize version list expander
-        var versionsList = $('.package .versions')[0];
-        if (versionsList.offsetHeight < versionsList.scrollHeight) {
-            $('.package .versions-expander').removeClass('hidden').on('click', function () {
-                $(this).addClass('hidden');
-                $(versionsList).css('max-height', 'inherit');
-            });
-        }
+        initializeVersionListExpander();
     };
 })(jQuery);

+ 1 - 1
src/Packagist/WebBundle/Resources/views/package/stats.html.twig

@@ -76,7 +76,7 @@
                             <div class="package-aside versions-wrapper">
                                 <ul class="versions">
                                     {% for majorVersion in majorVersions %}
-                                        {% set expanded = majorVersion == expandedId|default(false) %}
+                                        {% set expanded = ('major/' ~ majorVersion) == expandedId|default(false) %}
                                         <li class="details-toggler version{% if loop.last %} last{% endif %}{% if expanded %} open{% endif %}" data-version-id="major/{{ majorVersion }}">
                                             <a href="#major/{{ majorVersion }}" class="version-number">
                                                 {{- majorVersion -}}