per-board study setup (restructure columns, reset, arm RecoReco) via Integrations (v0.3.0)

This commit is contained in:
2026-07-11 15:34:39 -03:00
parent e7e2b930f3
commit 9247057179
5 changed files with 208 additions and 3 deletions

View File

@@ -3,6 +3,7 @@
namespace Kanboard\Plugin\QualKard\Helper;
use Kanboard\Core\Base;
use Kanboard\Model\TaskModel;
/**
* QualKard helper: the qualcard grading math (ported from Ruben's C program) plus the per-board gates.
@@ -118,4 +119,78 @@ class QualKardHelper extends Base
{
return (int) $this->projectMetadataModel->get($project_id, 'qualkard_col_'.$role, 0);
}
/**
* Write the inert RecoReco config for a card: move-mode to Study, fire from anywhere but Done,
* daily (innocuous -- QualKard sets the due date on each grade), anchor 0 so RecoReco skips it
* until the first grade arms it. QualKard never toggles recoreco_enabled again.
*
* @param int $task_id
* @param int $project_id
*/
public function arm($task_id, $project_id)
{
$this->taskMetadataModel->save($task_id, array(
'recoreco_enabled' => 1,
'recoreco_move' => 1,
'recoreco_target_column' => $this->col($project_id, 'study'),
'recoreco_trigger_column' => $this->col($project_id, 'done'),
'recoreco_trigger_invert' => 1,
'recoreco_frequency' => 'daily',
'recoreco_anchor' => 0,
));
}
/**
* Reset one card: move to Drafts, mark unread, arm inert, clear the due date.
*
* @param int $task_id
* @param int $project_id
*/
public function resetCard($task_id, $project_id)
{
$task = $this->taskFinderModel->getById($task_id);
if (empty($task)) {
return;
}
$this->taskPositionModel->movePosition($project_id, $task_id, $this->col($project_id, 'draft'), 1, (int) $task['swimlane_id'], false);
$this->taskMetadataModel->save($task_id, array('qualkard_avg' => '')); // '' = unread
$this->arm($task_id, $project_id);
$this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update(array('date_due' => 0));
}
/**
* Reset every card on a board.
*
* @param int $project_id
*/
public function resetAll($project_id)
{
foreach ($this->taskFinderModel->getAll($project_id) as $task) {
$this->resetCard($task['id'], $project_id);
}
}
/**
* Grade a card by a score (a grade column's value): update the running average, compute the next
* due date, and arm the anchor. RecoReco moves the card from here.
*
* @param int $task_id
* @param float $score
*/
public function grade($task_id, $score)
{
$raw = $this->taskMetadataModel->get($task_id, 'qualkard_avg', '');
$old = ($raw === '' || $raw === null) ? null : (float) $raw;
$avg = self::nextAverage($old, $score);
$due = self::dueFrom($avg);
$this->taskMetadataModel->save($task_id, array(
'qualkard_avg' => (string) $avg,
'recoreco_anchor' => $due,
));
$this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update(array('date_due' => $due));
}
}