Skip to content

Commit

Permalink
Merge pull request #33 from mintopia/feature/eigenkarma
Browse files Browse the repository at this point in the history
First work in progress for trustscore/eigenkarma
  • Loading branch information
mintopia authored Dec 31, 2024
2 parents 2877a64 + afa40f6 commit 6f873bb
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 1 deletion.
38 changes: 38 additions & 0 deletions app/Console/Commands/PartyCalculateTrust.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace App\Console\Commands;

use App\Models\Party;
use Illuminate\Console\Command;

class PartyCalculateTrust extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'party:calculatetrust {party}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Calculate trust scores for a party';

/**
* Execute the console command.
*/
public function handle()
{
$code = $this->input->getArgument('party');
$party = Party::whereCode($code)->first();
if (!$party) {
$this->error("Unable to find a party with code {$code}");
return self::FAILURE;
}
$party->calculateTrustScores();
return self::SUCCESS;
}
}
85 changes: 85 additions & 0 deletions app/Models/Party.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use NumPHP\LinAlg\LinAlg;

/**
* @mixin IdeHelperParty
Expand Down Expand Up @@ -58,6 +59,11 @@ public function user(): BelongsTo
return $this->belongsTo(User::class);
}

public function trustedUser(): BelongsTo
{
return $this->belongsTo(User::class, 'trusted_user_id');
}

public function upcoming(): HasMany
{
return $this->hasMany(UpcomingSong::class);
Expand Down Expand Up @@ -396,6 +402,23 @@ protected function syncPlaylist(object $playlist)
}
}
}
$remaining = $toAdd = $songsToAdd->count();

if ($remaining > 0) {
// Didn't add enough songs, try and find *any* using old method
$ids = $songsToAdd->pluck('id');
$toAdd = $this->upcoming()
->whereNull('queued_at')
->whereNotIn('id', $ids)
->orderBy('score', 'DESC')
->orderBy('created_at', 'ASC')
->orderBy('id', 'ASC')
->limit($remaining)
->get();
foreach ($toAdd as $song) {
$songsToAdd->push($song);
}
}
} else {
$songsToAdd = $this->upcoming()
->whereNull('queued_at')
Expand Down Expand Up @@ -680,4 +703,66 @@ public function checkDownvotesForUser(User $user): void
throw new VoteException('You have downvoted too many songs');
}
}

public function calculateTrustScores(): void
{
if ($this->trustedUser === null) {
Log::debug("No trusted user, unable to calculate trust score");
return;
}

$voteMap = [];
$votes = Vote::whereHas('upcomingSong', function ($query) {
$query->wherePartyId($this->id);
})->where('value', '>', 0)->with(['user', 'upcomingSong', 'upcomingSong.user'])->get();

$userIds = $votes->pluck('user_id');
$userIds->push(0);
$fill = [];
foreach ($userIds as $id) {
$fill[$id] = 0;
}
foreach ($userIds as $id) {
$voteMap[$id] = $fill;
}
foreach ($votes as $vote) {
$songUserId = $vote->upcomingSong->user->id ?? 0;
$voteMap[$vote->user->id][$songUserId] += $vote->value;
}
$userMap = array_keys($voteMap);
$voteMap = array_values($voteMap);
foreach ($voteMap as $i => $row) {
$voteMap[$i] = array_values($row);
$voteMap[$i][$i] = -1;
if ($userMap[$i] === $this->trustedUser->id) {
$voteMap[$i][$i] = 1;
}
}

$trustedUserMatrix = array_fill(0, count($voteMap), 0);
$trustedUserIndex = array_search($this->trustedUser->id, $userMap);
$trustedUserMatrix[$trustedUserIndex] = 1;

$solved = LinAlg::solve($voteMap, $trustedUserMatrix);
$scores = $solved->getData();

$membersIndexed = [];
$members = $this->members()->with('user')->get();
foreach ($members as $member) {
$membersIndexed[$member->user->id] = $member;
}

foreach ($scores as $index => $score) {
$userId = $userMap[$index] ?? null;
if ($userId === null) {
continue;
}
if (!isset($membersIndexed[$userId])) {
continue;
}
$membersIndexed[$userId]->trustscore = $score;
$membersIndexed[$userId]->save();
}
Log::debug("Finished calculating trust scores");
}
}
2 changes: 2 additions & 0 deletions app/Models/PartyMember.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use NumPHP\Core\NumArray;
use NumPHP\LinAlg\LinAlg;

/**
* @mixin IdeHelperPartyMember
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"laravel/socialite": "^5.11",
"laravel/telescope": "^5.0",
"laravel/tinker": "^2.8",
"numphp/numphp": "^1.2",
"pusher/pusher-php-server": "^7.2",
"ramsey/uuid": "^4.7",
"socialiteproviders/discord": "^4.2",
Expand Down
57 changes: 56 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions database/migrations/2024_12_31_005027_add_trustscore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('parties', function (Blueprint $table) {
$table->boolean('trustscore')->default(false)->after('weighted');
$table->unsignedBigInteger('trusted_user_id')->nullable()->default(null)->after('trustscore');
$table->foreign('trusted_user_id')->references('id')->on('users')->nullOnDelete();
});

Schema::table('party_members', function (Blueprint $table) {
$table->float('trustscore')->default(0)->after('banned');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('parties', function (Blueprint $table) {
$table->dropForeign('parties_trusted_user_id_foreign');
$table->dropColumn(['trustscore', 'trusted_user_id']);
});

Schema::table('party_members', function (Blueprint $table) {
$table->dropColumn('trustscore');
});
}
};

0 comments on commit 6f873bb

Please sign in to comment.