From b63c1dc9b1ed41626b99ba852a9a00ed417059ba Mon Sep 17 00:00:00 2001
From: Yassine Doghri <yassine@doghri.fr>
Date: Tue, 28 Feb 2023 16:47:13 +0000
Subject: [PATCH] feat: add downloads count to episode list

---
 app/Entities/Episode.php                      |  3 ++
 app/Helpers/components_helper.php             | 33 +++++++++++++++++++
 .../Admin/Controllers/EpisodeController.php   | 17 +++++++---
 modules/Admin/Language/en/Episode.php         |  1 +
 themes/cp_admin/episode/list.php              |  7 ++++
 5 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php
index cf1fd027eb..4850b8018b 100644
--- a/app/Entities/Episode.php
+++ b/app/Entities/Episode.php
@@ -72,6 +72,7 @@ use RuntimeException;
  * @property bool $is_published_on_hubs
  * @property int $posts_count
  * @property int $comments_count
+ * @property int $downloads
  * @property EpisodeComment[]|null $comments
  * @property bool $is_premium
  * @property int $created_by
@@ -109,6 +110,8 @@ class Episode extends Entity
 
     protected ?Chapters $chapters = null;
 
+    protected int $downloads = 0;
+
     /**
      * @var Person[]|null
      */
diff --git a/app/Helpers/components_helper.php b/app/Helpers/components_helper.php
index d831684a46..6099eb94ce 100644
--- a/app/Helpers/components_helper.php
+++ b/app/Helpers/components_helper.php
@@ -447,3 +447,36 @@ if (! function_exists('category_label')) {
 }
 
 // ------------------------------------------------------------------------
+
+if (! function_exists('downloads_abbr')) {
+    function downloads_abbr(int $downloads): string
+    {
+        if ($downloads < 1000) {
+            return (string) $downloads;
+        }
+
+        $option = match (true) {
+            $downloads < 1_000_000 => [
+                'divider' => 1_000,
+                'suffix' => 'K',
+            ],
+            $downloads < 1_000_000_000 => [
+                'divider' => 1_000_000,
+                'suffix' => 'M',
+            ],
+            default => [
+                'divider' => 1_000_000_000,
+                'suffix' => 'B',
+            ],
+        };
+        $formatter = new NumberFormatter(service('request')->getLocale(), NumberFormatter::DECIMAL);
+
+        $formatter->setPattern('#,##0.##');
+
+        $abbr = $formatter->format($downloads / $option['divider']) . $option['suffix'];
+
+        return <<<HTML
+            <abbr title="{$downloads}">{$abbr}</abbr>
+        HTML;
+    }
+}
diff --git a/modules/Admin/Controllers/EpisodeController.php b/modules/Admin/Controllers/EpisodeController.php
index 903cab0d5a..73b382d409 100644
--- a/modules/Admin/Controllers/EpisodeController.php
+++ b/modules/Admin/Controllers/EpisodeController.php
@@ -71,19 +71,28 @@ class EpisodeController extends BaseController
             // Use LIKE operator as a fallback.
             if (strlen($query) < 4) {
                 $episodes = (new EpisodeModel())
-                    ->where('podcast_id', $this->podcast->id)
+                    ->select('episodes.*, IFNULL(SUM(ape.hits),0) as downloads')
+                    ->join('analytics_podcasts_by_episode ape', 'episodes.id=ape.episode_id', 'left')
+                    ->where('episodes.podcast_id', $this->podcast->id)
                     ->like('title', $query)
                     ->orLike('description_markdown', $query)
+                    ->groupBy('episodes.id')
                     ->orderBy('-`published_at`', '', false)
                     ->orderBy('created_at', 'desc');
             } else {
                 $episodes = (new EpisodeModel())
-                    ->where('podcast_id', $this->podcast->id)
-                    ->where("MATCH (title, description_markdown) AGAINST ('{$query}')");
+                    ->select('episodes.*, IFNULL(SUM(ape.hits),0) as downloads')
+                    ->join('analytics_podcasts_by_episode ape', 'episodes.id=ape.episode_id', 'left')
+                    ->where('episodes.podcast_id', $this->podcast->id)
+                    ->where("MATCH (title, description_markdown) AGAINST ('{$query}')")
+                    ->groupBy('episodes.id');
             }
         } else {
             $episodes = (new EpisodeModel())
-                ->where('podcast_id', $this->podcast->id)
+                ->select('episodes.*, IFNULL(SUM(ape.hits),0) as downloads')
+                ->join('analytics_podcasts_by_episode ape', 'episodes.id=ape.episode_id', 'left')
+                ->where('episodes.podcast_id', $this->podcast->id)
+                ->groupBy('episodes.id')
                 ->orderBy('-`published_at`', '', false)
                 ->orderBy('created_at', 'desc');
         }
diff --git a/modules/Admin/Language/en/Episode.php b/modules/Admin/Language/en/Episode.php
index 91313a7c5a..98498bee21 100644
--- a/modules/Admin/Language/en/Episode.php
+++ b/modules/Admin/Language/en/Episode.php
@@ -55,6 +55,7 @@ return [
         }',
         'episode' => 'Episode',
         'visibility' => 'Visibility',
+        'downloads' => 'Downloads',
         'comments' => 'Comments',
         'actions' => 'Actions',
     ],
diff --git a/themes/cp_admin/episode/list.php b/themes/cp_admin/episode/list.php
index ff82cdfe71..7154b04956 100644
--- a/themes/cp_admin/episode/list.php
+++ b/themes/cp_admin/episode/list.php
@@ -82,9 +82,16 @@ data_table(
                 return publication_pill(
                     $episode->published_at,
                     $episode->publication_status,
+                    'text-sm'
                 );
             },
         ],
+        [
+            'header' => lang('Episode.list.downloads'),
+            'cell' => function ($episode): string {
+                return downloads_abbr($episode->downloads);
+            },
+        ],
         [
             'header' => lang('Episode.list.comments'),
             'cell' => function ($episode): int {
-- 
GitLab