From 8821e4a90b723dc351c0c88de4322596d41d20b9 Mon Sep 17 00:00:00 2001
From: Nico Oelgart <nicoswd@gmail.com>
Date: Sat, 16 Mar 2024 17:47:32 +0100
Subject: [PATCH] Add pretty calendar diff to release

Also fixes new releases because of metadata updates
---
 .github/workflows/update-calendar.yml |   9 +-
 bin/calendar-diff                     | 256 ++++++++++++++++++++++++++
 2 files changed, 260 insertions(+), 5 deletions(-)
 create mode 100755 bin/calendar-diff

diff --git a/.github/workflows/update-calendar.yml b/.github/workflows/update-calendar.yml
index ce283e8..99e0935 100644
--- a/.github/workflows/update-calendar.yml
+++ b/.github/workflows/update-calendar.yml
@@ -42,17 +42,16 @@ jobs:
       - name: "Check diff"
         id: check_diff
         run: |
-          curl -sSL "https://calendar.ifsc.stream/?format=json&nocache=1" --output current-calendar.json
+          curl -sSL "https://calendar.ifsc.stream/?format=json&nocache=1" --output old-calendar.json
           
           if [ $(jq '.events | length' "${{ env.CALENDAR_FILE_JSON }}") -lt 9 ]; then
             echo "Too few events found in current calendar"
             exit 1
           fi
           
-          diff -C 3 current-calendar.json "${{ env.CALENDAR_FILE_JSON }}" > calendar.diff || true
+          php bin/calendar-diff old-calendar.json "${{ env.CALENDAR_FILE_JSON }}" > diff.md || true
           
-          if [ $(wc -w < calendar.diff) -gt 0 ]; then
-            echo -e "Changes:\n\`\`\`diff\n$(cat calendar.diff)\n\`\`\`" > calendar.diff
+          if [ $(wc -w < diff.md) -gt 0 ]; then
             echo "has_changes=true" >> $GITHUB_OUTPUT
           else
             echo "has_changes=false" >> $GITHUB_OUTPUT
@@ -74,7 +73,7 @@ jobs:
             ${{ env.CALENDAR_FILE_JSON }}
           tag_name: ${{ steps.date.outputs.tag_name }}
           name: ${{ steps.date.outputs.name }}
-          body_path: calendar.diff
+          body_path: diff.md
 
       - name: "Run latest-tag"
         if: steps.check_diff.outputs.has_changes == 'true'
diff --git a/bin/calendar-diff b/bin/calendar-diff
new file mode 100755
index 0000000..d921651
--- /dev/null
+++ b/bin/calendar-diff
@@ -0,0 +1,256 @@
+#!/usr/bin/env php
+<?php declare(strict_types=1);
+
+/**
+ * @license  http://opensource.org/licenses/mit-license.php MIT
+ * @link     https://github.com/nicoSWD
+ * @author   Nicolas Oelgart <nico@oelgart.com>
+ */
+
+function generate_start_list_diff(array $oldStartList, array $newStartList): string
+{
+    if ($oldStartList === $newStartList) {
+        return '';
+    }
+
+    $diff = '';
+
+    foreach ($newStartList as $key => $athlete) {
+        if (isset($oldStartList[$key])) {
+            if ($athlete !== $oldStartList[$key]) {
+                $diff .= "|Replaced|<s>{$oldStartList[$key]['first_name']}</s>|<s>{$oldStartList[$key]['last_name']}</s>|{$athlete['first_name']}|{$athlete['last_name']}" . PHP_EOL;
+            }
+        } else {
+            $diff .= "|Added|{$athlete['first_name']}|{$athlete['last_name']}|---|---|" . PHP_EOL;
+        }
+    }
+
+    foreach ($oldStartList as $key => $athlete) {
+        if (!isset($newStartList[$key])) {
+            $diff .= "|Removed|<s>{$athlete['first_name']}</s>|<s>{$athlete['last_name']}</s>|---|---|" . PHP_EOL;
+        }
+    }
+
+    return $diff;
+}
+
+function normalize_value(mixed $value): string
+{
+    if (empty($value)) {
+        return 'null';
+    }
+
+    if (is_array($value)) {
+        $value = implode(', ', $value);
+    }
+
+    return (string) $value;
+}
+
+function find_round_by_name(array $oldRounds, string $name): ?array
+{
+    foreach ($oldRounds as $oldRound) {
+        if ($oldRound['name'] === $name) {
+            return $oldRound;
+        }
+    }
+
+    return null;
+}
+
+function get_event_by_id(array $events, int $eventId): ?array
+{
+    foreach ($events as $event) {
+        if ($event['id'] === $eventId) {
+            return $event;
+        }
+    }
+
+    return null;
+}
+
+function normalize_events_from_file(string $fileName): array
+{
+    return json_decode(file_get_contents($fileName), associative: true)['events'];
+}
+
+function generate_round_diff(array $oldRounds, array $newRounds): string
+{
+    $diff = '';
+    $changedRounds = '';
+    $addedRounds = '';
+
+    foreach ($newRounds as $newRound) {
+        $oldRound = find_round_by_name($oldRounds, $newRound['name']);
+
+        if (!$oldRound) {
+            foreach ($newRound as $value) {
+                $addedRounds .= "|" . normalize_value($value);
+            }
+
+            $addedRounds .= "|" . PHP_EOL;
+        } else {
+            $roundDiff = '';
+            foreach ($newRound as $key => $newValue) {
+                $oldValue = $oldRound[$key];
+
+                if ($newValue !== $oldValue) {
+                    $roundDiff .= "|{$key}  |" . normalize_value($oldValue) . "|" . normalize_value($newValue) . "|" . PHP_EOL;
+                }
+            }
+
+            if ($roundDiff) {
+                $changedRounds .= "| {$newRound['name']}   |---            |---              |" . PHP_EOL;
+                $changedRounds .= "|-----------------------|---------------|-----------------|" . PHP_EOL;
+                $changedRounds .= "| **Key**               | **Old Value** | **New Value**   |" . PHP_EOL;
+                $changedRounds .= $roundDiff . PHP_EOL;
+            }
+        }
+    }
+
+    if ($changedRounds) {
+        $diff .= PHP_EOL;
+        $diff .= "#### Changed Rounds" . PHP_EOL;
+        $diff .= $changedRounds;
+    }
+
+    if ($addedRounds) {
+        $diff .= "#### Added Rounds" . PHP_EOL;
+        $diff .= "|" . implode('|', array_keys($newRounds[0])) . "|" . PHP_EOL;
+        $diff .= "|" . str_repeat('-------------|', count($newRounds[0])) . PHP_EOL;
+        $diff .= $addedRounds . PHP_EOL . PHP_EOL;
+    }
+
+    return $diff;
+}
+
+$oldEvents = normalize_events_from_file($argv[1]);
+$newEvents = normalize_events_from_file($argv[2]);
+
+$diff = '';
+$addedEvents = '';
+$removedEvents = '';
+
+foreach ($newEvents as $newEvent) {
+    if (!get_event_by_id($oldEvents, $newEvent['id'])) {
+        $addedEvents .= "### 🏆 {$newEvent['name']}" . PHP_EOL;
+        $addedEvents .= "|Key|Value|" . PHP_EOL;
+        $addedEvents .= "|---|-----|" . PHP_EOL;
+
+        foreach ($newEvent as $key => $value) {
+            if ($key === 'rounds' || $key === 'start_list') {
+                continue;
+            }
+
+            $addedEvents .= "|$key|" . normalize_value($value) ."|" . PHP_EOL;
+        }
+
+        $addedEvents .= PHP_EOL;
+        $addedEvents .= "#### đŸŽ¯ Rounds:" . PHP_EOL;
+        $addedEvents .= "|" . implode('|', array_keys($newEvent['rounds'][0])) . "|" . PHP_EOL;
+        $addedEvents .= "|" . str_repeat('-------------|', count($newEvent['rounds'][0])) . PHP_EOL;
+
+        foreach ($newEvent['rounds'] as $round) {
+            foreach ($round as $value) {
+                $addedEvents .= "|" . normalize_value($value);
+            }
+            $addedEvents .= "|" . PHP_EOL;
+        }
+
+        $addedEvents .= PHP_EOL;
+        $addedEvents .= "#### 📋 Start List:" . PHP_EOL;
+        $addedEvents .= "|" . implode('|', array_keys($newEvent['start_list'][0])) . "|" . PHP_EOL;
+        $addedEvents .= "|" . str_repeat('-------------|', count($newEvent['start_list'][0])) . PHP_EOL;
+
+        foreach ($newEvent['start_list'] as $startList) {
+            foreach ($startList as $value) {
+                $addedEvents .= "|" . normalize_value($value);
+            }
+            $addedEvents .= "|" . PHP_EOL;
+        }
+    }
+}
+
+foreach ($oldEvents as $oldEvent) {
+    if (!get_event_by_id($newEvents, $oldEvent['id'])) {
+        $removedEvents .= "|🏆 {$oldEvent['name']}|---|---    |" . PHP_EOL;
+        $removedEvents .= "|-------------|---------|-------|" . PHP_EOL;
+        $removedEvents .= "|**Round**    |**Starts At**|**Ends At**|" . PHP_EOL;
+
+        foreach ($oldEvent['rounds'] as $round) {
+            $removedEvents .= "|<s>{$round['name']}</s>|<s>{$round['starts_at']}</s>|<s>{$round['ends_at']}</s>" . PHP_EOL;
+        }
+
+        $removedEvents .= PHP_EOL;
+    }
+}
+
+if ($addedEvents) {
+    $diff .= "🎉 Added Events:" . PHP_EOL;
+    $diff .= "===============" . PHP_EOL;
+    $diff .= $addedEvents . PHP_EOL;
+}
+
+if ($removedEvents) {
+    $diff .= "🗑ī¸ Removed Events:" . PHP_EOL;
+    $diff .= "===============" . PHP_EOL;
+    $diff .= $removedEvents . PHP_EOL;
+}
+
+$changedEvents = '';
+
+foreach ($newEvents as $newEvent) {
+    $oldEvent = get_event_by_id($oldEvents, $newEvent['id']);
+
+    if (!$oldEvent) {
+        continue;
+    }
+
+    $newDiff = '';
+    $roundDiff = '';
+    $startListDiff = '';
+
+    foreach ($newEvent as $key => $value) {
+        if ($key === 'rounds') {
+            $roundDiff .= generate_round_diff($oldEvent[$key], $value);
+        } elseif ($key === 'start_list') {
+            $startListDiff .= generate_start_list_diff($oldEvent[$key], $value);
+        } elseif (normalize_value($value) !== normalize_value($oldEvent[$key])) {
+            $newDiff .= "|$key|" . normalize_value($oldEvent[$key]) ."|" . normalize_value($value) . "|" . PHP_EOL;
+        }
+    }
+
+    if ($newDiff || $roundDiff || $startListDiff) {
+        $changedEvents .= "#### 🏆 {$newEvent['name']}" . PHP_EOL;
+
+        if ($newDiff) {
+            $changedEvents .= "| ℹī¸ Event Details          | ---           | ---            |" . PHP_EOL;
+            $changedEvents .= "|------------------------|---------------|----------------|" . PHP_EOL;
+            $changedEvents .= "| **Key**                | **Old Value** | **New Value**  |" . PHP_EOL;
+            $changedEvents .= $newDiff;
+        }
+
+        if ($roundDiff) {
+            $changedEvents .= PHP_EOL;
+            $changedEvents .= $roundDiff;
+        }
+
+        if ($startListDiff) {
+            $changedEvents .= PHP_EOL;
+            $changedEvents .= "| 📋 Start List | ---             | ---           | ---                | ---               |" . PHP_EOL;
+            $changedEvents .= "|---------------|-----------------|---------------|--------------------|-------------------|" . PHP_EOL;
+            $changedEvents .= "| **Status**    | **First Name**  | **Last Name** | **New First Name** | **New Last Name** |" . PHP_EOL;
+            $changedEvents .= $startListDiff . PHP_EOL;
+        }
+
+        $changedEvents .= PHP_EOL;
+    }
+}
+
+if ($changedEvents) {
+    $diff .= "✏ī¸ Changed Events:" . PHP_EOL;
+    $diff .= "==================" . PHP_EOL;
+    $diff .= $changedEvents;
+}
+
+echo $diff;