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
25 changes: 13 additions & 12 deletions app/Queries/BulkActivityQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@

/**
* The accounts that followed, starred, replied or posted the most in the last days, the busiest first, bots left
* out, as long as they reached a threshold no normal use does: a rate a day times the days. Newcomers are the people
* reached who signed up in that time too, which tells an account working through every new signup from an active
* member. Deleted commits still count: what was removed was still sent.
* out, as long as they reached a threshold no normal use does: a rate a day times the days. Whoever is reached is the
* commit's author, so an organization rather than the member who posted for it. Newcomers are the people reached who
* signed up in that time too, which tells an account working through every new signup from an active member.
* Deleted commits still count: what was removed was still sent.
*
* - Follows: people followed, leaving out the follows imported from GitHub, created in the transaction that records
* either side's import, so the 10 seconds before it.
Expand Down Expand Up @@ -74,28 +75,28 @@ public function get(int $days, int $limit = 10): array
limit ?
SQL, [$since, $bots, $since, $thresholds['follows']['total'], $limit]),
'stars' => DB::select(<<<'SQL'
select s.user_id, count(*) as count, count(distinct c.user_id) as reach,
count(distinct c.user_id) filter (where author.created_at >= ?) as newcomers
select s.user_id, count(*) as count, count(distinct c.author_id) as reach,
count(distinct c.author_id) filter (where author.created_at >= ?) as newcomers
from stars s
join users starrer on starrer.id = s.user_id
join commits c on c.id = s.commit_id
join users author on author.id = c.user_id
left join users author on author.id = c.author_id
where starrer.github_id <> all(?::text[]) and s.created_at >= ?
group by s.user_id
having count(distinct c.user_id) >= ?
having count(distinct c.author_id) >= ?
order by reach desc, count desc, s.user_id
limit ?
SQL, [$since, $bots, $since, $thresholds['stars']['total'], $limit]),
'replies' => DB::select(<<<'SQL'
select r.user_id, count(*) as count, count(distinct p.user_id) as reach,
count(distinct p.user_id) filter (where author.created_at >= ?) as newcomers
select r.user_id, count(*) as count, count(distinct p.author_id) as reach,
count(distinct p.author_id) filter (where author.created_at >= ?) as newcomers
from commits r
join users replier on replier.id = r.user_id
join commits p on p.id = r.parent_id
join users author on author.id = p.user_id
where replier.github_id <> all(?::text[]) and r.created_at >= ? and p.user_id <> r.user_id
left join users author on author.id = p.author_id
where replier.github_id <> all(?::text[]) and r.created_at >= ? and p.author_id is distinct from r.user_id
group by r.user_id
having count(distinct p.user_id) >= ?
having count(distinct p.author_id) >= ?
order by reach desc, count desc, r.user_id
limit ?
SQL, [$since, $bots, $since, $thresholds['replies']['total'], $limit]),
Expand Down
39 changes: 39 additions & 0 deletions tests/Unit/Queries/BulkActivityQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Actions\CreateTomFriendship;
use App\Models\Commit;
use App\Models\Organization;
use App\Models\User;
use App\Queries\BulkActivityQuery;
use Illuminate\Database\Eloquent\Collection;
Expand Down Expand Up @@ -83,6 +84,44 @@
->sequence(fn ($row) => $row->user->id->toBe($mass->id)->reach->toBe(20)->count->toBe(20));
});

it("counts stars and replies on an organization's commits as reaching the organization, not the members who posted them", function (): void {
$organization = Organization::factory()->create();
$commits = ($this->people)(30, '2026-10-23 00:00:00')->map(fn (User $member): Commit => Commit::factory()->for($member)->create([
'author_id' => $organization->id,
'created_at' => '2026-10-22 00:00:00',
]));
$fan = User::factory()->create();
$mass = User::factory()->create();

$commits->each(function (Commit $commit) use ($fan): void {
$commit->stars()->attach($fan, ['created_at' => '2026-10-23 01:00:00', 'updated_at' => '2026-10-23 01:00:00']);
Commit::factory()->for($fan)->for($commit, 'parent')->create(['created_at' => '2026-10-23 01:00:00']);
});
($this->star)($mass, ($this->people)(29));
$commits->first()->stars()->attach($mass, ['created_at' => '2026-10-23 01:00:00', 'updated_at' => '2026-10-23 01:00:00']);

expect(($this->activity)()['stars'])
->toHaveCount(1)
->sequence(fn ($row) => $row->user->id->toBe($mass->id)->reach->toBe(30)->newcomers->toBe(0))
->and(($this->activity)()['replies'])->toBe([]);
});

it('still counts stars and replies on the commits of a deleted organization, which reach no one', function (): void {
$starrer = User::factory()->create();
$replier = User::factory()->create();
$orphan = Commit::factory()->create(['author_id' => null, 'created_at' => '2026-10-22 00:00:00']);

($this->star)($starrer, ($this->people)(30));
$orphan->stars()->attach($starrer, ['created_at' => '2026-10-23 01:00:00', 'updated_at' => '2026-10-23 01:00:00']);
($this->reply)($replier, ($this->people)(20));
Commit::factory()->for($replier)->for($orphan, 'parent')->create(['created_at' => '2026-10-23 01:00:00']);

expect(($this->activity)()['stars'])
->sequence(fn ($row) => $row->user->id->toBe($starrer->id)->reach->toBe(30)->count->toBe(31))
->and(($this->activity)()['replies'])
->sequence(fn ($row) => $row->user->id->toBe($replier->id)->reach->toBe(20)->count->toBe(21));
});

it('lists the accounts that posted 15 times a day', function (): void {
$mass = User::factory()->create();
$prolific = User::factory()->create();
Expand Down
Loading