Commit f00867a2 authored by Mark Harding's avatar Mark Harding
Browse files

Merge branch 'goal/ReportingAndModeration.cohort-pick' into 'epic/ReportingAndModeration'

(chore): Cohort pick procedure

See merge request !172
parents 2bfd6871 4e264578
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ class Moderation extends Cli\Controller implements Interfaces\CliControllerInter
        $reportUrn = $this->getOpt('report');
        $juryType = $this->getOpt('jury-type') ?? null;
        $respond = $this->getOpt('respond') ?? null;
        $activeThreshold = $this->getOpt('active-threshold') ?? 5 * 60;

        if (!$userId || !$reportUrn) {
            $this->out([
@@ -79,9 +80,13 @@ class Moderation extends Cli\Controller implements Interfaces\CliControllerInter
            $appeal = new Core\Reports\Appeals\Appeal();
            $appeal->setReport($report);

            $summonsManager->summon($appeal, [ $user->guid ]);
            $missing = $summonsManager->summon($appeal, [
                'include_only' => [ (string) $user->guid ],
                'active_threshold' => (int) $activeThreshold,
            ]);

            $this->out("Summoned {$user->guid} to {$reportUrn}");
            $this->out("${missing} juror(s) missing.");
        } else {
            $summons = new Summons();
            $summons
@@ -128,7 +133,7 @@ class Moderation extends Cli\Controller implements Interfaces\CliControllerInter
            ->setReport($report)
            ->setOwnerGuid($report->getEntityOwnerGuid());

        $cohort = $summonsManager->summon($appeal, null);
        $cohort = $summonsManager->summon($appeal);

        var_dump($cohort);
    }
+3 −1
Original line number Diff line number Diff line
@@ -47,7 +47,9 @@ class ReportsAppealSummon implements QueueRunner

                /** @var Manager $manager */
                $manager = Di::_()->get('Moderation\Summons\Manager');
                $missing = $manager->summon($appeal, $cohort);
                $missing = $manager->summon($appeal, [
                    'include_only' => $cohort ?: null,
                ]);

                if ($missing > 0) {
                    echo "Missing {$missing} juror(s). Deferring..." . PHP_EOL;
+9 −8
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ class Cohort
        $this->repository = $repository ?: new Repository();
        $this->pool = $pool ?: new Pool();
        $this->poolSize = $poolSize ?: 400;
        $this->maxPages = $maxPages ?: 1; // NOTE: Normally capped to 20.
        $this->maxPages = $maxPages ?: 2; // NOTE: Normally capped to 20.
    }

    /**
@@ -54,6 +54,8 @@ class Cohort
            'size' => 0,
            'for' => null,
            'except' => [],
            'except_hashes' => [],
            'include_only' => null,
            'active_threshold' => null,
        ], $opts);

@@ -62,9 +64,9 @@ class Cohort
        $page = 0;

        while (true) {
            if ($page > $this->maxPages) {
            if ($page >= $this->maxPages) {
                // Max = PoolSize * MaxPages
                error_log('Cannot gather a cohort');
                error_log("Warning: Cannot gather a full cohort on {$this->maxPages} partitions");
                break;
            }

@@ -73,16 +75,15 @@ class Cohort
                'platform' => 'browser',
                'for' => $opts['for'],
                'except' => $opts['except'],
                'except_hashes' => $opts['except_hashes'],
                'include_only' => $opts['include_only'],
                'validated' => true,
                'page' => $page,
                'size' => $this->poolSize,
                'page' => $page,
                'max_pages' => $this->maxPages,
            ]);

            $j = 0;
            foreach ($pool as $userGuid) {
                $j++;

                // TODO: Check subs
                $cohort[] = $userGuid;

@@ -91,7 +92,7 @@ class Cohort
                }
            }

            if ($j === 0 || count($cohort) >= $opts['size']) {
            if (count($cohort) >= $opts['size']) {
                break;
            }

+62 −37
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@ use Minds\Core\Queue\Runners\ReportsAppealSummon;
use Minds\Core\Reports\Appeals\Appeal;
use Minds\Core\Reports\Summons\Delegates;
use Minds\Core\Reports\Manager as ReportsManager;
use Minds\Core\Reports\UserReports\UserReport;
use Minds\Helpers\Text;

class Manager
{
@@ -36,6 +38,7 @@ class Manager
     * Manager constructor.
     * @param Cohort $cohort
     * @param Repository $repository
     * @param ReportsManager $reportsManager
     * @param QueueClient $queueClient
     * @param Delegates\SocketDelegate $socketDelegate
     * @throws Exception
@@ -50,37 +53,44 @@ class Manager
    {
        $this->cohort = $cohort ?: new Cohort();
        $this->repository = $repository ?: new Repository();
        $this->reportsManager = $reportsManager ?: new ReportsManager;
        $this->reportsManager = $reportsManager ?: new ReportsManager();
        $this->queueClient = $queueClient ?: Client::build();
        $this->socketDelegate = $socketDelegate ?: new Delegates\SocketDelegate();
    }

    /**
     * @param Appeal $appeal
     * @param array $cohort
     * @param array $opts
     * @return int
     * @throws Exception
     */
    public function summon(Appeal $appeal, $cohort = null)
    public function summon(Appeal $appeal, array $opts = [])
    {
        $opts = array_merge([
            'include_only' => null,
            'active_threshold' => 5 * 60,
            'jury_size' => 12,
            'awaiting_ttl' => 120,
        ], $opts);

        // Get a fresh report to collect completed jurors

        $report = $report = $this->reportsManager->getReport($appeal->getReport()->getUrn());
        $reportUrn = $report->getUrn();
        $juryType = 'appeal_jury';

        $missing = 0;
        $completedJurorGuids = array_map(function($decision) {
            return $decision->getJurorGuid();
        }, array_merge($report->getAppealJuryDecisions() ?: [], $report->getInitialJuryDecisions() ?: []));

        // Get all summonses for this case

        if (!$cohort) {
        $summonses = iterator_to_array($this->repository->getList([
            'report_urn' => $reportUrn,
            'jury_type' => $juryType,
        ]));

            $completedJurorGuids = array_map(function($decision) {
                return $decision->getJurorGuid();
            }, array_merge($report->getAppealJuryDecisions(), $report->getInitialJuryDecisions()));

            // Remove the summons of jurors who have already voted
        // Remove the summonses of jurors who have already voted

        $summonses = array_filter($summonses, function (Summons $summons) use ($completedJurorGuids) {
            return !in_array($summons->getJurorGuid(), $completedJurorGuids);
@@ -88,11 +98,9 @@ class Manager

        // Check how many are missing

            $notDeclined = array_filter($summonses, function (Summons $summons) {
        $missing = $opts['jury_size'] - count(array_filter($summonses, function (Summons $summons) {
            return $summons->isAccepted() || $summons->isAwaiting();
            });

            $missing = 12 - count($notDeclined);
        }));

        // If we have a full jury, don't summon

@@ -100,19 +108,34 @@ class Manager
            return 0;
        }

            // Reduce jury to juror guids and try to pick up to missing size
        // Create an array of channel GUIDs that are involved in this case

            $pendingJurorGuids = array_map(function (Summons $summons) {
        $alreadyInvolvedGuids = array_map(function (Summons $summons) {
            return (string) $summons->getJurorGuid();
        }, $summonses);

        $alreadyInvolvedGuids = array_merge($alreadyInvolvedGuids, array_map(function (UserReport $userReport) {
            return $userReport->getReporterGuid();
        }, $report->getReports()));

        $alreadyInvolvedGuids = array_values(array_unique(Text::buildArray($alreadyInvolvedGuids)));

        // Create an array of channel phone hashes that are involved in this case

        $alreadyInvolvedPhoneHashes = $report->getUserHashes() ?: [];

        // Pick up to missing size

        $cohort = $this->cohort->pick([
            'size' => $missing,
            'for' => $appeal->getOwnerGuid(),
                'except' => $pendingJurorGuids,
                'active_threshold' => 5 * 60,
            'except' => $alreadyInvolvedGuids,
            'except_hashes' => $alreadyInvolvedPhoneHashes,
            'include_only' => $opts['include_only'],
            'active_threshold' => $opts['active_threshold'],
        ]);
        }

        // Build Summonses

        foreach ($cohort as $juror) {
            $summons = new Summons();
@@ -120,13 +143,15 @@ class Manager
                ->setReportUrn($reportUrn)
                ->setJuryType($juryType)
                ->setJurorGuid($juror)
                ->setTtl(120)
                ->setTtl($opts['awaiting_ttl'])
                ->setStatus('awaiting');

            $this->repository->add($summons);
            $this->socketDelegate->onSummon($summons);
        }

        //

        return $missing;
    }

+28 −1
Original line number Diff line number Diff line
@@ -43,9 +43,12 @@ class Pool
    public function getList(array $opts = [])
    {
        $opts = array_merge([
            'for' => null,
            'active_threshold' => 0,
            'platform' => null,
            'for' => null,
            'except' => null,
            'except_hashes' => null,
            'include_only' => null,
            'validated' => false,
            'size' => 10,
            'page' => 0,
@@ -123,7 +126,19 @@ class Pool
            ];
        }

        if ($opts['include_only']) {
            $body['query']['bool']['must'][] = [
                'terms' => [
                    'user_guid' => $opts['include_only'],
                ],
            ];
        }

        if ($opts['except']) {
            if (!isset($body['query']['bool']['must_not'])) {
                $body['query']['bool']['must_not'] = [];
            }

            $body['query']['bool']['must_not'][] = [
                'terms' => [
                    'user_guid' => $opts['except'],
@@ -131,6 +146,18 @@ class Pool
            ];
        }

        if ($opts['except_hashes']) {
            if (!isset($body['query']['bool']['must_not'])) {
                $body['query']['bool']['must_not'] = [];
            }

            $body['query']['bool']['must_not'][] = [
                'terms' => [
                    'user_phone_number_hash' => $opts['except_hashes'],
                ],
            ];
        }

        if ($opts['validated']) {
            $body['query']['bool']['must'][] = [
                'exists' => [
Loading