restore module training in dev
This commit is contained in:
parent
fcab3d6494
commit
e287172313
36
modules/training/controllers/admin/AdminPharmacy.php
Normal file
36
modules/training/controllers/admin/AdminPharmacy.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
class AdminTrainingPharmacyController extends ModuleAdminController
|
||||
{
|
||||
public function __construct() {
|
||||
|
||||
$this->table = 'training_pharmacy';
|
||||
$this->className = 'TrainingPharmacy';
|
||||
$this->identifier = 'id_training_pharmacy';
|
||||
$this->lang = FALSE;
|
||||
$this->deleted = FALSE;
|
||||
$this->bootstrap = TRUE;
|
||||
$this->_defaultOrderBy = 'codeclient';
|
||||
$this->toolbar_btn = array();
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->actions = array();
|
||||
|
||||
$this->fields_list = array(
|
||||
'id_training_pharmacy' => array(
|
||||
'title' => 'ID',
|
||||
'width' => 25
|
||||
),
|
||||
'codeclient' => array(
|
||||
'title' => $this->module->l('Code client'),
|
||||
'width' => 60,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function renderView()
|
||||
{
|
||||
return $this->renderList();
|
||||
}
|
||||
}
|
278
modules/training/controllers/admin/AdminTraining.php
Normal file
278
modules/training/controllers/admin/AdminTraining.php
Normal file
@ -0,0 +1,278 @@
|
||||
<?php
|
||||
ini_set('upload_max_filesize', '100M');
|
||||
ini_set('post_max_size', '20M');
|
||||
|
||||
include_once(_PS_MODULE_DIR_.'training/models/TrainingBilan.php');
|
||||
|
||||
class AdminTrainingController extends ModuleAdminController
|
||||
{
|
||||
private $path_files = '';
|
||||
private $path_output_files = '';
|
||||
|
||||
function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->path_files = __DIR__.'/../../upload/';
|
||||
$this->url_upload_path = '/modules/training/upload/';
|
||||
$this->path_output_files = __DIR__.'/../../output/';
|
||||
$this->url_output_path = '/modules/training/output/';
|
||||
}
|
||||
|
||||
function display()
|
||||
{
|
||||
$this->setTemplate('home.tpl');
|
||||
|
||||
$this->context->smarty->assign(array(
|
||||
'link_training_modules' => Context::getContext()->link->getAdminLink('AdminTrainingModules', true),
|
||||
'link_self' => Context::getContext()->link->getAdminLink('AdminTraining', true),
|
||||
));
|
||||
|
||||
parent::display();
|
||||
}
|
||||
|
||||
function initContent()
|
||||
{
|
||||
$this->context->smarty->assign('url_upload_path', $this->url_upload_path);
|
||||
$this->context->smarty->assign('url_output_path', $this->url_output_path);
|
||||
|
||||
$this->context->smarty->assign('used_module_file', Configuration::get('TRAINING_USED_MODULE_FILE'));
|
||||
$this->context->smarty->assign('used_pharmacy_file', Configuration::get('TRAINING_USED_PHARMACY_FILE'));
|
||||
$this->context->smarty->assign('used_results_file', Configuration::get('TRAINING_USED_RESULT_FILE'));
|
||||
$this->context->smarty->assign('used_fixedcodes_file', Configuration::get('TRAINING_FIXEDCODES_FILE'));
|
||||
$this->context->smarty->assign('bilan_file', Configuration::get('TRAINING_BILAN_FILE'));
|
||||
$this->context->smarty->assign('corrected_results_file', Configuration::get('TRAINING_CORRECTED_RESULT_FILE'));
|
||||
$this->context->smarty->assign('corrected_codes_file', Configuration::get('TRAINING_CORRECTED_CODES_FILE'));
|
||||
|
||||
if (Tools::isSubmit('submitFileModules')) {
|
||||
$this->submitFileModules();
|
||||
}
|
||||
elseif (Tools::isSubmit('submitFilePharmacies')) {
|
||||
$this->submitFilePharmacies();
|
||||
}
|
||||
elseif (Tools::isSubmit('submitFileResults')) {
|
||||
$this->submitFileResults();
|
||||
}
|
||||
elseif (Tools::isSubmit('generateBilan')) {
|
||||
$this->generateBilan();
|
||||
}
|
||||
elseif (Tools::isSubmit('submitFileFixedCodes')) {
|
||||
$this->submitFileFixedCodes();
|
||||
}
|
||||
}
|
||||
|
||||
function submitFileModules()
|
||||
{
|
||||
$file = $this->moveUploadedCsvFile('module_filename');
|
||||
|
||||
if ($file!==null) {
|
||||
$ok = TrainingModule::importFromCsvFile($file->path);
|
||||
|
||||
if ($ok) {
|
||||
Configuration::updateValue('TRAINING_USED_MODULE_FILE', $file->name);
|
||||
$this->context->smarty->assign('used_module_file', $file->name);
|
||||
$this->confirmations[] = $this->l(sprintf('Importation des modules et catégories réussie.'));
|
||||
}
|
||||
else {
|
||||
$this->errors[] = $this->l('Echec de l\'importation des modules et catégories. Il est possible de les éditer manuellement.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function submitFilePharmacies()
|
||||
{
|
||||
$file = $this->moveUploadedCsvFile('pharmacy_filename');
|
||||
|
||||
if ($file!==null) {
|
||||
$ok = TrainingPharmacy::importFromCsvFile($file->path);
|
||||
|
||||
if ($ok) {
|
||||
Configuration::updateValue('TRAINING_USED_PHARMACY_FILE', $file->name);
|
||||
$this->context->smarty->assign('used_pharmacy_file', $file->name);
|
||||
$this->confirmations[] = $this->l(sprintf('Importation des pharmacies réussie.'));
|
||||
}
|
||||
else {
|
||||
$this->errors[] = $this->l(sprintf('Echec de l\'importation des pharmacies.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function submitFileResults()
|
||||
{
|
||||
$file = $this->moveUploadedCsvFile('results_filename');
|
||||
|
||||
if ($file!==null) {
|
||||
Configuration::updateValue('TRAINING_USED_RESULT_FILE', $file->name);
|
||||
$this->context->smarty->assign('used_results_file', $file->name);
|
||||
}
|
||||
}
|
||||
|
||||
function submitFileFixedCodes()
|
||||
{
|
||||
$file = $this->moveUploadedCsvFile('fixed_codes_filename');
|
||||
|
||||
if ($file!==null) {
|
||||
$ok = TrainingFixedCodeClient::importFromCsvFile($file->path);
|
||||
|
||||
if ($ok) {
|
||||
Configuration::updateValue('TRAINING_FIXEDCODES_FILE', $file->name);
|
||||
$this->context->smarty->assign('used_fixedcodes_file', $file->name);
|
||||
$this->confirmations[] = $this->l(sprintf('Importation des codes clients corrigés réussie.'));
|
||||
}
|
||||
else {
|
||||
$this->errors[] = $this->l(sprintf('Echec de l\'importation des codes clients corrigés.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateBilan()
|
||||
{
|
||||
// check all required data are defined
|
||||
if (!TrainingPharmacy::hasAnyRow()) {
|
||||
$this->errors[] = $this->l(sprintf('Merci d\'importer un fichier csv pharmacies.'));
|
||||
}
|
||||
|
||||
if (!TrainingModule::hasAnyRow()) {
|
||||
$this->errors[] = $this->l(sprintf('Merci d\'ajouter les modules/catégories.'));
|
||||
}
|
||||
|
||||
$result_filename = Configuration::get('TRAINING_USED_RESULT_FILE');
|
||||
if (empty($result_filename)) {
|
||||
$this->errors[] = $this->l(sprintf('Merci d\'importer un fichier de résultats.'));
|
||||
}
|
||||
|
||||
if (0 == count($this->errors)) {
|
||||
|
||||
$d = new DateTime();
|
||||
$corrected_results_file = 'RESULTAT_CORRIGE_'.$d->format('Y-m-d_His').'.csv';
|
||||
$corrected_codes_file = 'CODES_CORRIGE_'.$d->format('Y-m-d_His').'.csv';
|
||||
$bilan_file = 'BILAN_'.$d->format('Y-m-d_His').'.csv';
|
||||
|
||||
$ok = TrainingBilan::generate(
|
||||
$this->path_files.$result_filename,
|
||||
$this->path_output_files.$corrected_results_file,
|
||||
$this->path_output_files.$bilan_file,
|
||||
$this->path_output_files.$corrected_codes_file
|
||||
);
|
||||
if ($ok) {
|
||||
Configuration::updateValue('TRAINING_BILAN_FILE', $bilan_file);
|
||||
Configuration::updateValue('TRAINING_CORRECTED_RESULT_FILE', $corrected_results_file);
|
||||
Configuration::updateValue('TRAINING_CORRECTED_CODES_FILE', $corrected_codes_file);
|
||||
|
||||
$this->context->smarty->assign('bilan_file', Configuration::get('TRAINING_BILAN_FILE'));
|
||||
$this->context->smarty->assign('corrected_results_file', Configuration::get('TRAINING_CORRECTED_RESULT_FILE'));
|
||||
$this->context->smarty->assign('corrected_codes_file', Configuration::get('TRAINING_CORRECTED_CODES_FILE'));
|
||||
|
||||
$this->confirmations[] = 'Génération des fichiers "bilan", "résultats corrigés" et "codes corrigés" réussie :
|
||||
<br />
|
||||
<br />
|
||||
<ul>
|
||||
<li>- Télécharger : <a href="'.$this->url_output_path.$bilan_file.'" target="_blank">'.$bilan_file.'</a></li>
|
||||
<li>- Télécharger : <a href="'.$this->url_output_path.$corrected_results_file.'" target="_blank">'.$corrected_results_file.'</a></li>
|
||||
<li>- Télécharger : <a href="'.$this->url_output_path.$corrected_codes_file.'" target="_blank">'.$corrected_codes_file.'</a></li>
|
||||
</ul>
|
||||
<br />
|
||||
Retrouvez les liens vers ces fichiers depuis l\'encadré BILAN / RESULTAT CORRIGÉ.
|
||||
';
|
||||
}
|
||||
else {
|
||||
$this->errors[] = $this->l(sprintf('Echec du traitement du fichier résultat.'));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function moveUploadedCsvFile($input_name)
|
||||
{
|
||||
if (!isset($_FILES[$input_name])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$f = (object)$_FILES[$input_name];
|
||||
$f->name = basename($f->name);
|
||||
$f->path = $this->path_files.$f->name;
|
||||
|
||||
$max_file_size = $this->getFileSizeMax();
|
||||
|
||||
if ($f->error === UPLOAD_ERR_OK) {
|
||||
if ($f->size > $max_file_size) {
|
||||
$f->error = UPLOAD_ERR_FORM_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if ($f->error !== UPLOAD_ERR_OK) {
|
||||
$this->errors[] = $this->codeToMessage($f, $max_file_size);
|
||||
return null;
|
||||
}
|
||||
|
||||
// ensure it is a CSV
|
||||
$path_parts = pathinfo($f->path);
|
||||
if (strtolower($path_parts['extension'])!='csv') {
|
||||
$this->errors[] = $this->l('Seul un fichier CSV est accepté (extension ".csv").');
|
||||
return null;
|
||||
}
|
||||
|
||||
$success = move_uploaded_file($f->tmp_name,$f->path);
|
||||
|
||||
if (!$success) {
|
||||
$this->errors[] = $this->l(sprintf('Echec lors l\'envoi du fichier %s.', $f->name));
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->confirmations[] = $this->l(sprintf('Envoi du fichier "%s" réussi.', $f->name));
|
||||
|
||||
return $f;
|
||||
}
|
||||
|
||||
private function getFileSizeMax()
|
||||
{
|
||||
$max_ini_file_size = ini_get('upload_max_filesize');
|
||||
if (preg_match('/G/i', $max_ini_file_size)) {
|
||||
$max_ini_file_size *= 1000000000;
|
||||
}
|
||||
if (preg_match('/M/i', $max_ini_file_size)) {
|
||||
$max_ini_file_size *= 1000000;
|
||||
}
|
||||
if (preg_match('/K/i', $max_ini_file_size)) {
|
||||
$max_ini_file_size *= 1000;
|
||||
}
|
||||
|
||||
$max_file_size = Tools::getValue('MAX_FILE_SIZE', $max_ini_file_size);
|
||||
if ($max_file_size > $max_ini_file_size) {
|
||||
$max_file_size = $max_ini_file_size;
|
||||
}
|
||||
|
||||
return $max_file_size;
|
||||
}
|
||||
|
||||
private function codeToMessage($file, $max_file_size)
|
||||
{
|
||||
switch ($file->error) {
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
$message = $this->l(sprintf('Erreur - Le fichier dépasse la taille maximale autorisée par le serveur : %d octets.', $max_file_size));
|
||||
break;
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
$message = $this->l(sprintf('Erreur - Le fichier dépasse la taille maximale autorisée : %d octets.', $max_file_size));
|
||||
break;
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
$message = $this->l('Erreur - Le fichier a été envoyé partiellement.');
|
||||
break;
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
$message = $this->l('Erreur - Merci de sélectionner un fichier.');
|
||||
break;
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
$message = $this->l("Erreur serveur - répertoire temporaire manquant.");
|
||||
break;
|
||||
case UPLOAD_ERR_CANT_WRITE:
|
||||
$message = $this->l("Erreur serveur - répertoire non autorisé en écriture.");
|
||||
break;
|
||||
case UPLOAD_ERR_EXTENSION:
|
||||
$message = $this->l("Erreur - Type de fichier refusé.");
|
||||
break;
|
||||
|
||||
default:
|
||||
$message = $this->l("Erreur serveur - Erreur inconnue.");
|
||||
break;
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
class AdminTrainingFixedCodeClientController extends ModuleAdminController
|
||||
{
|
||||
public function __construct() {
|
||||
|
||||
$this->table = 'training_fixedcodeclient';
|
||||
$this->className = 'TrainingFixedCodeClient';
|
||||
$this->identifier = 'id_training_fixedcodeclient';
|
||||
$this->lang = FALSE;
|
||||
$this->deleted = FALSE;
|
||||
$this->bootstrap = TRUE;
|
||||
$this->_defaultOrderBy = 'codeclient_from_results';
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->actions = array('edit');
|
||||
|
||||
$this->fields_list = array(
|
||||
'id_training_fixedcodeclient' => array(
|
||||
'title' => 'ID',
|
||||
'width' => 40
|
||||
),
|
||||
'codeclient_from_results' => array(
|
||||
'title' => $this->module->l('Code client trouvé dans les résultats'),
|
||||
'width' => 200,
|
||||
),
|
||||
'codeclient_fixed' => array(
|
||||
'title' => $this->module->l('Code client corrigé'),
|
||||
'width' => 150,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function renderView()
|
||||
{
|
||||
return $this->renderList();
|
||||
}
|
||||
|
||||
public function renderForm()
|
||||
{
|
||||
$this->fields_form = array(
|
||||
'submit' => array(
|
||||
'name' => 'submitTrainingFixedCodeClient',
|
||||
'title' => $this->l('Save'),
|
||||
),
|
||||
'input' => array(
|
||||
array(
|
||||
'type' => 'text',
|
||||
'label' => $this->l('Code client trouvé dans les résultats'),
|
||||
'name' => 'codeclient_from_results',
|
||||
'required' => TRUE,
|
||||
'lang' => FALSE,
|
||||
'size' => 128
|
||||
),
|
||||
array(
|
||||
'type' => 'text',
|
||||
'label' => $this->l('Code client corrigé'),
|
||||
'name' => 'codeclient_fixed',
|
||||
'required' => TRUE,
|
||||
'lang' => FALSE,
|
||||
'size' => 128
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
return parent::renderForm();
|
||||
}
|
||||
}
|
69
modules/training/controllers/admin/AdminTrainingModules.php
Normal file
69
modules/training/controllers/admin/AdminTrainingModules.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
class AdminTrainingModulesController extends ModuleAdminController
|
||||
{
|
||||
public function __construct() {
|
||||
|
||||
$this->table = 'training_module';
|
||||
$this->className = 'TrainingModule';
|
||||
$this->identifier = 'id_training_module';
|
||||
$this->lang = FALSE;
|
||||
$this->deleted = FALSE;
|
||||
$this->bootstrap = TRUE;
|
||||
$this->_defaultOrderBy = 'category_name';
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->actions = array('edit', 'delete');
|
||||
|
||||
$this->fields_list = array(
|
||||
'id_training_module' => array(
|
||||
'title' => 'ID',
|
||||
'width' => 40
|
||||
),
|
||||
'module_name' => array(
|
||||
'title' => $this->module->l('Module'),
|
||||
'width' => 150,
|
||||
),
|
||||
'category_name' => array(
|
||||
'title' => $this->module->l('Catégorie'),
|
||||
'width' => 150,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function renderView()
|
||||
{
|
||||
return $this->renderList();
|
||||
}
|
||||
|
||||
public function renderForm()
|
||||
{
|
||||
$this->fields_form = array(
|
||||
'submit' => array(
|
||||
'name' => 'submitTrainingModule',
|
||||
'title' => $this->l('Save'),
|
||||
),
|
||||
'input' => array(
|
||||
array(
|
||||
'type' => 'text',
|
||||
'label' => $this->l('Libellé module'),
|
||||
'name' => 'module_name',
|
||||
'required' => TRUE,
|
||||
'lang' => FALSE,
|
||||
'size' => 128
|
||||
),
|
||||
array(
|
||||
'type' => 'text',
|
||||
'label' => $this->l('Libellé catégorie'),
|
||||
'name' => 'category_name',
|
||||
'required' => TRUE,
|
||||
'lang' => FALSE,
|
||||
'size' => 128
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
return parent::renderForm();
|
||||
}
|
||||
}
|
35
modules/training/controllers/admin/AdminTrainingPharmacy.php
Normal file
35
modules/training/controllers/admin/AdminTrainingPharmacy.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
class AdminTrainingPharmacyController extends ModuleAdminController
|
||||
{
|
||||
public function __construct() {
|
||||
|
||||
$this->table = 'training_pharmacy';
|
||||
$this->className = 'TrainingPharmacy';
|
||||
$this->identifier = 'id_training_pharmacy';
|
||||
$this->lang = FALSE;
|
||||
$this->deleted = FALSE;
|
||||
$this->bootstrap = TRUE;
|
||||
$this->_defaultOrderBy = 'codeclient';
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->actions = array();
|
||||
|
||||
$this->fields_list = array(
|
||||
'id_training_pharmacy' => array(
|
||||
'title' => 'ID',
|
||||
'width' => 40
|
||||
),
|
||||
'codeclient' => array(
|
||||
'title' => $this->module->l('Code client'),
|
||||
'width' => 150,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function renderView()
|
||||
{
|
||||
return $this->renderList();
|
||||
}
|
||||
}
|
35
modules/training/index.php
Normal file
35
modules/training/index.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/*
|
||||
* 2007-2013 PrestaShop
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License (AFL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://opensource.org/licenses/afl-3.0.php
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to http://www.prestashop.com for more information.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright 2007-2013 PrestaShop SA
|
||||
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
|
||||
* International Registered Trademark & Property of PrestaShop SA
|
||||
*/
|
||||
|
||||
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
|
||||
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
|
||||
|
||||
header("Cache-Control: no-store, no-cache, must-revalidate");
|
||||
header("Cache-Control: post-check=0, pre-check=0", false);
|
||||
header("Pragma: no-cache");
|
||||
|
||||
header("Location: ../");
|
||||
exit;
|
BIN
modules/training/logo.gif
Normal file
BIN
modules/training/logo.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 778 B |
BIN
modules/training/logo.png
Normal file
BIN
modules/training/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
46
modules/training/models/ArrayExtended.php
Normal file
46
modules/training/models/ArrayExtended.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
class ArrayExtended implements IteratorAggregate, ArrayAccess
|
||||
{
|
||||
private $collection;
|
||||
|
||||
function __construct(array $ar = array())
|
||||
{
|
||||
$this->collection = $ar;
|
||||
}
|
||||
|
||||
function toArray()
|
||||
{
|
||||
return $this->collection;
|
||||
}
|
||||
|
||||
function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->collection);
|
||||
}
|
||||
|
||||
function offsetExists($offset)
|
||||
{
|
||||
return isset($this->collection[$offset]);
|
||||
}
|
||||
|
||||
function offsetSet($offset, $value)
|
||||
{
|
||||
if (is_null($offset)) {
|
||||
$this->collection[] = $value;
|
||||
}
|
||||
elseif (!empty($offset)) {
|
||||
$this->collection[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
function offsetUnset($offset)
|
||||
{
|
||||
unset($this->collection[$offset]);
|
||||
}
|
||||
|
||||
function offsetGet($offset)
|
||||
{
|
||||
return isset($this->collection[$offset])?$this->collection[$offset]:null;
|
||||
}
|
||||
}
|
||||
|
140
modules/training/models/CSVReader.php
Normal file
140
modules/training/models/CSVReader.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
require_once(__DIR__.'/ArrayExtended.php');
|
||||
class CSVReader
|
||||
{
|
||||
private $file_path = '';
|
||||
private $params = array('enable_skip_line' => false, 'auto_detect'=>true, 'separator' => ',', 'enclosure' => '"', 'expected_header' => array());
|
||||
private $handlers = array('start' => null, 'error' => null);
|
||||
private $current_num_line = 0;
|
||||
private $expected_cols_index = array();
|
||||
|
||||
function __construct($file_path)
|
||||
{
|
||||
$this->file_path = $file_path;
|
||||
$this->handlers = array(
|
||||
'start' => function() {},
|
||||
'error' => function($failed_line_number) {},
|
||||
'readline' => function(ArrayExtended $cols) { return true; }
|
||||
);
|
||||
}
|
||||
|
||||
function setExpectedHeader(array $expected_header)
|
||||
{
|
||||
$this->params['expected_header'] = $expected_header;
|
||||
return $this;
|
||||
}
|
||||
|
||||
function enableSkipHeader()
|
||||
{
|
||||
$this->params['enable_skip_line'] = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// func : function()
|
||||
function setOnStart(callable $func)
|
||||
{
|
||||
$this->handlers['start'] = $func;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// func : function(ArrayExtended $columns)
|
||||
function setOnEachLine(callable $func)
|
||||
{
|
||||
$this->handlers['readline'] = $func;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// func : function(int failed_line_number)
|
||||
function setOnError(callable $func)
|
||||
{
|
||||
$this->handlers['error'] = $func;
|
||||
return $this;
|
||||
}
|
||||
|
||||
function readLines()
|
||||
{
|
||||
$ok = false;
|
||||
$this->current_num_line = 0;
|
||||
|
||||
if ($this->params['auto_detect']) {
|
||||
$this->autoDetect($this->file_path);
|
||||
}
|
||||
|
||||
$f = fopen($this->file_path, 'r');
|
||||
if ($f) {
|
||||
call_user_func($this->handlers['start']);
|
||||
|
||||
$ok = true;
|
||||
if ($this->params['enable_skip_line'] || count($this->params['expected_header'])>0) {
|
||||
$ok = $this->checkHeader(fgetcsv($f, 0, $this->params['separator'], $this->params['enclosure']));
|
||||
}
|
||||
|
||||
while ($ok && ($cols = fgetcsv($f, 0, $this->params['separator'], $this->params['enclosure']))) {
|
||||
$real_cols = array();
|
||||
|
||||
foreach($this->expected_cols_index as $real_index => $index) {
|
||||
$real_cols[$index] = $cols[$real_index];
|
||||
}
|
||||
$ok = call_user_func($this->handlers['readline'], new ArrayExtended($real_cols));
|
||||
$this->current_num_line++;
|
||||
}
|
||||
|
||||
fclose($f);
|
||||
}
|
||||
|
||||
if (!$ok) {
|
||||
call_user_func($this->handlers['error'], $this->current_num_line);
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
private function checkHeader(array $cols)
|
||||
{
|
||||
$ok = true;
|
||||
|
||||
foreach($this->params['expected_header'] as $expected_col => $expected_pos) {
|
||||
$found = false;
|
||||
foreach($cols as $index => $col) {
|
||||
if (strtolower($col) == strtolower($expected_col)) {
|
||||
$this->expected_cols_index[$index] = $expected_pos;
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$ok = $ok && $found;
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
private function autoDetect($file_path)
|
||||
{
|
||||
if ($f = fopen($file_path, 'r')) {
|
||||
$line = fgets($f);
|
||||
|
||||
if ($line[0] == '"') {
|
||||
$line[0] = '';
|
||||
$line = str_replace("\\\"", '', $line);
|
||||
$line = str_replace("\"\"", '', $line);
|
||||
$line = strstr($line, '"');
|
||||
if (strlen($line)>1) {
|
||||
$this->params['separator'] = $line[1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (strpos($line, ';')!==false) {
|
||||
$this->params['separator'] = ';';
|
||||
}
|
||||
elseif (strpos($line, ',')!==false) {
|
||||
$this->params['separator'] = ',';
|
||||
}
|
||||
elseif (strpos($line, '|')!==false) {
|
||||
$this->params['separator'] = '|';
|
||||
}
|
||||
}
|
||||
|
||||
fclose($f);
|
||||
}
|
||||
}
|
||||
}
|
107
modules/training/models/CSVWriter.php
Normal file
107
modules/training/models/CSVWriter.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
class CSVWriter
|
||||
{
|
||||
const SAVE = 1;
|
||||
const OUTPUT_FOR_DOWNLOAD = 2;
|
||||
|
||||
private $file_path;
|
||||
private $buffer;
|
||||
private $header = null;
|
||||
private $footer = null;
|
||||
private $lines = array();
|
||||
|
||||
function __construct($file_path)
|
||||
{
|
||||
$this->file_path = $file_path;
|
||||
}
|
||||
|
||||
function setHeader(array $line)
|
||||
{
|
||||
$this->header = $line;
|
||||
return $this;
|
||||
}
|
||||
|
||||
function addLine(array $line)
|
||||
{
|
||||
$this->lines[] = $line;
|
||||
return $this;
|
||||
}
|
||||
|
||||
function setFooter(array $line)
|
||||
{
|
||||
$this->footer = $line;
|
||||
return $this;
|
||||
}
|
||||
|
||||
function sortBy($index_column, $ascendant=true)
|
||||
{
|
||||
usort($this->lines,
|
||||
function(array $a, array $b) use ($index_column, $ascendant) {
|
||||
if ($a[$index_column] < $b[$index_column]) {
|
||||
return $ascendant?-1:1;
|
||||
}
|
||||
else if ($a[$index_column] > $b[$index_column]) {
|
||||
return $ascendant?1:-1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
function save()
|
||||
{
|
||||
if (empty($this->buffer)) {
|
||||
$this->flush();
|
||||
}
|
||||
|
||||
$f = fopen($this->file_path, 'w');
|
||||
if ($f) {
|
||||
fwrite($f, $this->buffer);
|
||||
fclose($f);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
function outputForDownload()
|
||||
{
|
||||
if (empty($this->buffer)) {
|
||||
$this->flush();
|
||||
}
|
||||
|
||||
header("Pragma: public");
|
||||
header("Expires: 0");
|
||||
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
|
||||
header("Cache-Control: private",false);
|
||||
header("Content-Type: text/csv");
|
||||
header("Content-Disposition: attachment; filename=\"".basename($this->file_path)."\";" );
|
||||
header("Content-Transfer-Encoding: binary");
|
||||
exit($this->buffer);
|
||||
}
|
||||
|
||||
private function flush()
|
||||
{
|
||||
$output = fopen('php://output', 'w');
|
||||
|
||||
ob_start();
|
||||
|
||||
if (is_array($this->header)) {
|
||||
fwrite($output, implode(',', $this->header).PHP_EOL);
|
||||
}
|
||||
|
||||
foreach($this->lines as $line) {
|
||||
fwrite($output, implode(',', $line).PHP_EOL);
|
||||
}
|
||||
|
||||
if (is_array($this->footer)) {
|
||||
fwrite($output, implode(',', $this->footer).PHP_EOL);
|
||||
}
|
||||
|
||||
$this->buffer = ob_get_clean();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
207
modules/training/models/TrainingBilan.php
Normal file
207
modules/training/models/TrainingBilan.php
Normal file
@ -0,0 +1,207 @@
|
||||
<?php
|
||||
require_once __DIR__.'/CSVReader.php';
|
||||
require_once __DIR__.'/CSVWriter.php';
|
||||
require_once __DIR__.'/TrainingModule.php';
|
||||
require_once __DIR__.'/TrainingPharmacy.php';
|
||||
require_once __DIR__.'/TrainingFixedCodeClient.php';
|
||||
|
||||
class TrainingBilan
|
||||
{
|
||||
|
||||
const TOTAL_MARGIN = 2;
|
||||
|
||||
const COL_MODULE_NAME = 1;
|
||||
const COL_CODE_CLIENT = 2;
|
||||
const COL_FIRSTNAME = 3;
|
||||
const COL_LASTNAME = 4;
|
||||
const COL_TEL = 5;
|
||||
const COL_PHARMACY = 6;
|
||||
const COL_CITY = 7;
|
||||
const COL_ATTEMPT_DATE = 8;
|
||||
const COL_TOTAL_VIEWED = 9;
|
||||
const COL_TOTAL_SLIDES = 10;
|
||||
const COL_VALIDE = 11;
|
||||
|
||||
const OUTPUT_COLUMN_CODECLIENT = 0;
|
||||
|
||||
static private $bilan = array();
|
||||
static private $pharmacies_notfound = array();
|
||||
static private $modules = null;
|
||||
static private $pharmacies = null;
|
||||
static private $pharmacies_fixeds = null;
|
||||
static private $results_corrected = array();
|
||||
|
||||
static function generate(
|
||||
$result_file_path,
|
||||
$output_corrected_result_file_path,
|
||||
$output_bilan_file_path,
|
||||
$output_corrected_codes_file_path
|
||||
)
|
||||
{
|
||||
// Define the expected header of the result file
|
||||
$expectedHeader = array(
|
||||
'Module' => self::COL_MODULE_NAME,
|
||||
'Code Client' => self::COL_CODE_CLIENT,
|
||||
'Prenom' => self::COL_FIRSTNAME,
|
||||
'NOM' => self::COL_LASTNAME,
|
||||
'Telephone' => self::COL_TEL,
|
||||
'Nom pharmacie' => self::COL_PHARMACY,
|
||||
'Ville' => self::COL_CITY,
|
||||
'Date' => self::COL_ATTEMPT_DATE,
|
||||
'Slides Vues' => self::COL_TOTAL_VIEWED,
|
||||
'Total Slides' => self::COL_TOTAL_SLIDES,
|
||||
);
|
||||
|
||||
self::$bilan = array();
|
||||
self::$pharmacies_notfound = array();
|
||||
|
||||
self::$modules = TrainingModule::loadAll();
|
||||
self::$pharmacies = TrainingPharmacy::loadAll();
|
||||
self::$pharmacies_fixeds = TrainingFixedCodeClient::loadOnlyFixed();
|
||||
|
||||
|
||||
// Read the imported result file
|
||||
$reader = new CSVReader($result_file_path);
|
||||
$ok = $reader->enableSkipHeader()
|
||||
->setExpectedHeader($expectedHeader)
|
||||
->setOnEachLine("TrainingBilan::computeLineResult")
|
||||
->readLines();
|
||||
|
||||
if (!$ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add all (maybe new) incorrect codes
|
||||
TrainingFixedCodeClient::addIfNotExist(self::$pharmacies_notfound);
|
||||
|
||||
// Export the correct result file in the same format (with 1 additionnal column)
|
||||
$newHeader = $expectedHeader + array('Validé' => self::COL_VALIDE);
|
||||
$csv_output = new CSVWriter($output_corrected_result_file_path);
|
||||
self::exportCorrectedResults($newHeader, $csv_output);
|
||||
$csv_output->save();
|
||||
|
||||
// Export the corrected codes file in the same format of the codes clients file imported in class TrainingFixedCodeClient
|
||||
TrainingFixedCodeClient::exportIntoCsvFile($output_corrected_codes_file_path);
|
||||
|
||||
// Export the "bilan"
|
||||
$csv_output = new CSVWriter($output_bilan_file_path);
|
||||
self::exportResults($csv_output);
|
||||
$csv_output->sortBy(self::OUTPUT_COLUMN_CODECLIENT);
|
||||
$csv_output->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static function computeLineResult(ArrayExtended $cols)
|
||||
{
|
||||
$code_client = strtolower($cols[self::COL_CODE_CLIENT]);
|
||||
$low_module_name = strtolower($cols[self::COL_MODULE_NAME]);
|
||||
|
||||
if (empty($code_client)) {
|
||||
$code_client = TrainingFixedCodeClient::CODE_EMPTY;
|
||||
}
|
||||
|
||||
// for the current code found in result file,
|
||||
// retrive the corrected one if any
|
||||
// or add it in the list of not found ones
|
||||
if (!isset(self::$pharmacies[$code_client])) {
|
||||
|
||||
if (!isset(self::$pharmacies_fixeds[$code_client])) {
|
||||
self::$pharmacies_notfound[$code_client] = 1;
|
||||
// continue reading CSV
|
||||
// don't add this code into the corrected result list
|
||||
return true;
|
||||
}
|
||||
|
||||
$code_client = self::$pharmacies_fixeds[$code_client];
|
||||
}
|
||||
|
||||
// create the line in the bilan
|
||||
if (!isset(self::$bilan[$code_client])) {
|
||||
self::$bilan[$code_client] = array();
|
||||
}
|
||||
|
||||
if (!isset(self::$bilan[$code_client][$low_module_name])) {
|
||||
self::$bilan[$code_client][$low_module_name] = 0;
|
||||
}
|
||||
|
||||
// update the "success" total for the current code
|
||||
if (($cols[self::COL_TOTAL_SLIDES] - $cols[self::COL_TOTAL_VIEWED]) <= self::TOTAL_MARGIN) {
|
||||
self::$bilan[$code_client][$low_module_name]++;
|
||||
$cols[self::COL_VALIDE] = 'oui';
|
||||
}
|
||||
else {
|
||||
$cols[self::COL_VALIDE] = 'non';
|
||||
}
|
||||
|
||||
// add the result line in the corrected results list (with the corrected code)
|
||||
$cols[self::COL_CODE_CLIENT] = $code_client;
|
||||
self::$results_corrected[] = $cols->toArray();
|
||||
|
||||
// continue reading CSV
|
||||
return true;
|
||||
}
|
||||
|
||||
static function exportCorrectedResults(array $header, CSVWriter $csv_output)
|
||||
{
|
||||
$csv_output->setHeader(array_keys($header));
|
||||
foreach(self::$results_corrected as $row) {
|
||||
$csv_output->addLine($row);
|
||||
}
|
||||
}
|
||||
|
||||
static function exportResults(CSVWriter $csv_output)
|
||||
{
|
||||
// init total and export header
|
||||
$total = array();
|
||||
$row = array('Codes clients');
|
||||
foreach (self::$modules as $category_name => $category_modules) {
|
||||
foreach ($category_modules as $module_name) {
|
||||
$row[] = $module_name;
|
||||
$total[$module_name] = 0;
|
||||
}
|
||||
$row[] = 'Module '.$category_name;
|
||||
$total[$category_name] = 0;
|
||||
}
|
||||
$csv_output->setHeader($row);
|
||||
|
||||
// export lines
|
||||
foreach (self::$bilan as $code_client => $results) {
|
||||
$row = array($code_client);
|
||||
foreach (self::$modules as $category_name => $category_modules) {
|
||||
|
||||
$result_min = -1;
|
||||
foreach ($category_modules as $module_name) {
|
||||
|
||||
$low_module_name = strtolower($module_name);
|
||||
if (!isset($results[$low_module_name])) {
|
||||
$result_min = 0;
|
||||
$result = 0;
|
||||
}
|
||||
else {
|
||||
if ($result_min == -1 || $result_min > $results[$low_module_name]) {
|
||||
$result_min = $results[$low_module_name];
|
||||
}
|
||||
$result = $results[$low_module_name];
|
||||
}
|
||||
$row[] = $result;
|
||||
$total[$module_name] += $result;
|
||||
}
|
||||
|
||||
$row[] = $result_min;
|
||||
$total[$category_name] += $result_min;
|
||||
}
|
||||
$csv_output->addLine($row);
|
||||
}
|
||||
|
||||
// export total
|
||||
$row = array('Total général');
|
||||
foreach (self::$modules as $category_name => $category_modules) {
|
||||
foreach ($category_modules as $module_name) {
|
||||
$row[] = $total[$module_name];
|
||||
}
|
||||
$row[] = $total[$category_name];
|
||||
}
|
||||
$csv_output->setFooter($row);
|
||||
}
|
||||
}
|
158
modules/training/models/TrainingFixedCodeClient.php
Normal file
158
modules/training/models/TrainingFixedCodeClient.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
require_once(__DIR__.'/ArrayExtended.php');
|
||||
|
||||
class TrainingFixedCodeClient extends ObjectModel
|
||||
{
|
||||
public $id_training_fixedcodeclient;
|
||||
public $codeclient_from_results;
|
||||
public $codeclient_fixed;
|
||||
|
||||
const CODE_CLIENT_WRONG = 1;
|
||||
const CODE_CLIENT_RIGHT = 2;
|
||||
const CODE_EMPTY = "__code_vide__";
|
||||
|
||||
private static $header = array(
|
||||
'CC faux'=>self::CODE_CLIENT_WRONG,
|
||||
'CC bon'=>self::CODE_CLIENT_RIGHT
|
||||
);
|
||||
|
||||
public static $definition = array(
|
||||
'table' => 'training_fixedcodeclient',
|
||||
'primary' => 'id_training_fixedcodeclient',
|
||||
'multilang' => false,
|
||||
'fields' => array(
|
||||
'id_training_fixedcodeclient' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
|
||||
'codeclient_from_results' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isString'),
|
||||
'codeclient_fixed' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isString'),
|
||||
),
|
||||
);
|
||||
|
||||
static function exportIntoCsvFile($file_path)
|
||||
{
|
||||
$csv_output = new CSVWriter($file_path);
|
||||
|
||||
$csv_output->setHeader(array_keys(self::$header));
|
||||
|
||||
$sql = 'SELECT `codeclient_from_results`, `codeclient_fixed`
|
||||
FROM `'._DB_PREFIX_.'training_fixedcodeclient`
|
||||
';
|
||||
foreach (Db::getInstance()->executeS($sql) as $row) {
|
||||
$csv_output->addLine(array($row['codeclient_from_results'], $row['codeclient_fixed']));
|
||||
}
|
||||
|
||||
$csv_output->save();
|
||||
}
|
||||
|
||||
static function importFromCsvFile($file_path)
|
||||
{
|
||||
$imported_codes = array();
|
||||
|
||||
$reader = new CSVReader($file_path);
|
||||
$ok = $reader
|
||||
->setExpectedHeader(self::$header)
|
||||
->setOnEachLine(function(ArrayExtended $cols) use (&$imported_codes) {
|
||||
$wrong_code = strtolower((string)$cols[self::CODE_CLIENT_WRONG]);
|
||||
$right_code = strtolower((string)$cols[self::CODE_CLIENT_RIGHT]);
|
||||
|
||||
if (empty($wrong_code)) {
|
||||
$wrong_code = self::CODE_EMPTY;
|
||||
}
|
||||
$imported_codes[$wrong_code] = $right_code;
|
||||
return true;
|
||||
})
|
||||
->readLines();
|
||||
|
||||
if ($ok && count($imported_codes)>0) {
|
||||
|
||||
$existing_codes = array();
|
||||
|
||||
// load all already saved codes
|
||||
foreach (Db::getInstance()->executeS('
|
||||
SELECT `codeclient_from_results`, `codeclient_fixed`
|
||||
FROM `'._DB_PREFIX_.'training_fixedcodeclient`
|
||||
') as $row) {
|
||||
$existing_codes[$row['codeclient_from_results']] = $row['codeclient_fixed'];
|
||||
}
|
||||
|
||||
// insert new wrong codes with their correction
|
||||
$values = array();
|
||||
foreach(array_diff_key($imported_codes, $existing_codes) as $wrong_code => $right_code) {
|
||||
$values[] = '(\''.pSql($wrong_code).'\', \''.pSql($right_code).'\')';
|
||||
}
|
||||
|
||||
if (count($values)>0) {
|
||||
$ok = Db::getInstance()->execute('
|
||||
INSERT INTO `'._DB_PREFIX_.'training_fixedcodeclient`
|
||||
(`codeclient_from_results`, `codeclient_fixed`)
|
||||
VALUES
|
||||
'.implode(',', $values).'
|
||||
');
|
||||
}
|
||||
|
||||
// replace existing uncorrected code with their correction
|
||||
foreach(array_intersect_key($imported_codes, $existing_codes) as $wrong_code => $right_code) {
|
||||
$ok = $ok && Db::getInstance()->execute('
|
||||
UPDATE `'._DB_PREFIX_.'training_fixedcodeclient`
|
||||
SET `codeclient_fixed` = \''.pSql($right_code).'\'
|
||||
WHERE `codeclient_from_results` = \''.pSql($wrong_code).'\'
|
||||
');
|
||||
}
|
||||
}
|
||||
unset($imported_codes);
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
static function loadOnlyFixed()
|
||||
{
|
||||
$result = new ArrayExtended();
|
||||
|
||||
$sql = 'SELECT `codeclient_from_results`, `codeclient_fixed`
|
||||
FROM `'._DB_PREFIX_.'training_fixedcodeclient`
|
||||
WHERE codeclient_fixed <> \'\'';
|
||||
foreach (Db::getInstance()->executeS($sql) as $row) {
|
||||
$result[$row['codeclient_from_results']] = $row['codeclient_fixed'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
static function addIfNotExist(array $pharmacies_not_found)
|
||||
{
|
||||
// get already saved codes to correct
|
||||
$existing_codes = array();
|
||||
|
||||
$sql = 'SELECT LOWER(`codeclient_from_results`) as `codeclient_from_results`
|
||||
FROM `'._DB_PREFIX_.'training_fixedcodeclient`';
|
||||
foreach (Db::getInstance()->executeS($sql) as $row) {
|
||||
$existing_codes[] = $row['codeclient_from_results'];
|
||||
}
|
||||
|
||||
// remove duplicate keys
|
||||
$unique_pharmacies_not_found = array();
|
||||
foreach ($pharmacies_not_found as $key => $val) {
|
||||
$key = strtolower($key);
|
||||
if (!isset($unique_pharmacies_not_found[$key])) {
|
||||
$unique_pharmacies_not_found[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
$unique_pharmacies_not_found = array_keys($unique_pharmacies_not_found);
|
||||
|
||||
// filter new codes not already present in DB (and create the SQL value string)
|
||||
$values = array();
|
||||
foreach(array_diff($unique_pharmacies_not_found, $existing_codes) as $code_client) {
|
||||
$values[] = '\''.pSql((string)$code_client).'\', \'\'';
|
||||
}
|
||||
|
||||
// if any new codes, add them
|
||||
if (count($values)>0) {
|
||||
Db::getInstance()->execute(
|
||||
'INSERT INTO `'._DB_PREFIX_.'training_fixedcodeclient`
|
||||
(codeclient_from_results, codeclient_fixed)
|
||||
VALUES
|
||||
('.implode('),(', $values).')'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
88
modules/training/models/TrainingModule.php
Normal file
88
modules/training/models/TrainingModule.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
require_once __DIR__.'/CSVReader.php';
|
||||
|
||||
class TrainingModule extends ObjectModel
|
||||
{
|
||||
const MODULE_NAME = 0;
|
||||
const CATEGORY_NAME = 1;
|
||||
|
||||
public $id_training_module;
|
||||
public $module_name;
|
||||
public $category_name;
|
||||
|
||||
public static $definition = array(
|
||||
'table' => 'training_module',
|
||||
'primary' => 'id_training_module',
|
||||
'multilang' => false,
|
||||
'fields' => array(
|
||||
'id_training_module' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
|
||||
'module_name' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isString'),
|
||||
'category_name' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isString'),
|
||||
),
|
||||
);
|
||||
|
||||
private static $failed_line_number = 0;
|
||||
|
||||
static function importFromCsvFile($file_path)
|
||||
{
|
||||
$values = array();
|
||||
|
||||
$reader = new CSVReader($file_path);
|
||||
$ok = $reader
|
||||
->enableSkipHeader()
|
||||
->setExpectedHeader(array(
|
||||
'Modules' => self::MODULE_NAME,
|
||||
'Catégories'=>self::CATEGORY_NAME
|
||||
))
|
||||
->setOnStart(function() {
|
||||
self::deleteAll();
|
||||
})
|
||||
->setOnEachLine(function(ArrayExtended $cols) use (&$values) {
|
||||
$values[] = '\''.pSql((string)$cols[self::MODULE_NAME]).'\', \''.pSql((string)$cols[self::CATEGORY_NAME]).'\'';
|
||||
return true;
|
||||
})
|
||||
->setOnError(function($failed_line_number) {
|
||||
self::$failed_line_number = $failed_line_number;
|
||||
self::deleteAll();
|
||||
})
|
||||
->readLines();
|
||||
|
||||
if ($ok && count($values)>0) {
|
||||
$ok = Db::getInstance()->execute('
|
||||
INSERT INTO `'._DB_PREFIX_.'training_module`
|
||||
(`module_name`, `category_name`)
|
||||
VALUES
|
||||
('.implode('),(', $values).')
|
||||
');
|
||||
}
|
||||
unset($values);
|
||||
return $ok;
|
||||
}
|
||||
|
||||
static function deleteAll()
|
||||
{
|
||||
Db::getInstance()->execute('TRUNCATE `'._DB_PREFIX_.'training_module`');
|
||||
}
|
||||
|
||||
static function hasAnyRow()
|
||||
{
|
||||
return Db::getInstance()->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.'training_module`') > 0;
|
||||
}
|
||||
|
||||
static function loadAll()
|
||||
{
|
||||
$result = array();
|
||||
|
||||
$sql = 'SELECT `module_name`, `category_name`
|
||||
FROM `'._DB_PREFIX_.'training_module`
|
||||
ORDER BY `category_name` ASC, `module_name` ASC';
|
||||
foreach (Db::getInstance()->executeS($sql) as $row) {
|
||||
if (!isset($result[$row['category_name']])) {
|
||||
$result[$row['category_name']] = array();
|
||||
}
|
||||
$result[$row['category_name']][] = $row['module_name'];
|
||||
}
|
||||
|
||||
return new ArrayExtended($result);
|
||||
}
|
||||
}
|
81
modules/training/models/TrainingPharmacy.php
Normal file
81
modules/training/models/TrainingPharmacy.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
require_once(__DIR__.'/CSVReader.php');
|
||||
|
||||
class TrainingPharmacy extends ObjectModel
|
||||
{
|
||||
const CODE_CLIENT = 1;
|
||||
|
||||
public $id_training_pharmacy;
|
||||
public $codeclient;
|
||||
|
||||
public static $definition = array(
|
||||
'table' => 'training_pharmacy',
|
||||
'primary' => 'id_training_pharmacy',
|
||||
'multilang' => false,
|
||||
'fields' => array(
|
||||
'id_training_pharmacy' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
|
||||
'codeclient' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isString'),
|
||||
),
|
||||
);
|
||||
|
||||
private static $failed_line_number = 0;
|
||||
|
||||
static function importFromCsvFile($file_path)
|
||||
{
|
||||
$values = array();
|
||||
|
||||
$reader = new CSVReader($file_path);
|
||||
$ok = $reader
|
||||
->enableSkipHeader()
|
||||
->setExpectedHeader(array(
|
||||
'CodeClt'=>self::CODE_CLIENT
|
||||
))
|
||||
->setOnStart(function() {
|
||||
self::deleteAll();
|
||||
})
|
||||
->setOnEachLine(function(ArrayExtended $cols) use (&$values) {
|
||||
$values[] = '\''.pSql((string)$cols[self::CODE_CLIENT]).'\'';
|
||||
return true;
|
||||
})
|
||||
->setOnError(function($failed_line_number) {
|
||||
self::$failed_line_number = $failed_line_number;
|
||||
self::deleteAll();
|
||||
})
|
||||
->readLines();
|
||||
|
||||
if ($ok && count($values)>0) {
|
||||
$ok = Db::getInstance()->execute('
|
||||
INSERT INTO `'._DB_PREFIX_.'training_pharmacy`
|
||||
(`codeclient`)
|
||||
VALUES
|
||||
('.implode('),(', $values).')
|
||||
');
|
||||
}
|
||||
unset($values);
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
static function deleteAll()
|
||||
{
|
||||
Db::getInstance()->execute('TRUNCATE `'._DB_PREFIX_.'training_pharmacy`');
|
||||
}
|
||||
|
||||
static function hasAnyRow()
|
||||
{
|
||||
return Db::getInstance()->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.'training_pharmacy`') > 0;
|
||||
}
|
||||
|
||||
static function loadAll()
|
||||
{
|
||||
$result = new ArrayExtended();
|
||||
|
||||
$sql = 'SELECT `codeclient`, `id_training_pharmacy`
|
||||
FROM `'._DB_PREFIX_.'training_pharmacy`';
|
||||
foreach (Db::getInstance()->executeS($sql) as $row) {
|
||||
$result[$row['codeclient']] = $row['id_training_pharmacy'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
68
modules/training/test.php
Normal file
68
modules/training/test.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once(__DIR__.'/models/CSVReader.php');
|
||||
|
||||
function debug($var)
|
||||
{
|
||||
echo "<pre>";
|
||||
print_r($var);
|
||||
echo "</pre>";
|
||||
}
|
||||
|
||||
$original = new ArrayExtended();
|
||||
$original_score = new ArrayExtended();
|
||||
|
||||
$reader = new CSVReader(__DIR__.'/upload/Bilan_original.csv');
|
||||
$reader ->skipHeader()
|
||||
->onEachLine(function(ArrayExtended $cols) use($original, $original_score) {
|
||||
$original[] = $cols[0];
|
||||
$original_score[] = $cols[1];
|
||||
return true;
|
||||
})
|
||||
->onError(function($failed_num_line) {
|
||||
debug($failed_num_line);
|
||||
})
|
||||
->read();
|
||||
|
||||
$new_version = new ArrayExtended();
|
||||
$new_version_score = new ArrayExtended();
|
||||
|
||||
$reader = new CSVReader(__DIR__.'/upload/BILAN.csv');
|
||||
$reader ->skipHeader()
|
||||
->setDelimiter("\"")
|
||||
->onEachLine(function(ArrayExtended $cols) use($new_version, $new_version_score) {
|
||||
$new_version[] = $cols[0];
|
||||
$new_version_score[] = $cols[1];
|
||||
return true;
|
||||
})
|
||||
->onError(function($failed_num_line) {
|
||||
debug($failed_num_line);
|
||||
})
|
||||
->read();
|
||||
|
||||
$original = $original->toArray();
|
||||
$original_score = $original_score->toArray();
|
||||
$new_version = $new_version->toArray();
|
||||
$new_version_score = $new_version_score->toArray();
|
||||
|
||||
//debug($new_version);
|
||||
|
||||
//debug(array_diff_assoc($new_version, $original));
|
||||
//debug(array_diff_assoc($original, $new_version));
|
||||
$diff = array();
|
||||
foreach(array_diff($original, $new_version) as $i => $code_client) {
|
||||
$diff[$code_client] = $original_score[$i];
|
||||
}
|
||||
|
||||
debug($diff);
|
||||
/*
|
||||
$diff = array();
|
||||
foreach(array_diff($new_version, $original) as $i => $code_client) {
|
||||
$diff[$code_client] = $new_version_score[$i];
|
||||
}
|
||||
|
||||
debug($diff);
|
||||
*/
|
167
modules/training/training.php
Normal file
167
modules/training/training.php
Normal file
@ -0,0 +1,167 @@
|
||||
<?php
|
||||
if (!defined('_PS_VERSION_'))
|
||||
exit;
|
||||
|
||||
include_once(_PS_MODULE_DIR_.'training/models/TrainingModule.php');
|
||||
include_once(_PS_MODULE_DIR_.'training/models/TrainingPharmacy.php');
|
||||
include_once(_PS_MODULE_DIR_.'training/models/TrainingFixedCodeClient.php');
|
||||
|
||||
class Training extends Module
|
||||
{
|
||||
public function __construct(){
|
||||
$this->name = 'training';
|
||||
$this->tab = 'administration';
|
||||
$this->version = '1.0';
|
||||
$this->author = 'Antadis';
|
||||
$this->need_instance = 0;
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->displayName = $this->l('Analyse formation en ligne');
|
||||
$this->description = $this->l('Permet d\'importer les résultats de la formation en ligne et d\'en extraire un bilan');
|
||||
$this->confirmUninstall = $this->l('Etes vous sur de vouloir désinstaller ce module ?');
|
||||
}
|
||||
|
||||
public function install()
|
||||
{
|
||||
if (!parent::install()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$queries = array(
|
||||
'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'training_module` (
|
||||
`id_training_module` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`module_name` VARCHAR(255) NOT NULL,
|
||||
`category_name` VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (`id_training_module`),
|
||||
UNIQUE(`module_name`)
|
||||
|
||||
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8',
|
||||
|
||||
'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'training_pharmacy` (
|
||||
`id_training_pharmacy` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`codeclient` VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (`id_training_pharmacy`)
|
||||
|
||||
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8',
|
||||
|
||||
'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'training_fixedcodeclient` (
|
||||
`id_training_fixedcodeclient` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`codeclient_from_results` VARCHAR(255) NOT NULL,
|
||||
`codeclient_fixed` VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (`id_training_fixedcodeclient`),
|
||||
UNIQUE(`codeclient_from_results`)
|
||||
|
||||
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8'
|
||||
);
|
||||
|
||||
if (!self::dbExecute($queries)) {
|
||||
$this->uninstall();
|
||||
return false;
|
||||
}
|
||||
|
||||
$tab = new Tab();
|
||||
$tab->class_name = 'AdminTrainingRoot';
|
||||
$tab->id_parent = 0;
|
||||
$tab->module = $this->name;
|
||||
$tab->name[(int)Configuration::get('PS_LANG_DEFAULT')] = $this->l('Analyse formation en ligne');
|
||||
|
||||
if (!$tab->add()) {
|
||||
$this->uninstall();
|
||||
return false;
|
||||
}
|
||||
|
||||
$tab = new Tab();
|
||||
$tab->class_name = 'AdminTraining';
|
||||
$tab->id_parent = (int)Tab::getIdFromClassName('AdminTrainingRoot');
|
||||
$tab->module = $this->name;
|
||||
$tab->name[(int)Configuration::get('PS_LANG_DEFAULT')] = $this->l('Analyse formation en ligne');
|
||||
|
||||
if (!$tab->add()) {
|
||||
$this->uninstall();
|
||||
return false;
|
||||
}
|
||||
|
||||
$tab = new Tab();
|
||||
$tab->class_name = 'AdminTrainingModules';
|
||||
$tab->id_parent = (int)Tab::getIdFromClassName('AdminTrainingRoot');
|
||||
$tab->module = $this->name;
|
||||
$tab->name[(int)Configuration::get('PS_LANG_DEFAULT')] = $this->l('Modules/Catégories formation');
|
||||
|
||||
if (!$tab->add()) {
|
||||
$this->uninstall();
|
||||
return false;
|
||||
}
|
||||
|
||||
$tab = new Tab();
|
||||
$tab->class_name = 'AdminTrainingPharmacy';
|
||||
$tab->id_parent = (int)Tab::getIdFromClassName('AdminTrainingRoot');
|
||||
$tab->module = $this->name;
|
||||
$tab->name[(int)Configuration::get('PS_LANG_DEFAULT')] = $this->l('Pharmacies');
|
||||
|
||||
if (!$tab->add()) {
|
||||
$this->uninstall();
|
||||
return false;
|
||||
}
|
||||
|
||||
$tab = new Tab();
|
||||
$tab->class_name = 'AdminTrainingFixedCodeClient';
|
||||
$tab->id_parent = (int)Tab::getIdFromClassName('AdminTrainingRoot');
|
||||
$tab->module = $this->name;
|
||||
$tab->name[(int)Configuration::get('PS_LANG_DEFAULT')] = $this->l('Correction codes clients');
|
||||
|
||||
if (!$tab->add()) {
|
||||
$this->uninstall();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function uninstall()
|
||||
{
|
||||
self::dbExecute(array(
|
||||
'DROP TABLE IF EXISTS `'._DB_PREFIX_.'training_module`',
|
||||
'DROP TABLE IF EXISTS `'._DB_PREFIX_.'training_pharmacy`',
|
||||
'DROP TABLE IF EXISTS `'._DB_PREFIX_.'training_fixedcodeclient`'
|
||||
));
|
||||
|
||||
Configuration::deleteByName('TRAINING_USED_MODULE_FILE');
|
||||
Configuration::deleteByName('TRAINING_USED_PHARMACY_FILE');
|
||||
Configuration::deleteByName('TRAINING_USED_RESULT_FILE');
|
||||
Configuration::deleteByName('TRAINING_FIXEDCODES_FILE');
|
||||
Configuration::deleteByName('TRAINING_BILAN_FILE');
|
||||
Configuration::deleteByName('TRAINING_CORRECTED_RESULT_FILE');
|
||||
Configuration::deleteByName('TRAINING_CORRECTED_CODES_FILE');
|
||||
|
||||
|
||||
|
||||
$tab = new Tab(Tab::getIdFromClassName('AdminTrainingFixedCodeClient'));
|
||||
$tab->delete();
|
||||
|
||||
$tab = new Tab(Tab::getIdFromClassName('AdminTrainingPharmacy'));
|
||||
$tab->delete();
|
||||
|
||||
$tab = new Tab(Tab::getIdFromClassName('AdminTrainingModules'));
|
||||
$tab->delete();
|
||||
|
||||
$tab = new Tab(Tab::getIdFromClassName('AdminTraining'));
|
||||
$tab->delete();
|
||||
|
||||
$tab = new Tab(Tab::getIdFromClassName('AdminTrainingRoot'));
|
||||
$tab->delete();
|
||||
|
||||
return parent::uninstall();
|
||||
}
|
||||
|
||||
private static function dbExecute(array $queries){
|
||||
foreach($queries as $query){
|
||||
if( !Db::getInstance()->execute($query)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
172
modules/training/views/templates/admin/training/home.tpl
Normal file
172
modules/training/views/templates/admin/training/home.tpl
Normal file
@ -0,0 +1,172 @@
|
||||
<h1>{l s='Analyse de formations en ligne' mod='training'}</h1>
|
||||
|
||||
<div>
|
||||
- L'intitulé des colonnes des fichiers doit être identique à ce qui est indiqué dans les commentaires "Entête/colonnes obligatoires" (à l'espace près entre les termes).<br />
|
||||
La casse n'a pas d'importante (majuscules / minuscules).
|
||||
<br /><br />
|
||||
- Les noms de catégories et modules doivent être identiques entre le fichier "modules/catégories" et le fichier "résultats"
|
||||
sinon le générateur de bilan ne pourra pas retrouver ces informations.
|
||||
<br />
|
||||
La casse n'a pas d'importante ici aussi (majuscules / minuscules).
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<form method="post" action="{$link_self}" name="uploadModules" class="defaultForm" enctype="multipart/form-data">
|
||||
<fieldset>
|
||||
<legend>{l s='1 - Modules/Catégories' mod='training'}</legend>
|
||||
|
||||
<label>Entête/Colonnes obligatoires :</label>
|
||||
<div class="margin-form">
|
||||
|
||||
- Modules<br />
|
||||
- Catégories
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<label for="module_filename">{l s='Importer un csv contenant les modules/catégories ' mod='training'} :</label>
|
||||
<div class="margin-form">
|
||||
<input type="file" name="module_filename">
|
||||
</div>
|
||||
|
||||
<div class="margin-form">
|
||||
<input class="button" type="submit" name="submitFileModules" value="Importer les modules/catégories">
|
||||
</div>
|
||||
|
||||
<label for="">{l s='Fichier actuellement utilisé' mod='training'} :</label>
|
||||
{if isset($used_module_file) && $used_module_file!=''}
|
||||
<div class="margin-form"><a href='{$url_upload_path}{$used_module_file}' target='_blank'>{$used_module_file}</a></div>
|
||||
{else}
|
||||
<div class="margin-form"> aucun </div>
|
||||
{/if}
|
||||
</fieldset>
|
||||
<br />
|
||||
</form>
|
||||
|
||||
<form method="post" action="{$link_self}" name="uploadPharmacy" class="defaultForm" enctype="multipart/form-data">
|
||||
<fieldset>
|
||||
<legend>{l s='2 - Pharmacies' mod='training'}</legend>
|
||||
|
||||
<label>Entête/Colonnes obligatoires :</label>
|
||||
<div class="margin-form">
|
||||
|
||||
- CodeClt<br />
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<label for="pharmacy_filename">{l s='Importer un csv contenant les pharmacies avec leur code client' mod='training'} :</label>
|
||||
<div class="margin-form">
|
||||
<input type="file" name="pharmacy_filename">
|
||||
</div>
|
||||
|
||||
<div class="margin-form">
|
||||
<input class="button" type="submit" name="submitFilePharmacies" value="Importer les pharmacies">
|
||||
</div>
|
||||
|
||||
<label for="">{l s='Fichier actuellement utilisé' mod='training'} :</label>
|
||||
{if isset($used_pharmacy_file) && $used_pharmacy_file!=''}
|
||||
<div class="margin-form"><a href='{$url_upload_path}{$used_pharmacy_file}' target='_blank'>{$used_pharmacy_file}</a></div>
|
||||
{else}
|
||||
<div class="margin-form">- aucun -</div>
|
||||
{/if}
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
|
||||
<form method="post" action="{$link_self}" name="uploadFixedCodes" class="defaultForm" enctype="multipart/form-data">
|
||||
<fieldset>
|
||||
<legend>{l s='3 - Codes corrigés (facultatif)' mod='training'}</legend>
|
||||
|
||||
<label>Entête/Colonnes obligatoires :</label>
|
||||
<div class="margin-form">
|
||||
|
||||
- CC faux<br />
|
||||
- CC bon
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<label for="fixed_codes_filename">{l s='Importer un csv contenant les codes faux et leur correction' mod='training'} :</label>
|
||||
<div class="margin-form">
|
||||
<input type="file" name="fixed_codes_filename">
|
||||
</div>
|
||||
|
||||
<div class="margin-form">
|
||||
<input class="button" type="submit" name="submitFileFixedCodes" value="Importer et mettre à jour les codes corrigés">
|
||||
</div>
|
||||
|
||||
<label for="">{l s='Dernier fichier importé' mod='training'} :</label>
|
||||
{if isset($used_fixedcodes_file) && $used_fixedcodes_file!=''}
|
||||
<div class="margin-form"><a href='{$url_upload_path}{$used_fixedcodes_file}' target='_blank'>{$used_fixedcodes_file}</a></div>
|
||||
{else}
|
||||
<div class="margin-form">- aucun -</div>
|
||||
{/if}
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
|
||||
<fieldset>
|
||||
<legend>{l s='LE BILAN' mod='training'}</legend>
|
||||
<form method="post" action="{$link_self}" name="uploadResults" class="defaultForm" enctype="multipart/form-data">
|
||||
|
||||
<label>Entête/Colonnes obligatoires :</label>
|
||||
<div class="margin-form">
|
||||
|
||||
- Module<br />
|
||||
- Code Client<br />
|
||||
- Prenom<br />
|
||||
- NOM<br />
|
||||
- Telephone<br />
|
||||
- Nom pharmacie<br />
|
||||
- Ville<br />
|
||||
- Date<br />
|
||||
- Slides Vues<br />
|
||||
- Total Slides<br />
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<label for="results_filename">{l s='Importer un fichier csv contenant les résultats ' mod='training'} :</label>
|
||||
<div class="margin-form">
|
||||
<input type="hidden" name="MAX_FILE_SIZE" value="16000000" />
|
||||
<input type="file" name="results_filename"> (taille max : 16 Mo)
|
||||
</div>
|
||||
|
||||
<div class="margin-form">
|
||||
<input class="button" type="submit" id="submitFileResults" name="submitFileResults" value="Transmettre le fichier de résultats">
|
||||
</div>
|
||||
|
||||
<label for="">{l s='Fichier actuellement utilisé' mod='training'} :</label>
|
||||
{if isset($used_results_file) && $used_results_file!=''}
|
||||
<div class="margin-form"><a href='{$url_upload_path}{$used_results_file}' target='_blank'>{$used_results_file}</a></div>
|
||||
{else}
|
||||
<div class="margin-form">- aucun -</div>
|
||||
{/if}
|
||||
|
||||
</form>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
|
||||
<form method="post" action="{$link_self}" name="generateBilan" class="defaultForm">
|
||||
<div class="margin-form">
|
||||
<input class="button" type="submit" name="generateBilan" value="{if isset($bilan_file) && $bilan_file!=''}RE-{/if}GENERER LE BILAN">
|
||||
</div>
|
||||
|
||||
<div id='links_result_files'>
|
||||
{if isset($bilan_file) && $bilan_file!=''}
|
||||
<label></label>
|
||||
<div class="margin-form"><a href='{$url_output_path}{$bilan_file}' target='_blank'>Télécharger le dernier bilan : {$bilan_file}</a></div>
|
||||
{/if}
|
||||
|
||||
{if isset($corrected_results_file) && $corrected_results_file!=''}
|
||||
<label></label>
|
||||
<div class="margin-form"><a href='{$url_output_path}{$corrected_results_file}' target='_blank'>Télécharger le dernier résultat corrigé : {$corrected_results_file}</a></div>
|
||||
{/if}
|
||||
|
||||
{if isset($corrected_codes_file) && $corrected_codes_file!=''}
|
||||
<label></label>
|
||||
<div class="margin-form"><a href='{$url_output_path}{$corrected_codes_file}' target='_blank'>Télécharger les derniers codes corrigés : {$corrected_codes_file}</a></div>
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
Loading…
Reference in New Issue
Block a user