Skip to content

Commit

Permalink
Fix those problems with nexttag assignment.
Browse files Browse the repository at this point in the history
  • Loading branch information
kohler committed Jan 28, 2024
1 parent 643dec5 commit b734dc7
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 87 deletions.
36 changes: 22 additions & 14 deletions src/assigners/a_status.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,38 @@ function fresh() {
}
}

class Withdraw_PreapplyFunction implements AssignmentPreapplyFunction {
class WithdrawVotesAssigner implements AssignmentPreapplyFunction {
// When withdrawing a paper, remove voting tags so people don't have
// phantom votes.
private $pid;
private $ltag;
function __construct($pid) {
$this->pid = $pid;
private $pids = [];
function __construct(AssignmentState $astate) {
}
function add_paper(PaperInfo $prow) {
$this->pids[] = $prow->paperId;
}
function preapply(AssignmentState $state) {
$res = $state->query_items(new Status_Assignable($this->pid));
if (!$res
|| $res[0]["_withdrawn"] <= 0
|| $res[0]->pre("_withdrawn") > 0) {
$wpids = [];
foreach ($this->pids as $pid) {
$res = $state->query_items(new Status_Assignable($pid));
if ($res
&& $res[0]["_withdrawn"] > 0
&& $res[0]->pre("_withdrawn") <= 0) {
$wpids[] = $pid;
}
}
if (empty($wpids)) {
return;
}
$ltre = [];
foreach ($state->conf->tags()->entries_having(TagInfo::TFM_VOTES) as $ti) {
$ltre[] = $ti->tag_regex();
}
$res = $state->query(new Tag_Assignable($this->pid, null));
$tag_re = '{\A(?:\d+~|)(?:' . join("|", $ltre) . ')\z}i';
foreach ($res as $x) {
if (preg_match($tag_re, $x->ltag)) {
$state->add(new Tag_Assignable($this->pid, $x->ltag, null, null, true));
foreach ($wpids as $pid) {
foreach ($state->query(new Tag_Assignable($pid, null)) as $x) {
if (preg_match($tag_re, $x->ltag)) {
$state->add(new Tag_Assignable($pid, $x->ltag, null, null, true));
}
}
}
}
Expand Down Expand Up @@ -106,7 +114,7 @@ function apply(PaperInfo $prow, Contact $contact, $req, AssignmentState $state)
$res->_submitted = -$res->_submitted;
if ($state->conf->tags()->has(TagInfo::TFM_VOTES)) {
Tag_Assignable::load($state);
$state->register_preapply_function("withdraw {$prow->paperId}", new Withdraw_PreapplyFunction($prow->paperId));
$state->callable("WithdrawVotesAssigner")->add_paper($prow);
}
}
$r = $req["withdraw_reason"];
Expand Down
120 changes: 60 additions & 60 deletions src/assigners/a_tag.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,64 +45,76 @@ function equals($q) {
&& ($q->_index ?? $this->_index) === $this->_index;
}
static function load(AssignmentState $state) {
if (!$state->mark_type("tag", ["pid", "ltag"], "Tag_Assigner::make")) {
return;
}
foreach ($state->prows() as $prow) {
foreach (Tagger::split_unpack($prow->all_tags_text()) as $ti) {
$state->load(new Tag_Assignable($prow->paperId, strtolower($ti[0]), $ti[0], $ti[1]));
if ($state->mark_type("tag", ["pid", "ltag"], "Tag_Assigner::make")) {
foreach ($state->prows() as $prow) {
self::load_prow($state, $prow);
}
}
Dbl::free($result);
}
static function load_prow(AssignmentState $state, PaperInfo $prow) {
foreach (Tagger::split_unpack($prow->all_tags_text()) as $ti) {
$state->load(new Tag_Assignable($prow->paperId, strtolower($ti[0]), $ti[0], $ti[1]));
}
}
}

class NextTagAssigner implements AssignmentPreapplyFunction {
/** @var string */
private $tag;
/** @var array<int,?float> */
public $pidindex = [];
/** @var float */
private $first_index;
/** @var float */
private $next_index;
class NextTagAssignmentState {
/** @var AssignmentState */
private $astate;
/** @var bool */
private $isseq;
function __construct($state, $tag, $index, $isseq) {
$this->tag = $tag;
$ltag = strtolower($tag);
$res = $state->query(new Tag_Assignable(null, $ltag));
foreach ($res as $x) {
$this->pidindex[$x->pid] = $x->_index;
}
asort($this->pidindex);
if ($index === null) {
$indexes = array_values($this->pidindex);
sort($indexes);
$index = count($indexes) ? $indexes[count($indexes) - 1] : 0;
$index += Tagger::value_increment($isseq);
}
$this->first_index = $this->next_index = ceil($index);
$this->isseq = $isseq;
private $all = false;
/** @var ?string */
private $prev_ltag;
/** @var ?int */
private $expected_state_version;
/** @var ?float */
private $prev_value;

function __construct(AssignmentState $astate) {
$this->astate = $astate;
}
function next_index($isseq) {
$index = $this->next_index;
$this->next_index += Tagger::value_increment($isseq);
return (float) $index;
/** @param bool $x
* @return $this */
function set_all($x) {
$this->all = $x;
return $this;
}
function preapply(AssignmentState $state) {
if ($this->next_index == $this->first_index) {
private function resolve_all() {
if ($this->all) {
return;
}
$ltag = strtolower($this->tag);
foreach ($this->pidindex as $pid => $index) {
if ($index >= $this->first_index && $index < $this->next_index) {
$x = $state->query_unedited(new Tag_Assignable($pid, $ltag));
if (!empty($x)) {
$state->add(new Tag_Assignable($pid, $ltag, $this->tag, $this->next_index($this->isseq), true));
}
$known = $this->astate->paper_ids();
$arg = empty($known) ? [] : ["where" => "Paper.paperId not in (" . join(",", $known) . ")"];
$arg["tags"] = true;
foreach ($this->astate->user->paper_set($arg) as $prow) {
$this->astate->add_prow($prow);
Tag_Assignable::load_prow($this->astate, $prow);
}
$this->all = true;
}
function compute_next(PaperInfo $prow, $ltag, $isseq) {
if ($this->astate->state_version() === $this->expected_state_version
&& $this->prev_ltag === $ltag) {
$this->prev_value += Tagger::value_increment($isseq);
++$this->expected_state_version;
return $this->prev_value;
}
$this->resolve_all();
$items = $this->astate->query_items(new Tag_Assignable(null, $ltag));
$maxvalue = null;
foreach ($items as $item) {
$value = $item["_index"];
if ($item->pid() !== $prow->paperId
&& ($maxvalue === null || $value > $maxvalue)
&& ($p = $this->astate->prow($item->pid()))
&& $this->astate->user->can_view_tag($p, $ltag)) {
$maxvalue = floor($value);
}
}
$this->prev_value = ($maxvalue ?? 0.0) + Tagger::value_increment($isseq);
$this->prev_ltag = $ltag;
$this->expected_state_version = $this->astate->state_version() + 1;
return $this->prev_value;
}
}

Expand All @@ -128,6 +140,7 @@ function __construct(Conf $conf, $aj) {
}
function expand_papers($req, AssignmentState $state) {
if ($this->itype >= self::I_NEXT) {
$state->callable("NextTagAssignmentState")->set_all(true);
return "ALL";
} else {
return parent::expand_papers($req, $state);
Expand Down Expand Up @@ -304,7 +317,7 @@ private function apply1($tag, PaperInfo $prow, Contact $contact, $req,
$ntag = $xuser . $xtag;
$ltag = strtolower($ntag);
if ($xitype === self::I_NEXT || $xitype === self::I_NEXTSEQ) {
$nvalue = $this->apply_next_index($prow->paperId, $xitype, $ntag, $nvalue, $state);
$nvalue = $state->callable("NextTagAssignmentState")->compute_next($prow, $ltag, $xitype);
} else if ($nvalue === null) {
$items = $state->query_items(new Tag_Assignable($prow->paperId, $ltag),
AssignmentState::INCLUDE_DELETED);
Expand Down Expand Up @@ -332,19 +345,6 @@ private function apply1($tag, PaperInfo $prow, Contact $contact, $req,
}
return true;
}
/** @param int $pid
* @param 1|2 $xitype
* @param string $tag
* @param ?float $nvalue
* @return float */
private function apply_next_index($pid, $xitype, $tag, $nvalue, AssignmentState $state) {
$ltag = strtolower($tag);
// NB ignore $index on second & subsequent nexttag assignments
$fin = $state->register_preapply_function("seqtag {$ltag}", new NextTagAssigner($state, $tag, $nvalue, $xitype === self::I_NEXTSEQ));
assert($fin instanceof NextTagAssigner);
unset($fin->pidindex[$pid]);
return $fin->next_index($xitype === self::I_NEXTSEQ);
}
/** @param string $xuser
* @param string $xtag */
private function apply_remove(PaperInfo $prow, AssignmentState $state, $xuser, $xtag) {
Expand Down
38 changes: 25 additions & 13 deletions src/assignmentset.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,17 @@ class AssignmentItemSet {
class AssignmentState extends MessageSet {
/** @var array<int,AssignmentItemSet> */
private $st = [];
/** @var int */
private $stversion = 0;
/** @var array<string,list<string>> */
private $types = [];
/** @var array<string,callable(AssignmentItem,AssignmentState):Assigner> */
private $realizers = [];
/** @var Conf */
/** @var Conf
* @readonly */
public $conf;
/** @var Contact */
/** @var Contact
* @readonly */
public $user; // executor
/** @var Contact */
public $reviewer; // default contact
Expand Down Expand Up @@ -203,8 +207,7 @@ class AssignmentState extends MessageSet {
private $nonexact_msgs = [];
/** @var bool */
public $has_user_error = false;
/** @var array<string,AssignmentPreapplyFunction> */
private $preapply_functions = [];
private $callables = [];

function __construct(Contact $user) {
$this->conf = $user->conf;
Expand Down Expand Up @@ -355,13 +358,18 @@ function make_filter($key, $q) {
return $cf;
}

/** @return int */
function state_version() {
return $this->stversion;
}
/** @template T
* @param T $q
* @return list<T> */
function remove($q) {
$res = [];
foreach ($this->query_items($q) as $item) {
$res[] = $item->delete_at($this->landmark);
++$this->stversion;
}
return $res;
}
Expand All @@ -375,6 +383,7 @@ function remove_if($q, $predicate) {
if (!$predicate
|| call_user_func($predicate, $item->after ?? $item->before)) {
$res[] = $item->delete_at($this->landmark);
++$this->stversion;
}
}
return $res;
Expand All @@ -392,6 +401,7 @@ function add($x) {
$item->after = $x;
$item->deleted = false;
$item->landmark = $this->landmark;
++$this->stversion;
return $item;
}

Expand Down Expand Up @@ -537,18 +547,20 @@ function clear_messages() {
$this->has_user_error = false;
}

/** @param string $name
* @param AssignmentPreapplyFunction $hook
* @return AssignmentPreapplyFunction */
function register_preapply_function($name, $hook) {
if (!isset($this->preapply_functions[$name])) {
$this->preapply_functions[$name] = $hook;
/** @template T
* @param class-string<T> $name
* @return T */
function callable($name, ...$args) {
if (!isset($this->callables[$name])) {
$this->callables[$name] = new $name($this, ...$args);
}
return $this->preapply_functions[$name];
return $this->callables[$name];
}

function call_preapply_functions() {
foreach ($this->preapply_functions as $f) {
$f->preapply($this);
foreach ($this->callables as $k) {
if ($k instanceof AssignmentPreapplyFunction)
$k->preapply($this);
}
}
}
Expand Down

0 comments on commit b734dc7

Please sign in to comment.