Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions app/Helper/MWTimestampHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,12 @@
namespace App\Helper;

use Carbon\CarbonImmutable;
use Carbon\Exceptions\InvalidFormatException;

class MWTimestampHelper {
private const MWTimestampFormat = 'YmdHis';

public static function getCarbonFromMWTimestamp(string $MWTimestamp): CarbonImmutable {
$carbon = CarbonImmutable::createFromFormat(self::MWTimestampFormat, $MWTimestamp);
if ($carbon === null) {
throw new InvalidFormatException('Unable to create Carbon object');
}

return $carbon;
}
Expand Down
3 changes: 3 additions & 0 deletions app/Http/Controllers/ConversionMetricController.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ public function index(Request $request) {
$daysSinceLastEdit = null;

if ($wikiLastEditedTime !== null) {
// cast to int to retain Carbon 2 behaviour of using whole days
$daysSinceLastEdit = (int) $wikiLastEditedTime->diffInDays($current_date, false);
}

if ($daysSinceLastEdit !== null && $daysSinceLastEdit >= 90) {
// cast to int to retain Carbon 2 behaviour of using whole days
$time_before_wiki_abandoned_days = (int) $wiki->created_at->diffInDays($wikiLastEditedTime, false);
}
if ($wikiFirstEditedTime !== null) {
// cast to int to retain Carbon 2 behaviour of using whole days
$time_to_engage_days = (int) $wiki->created_at->diffInDays($wikiFirstEditedTime, false);
}
$wiki_number_of_editors = $wiki->wikiSiteStats()->first()['activeusers'] ?? null;
Expand Down
2 changes: 2 additions & 0 deletions app/Jobs/PlatformStatsSummaryJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ public function prepareStats(array $allStats, $wikis): array {
// is it edited in the last 90 days?
if (!is_null($stats['lastEdit'])) {
$lastTimestamp = MWTimestampHelper::getCarbonFromMWTimestamp(intval($stats['lastEdit']));
// cast to int retain Carbon 2 behaviour of using whole days
// pass `$absolute = true` argument to retain Carbon 2 behaviour of returning an absolute diff
$diff = (int) $lastTimestamp->diffInSeconds($currentTime, true);

if ($diff <= $this->inactiveThreshold) {
Expand Down
1 change: 1 addition & 0 deletions app/Jobs/SendEmptyWikiNotificationsJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public function checkIfWikiIsOldAndEmpty(Wiki $wiki) {
$emptyDaysThreshold = config('wbstack.wiki_empty_notification_threshold');
$createdAt = $wiki->created_at;
$now = CarbonImmutable::now();
// cast to int to retain Carbon 2 behaviour of using whole days
$emptyWikiDays = (int) $createdAt->diffInDays($now, true);

$firstEdited = $wiki->wikiLifecycleEvents->first_edited;
Expand Down
51 changes: 51 additions & 0 deletions tests/Jobs/PlatformStatsSummaryJobTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,4 +274,55 @@ public function testCreationStats() {
);

}

public function testPrepareStatsTreatsSecondPrecisionTimestampAtThresholdAsActive() {
$currentTime = CarbonImmutable::now();

$wiki = Wiki::factory()->create(['deleted_at' => null, 'domain' => 'thresholdtest.com']);
WikiDb::create([
'name' => 'mwdb_threshold_' . $wiki->id,
'user' => 'user',
'password' => 'password',
'version' => 'version',
'prefix' => 'prefix',
'wiki_id' => $wiki->id,
]);

Http::fake([
$this->mwBackendHost . '/w/api.php?action=query&list=allpages&apnamespace=122&apcontinue=&aplimit=max&format=json' => Http::response([
'query' => ['allpages' => []],
], 200),
$this->mwBackendHost . '/w/api.php?action=query&list=allpages&apnamespace=120&apcontinue=&aplimit=max&format=json' => Http::response([
'query' => ['allpages' => []],
], 200),
]);

$job = new PlatformStatsSummaryJob;

// This is a hack to override the `private` `PlatformStatsSummaryJob::mwHostResolver` property.
// See https://www.php.net/manual/en/closure.call.php for more details on how this works.
// TODO: figure out how to stub the `DatabaseManager` correctly and/or refactor the Job so that
// we can more easily inject dependencies in the tests.
(function ($resolver): void {
$this->mwHostResolver = $resolver;
})->call($job, $this->mockMwHostResolver);
Comment thread
dati18 marked this conversation as resolved.

$groups = $job->prepareStats([
[
'wiki' => 'thresholdtest.com',
'edits' => 1,
'pages' => 1,
'users' => 1,
'active_users' => 1,
'lastEdit' => MWTimestampHelper::getMWTimestampFromCarbon(
$currentTime->subSeconds(config('wbstack.platform_summary_inactive_threshold'))
),
'first100UsingOauth' => '0',
'platform_summary_version' => 'v1',
],
], [$wiki]);

$this->assertSame(1, $groups['edited_last_90_days']);
$this->assertSame(0, $groups['not_edited_last_90_days']);
}
}
21 changes: 20 additions & 1 deletion tests/Jobs/SendEmptyWikiNotificationsJobTest.php
Comment thread
dati18 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function testEmptyWikiNotificationsSendNotification() {
Notification::fake();
$user = User::factory()->create(['verified' => true]);
$wiki = Wiki::factory()->create(['created_at' => $thresholdDaysAgo]);
$manager = WikiManager::factory()->create(['wiki_id' => $wiki->id, 'user_id' => $user->id]);
WikiManager::factory()->create(['wiki_id' => $wiki->id, 'user_id' => $user->id]);
Comment thread
dati18 marked this conversation as resolved.
$wiki->wikiLifecycleEvents()->updateOrCreate(['first_edited' => null]);

$job = new SendEmptyWikiNotificationsJob;
Expand All @@ -50,6 +50,25 @@ public function testEmptyWikiNotificationsSendNotification() {
);
}

// empty wikis, that are almost old enough (29 days and 23 hrs)
public function testEmptyWikiNotificationsNotificationNotSent() {
$thresholdDaysAgo = Carbon::now()
->subDays((config('wbstack.wiki_empty_notification_threshold') - 1))
->subHours(23)
->toDateTimeString();

Notification::fake();
$user = User::factory()->create(['verified' => true]);
$wiki = Wiki::factory()->create(['created_at' => $thresholdDaysAgo]);
WikiManager::factory()->create(['wiki_id' => $wiki->id, 'user_id' => $user->id]);
$wiki->wikiLifecycleEvents()->updateOrCreate(['first_edited' => null]);

$job = new SendEmptyWikiNotificationsJob;
$job->handle();
Comment thread
dati18 marked this conversation as resolved.

Notification::assertNothingSent();
}

// fresh wiki that does not have lifecycle event records yet
public function testEmptyWikiNotificationsFreshWiki() {
$now = Carbon::now()->toDateTimeString();
Expand Down
33 changes: 33 additions & 0 deletions tests/Routes/Wiki/ConversionMetricTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,39 @@ public function testDownloadJson() {
);
}

public function testDownloadJsonTruncatesFractionalDayDiffs() {
$currentDate = CarbonImmutable::now();
$createdAt = $currentDate->subDays(200)->subHours(12); // 200.5 days ago
$firstEditedAt = $createdAt->addDays(1)->addHours(12); // 1.5 days after
$lastEditedAt = $currentDate->subDays(100); // 100 days ago

$wiki = Wiki::factory()->create([
'domain' => 'fractional.days.cloud',
'sitename' => 'Fractional Days Site',
]);
WikiSiteStats::factory()->create([
'wiki_id' => $wiki->id,
'pages' => 77,
'activeusers' => 2,
]);
$wiki->created_at = $createdAt;
$wiki->wikiLifecycleEvents()->updateOrCreate([
'first_edited' => $firstEditedAt,
'last_edited' => $lastEditedAt,
]);
$wiki->save();

$response = $this->getJson($this->route);

$response->assertStatus(200);
$response->assertJsonFragment([
'domain' => 'fractional.days.cloud',
'time_to_engage_days' => 1,
'time_before_wiki_abandoned_days' => 100,
'number_of_active_editors' => 2,
]);
}

public function testFunctionalWithMissingLifecycleEventsandStats() {
$wiki = Wiki::factory()->create([
'domain' => 'very.new.wikibase.cloud', 'sitename' => 'bsite',
Expand Down
Loading