Skip to content

[WIP] Feat/push likes to wiki pages#299

Open
alterphp wants to merge 2 commits into
masterfrom
feat/push-likes-to-wiki-pages
Open

[WIP] Feat/push likes to wiki pages#299
alterphp wants to merge 2 commits into
masterfrom
feat/push-likes-to-wiki-pages

Conversation

@alterphp
Copy link
Copy Markdown
Collaborator

@alterphp alterphp commented Dec 1, 2025

Resolves #278

Nouvelle propriété du wiki

Number of likes

Variables d'environnement à configurer

  • WIKI_BOT_USERNAME
  • WIKI_BOT_PASSWORD

Permissions données au robot créé sur le wiki pour poster le nombre de likes

  • Droits de base
  • Accès à haut volume (robot)
  • Modifier des pages existantes
  • Modifier des pages protégées

Programmation de la commande

Jouer la commande une première fois pour rattrapage sur 10 ans : php artisan pages:export-details-to-wiki --since-x-days 3650

La commande est configurée pour être jouée tous les jours à 22h45, et traiter les pages qui ont eu des interactrions modifiées sur les 3 derniers jours.

Summary by CodeRabbit

  • New Features

    • Automated daily export of page likes to the French wiki (scheduled at 22:45, invoked with a 3‑day export window).
    • Command-line option to configure export window (default 15 days).
    • New Wiki REST API client to post page likes and handle authentication.
    • Per-locale processing with isolated error handling to avoid cross-locale failures.
  • Chores

    • Database schema and locale configuration updated to store and populate the wiki REST API URL.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 1, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new Artisan command to export per-page "Likes" to the French wiki, a WikiSemanticApiClient to update wiki semantic properties via REST, a migration and model fillable for the REST endpoint, and schedules the command to run daily.

Changes

Cohort / File(s) Summary
Console Command & Scheduling
app/Console/Commands/ExportPageDetailsToWiki.php, app/Console/Kernel.php
New ExportPageDetailsToWiki command with --since-x-days option (default 15, scheduled with --since-x-days=3 daily). Iterates locales (filters fr), queries pages with >=1 like filtered by latest like updated_at threshold, posts per-page like counts using WikiSemanticApiClient, and wraps per-locale and per-page operations in try/catch with info/success/error logging.
Wiki API Client
app/Src/WikiSemanticApiClient.php
New WikiSemanticApiClient class that manages authentication (fetch login token, login with bot creds), cookie jar, normalizes page titles, constructs REST URIs, and exposes postPageLikesAmount(string $pageTitle, int $likesAmount): bool which PUTs a semantic property update and returns success or throws on API errors.
Model Configuration
app/LocalesConfig.php
Added wiki_restapi_url to the model's $fillable array for mass assignment.
Database Migration
database/migrations/2025_12_01_220632_add_rest_api_url.php
Adds wiki_restapi_url (string, 191) column after wiki_api_url, backfills it by replacing api.phprest.php for existing rows, and provides down() to drop the column.

Sequence Diagram

sequenceDiagram
    participant Scheduler as Laravel Scheduler
    participant Command as ExportPageDetailsToWiki
    participant DB as Database
    participant Client as WikiSemanticApiClient
    participant WikiAPI as Wiki REST API

    Scheduler->>Command: Run scheduled job
    Command->>DB: Query pages with likes (grouped by title/wiki, HAVING MAX(i.updated_at) >= threshold)
    DB-->>Command: Return grouped page results

    loop For each locale (filter to "fr")
        Command->>Client: instantiate with wiki_api_url, wiki_restapi_url
        loop For each page result
            Command->>Client: postPageLikesAmount(title, likes)
            alt Client needs auth
                Client->>WikiAPI: GET login token
                WikiAPI-->>Client: token
                Client->>WikiAPI: POST login (bot creds + token)
                WikiAPI-->>Client: login response
            end
            Client->>WikiAPI: PUT semantic property (normalized title, likes)
            WikiAPI-->>Client: result
            Client-->>Command: return success/failure
            alt Success
                Command->>Command: Log success
            else Failure
                Command->>Command: Log per-page error
            end
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Inspect authentication, cookie handling, and error propagation in app/Src/WikiSemanticApiClient.php.
  • Verify the DB query in ExportPageDetailsToWiki (grouping, HAVING MAX(i.updated_at) semantics) and any SQL bindings.
  • Confirm scheduling entry and default vs. scheduled --since-x-days values in app/Console/Kernel.php.
  • Check migration backfill correctness and possible edge cases for existing wiki_api_url formats.

Poem

🐰 I hopped through tables, tokens, and rest,

I carried each like to the French wiki's nest,
With cookies and PUTs I danced through the night,
Pages now shine with their like-counts bright,
A bunny's small joy, coded just right.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[WIP] Feat/push likes to wiki pages' clearly describes the main feature being added: pushing likes data to wiki pages.
Linked Issues check ✅ Passed The PR implements the core requirement from #278: inserting the number of likes as semantic data on wiki pages to enable creation of TOP lists.
Out of Scope Changes check ✅ Passed All changes directly support the objective of pushing likes to wiki pages: new command, API client, database migration, and scheduled task configuration are all in scope.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/push-likes-to-wiki-pages

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@alterphp alterphp force-pushed the feat/push-likes-to-wiki-pages branch from d1f9f57 to 3c69c06 Compare December 7, 2025 23:09
@alterphp alterphp requested a review from Copilot December 10, 2025 08:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds functionality to export page like counts from the application to wiki pages using the MediaWiki Semantic API. The feature includes a new command to push like counts to wiki pages and the necessary authentication infrastructure.

Key changes:

  • Adds a new WikiSemanticApiClient class to interact with the MediaWiki Semantic API for updating page properties
  • Implements a console command pages:export-details-to-wiki to export like counts from the database to the wiki
  • Adds database migration to store wiki REST API URLs in the locales_config table

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
wiki-bot-passwords.md Documents required bot permissions for posting like counts to wiki pages
database/migrations/2025_12_01_220632_add_rest_api_url.php Adds wiki_restapi_url column to locales_config table and populates it based on existing API URLs
app/Src/WikiSemanticApiClient.php Implements client for authenticating with and posting semantic properties to MediaWiki
app/LocalesConfig.php Adds wiki_restapi_url to fillable attributes
app/Console/Commands/ExportPageDetailsToWiki.php Implements command to query like counts and export them to wiki (incomplete)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/Src/WikiSemanticApiClient.php
Comment on lines +96 to +97
}
}
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Direct usage of env() in application code is discouraged in Laravel. Environment variables should be accessed through config files. Create a config file (e.g., config/wiki.php) and retrieve values using config('wiki.bot_username') instead.

Suggested change
}
}
'lgname' => config('wiki.bot_username'),
'lgpassword' => config('wiki.bot_password'),

Copilot uses AI. Check for mistakes.
Comment thread app/Console/Commands/ExportPageDetailsToWiki.php
Comment thread app/Console/Commands/ExportPageDetailsToWiki.php
@alterphp alterphp force-pushed the feat/push-likes-to-wiki-pages branch from a966231 to 389b53f Compare December 10, 2025 08:39
@alterphp alterphp marked this pull request as ready for review December 10, 2025 09:01
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
app/Src/WikiSemanticApiClient.php (1)

83-84: Direct env() usage is discouraged in Laravel application code.

This is a duplicate of a previous review comment. Environment variables should be accessed through config files for consistency, caching benefits, and testability.

Create a config file (e.g., config/wiki.php):

return [
    'bot_username' => env('WIKI_BOT_USERNAME'),
    'bot_password' => env('WIKI_BOT_PASSWORD'),
];

Then apply this diff:

-                    'lgname' => env('WIKI_BOT_USERNAME'),
-                    'lgpassword' => env('WIKI_BOT_PASSWORD'),
+                    'lgname' => config('wiki.bot_username'),
+                    'lgpassword' => config('wiki.bot_password'),
🧹 Nitpick comments (2)
database/migrations/2025_12_01_220632_add_rest_api_url.php (1)

15-17: Consider making nullability explicit.

The column is implicitly nullable when added to an existing table, but it's clearer to be explicit about this constraint.

Apply this diff to make the intent explicit:

-            $table->string('wiki_restapi_url', 191)->after('wiki_api_url');
+            $table->string('wiki_restapi_url', 191)->nullable()->after('wiki_api_url');
app/Src/WikiSemanticApiClient.php (1)

62-62: Clarify the null coalescing operator usage.

The expression 'success' === $responseContent['result'] ?? false is confusing due to operator precedence. While it works correctly (because of the isset() check above), the intent would be clearer with explicit parentheses or a different structure.

Apply this diff to improve clarity:

-        return 'success' === $responseContent['result'] ?? false;
+        return ($responseContent['result'] ?? '') === 'success';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f49c41 and 389b53f.

📒 Files selected for processing (5)
  • app/Console/Commands/ExportPageDetailsToWiki.php (1 hunks)
  • app/Console/Kernel.php (1 hunks)
  • app/LocalesConfig.php (1 hunks)
  • app/Src/WikiSemanticApiClient.php (1 hunks)
  • database/migrations/2025_12_01_220632_add_rest_api_url.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/Console/Commands/ExportPageDetailsToWiki.php (2)
app/LocalesConfig.php (1)
  • LocalesConfig (11-74)
app/Src/WikiSemanticApiClient.php (2)
  • WikiSemanticApiClient (19-98)
  • postPageLikesAmount (36-63)
🔇 Additional comments (5)
app/Console/Kernel.php (1)

31-31: LGTM! Command scheduling is appropriate.

The scheduled command runs at a suitable time between other tasks and correctly passes the --since-x-days=3 parameter as specified in the PR description.

app/LocalesConfig.php (1)

19-19: LGTM! Fillable attribute addition is correct.

The addition of wiki_restapi_url to the fillable array aligns with the migration and enables proper mass assignment for this field.

app/Console/Commands/ExportPageDetailsToWiki.php (2)

19-37: LGTM! Solid error isolation and locale handling.

The per-locale error handling ensures that a failure in one locale doesn't prevent processing others. The FR-only filter aligns with the current scope mentioned in the PR description.


40-95: LGTM! Export logic is well-structured with comprehensive error handling.

The method correctly:

  • Queries pages with likes updated since the threshold
  • Handles errors per page without failing the entire export
  • Provides detailed logging for success and failure cases

The variable naming issues mentioned in past review comments appear to be resolved.

database/migrations/2025_12_01_220632_add_rest_api_url.php (1)

19-25: The URL transformation is safe and follows the standard MediaWiki API pattern. The wiki_api_url column is defined as NOT NULL in the initial migration and is consistently used across the codebase (WikiClient and WikiSemanticApiClient) with the expected format ending in api.php. The REPLACE logic to transform api.php to rest.php directly corresponds to MediaWiki's standard pattern for the legacy API vs. REST API endpoints.

Comment thread app/Src/WikiSemanticApiClient.php
Comment thread app/Src/WikiSemanticApiClient.php Outdated
@alterphp alterphp force-pushed the feat/push-likes-to-wiki-pages branch from 389b53f to 59c92a9 Compare December 15, 2025 22:47
@alterphp alterphp force-pushed the feat/push-likes-to-wiki-pages branch from 59c92a9 to bf077df Compare December 15, 2025 23:05
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
app/Console/Commands/ExportPageDetailsToWiki.php (2)

26-29: Consider making the locale filter configurable.

The hardcoded FR-only filter works for the current requirements, but making it configurable (e.g., via command option --locale=fr or environment variable) would improve flexibility for future expansion to other wikis.

Example implementation:

-    protected $signature = 'pages:export-details-to-wiki {--since-x-days=15}';
+    protected $signature = 'pages:export-details-to-wiki {--since-x-days=15} {--locale=fr}';

     // In handle() method:
-            // On ne traite que le wiki FR pour l'instant
-            if ('fr' !== $localeConfig->code) {
+            if ($this->option('locale') !== $localeConfig->code) {
                 continue;
             }

60-94: Consider adding rate limiting and progress indication.

For robustness and better user experience, especially during manual backfill runs (e.g., --since-x-days 3650), consider:

  1. Rate limiting: Add a small delay between wiki API calls to avoid hitting rate limits
  2. Progress indication: Use $this->output->progressStart() and progressAdvance() to show progress

Example additions:

+        $this->output->progressStart(count($rawResults));
+
         foreach ($rawResults as $eligiblePage) {
             try {
                 $success = $client->postPageLikesAmount($eligiblePage->title, $eligiblePage->likes_amount);
 
                 if (!$success) {
                     $this->error(
                         sprintf(
                             'Failed to export Likes (%d) for page "%s" (%s)',
                             $eligiblePage->likes_amount,
                             $eligiblePage->title,
                             $eligiblePage->wiki
                         )
                     );
                 } else {
                     $this->info(
                         sprintf(
                             'Successfully exported Likes (%d) for page "%s" (%s)',
                             $eligiblePage->likes_amount,
                             $eligiblePage->title,
                             $eligiblePage->wiki
                         )
                     );
                 }
+                
+                // Rate limiting: small delay between API calls
+                usleep(100000); // 100ms delay
             } catch (\Throwable $e) {
                 $this->error(
                     sprintf(
                         'Error exporting Likes (%d) for page "%s" (%s): %s',
                         $eligiblePage->likes_amount,
                         $eligiblePage->title,
                         $eligiblePage->wiki,
                         $e->getMessage()
                     )
                 );
             }
+            $this->output->progressAdvance();
         }
+        
+        $this->output->progressFinish();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59c92a9 and bf077df.

📒 Files selected for processing (5)
  • app/Console/Commands/ExportPageDetailsToWiki.php (1 hunks)
  • app/Console/Kernel.php (1 hunks)
  • app/LocalesConfig.php (1 hunks)
  • app/Src/WikiSemanticApiClient.php (1 hunks)
  • database/migrations/2025_12_01_220632_add_rest_api_url.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • database/migrations/2025_12_01_220632_add_rest_api_url.php
  • app/Src/WikiSemanticApiClient.php
  • app/Console/Kernel.php
🔇 Additional comments (2)
app/LocalesConfig.php (1)

19-19: LGTM!

The addition of wiki_restapi_url to the fillable attributes is correct and consistent with the other wiki-related configuration fields.

app/Console/Commands/ExportPageDetailsToWiki.php (1)

47-54: SQL query logic is correct as designed.

The HAVING clause properly filters pages with recent activity (interactions updated within the threshold). The query correctly exports total likes for pages with recent activity—not just recent likes. This is the intended behavior: the --since-x-days parameter determines which pages to export (those with recent interaction updates), while likes_amount reflects the cumulative total of all likes (follow = 1 interactions) for that page.

The query is well-optimized with indexes on interactions(page_id, wiki) supporting the JOIN condition, and schema constraints ensure data integrity.


class ExportPageDetailsToWiki extends Command
{
protected $signature = 'pages:export-details-to-wiki {--since-x-days=15}';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add type validation for the since-x-days option.

The option value is used in line 21 with sprintf('%d days', ...) which expects an integer, but Laravel command options return strings by default. If a non-numeric value is provided, this could cause unexpected behavior.

Apply this diff to add type safety:

     public function handle(): void
     {
-        $dateThreshold = Carbon::now()->sub(sprintf('%d days', $this->option('since-x-days')))->setTime(0, 0, 0);
+        $sinceDays = (int) $this->option('since-x-days');
+        if ($sinceDays < 1) {
+            $this->error('Option --since-x-days must be a positive integer.');
+            return;
+        }
+        $dateThreshold = Carbon::now()->sub(sprintf('%d days', $sinceDays))->setTime(0, 0, 0);

Also applies to: 21-21

🤖 Prompt for AI Agents
In app/Console/Commands/ExportPageDetailsToWiki.php around line 15 (and usage at
line 21), the `since-x-days` option is taken as a string but used with
sprintf('%d days', ...); validate and coerce it to an integer before use: fetch
the option, ensure it is numeric (or cast to int after validation), throw a
clear InvalidArgumentException or call $this->error()/return if validation
fails, and then pass the validated integer into sprintf so non-numeric input is
rejected and type-safety is ensured.


private function handleExportPageLikes(LocalesConfig $localeConfig, Carbon $dateThreshold): void
{
$client = new WikiSemanticApiClient($localeConfig->toArray());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify WikiSemanticApiClient exists and check its implementation

echo "=== Finding WikiSemanticApiClient class ==="
fd -e php -i wikisemanticapiclient

echo -e "\n=== Searching for WikiSemanticApiClient references ==="
rg -n "class WikiSemanticApiClient" --type=php

echo -e "\n=== Checking postPageLikesAmount method ==="
rg -n "postPageLikesAmount" --type=php -A 3

echo -e "\n=== Checking environment variable usage in config/wiki.php or similar ==="
fd -e php wiki && rg -n "WIKI_BOT_USERNAME|WIKI_BOT_PASSWORD" --type=php

Repository: neayi/insights

Length of output: 2132


🏁 Script executed:

#!/bin/bash
# Inspect WikiSemanticApiClient constructor and initialization

echo "=== Reading WikiSemanticApiClient class (first 100 lines) ==="
head -100 app/Src/WikiSemanticApiClient.php

echo -e "\n=== Checking for env() calls and validation ==="
rg -n "env\(" app/Src/WikiSemanticApiClient.php -B 2 -A 2

Repository: neayi/insights

Length of output: 4016


🏁 Script executed:

#!/bin/bash
# Check ExportPageDetailsToWiki.php context around line 42

echo "=== ExportPageDetailsToWiki.php lines 35-50 ==="
sed -n '35,50p' app/Console/Commands/ExportPageDetailsToWiki.php

echo -e "\n=== Check $localeConfig declaration and import ==="
head -50 app/Console/Commands/ExportPageDetailsToWiki.php | grep -n "localeConfig\|use\|class"

echo -e "\n=== Check if environment variables are validated in config ==="
fd -e php config && rg -n "WIKI_BOT_USERNAME|WIKI_BOT_PASSWORD" config/

Repository: neayi/insights

Length of output: 1952


🏁 Script executed:

#!/bin/bash
# Check LocalesConfig class structure

echo "=== Reading LocalesConfig.php ==="
head -150 app/LocalesConfig.php

echo -e "\n=== Searching for toArray method ==="
rg -n "toArray" app/LocalesConfig.php -A 10

Repository: neayi/insights

Length of output: 2277


🏁 Script executed:

#!/bin/bash
# Check for environment variable validation

echo "=== Checking config files for validation ==="
fd -e php config/ 2>/dev/null | head -10

echo -e "\n=== Checking for .env.example or validation of required env vars ==="
fd -e example
fd -e env | grep -E "env|example"

echo -e "\n=== Checking ServiceProvider or bootstrap for env validation ==="
rg -n "WIKI_BOT" --type=php -B 3 -A 3

Repository: neayi/insights

Length of output: 848


Add validation for required environment variables WIKI_BOT_USERNAME and WIKI_BOT_PASSWORD.

The WikiSemanticApiClient is correctly instantiated with the locale config array, and the method signature matches its usage. However, the authenticate() method (lines 88-89) accesses environment variables WIKI_BOT_USERNAME and WIKI_BOT_PASSWORD via env() without validation. If either variable is missing, the login will fail without a clear error message. Add validation in the constructor or at startup to ensure both variables are set before attempting authentication.

Also applies to: 62-62

🤖 Prompt for AI Agents
In app/Console/Commands/ExportPageDetailsToWiki.php around lines 42 and 62, the
code instantiates WikiSemanticApiClient then calls authenticate() which reads
WIKI_BOT_USERNAME and WIKI_BOT_PASSWORD via env() without checking they exist;
add explicit validation for both environment variables before attempting
authentication (either in this command just after creating the client or inside
the client constructor): if either env var is missing or empty, log a clear
error and abort/throw an exception with a descriptive message indicating which
variable is missing; ensure the check uses env(..., null) or equivalent and
treats empty strings as missing so authentication is never attempted with
invalid credentials.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ce serait intéressant d'insérer en tant que donnée sémantique le nombre de "Likes" de chaque page

2 participants