...
 
Commits (2)
......@@ -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);
}
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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,69 +53,89 @@ 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() ?: []));
if (!$cohort) {
$summonses = iterator_to_array($this->repository->getList([
'report_urn' => $reportUrn,
'jury_type' => $juryType,
]));
// Get all summonses for this case
$completedJurorGuids = array_map(function($decision) {
return $decision->getJurorGuid();
}, array_merge($report->getAppealJuryDecisions(), $report->getInitialJuryDecisions()));
$summonses = iterator_to_array($this->repository->getList([
'report_urn' => $reportUrn,
'jury_type' => $juryType,
]));
// 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);
});
$summonses = array_filter($summonses, function (Summons $summons) use ($completedJurorGuids) {
return !in_array($summons->getJurorGuid(), $completedJurorGuids);
});
// Check how many are missing
// Check how many are missing
$notDeclined = array_filter($summonses, function (Summons $summons) {
return $summons->isAccepted() || $summons->isAwaiting();
});
$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
// If we have a full jury, don't summon
if ($missing <= 0) {
return 0;
}
if ($missing <= 0) {
return 0;
}
// Create an array of channel GUIDs that are involved in this case
// Reduce jury to juror guids and try to pick up to missing size
$alreadyInvolvedGuids = array_map(function (Summons $summons) {
return (string) $summons->getJurorGuid();
}, $summonses);
$pendingJurorGuids = array_map(function (Summons $summons) {
return (string) $summons->getJurorGuid();
}, $summonses);
$alreadyInvolvedGuids = array_merge($alreadyInvolvedGuids, array_map(function (UserReport $userReport) {
return $userReport->getReporterGuid();
}, $report->getReports()));
$cohort = $this->cohort->pick([
'size' => $missing,
'for' => $appeal->getOwnerGuid(),
'except' => $pendingJurorGuids,
'active_threshold' => 5 * 60,
]);
}
$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' => $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;
}
......
......@@ -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' => [
......
......@@ -4,28 +4,29 @@
*/
namespace Minds\Core\Reports\UserReports;
use Minds\Entities\Report;
use Minds\Traits\MagicAttributes;
/**
* @method UserReport getReport(): Report
* @method UserReport getReportUrn(): string
* @method UserReport getReporterGuid(): long
* @method UserReport getReporterHash(): string
* @method UserReport getReasonCode(): int
* @method UserReport getSubReasonCode(): int
* @method UserReport getTimestamp: int
* @method Report getReport()
* @method string getReportUrn()
* @method int getReporterGuid()
* @method string getReporterHash()
* @method int getReasonCode()
* @method int getSubReasonCode()
* @method int getTimestamp()
*/
class UserReport
{
use MagicAttributes;
/** @var long $timestamp -< in ms*/
/** @var int $timestamp -< in ms*/
private $timestamp;
/** @var long $reporterGuid */
/** @var int $reporterGuid */
private $reporterGuid;
/** @var long $reporterHash */
/** @var int $reporterHash */
private $reporterHash;
/** @var Report $report */
......
......@@ -40,6 +40,8 @@ class CohortSpec extends ObjectBehavior
'platform' => 'browser',
'for' => 1000,
'except' => [ 1001, 1002 ],
'except_hashes' => [ '0303456', '0800888AFIP', '555KLA' ],
'include_only' => null,
'validated' => true,
'size' => 20,
'page' => 0,
......@@ -55,6 +57,8 @@ class CohortSpec extends ObjectBehavior
'platform' => 'browser',
'for' => 1000,
'except' => [ 1001, 1002 ],
'except_hashes' => [ '0303456', '0800888AFIP', '555KLA' ],
'include_only' => null,
'validated' => true,
'size' => 20,
'page' => 1,
......@@ -70,6 +74,8 @@ class CohortSpec extends ObjectBehavior
'platform' => 'browser',
'for' => 1000,
'except' => [ 1001, 1002 ],
'except_hashes' => [ '0303456', '0800888AFIP', '555KLA' ],
'include_only' => null,
'validated' => true,
'size' => 20,
'page' => 2,
......@@ -83,6 +89,8 @@ class CohortSpec extends ObjectBehavior
'size' => 3,
'for' => 1000,
'except' => [ 1001, 1002 ],
'except_hashes' => [ '0303456', '0800888AFIP', '555KLA' ],
'include_only' => null,
'active_threshold' => 100,
])
->shouldReturn([ 1010, 1011, 1012 ]);
......