mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-13 13:03:20 +00:00
PHRAS-3768_feedback-report-per-record (#4421)
* add command feedback:report ; bump back to 4.1.8-rc9 WIP OK TO TEST * aadd dry, log, ... ; move conf ; bump back to 4.1.8-rc8 WIP OK TO TEST * add command feedback:report ; bump back to 4.1.8-rc9 WIP OK TO TEST * aadd dry, log, ... ; move conf ; bump back to 4.1.8-rc8 WIP OK TO TEST * add default (disabled) conf in conf.d * Update Version.php bump version made in #4426
This commit is contained in:
@@ -57,6 +57,7 @@ use Alchemy\Phrasea\Command\Task\TaskState;
|
|||||||
use Alchemy\Phrasea\Command\Task\TaskStop;
|
use Alchemy\Phrasea\Command\Task\TaskStop;
|
||||||
use Alchemy\Phrasea\Command\Thesaurus\FindConceptsCommand;
|
use Alchemy\Phrasea\Command\Thesaurus\FindConceptsCommand;
|
||||||
use Alchemy\Phrasea\Command\Thesaurus\Translator\TranslateCommand;
|
use Alchemy\Phrasea\Command\Thesaurus\Translator\TranslateCommand;
|
||||||
|
use Alchemy\Phrasea\Command\Feedback\Report\FeedbackReportCommand;
|
||||||
use Alchemy\Phrasea\Command\UpgradeDBDatas;
|
use Alchemy\Phrasea\Command\UpgradeDBDatas;
|
||||||
use Alchemy\Phrasea\Command\User\UserApplicationsCommand;
|
use Alchemy\Phrasea\Command\User\UserApplicationsCommand;
|
||||||
use Alchemy\Phrasea\Command\User\UserCreateCommand;
|
use Alchemy\Phrasea\Command\User\UserCreateCommand;
|
||||||
@@ -180,6 +181,7 @@ $cli->command(new QueryParseCommand());
|
|||||||
$cli->command(new QuerySampleCommand());
|
$cli->command(new QuerySampleCommand());
|
||||||
$cli->command(new FindConceptsCommand());
|
$cli->command(new FindConceptsCommand());
|
||||||
$cli->command(new TranslateCommand());
|
$cli->command(new TranslateCommand());
|
||||||
|
$cli->command(new FeedbackReportCommand());
|
||||||
|
|
||||||
$cli->command(new WorkerExecuteCommand());
|
$cli->command(new WorkerExecuteCommand());
|
||||||
$cli->command(new WorkerHeartbeatCommand());
|
$cli->command(new WorkerHeartbeatCommand());
|
||||||
|
@@ -417,3 +417,27 @@ order-manager:
|
|||||||
download-hd:
|
download-hd:
|
||||||
expiration-days: null
|
expiration-days: null
|
||||||
expiration-override: false
|
expiration-override: false
|
||||||
|
|
||||||
|
feedback-report:
|
||||||
|
enabled: false
|
||||||
|
actions:
|
||||||
|
action_unvoted:
|
||||||
|
# if any participant has not voted, set the "incomplete" icon
|
||||||
|
status_bit: 8
|
||||||
|
value: '{% if vote.votes_unvoted > 0 %} 1 {% else %} 0 {% endif %}'
|
||||||
|
|
||||||
|
# because records not involved in a vote should not display a red or green flag, we need 2 sb
|
||||||
|
action_red:
|
||||||
|
# if _any_ vote is "no", set the red flag
|
||||||
|
status_bit: 9
|
||||||
|
value: '{% if vote.votes_no > 0 %} 1 {% else %} 0 {% endif %}'
|
||||||
|
action_green:
|
||||||
|
# if _all_ votes are "yes" (=no vote is "no"), set the green flag
|
||||||
|
status_bit: 10
|
||||||
|
value: '{% if vote.votes_no == 0 %} 1 {% else %} 0 {% endif %}'
|
||||||
|
|
||||||
|
action_log:
|
||||||
|
metadata: 'Validations'
|
||||||
|
method: "prepend"
|
||||||
|
delimiter: "\n"
|
||||||
|
value: 'Vote initated on {{ vote.created }} by {{ initiator ? initiator.getEmail() : "?" }} expired {{ vote.expired }} : {{ vote.voters_count }} participants, {{ vote.votes_unvoted }} unvoted, {{ vote.votes_no }} "no", {{ vote.votes_yes}} "yes".'
|
||||||
|
29
lib/Alchemy/Phrasea/Command/Feedback/Report/Action.php
Normal file
29
lib/Alchemy/Phrasea/Command/Feedback/Report/Action.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\Command\Feedback\Report;
|
||||||
|
|
||||||
|
use Twig_Environment;
|
||||||
|
use Twig_Template;
|
||||||
|
|
||||||
|
class Action
|
||||||
|
{
|
||||||
|
/** @var Twig_Template[] */
|
||||||
|
private $template = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $action_conf;
|
||||||
|
|
||||||
|
public function __construct(Twig_Environment $twig, array $action_conf)
|
||||||
|
{
|
||||||
|
$this->action_conf = $action_conf;
|
||||||
|
$this->template = $twig->createTemplate($action_conf['value']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValue(array $context)
|
||||||
|
{
|
||||||
|
return $this->template->render($context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\Command\Feedback\Report;
|
||||||
|
|
||||||
|
|
||||||
|
interface ActionInterface
|
||||||
|
{
|
||||||
|
function addAction(array &$actions, array $context);
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\Command\Feedback\Report;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class ConfigurationException extends Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,234 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\Command\Feedback\Report;
|
||||||
|
|
||||||
|
|
||||||
|
use Alchemy\Phrasea\Command\Command as phrCommand;
|
||||||
|
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
|
||||||
|
use Alchemy\Phrasea\Model\Repositories\UserRepository;
|
||||||
|
use appbox;
|
||||||
|
use Doctrine\DBAL\DBALException;
|
||||||
|
use Exception;
|
||||||
|
use PDO;
|
||||||
|
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
|
||||||
|
* @link www.phraseanet.com
|
||||||
|
*/
|
||||||
|
class FeedbackReportCommand extends phrCommand
|
||||||
|
{
|
||||||
|
/** @var InputInterface $input */
|
||||||
|
private $input;
|
||||||
|
/** @var OutputInterface $output */
|
||||||
|
private $output;
|
||||||
|
|
||||||
|
/** @var GlobalConfiguration */
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
public function configure()
|
||||||
|
{
|
||||||
|
$this->setName('feedback:report')
|
||||||
|
->setDescription('Report ended feedback results (votes) on records (set status-bits)')
|
||||||
|
->addOption('report', null, InputOption::VALUE_REQUIRED, "Report output format (all|condensed)", "all")
|
||||||
|
->addOption('dry', null, InputOption::VALUE_NONE, "list translations but don't apply.", null)
|
||||||
|
->setHelp("")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InputInterface $input
|
||||||
|
* @param OutputInterface $output
|
||||||
|
* @return int
|
||||||
|
* @throws DBALException
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
protected function doExecute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
// add cool styles
|
||||||
|
$style = new OutputFormatterStyle('black', 'yellow'); // , array('bold'));
|
||||||
|
$output->getFormatter()->setStyle('warning', $style);
|
||||||
|
|
||||||
|
$this->input = $input;
|
||||||
|
$this->output = $output;
|
||||||
|
|
||||||
|
// config must be ok
|
||||||
|
//
|
||||||
|
try {
|
||||||
|
$this->config = new GlobalConfiguration(
|
||||||
|
$this->getConf(),
|
||||||
|
$this->container['twig'],
|
||||||
|
$this->container['phraseanet.appbox'],
|
||||||
|
$input->getOption('dry'),
|
||||||
|
$input->getOption('report')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(Exception $e) {
|
||||||
|
$output->writeln(sprintf("<error>missing or bad configuration: %s</error>", $e->getMessage()));
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$this->config->isEnabled()) {
|
||||||
|
$output->writeln(sprintf("<info>configuration is not enabled</info>"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$appbox = $this->getAppBox();
|
||||||
|
|
||||||
|
$sql_update = "UPDATE `BasketElements` SET `vote_expired` = :expired WHERE `id` = :id";
|
||||||
|
$stmt_update = $appbox->get_connection()->prepare($sql_update);
|
||||||
|
|
||||||
|
$sql_select = "SELECT * FROM (
|
||||||
|
SELECT q1.*,
|
||||||
|
COUNT(bp.id) AS `voters_count`,
|
||||||
|
SUM(IF(ISNULL(`agreement`), 1 , 0)) AS `votes_unvoted`,
|
||||||
|
SUM(IF((`agreement`=0), 1, 0)) AS `votes_no`,
|
||||||
|
SUM(IF((`agreement`=1), 1, 0)) AS `votes_yes`
|
||||||
|
FROM (
|
||||||
|
SELECT SUBSTRING_INDEX(GROUP_CONCAT(b.`id` ORDER BY `vote_expires` DESC), ',', 1) AS `basket_id`,
|
||||||
|
b.`vote_created` AS `created`, b.`vote_initiator_id`,
|
||||||
|
MAX(b.`vote_expires`) AS `expired`, be.`id` AS `be_id`, be.`vote_expired` AS `be_vote_expired`,
|
||||||
|
be.`sbas_id`, be.`record_id`, CONCAT(be.`sbas_id`, '_', be.`record_id`) AS `sbid_rid`
|
||||||
|
FROM `BasketElements` AS be INNER JOIN `Baskets` AS b ON b.`id`=be.`basket_id`
|
||||||
|
WHERE b.`vote_expires` < NOW()
|
||||||
|
GROUP BY `sbid_rid`
|
||||||
|
) AS q1
|
||||||
|
INNER JOIN `BasketParticipants` AS bp ON bp.`basket_id`=q1.`basket_id`
|
||||||
|
LEFT JOIN `BasketElementVotes` AS bv ON bv.`participant_id`=bp.`id` AND bv.`basket_element_id`=`be_id`
|
||||||
|
GROUP BY q1.`sbid_rid`
|
||||||
|
HAVING ISNULL(`be_vote_expired`) OR `expired` > `be_vote_expired`
|
||||||
|
) AS q2 ORDER BY basket_id, record_id";
|
||||||
|
|
||||||
|
$last_basket_id = null;
|
||||||
|
$condensed = null;
|
||||||
|
$vote_initiator = null;
|
||||||
|
$stmt_select = $appbox->get_connection()->query($sql_select);
|
||||||
|
while ($row = $stmt_select->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
if($row['basket_id'] !== $last_basket_id) {
|
||||||
|
$this->outputCondensed($condensed);
|
||||||
|
$condensed = [
|
||||||
|
'voters_count' => $row['voters_count'],
|
||||||
|
'records_count' => 0,
|
||||||
|
'votes_unvoted' => 0,
|
||||||
|
'votes_no' => 0,
|
||||||
|
'votes_yes' => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
$vote_initiator = $this->findUser($row['vote_initiator_id']);
|
||||||
|
|
||||||
|
$this->output->writeln(sprintf("basket: %s, initated on %s by %s (%s), expired %s",
|
||||||
|
$last_basket_id = $row['basket_id'],
|
||||||
|
$row['created'],
|
||||||
|
$row['vote_initiator_id'],
|
||||||
|
$vote_initiator ? $vote_initiator->getEmail() : "<error>unknown</error>",
|
||||||
|
$row['expired'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if($this->config->getReportFormat() === 'all') {
|
||||||
|
$this->output->writeln(sprintf("\tdatabox: %s, record id: %s", $row['sbas_id'], $row['record_id']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ($databox = $this->config->getDatabox($row['sbas_id'])) === null) {
|
||||||
|
$this->output->writeln(sprintf("\t\t<error>unknown databox</error> (ignored)"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$record = $databox->get_record($row['record_id']);
|
||||||
|
}
|
||||||
|
catch(Exception $e) {
|
||||||
|
$this->output->writeln(sprintf("\t\t<error>unknown record</error> (ignored)"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$condensed['records_count']++;
|
||||||
|
foreach(['votes_unvoted', 'votes_no', 'votes_yes'] as $k) {
|
||||||
|
if($this->config->getReportFormat() !== 'condensed') {
|
||||||
|
$this->output->writeln(sprintf("\t\t%s: %s", $k, $row[$k]));
|
||||||
|
}
|
||||||
|
$condensed[$k] += $row[$k];
|
||||||
|
}
|
||||||
|
|
||||||
|
$setMetasActions = [];
|
||||||
|
foreach($this->config->getActions($databox) as $action) {
|
||||||
|
$action->addAction(
|
||||||
|
$setMetasActions,
|
||||||
|
[
|
||||||
|
'initiator' => $vote_initiator,
|
||||||
|
'vote' => $row,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($setMetasActions) > 0) {
|
||||||
|
$jsActions = json_encode($setMetasActions, JSON_PRETTY_PRINT);
|
||||||
|
if($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE) {
|
||||||
|
$this->output->writeln(sprintf("<info>JS : %s</info>", $jsActions));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$this->config->isDryRun()) {
|
||||||
|
$record->setMetadatasByActions(json_decode($jsActions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!$this->config->isDryRun()) {
|
||||||
|
$stmt_update->execute([
|
||||||
|
':expired' => $row['expired'],
|
||||||
|
':id' => $row['be_id']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->outputCondensed($condensed);
|
||||||
|
$stmt_select->closeCursor();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return appbox
|
||||||
|
*/
|
||||||
|
private function getAppBox(): appbox
|
||||||
|
{
|
||||||
|
return $this->container['phraseanet.appbox'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return PropertyAccess
|
||||||
|
*/
|
||||||
|
protected function getConf()
|
||||||
|
{
|
||||||
|
return $this->container['conf'];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findUser($user_id)
|
||||||
|
{
|
||||||
|
/** @var UserRepository $repo */
|
||||||
|
$repo = $this->container['repo.users'];
|
||||||
|
try {
|
||||||
|
return $repo->find($user_id);
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|null $condensed
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function outputCondensed($condensed)
|
||||||
|
{
|
||||||
|
if($condensed !== null && $this->config->getReportFormat() === 'condensed') {
|
||||||
|
foreach($condensed as $k => $v) {
|
||||||
|
$this->output->writeln(sprintf("\t%s: %s", $k, $v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,181 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\Command\Feedback\Report;
|
||||||
|
|
||||||
|
|
||||||
|
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
|
||||||
|
use appbox;
|
||||||
|
use collection;
|
||||||
|
use databox;
|
||||||
|
use databox_field;
|
||||||
|
use Twig_Environment;
|
||||||
|
|
||||||
|
Class GlobalConfiguration
|
||||||
|
{
|
||||||
|
const CONFIG_DIR = "/config/feedbackreport/";
|
||||||
|
const CONFIG_FILE = "configuration.yml";
|
||||||
|
|
||||||
|
private $configuration = null;
|
||||||
|
|
||||||
|
private $actions = []; // ActionInterface[], by sbas_id
|
||||||
|
|
||||||
|
private $databoxes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $dryRun;
|
||||||
|
/**
|
||||||
|
* @var Twig_Environment
|
||||||
|
*/
|
||||||
|
private $twig;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $reportFormat;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PropertyAccess $conf
|
||||||
|
* @param Twig_Environment $twig
|
||||||
|
* @param appbox $appBox
|
||||||
|
* @param bool $dryRun
|
||||||
|
* @param string $reportFormat
|
||||||
|
* @throws ConfigurationException
|
||||||
|
*/
|
||||||
|
public function __construct(PropertyAccess $conf, Twig_Environment $twig, appbox $appBox, bool $dryRun, string $reportFormat)
|
||||||
|
{
|
||||||
|
$this->twig = $twig;
|
||||||
|
$this->configuration = $conf->get(['feedback-report'], ['enabled' => false, 'actions' => []]);
|
||||||
|
$this->dryRun = $dryRun;
|
||||||
|
$this->reportFormat = $reportFormat;
|
||||||
|
|
||||||
|
if($this->isEnabled()) {
|
||||||
|
// sanitize sb
|
||||||
|
foreach ($this->configuration['actions'] as $action_name => $action_conf) {
|
||||||
|
if (array_key_exists('status_bit', $action_conf)) {
|
||||||
|
$bit = (int)($sbit = trim($action_conf['status_bit']));
|
||||||
|
if ($bit < 4 || $bit > 31) {
|
||||||
|
throw new ConfigurationException(sprintf("bad status bit (%s)", $sbit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// nb: "metadata" cannot be sanitized because validity depends on databox, and a basket may contain records from many dbx.
|
||||||
|
// unknown field will be ignored during actions creation.
|
||||||
|
}
|
||||||
|
|
||||||
|
// list databoxes and collections to access by id or by name
|
||||||
|
$this->databoxes = [];
|
||||||
|
foreach ($appBox->get_databoxes() as $databox) {
|
||||||
|
$sbas_id = $databox->get_sbas_id();
|
||||||
|
$sbas_name = $databox->get_dbname();
|
||||||
|
$this->databoxes[$sbas_id] = [
|
||||||
|
'dbox' => $databox,
|
||||||
|
'collections' => [],
|
||||||
|
'fields' => [],
|
||||||
|
];
|
||||||
|
$this->databoxes[$sbas_name] = &$this->databoxes[$sbas_id];
|
||||||
|
// list all collections
|
||||||
|
foreach ($databox->get_collections() as $collection) {
|
||||||
|
$coll_id = $collection->get_coll_id();
|
||||||
|
$coll_name = $collection->get_name();
|
||||||
|
$this->databoxes[$sbas_id]['collections'][$coll_id] = $collection;
|
||||||
|
$this->databoxes[$sbas_id]['collections'][$coll_name] = &$this->databoxes[$sbas_id]['collections'][$coll_id];
|
||||||
|
}
|
||||||
|
// list all fields
|
||||||
|
/** @var databox_field $dbf */
|
||||||
|
foreach($databox->get_meta_structure() as $dbf) {
|
||||||
|
$field_id = $dbf->get_id();
|
||||||
|
$field_name = $dbf->get_name();
|
||||||
|
$this->databoxes[$sbas_id]['fields'][$field_id] = $dbf;
|
||||||
|
$this->databoxes[$sbas_id]['fields'][$field_name] = &$this->databoxes[$sbas_id]['fields'][$field_id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|int $sbasIdOrName
|
||||||
|
* @return databox|null
|
||||||
|
*/
|
||||||
|
public function getDatabox($sbasIdOrName)
|
||||||
|
{
|
||||||
|
return isset($this->databoxes[$sbasIdOrName]) ? $this->databoxes[$sbasIdOrName]['dbox'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|int $sbasIdOrName
|
||||||
|
* @param string|int $collIdOrName
|
||||||
|
* @return collection|null
|
||||||
|
*/
|
||||||
|
public function getCollection($sbasIdOrName, $collIdOrName)
|
||||||
|
{
|
||||||
|
return $this->databoxes[$sbasIdOrName]['collections'][$collIdOrName] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|int $sbasIdOrName
|
||||||
|
* @return databox_field[]|null
|
||||||
|
*/
|
||||||
|
public function getFields($sbasIdOrName)
|
||||||
|
{
|
||||||
|
return $this->databoxes[$sbasIdOrName] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|int $sbasIdOrName
|
||||||
|
* @return databox_field|null
|
||||||
|
*/
|
||||||
|
public function getField($sbasIdOrName, $fieldIdOrName)
|
||||||
|
{
|
||||||
|
return $this->databoxes[$sbasIdOrName]['fields'][$fieldIdOrName] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isDryRun(): bool
|
||||||
|
{
|
||||||
|
return $this->dryRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEnabled(): bool
|
||||||
|
{
|
||||||
|
return !!$this->configuration['enabled'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getReportFormat(): string
|
||||||
|
{
|
||||||
|
return $this->reportFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ActionInterface[]
|
||||||
|
*/
|
||||||
|
public function getActions(\databox $databox): array
|
||||||
|
{
|
||||||
|
$sbas_id = $databox->get_sbas_id();
|
||||||
|
if(!array_key_exists($sbas_id, $this->actions)) {
|
||||||
|
$this->actions[$sbas_id] = [];
|
||||||
|
|
||||||
|
foreach($this->configuration['actions'] as $action_name => $action_conf) {
|
||||||
|
if(array_key_exists('status_bit', $action_conf)) {
|
||||||
|
$this->actions[$sbas_id][] = new StatusBitAction($this->twig, $action_conf);
|
||||||
|
}
|
||||||
|
else if(array_key_exists('metadata', $action_conf)) {
|
||||||
|
if(($f = $this->getField($databox->get_sbas_id(), $action_conf['metadata'])) !== null) {
|
||||||
|
$this->actions[$sbas_id][] = new MetadataAction($this->twig, $f->get_name(), $action_conf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->actions[$sbas_id];
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\Command\Feedback\Report;
|
||||||
|
|
||||||
|
use Twig_Environment;
|
||||||
|
|
||||||
|
class MetadataAction extends Action implements ActionInterface
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $fieldName;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $method;
|
||||||
|
/** @var string */
|
||||||
|
private $delimiter;
|
||||||
|
|
||||||
|
public function __construct(Twig_Environment $twig, string $fieldName, array $action_conf)
|
||||||
|
{
|
||||||
|
parent::__construct($twig, $action_conf);
|
||||||
|
$this->fieldName = $fieldName;
|
||||||
|
$this->method = array_key_exists('method', $action_conf) ? $action_conf['method'] : '';
|
||||||
|
$this->delimiter = array_key_exists('delimiter', $action_conf) ? $action_conf['delimiter'] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addAction(array &$actions, array $context)
|
||||||
|
{
|
||||||
|
if(!array_key_exists('metadatas', $actions)) {
|
||||||
|
$actions['metadatas'] = [];
|
||||||
|
}
|
||||||
|
$action = [
|
||||||
|
"field_name" => $this->fieldName,
|
||||||
|
"value" => trim($this->getValue($context))
|
||||||
|
];
|
||||||
|
if($this->method !== '') {
|
||||||
|
$action['method'] = $this->method;
|
||||||
|
}
|
||||||
|
if($this->delimiter !== '') {
|
||||||
|
$action['delimiter'] = $this->delimiter;
|
||||||
|
}
|
||||||
|
$actions['metadatas'][] = $action;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\Command\Feedback\Report;
|
||||||
|
|
||||||
|
use Twig_Environment;
|
||||||
|
|
||||||
|
class StatusBitAction extends Action implements ActionInterface
|
||||||
|
{
|
||||||
|
private $bit;
|
||||||
|
|
||||||
|
public function __construct(Twig_Environment $twig, array $action_conf)
|
||||||
|
{
|
||||||
|
parent::__construct($twig, $action_conf);
|
||||||
|
$bit = (int)($sbit = trim($action_conf['status_bit']));
|
||||||
|
// already sanitized
|
||||||
|
// if($bit < 4 || $bit > 31) {
|
||||||
|
// throw new ConfigurationException(sprintf("bad status bit (%s)", $sbit));
|
||||||
|
// }
|
||||||
|
$this->bit = $bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addAction(array &$actions, array $context)
|
||||||
|
{
|
||||||
|
if(!array_key_exists('status', $actions)) {
|
||||||
|
$actions['status'] = [];
|
||||||
|
}
|
||||||
|
$actions['status'][] = [
|
||||||
|
"bit" => $this->bit,
|
||||||
|
"state" => !!trim($this->getValue($context))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
75
lib/classes/patch/418RC8PHRAS3768.php
Normal file
75
lib/classes/patch/418RC8PHRAS3768.php
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Alchemy\Phrasea\Application;
|
||||||
|
|
||||||
|
class patch_418RC8PHRAS3768 implements patchInterface
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $release = '4.1.8-rc8';
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $concern = [base::APPLICATION_BOX];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function get_release()
|
||||||
|
{
|
||||||
|
return $this->release;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getDoctrineMigrations()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function require_all_upgrades()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function concern()
|
||||||
|
{
|
||||||
|
return $this->concern;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply(base $base, Application $app)
|
||||||
|
{
|
||||||
|
if ($base->get_base_type() === base::DATA_BOX) {
|
||||||
|
$this->patch_databox($base, $app);
|
||||||
|
}
|
||||||
|
elseif ($base->get_base_type() === base::APPLICATION_BOX) {
|
||||||
|
$this->patch_appbox($base, $app);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function patch_databox(databox $databox, Application $app)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private function patch_appbox(base $appbox, Application $app)
|
||||||
|
{
|
||||||
|
$cnx = $appbox->get_connection();
|
||||||
|
$sql = "ALTER TABLE `BasketElements` ADD `vote_expired` DATETIME NULL, ADD INDEX `vote_expired` (`vote_expired`)";
|
||||||
|
// try {
|
||||||
|
$cnx->exec($sql);
|
||||||
|
// }
|
||||||
|
// catch (\Exception $e) {
|
||||||
|
// the field already exist ?
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
@@ -438,3 +438,20 @@ externalservice:
|
|||||||
|
|
||||||
Console_logger_enabled_environments: [test]
|
Console_logger_enabled_environments: [test]
|
||||||
|
|
||||||
|
feedback-report:
|
||||||
|
enabled: false
|
||||||
|
actions:
|
||||||
|
action_unvoted:
|
||||||
|
status_bit: 8
|
||||||
|
value: '{% if vote.votes_unvoted > 0 %} 1 {% else %} 0 {% endif %}'
|
||||||
|
action_red:
|
||||||
|
status_bit: 9
|
||||||
|
value: '{% if vote.votes_no > 0 %} 1 {% else %} 0 {% endif %}'
|
||||||
|
action_green:
|
||||||
|
status_bit: 10
|
||||||
|
value: '{% if vote.votes_no == 0 %} 1 {% else %} 0 {% endif %}'
|
||||||
|
action_log:
|
||||||
|
metadata: Validations
|
||||||
|
method: prepend
|
||||||
|
delimiter: "\n"
|
||||||
|
value: 'Vote initated on {{ vote.created }} by {{ initiator ? initiator.getEmail() : "?" }} expired {{ vote.expired }} : {{ vote.voters_count }} participants, {{ vote.votes_unvoted }} unvoted, {{ vote.votes_no }} "no", {{ vote.votes_yes}} "yes".'
|
||||||
|
Reference in New Issue
Block a user