XML Inco import
This commit is contained in:
parent
8219e60947
commit
fee8055d19
13 changed files with 486 additions and 431 deletions
|
|
@ -2,158 +2,426 @@
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\IncoDatas;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Exceptions\AbortXmlImportException;
|
||||
use App\Models\XmlInco;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class XmlIncoReader implements ShouldQueue
|
||||
class XmlIncoReader extends XmlJobs
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $console = null;
|
||||
protected $disk = 'inco';
|
||||
|
||||
protected $imagesPath = 'images';
|
||||
|
||||
protected $takeColumns = [
|
||||
'INCO_COMPO.ID_INCO' => 'id', // primary key on model
|
||||
|
||||
'ID_SOCIETE' => 'id_societe',
|
||||
'NOM_SOCIETE' => 'nom_societe',
|
||||
|
||||
'PROD_SAISON' => 'prod_saison',
|
||||
'NOM' => 'nom',
|
||||
'NUMERO' => 'numero',
|
||||
'TAILLE_REF' => 'taille_ref',
|
||||
'ALLEGATIONS' => 'allegations',
|
||||
'CONSEIL_PREPA' => 'conseil_prepa',
|
||||
'ASTUCES' => 'astuces',
|
||||
'DESCRIPTIF' => 'descriptif',
|
||||
'ARG_VENTE' => 'arg_vente',
|
||||
'NB_PORTION' => 'nb_portion',
|
||||
'TYP_PORTION' => 'typ_portion',
|
||||
'PDS_MOY_THEO' => 'pds_moy_theo',
|
||||
'LIB_CONSO' => 'lib_conso',
|
||||
'LST_ING' => 'lst_ing',
|
||||
|
||||
// list alergene
|
||||
'LIB_ALLERGENE' => 'lib_allergene',
|
||||
|
||||
// Allegations
|
||||
'ALLEGAT_SANTE' => 'allegat_sante',
|
||||
'ALLEGAT_NUTRIT' => 'allegat_nutrit',
|
||||
'ALLEGAT_AUTRE' => 'allegat_autre',
|
||||
|
||||
|
||||
'INCO_COMPO' => 'inco_compo', // Consider this sub array
|
||||
'INCO_COMPO.VAL_ENERG_KJ_NUT' => 'val_energ_kj_nut',
|
||||
'INCO_COMPO.VAL_ENERG_KJ_R' => 'val_energ_kj_r',
|
||||
'INCO_COMPO.VAL_ENERG_KCAL_R' => 'val_energ_kcal_r',
|
||||
'INCO_COMPO.VAL_ENERG_KCAL_NUT' => 'val_energ_kcal_nut',
|
||||
'INCO_COMPO.MAT_GRASS_R' => 'mat_grass_r',
|
||||
'INCO_COMPO.MAT_GRASS_NUT' => 'mat_grass_nut',
|
||||
'INCO_COMPO.AGS_R' => 'ags_r',
|
||||
'INCO_COMPO.AGS_NUT' => 'ags_nut',
|
||||
'INCO_COMPO.GLUCIDES_R' => 'glucides_r',
|
||||
'INCO_COMPO.GLUCIDES_NUT' => 'glucides_nut',
|
||||
'INCO_COMPO.SUCRE_NUT' => 'sucre_nut',
|
||||
'INCO_COMPO.SUCRE_R' => 'sucre_r',
|
||||
'INCO_COMPO.FIB_ALIM_NUT' => 'fib_alim_nut',
|
||||
'INCO_COMPO.FIB_ALIM_R' => 'fib_alim_r',
|
||||
'INCO_COMPO.PROTEINE_R' => 'proteine_r',
|
||||
'INCO_COMPO.PROTEINE_NUT' => 'proteine_nut',
|
||||
'INCO_COMPO.SEL_R' => 'sel_r',
|
||||
'INCO_COMPO.SEL_NUT' => 'sel_nut',
|
||||
'INCO_COMPO.SODIUM_R' => 'sodium_r',
|
||||
'INCO_COMPO.ENER_E14_R' => 'ener_e14_r',
|
||||
'INCO_COMPO.FRUITS_R' => 'fruits_r',
|
||||
'INCO_COMPO.FRUITS_COQUE_SECH_R' => 'fruits_coque_sech_r',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_E14_R' => 'pourcent_apport_ref_e14_r',
|
||||
'INCO_COMPO.ENER_KJO_R' => 'ener_kjo_r',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_KJO_R' => 'pourcent_apport_ref_kjo_r',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_FAT_R' => 'pourcent_apport_ref_fat_r',
|
||||
'INCO_COMPO.FATSAT_R' => 'fatsat_r',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_FATSAT_R' => 'pourcent_apport_ref_fatsat_r',
|
||||
'INCO_COMPO.CHOVAL_R' => 'choval_r',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_CHOVAL_R' => 'pourcent_apport_ref_choval_r',
|
||||
'INCO_COMPO.SUGAR_R' => 'sugar_r',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_SUGAR_R' => 'pourcent_apport_ref_sugar_r',
|
||||
'INCO_COMPO.FIBTG_R' => 'fibtg_r',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_FIBTG_R' => 'pourcent_apport_ref_fibtg_r',
|
||||
'INCO_COMPO.PRO_R' => 'pro_r',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_PRO_R' => 'pourcent_apport_ref_pro_r',
|
||||
'INCO_COMPO.SALTEQ_R' => 'salteq_r',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_SALTEQ_R' => 'pourcent_apport_ref_salteq_r',
|
||||
'INCO_COMPO.APPORT_REF' => 'apport_ref',
|
||||
'INCO_COMPO.TAILLE_PORT' => 'taille_port',
|
||||
'INCO_COMPO.VAL_ENERG_KJ_P' => 'val_energ_kj_p',
|
||||
'INCO_COMPO.VAL_ENERG_KCAL_P' => 'val_energ_kcal_p',
|
||||
'INCO_COMPO.MAT_GRASS_P' => 'mat_grass_p',
|
||||
'INCO_COMPO.AGS_P' => 'ags_p',
|
||||
'INCO_COMPO.GLUCIDES_P' => 'glucides_p',
|
||||
'INCO_COMPO.SUCRE_P' => 'sucre_p',
|
||||
'INCO_COMPO.FIB_ALIM_P' => 'fib_alim_p',
|
||||
'INCO_COMPO.PROTEINE_P' => 'proteine_p',
|
||||
'INCO_COMPO.SODIUM_P' => 'sodium_p',
|
||||
'INCO_COMPO.FRUITS_P' => 'fruits_p',
|
||||
'INCO_COMPO.SEL_P' => 'sel_p',
|
||||
'INCO_COMPO.FRUITS_COQUE_SECH_P' => 'fruits_coque_sech_p',
|
||||
'INCO_COMPO.ENER_E14_P' => 'ener_e14_p',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_E14_P' => 'pourcent_apport_ref_e14_p',
|
||||
'INCO_COMPO.ENER_KJO_P' => 'ener_kjo_p',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_KJO_P' => 'pourcent_apport_ref_kjo_p',
|
||||
'INCO_COMPO.FAT_P' => 'fat_p',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_FAT_P' => 'pourcent_apport_ref_fat_p',
|
||||
'INCO_COMPO.FATSAT_P' => 'fatsat_p',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_FATSAT_P' => 'pourcent_apport_ref_fatsat_p',
|
||||
'INCO_COMPO.CHOVAL_P' => 'CHOVAL_P',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_CHOVAL_P' => 'pourcent_apport_ref_choval_p',
|
||||
'INCO_COMPO.SUGAR_P' => 'SUGAR_P',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_SUGAR_P' => 'pourcent_apport_ref_sugar_p',
|
||||
'INCO_COMPO.FIBTG_P' => 'fibtg_p',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_FIBTG_P' => 'pourcent_apport_ref_fibtg_p',
|
||||
'INCO_COMPO.PRO_P' => 'pro_p',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_PRO_P' => 'pourcent_apport_ref_pro_p',
|
||||
'INCO_COMPO.SALTEQ_P' => 'salteq_p',
|
||||
'INCO_COMPO.POURCENT_APPORT_REF_SALTEQ_P' => 'pourcent_apport_ref_salteq_p',
|
||||
'INCO_COMPO.APPORT_PORT' => 'apport_port',
|
||||
'INCO_COMPO.PART_P' => 'part_p',
|
||||
'INCO_COMPO.PART_G' => 'part_g',
|
||||
'INCO_COMPO.PART_L' => 'part_l',
|
||||
'INCO_COMPO.P_SUR_L' => 'p_sur_l',
|
||||
'INCO_COMPO.POURCENT_AGS' => 'pourcent_ags',
|
||||
'INCO_COMPO.POURCENT_AGI' => 'pourcent_agi',
|
||||
'INCO_COMPO.POURCENT_SS' => 'pourcent_ss',
|
||||
'INCO_COMPO.POURCENT_AMIDON' => 'pourcent_amidon',
|
||||
'INCO_COMPO.COMPOSANTE_N' => 'composante_n',
|
||||
'INCO_COMPO.COMPOSANTE_P' => 'composante_p',
|
||||
'INCO_COMPO.POURCENT_FRUITS' => 'pourcent_fruits',
|
||||
'INCO_COMPO.SCORE_NUTRI' => 'score_nutri',
|
||||
'INCO_COMPO.CLASSEMENT' => 'classement',
|
||||
'INCO_COMPO.MAT_GRASS_R_PREC' => 'mat_grass_r_prec',
|
||||
'INCO_COMPO.AGS_R_PREC' => 'ags_r_prec',
|
||||
'INCO_COMPO.GLUCIDES_R_PREC' => 'glucides_r_prec',
|
||||
'INCO_COMPO.SUCRE_R_PREC' => 'sucre_r_prec',
|
||||
'INCO_COMPO.FIB_ALIM_R_PREC' => 'fib_alim_r_prec',
|
||||
'INCO_COMPO.PROTEINE_R_PREC' => 'proteine_r_prec',
|
||||
'INCO_COMPO.SEL_R_PREC' => 'sel_r_prec',
|
||||
'INCO_COMPO.SODIUM_R_PREC' => 'sodium_r_prec',
|
||||
'INCO_COMPO.FRUITS_R_PREC' => 'fruits_r_prec',
|
||||
'INCO_COMPO.FRUITS_COQUE_SECH_R_PREC' => 'fruits_coque_sech_r_prec',
|
||||
];
|
||||
|
||||
protected $nullableFields = [
|
||||
'allegations',
|
||||
'astuces',
|
||||
'pds_moy_theo',
|
||||
'lib_conso', 'lst_ing',
|
||||
'allegat_sante', 'allegat_nutrit', 'allegat_autre',
|
||||
'inco_compo.val_energ_kj_nut',
|
||||
'inco_compo.val_energ_kj_r',
|
||||
'inco_compo.val_energ_kcal_r',
|
||||
'inco_compo.val_energ_kcal_nut',
|
||||
'inco_compo.mat_grass_r',
|
||||
'inco_compo.mat_grass_nut',
|
||||
'inco_compo.ags_r',
|
||||
'inco_compo.ags_nut',
|
||||
'inco_compo.glucides_r',
|
||||
'inco_compo.glucides_nut',
|
||||
'inco_compo.sucre_nut',
|
||||
'inco_compo.sucre_r',
|
||||
'inco_compo.fib_alim_nut',
|
||||
'inco_compo.fib_alim_r',
|
||||
'inco_compo.proteine_r',
|
||||
'inco_compo.proteine_nut',
|
||||
'inco_compo.sel_r',
|
||||
'inco_compo.sel_nut',
|
||||
'inco_compo.sodium_r',
|
||||
'inco_compo.ener_e14_r',
|
||||
'inco_compo.fruits_r',
|
||||
'inco_compo.fruits_coque_sech_r',
|
||||
'inco_compo.pourcent_apport_ref_e14_r',
|
||||
'inco_compo.ener_kjo_r',
|
||||
'inco_compo.pourcent_apport_ref_kjo_r',
|
||||
'inco_compo.pourcent_apport_ref_fat_r',
|
||||
'inco_compo.fatsat_r',
|
||||
'inco_compo.pourcent_apport_ref_fatsat_r',
|
||||
'inco_compo.choval_r',
|
||||
'inco_compo.pourcent_apport_ref_choval_r',
|
||||
'inco_compo.sugar_r',
|
||||
'inco_compo.pourcent_apport_ref_sugar_r',
|
||||
'inco_compo.fibtg_r',
|
||||
'inco_compo.pourcent_apport_ref_fibtg_r',
|
||||
'inco_compo.pro_r',
|
||||
'inco_compo.pourcent_apport_ref_pro_r',
|
||||
'inco_compo.salteq_r',
|
||||
'inco_compo.pourcent_apport_ref_salteq_r',
|
||||
'inco_compo.apport_ref',
|
||||
'inco_compo.taille_port',
|
||||
'inco_compo.val_energ_kj_p',
|
||||
'inco_compo.val_energ_kcal_p',
|
||||
'inco_compo.mat_grass_p',
|
||||
'inco_compo.ags_p',
|
||||
'inco_compo.glucides_p',
|
||||
'inco_compo.sucre_p',
|
||||
'inco_compo.fib_alim_p',
|
||||
'inco_compo.proteine_p',
|
||||
'inco_compo.sodium_p',
|
||||
'inco_compo.fruits_p',
|
||||
'inco_compo.sel_p',
|
||||
'inco_compo.fruits_coque_sech_p',
|
||||
'inco_compo.ener_e14_p',
|
||||
'inco_compo.pourcent_apport_ref_e14_p',
|
||||
'inco_compo.ener_kjo_p',
|
||||
'inco_compo.pourcent_apport_ref_kjo_p',
|
||||
'inco_compo.fat_p',
|
||||
'inco_compo.pourcent_apport_ref_fat_p',
|
||||
'inco_compo.fatsat_p',
|
||||
'inco_compo.pourcent_apport_ref_fatsat_p',
|
||||
'inco_compo.choval_p',
|
||||
'inco_compo.pourcent_apport_ref_choval_p',
|
||||
'inco_compo.sugar_p',
|
||||
'inco_compo.pourcent_apport_ref_sugar_p',
|
||||
'inco_compo.fibtg_p',
|
||||
'inco_compo.pourcent_apport_ref_fibtg_p',
|
||||
'inco_compo.pro_p',
|
||||
'inco_compo.pourcent_apport_ref_pro_p',
|
||||
'inco_compo.salteq_p',
|
||||
'inco_compo.pourcent_apport_ref_salteq_p',
|
||||
'inco_compo.apport_port',
|
||||
'inco_compo.part_p',
|
||||
'inco_compo.part_g',
|
||||
'inco_compo.part_l',
|
||||
'inco_compo.p_sur_l',
|
||||
'inco_compo.pourcent_ags',
|
||||
'inco_compo.pourcent_agi',
|
||||
'inco_compo.pourcent_ss',
|
||||
'inco_compo.pourcent_amidon',
|
||||
'inco_compo.composante_n',
|
||||
'inco_compo.composante_p',
|
||||
'inco_compo.pourcent_fruits',
|
||||
'inco_compo.score_nutri',
|
||||
'inco_compo.classement',
|
||||
'inco_compo.mat_grass_r_prec',
|
||||
'inco_compo.ags_r_prec',
|
||||
'inco_compo.glucides_r_prec',
|
||||
'inco_compo.sucre_r_prec',
|
||||
'inco_compo.fib_alim_r_prec',
|
||||
'inco_compo.proteine_r_prec',
|
||||
'inco_compo.sel_r_prec',
|
||||
'inco_compo.sodium_r_prec',
|
||||
'inco_compo.fruits_r_prec',
|
||||
'inco_compo.fruits_coque_sech_r_prec'
|
||||
];
|
||||
|
||||
protected $uniqueKeyField = ['id'];
|
||||
|
||||
protected $mergeColumns = [
|
||||
'allegations' => [
|
||||
'allegat_sante',
|
||||
'allegat_nutrit',
|
||||
'allegat_autre'
|
||||
],
|
||||
'nutritional_values' => [
|
||||
'val_energ_kj_nut',
|
||||
'val_energ_kj_r',
|
||||
'val_energ_kcal_r',
|
||||
'val_energ_kcal_nut',
|
||||
'mat_grass_r',
|
||||
'mat_grass_nut',
|
||||
'ags_r',
|
||||
'ags_nut',
|
||||
'glucides_r',
|
||||
'glucides_nut',
|
||||
'sucre_nut',
|
||||
'sucre_r',
|
||||
'fib_alim_nut',
|
||||
'fib_alim_r',
|
||||
'proteine_r',
|
||||
'proteine_nut',
|
||||
'sel_r',
|
||||
'sel_nut',
|
||||
'sodium_r',
|
||||
'ener_e14_r',
|
||||
'fruits_r',
|
||||
'fruits_coque_sech_r',
|
||||
'pourcent_apport_ref_e14_r',
|
||||
'ener_kjo_r',
|
||||
'pourcent_apport_ref_kjo_r',
|
||||
'pourcent_apport_ref_fat_r',
|
||||
'fatsat_r',
|
||||
'pourcent_apport_ref_fatsat_r',
|
||||
'choval_r',
|
||||
'pourcent_apport_ref_choval_r',
|
||||
'sugar_r',
|
||||
'pourcent_apport_ref_sugar_r',
|
||||
'fibtg_r',
|
||||
'pourcent_apport_ref_fibtg_r',
|
||||
'pro_r',
|
||||
'pourcent_apport_ref_pro_r',
|
||||
'salteq_r',
|
||||
'pourcent_apport_ref_salteq_r',
|
||||
'apport_ref',
|
||||
'taille_port',
|
||||
'val_energ_kj_p',
|
||||
'val_energ_kcal_p',
|
||||
'mat_grass_p',
|
||||
'ags_p',
|
||||
'glucides_p',
|
||||
'sucre_p',
|
||||
'fib_alim_p',
|
||||
'proteine_p',
|
||||
'sodium_p',
|
||||
'fruits_p',
|
||||
'sel_p',
|
||||
'fruits_coque_sech_p',
|
||||
'ener_e14_p',
|
||||
'pourcent_apport_ref_e14_p',
|
||||
'ener_kjo_p',
|
||||
'pourcent_apport_ref_kjo_p',
|
||||
'fat_p',
|
||||
'pourcent_apport_ref_fat_p',
|
||||
'fatsat_p',
|
||||
'pourcent_apport_ref_fatsat_p',
|
||||
'choval_p',
|
||||
'pourcent_apport_ref_choval_p',
|
||||
'sugar_p',
|
||||
'pourcent_apport_ref_sugar_p',
|
||||
'fibtg_p',
|
||||
'pourcent_apport_ref_fibtg_p',
|
||||
'pro_p',
|
||||
'pourcent_apport_ref_pro_p',
|
||||
'salteq_p',
|
||||
'pourcent_apport_ref_salteq_p',
|
||||
'apport_port',
|
||||
'part_p',
|
||||
'part_g',
|
||||
'part_l',
|
||||
'p_sur_l',
|
||||
'pourcent_ags',
|
||||
'pourcent_agi',
|
||||
'pourcent_ss',
|
||||
'pourcent_amidon',
|
||||
'composante_n',
|
||||
'composante_p',
|
||||
'pourcent_fruits',
|
||||
'score_nutri',
|
||||
'classement',
|
||||
'mat_grass_r_prec',
|
||||
'ags_r_prec',
|
||||
'glucides_r_prec',
|
||||
'sucre_r_prec',
|
||||
'fib_alim_r_prec',
|
||||
'proteine_r_prec',
|
||||
'sel_r_prec',
|
||||
'sodium_r_prec',
|
||||
'fruits_r_prec',
|
||||
'fruits_coque_sech_r_prec',
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* TODO: Job clean very old XML PARSED DATA
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->log(__CLASS__ . ' Démarrage du traitement');
|
||||
$files = Storage::disk('inco')->files();
|
||||
|
||||
// Suppression des fichiers dans archives qui ont plus de 2j
|
||||
$archives = Storage::disk('inco')->files('archives');
|
||||
foreach ($archives as $archive) {
|
||||
if ($archive == ".gitignore" || $archive == ".gitkeep") {
|
||||
continue;
|
||||
}
|
||||
if (Storage::disk('inco')->lastModified($archive) < strtotime(Carbon::now()->subDays(2))) {
|
||||
Storage::disk('inco')->delete($archive);
|
||||
}
|
||||
}
|
||||
$this->parseData('INCO', XmlInco::class, function (SimpleXMLElement $xmlData, array &$line) {
|
||||
|
||||
$progress = 0;
|
||||
$total = count($files) - 1;
|
||||
foreach ($files as $filename) {
|
||||
if ($filename == ".gitignore" || $filename == ".gitkeep") {
|
||||
continue;
|
||||
}
|
||||
// ---- Récupération du contenu
|
||||
$files = Storage::disk('inco')->get($filename);
|
||||
$fileContent = simplexml_load_string($files, null, LIBXML_NOCDATA);
|
||||
|
||||
// Transforme le fichier en tableau de données
|
||||
$inco = $this->extract($fileContent->INCO);
|
||||
|
||||
// Récupérer et enregistrer l'image
|
||||
$imageEncoded = $fileContent->INCO->MEDIA_VISUEL_EAI->MEDIA_VISUEL_EAI !== null ?
|
||||
(string)$fileContent->INCO->MEDIA_VISUEL_EAI->MEDIA_VISUEL_EAI : null;
|
||||
|
||||
if ($imageEncoded !== null) {
|
||||
$imgName = strval($fileContent->INCO->INCO_COMPO->TBL_INCO_COMPO->ID_INCO);
|
||||
$fileFullPath = (storage_path('inco/images/') . $imgName);
|
||||
|
||||
file_put_contents($fileFullPath, base64_decode($imageEncoded));
|
||||
|
||||
$info = getimagesize($fileFullPath);
|
||||
$mime = $info['mime'];
|
||||
switch ($mime) {
|
||||
case 'image/jpeg':
|
||||
$imgExtension = 'jpg';
|
||||
break;
|
||||
|
||||
case 'image/png':
|
||||
$imgExtension = 'png';
|
||||
break;
|
||||
|
||||
case 'image/gif':
|
||||
$imgExtension = 'gif';
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \Exception('Unknown image type.');
|
||||
// merge columns
|
||||
foreach ($this->mergeColumns as $key => $values) {
|
||||
$line[$key] = [];
|
||||
// merge columns and remove merged columns from line
|
||||
array_map(function ($kValue) use (&$line, $key) {
|
||||
if (array_key_exists($kValue, $line)) {
|
||||
$line[$key][$kValue] = $line[$kValue];
|
||||
unset($line[$kValue]);
|
||||
}
|
||||
}, $values);
|
||||
if ($key == 'allegations') {
|
||||
$line[$key] = implode(',', array_filter($line[$key]));
|
||||
$line[$key] = strlen($line[$key]) ? $line[$key] : null;
|
||||
}
|
||||
if ($key == 'nutritional_values') {
|
||||
$line[$key] = json_encode($line[$key], \JSON_FORCE_OBJECT);
|
||||
}
|
||||
|
||||
rename($fileFullPath, $fileFullPath . '.' . $imgExtension);
|
||||
}
|
||||
|
||||
// Mise en BDD des données du fichier xml
|
||||
$fields = [];
|
||||
$fields = $this->processFields($inco, $fields);
|
||||
// Boolean cast prod_saison
|
||||
$line['prod_saison'] = strtolower($line['prod_saison']) == 'non' ? false : true;
|
||||
|
||||
$imageFileName = $this->parseImage($xmlData);
|
||||
|
||||
// Ajout du chemin de l'image dans les champs a envoyé en base
|
||||
if ($imageEncoded !== null) {
|
||||
$fields['picture'] = 'inco/images/' . $imgName . '.' . $imgExtension;
|
||||
if ($imageFileName !== null) {
|
||||
$line['picture'] = sprintf('%s/%s', $this->disk, $imageFileName);
|
||||
}
|
||||
$checkFields = ['id_inco' => $fields["id_inco"], 'no_art' => $fields['no_art']];
|
||||
|
||||
IncoDatas::updateOrCreate($checkFields, $fields);
|
||||
|
||||
Storage::disk('inco')->move($filename, 'archives/' . $filename);
|
||||
$this->log(__CLASS__ . sprintf(' traitement %d sur %s ', $progress++, $total));
|
||||
}
|
||||
$this->log(__CLASS__ . ' Fin du traitement');
|
||||
});
|
||||
}
|
||||
|
||||
private function log(string $message)
|
||||
private function parseImage(SimpleXMLElement $xmlData)
|
||||
{
|
||||
if ($this->console) {
|
||||
$this->console->info($message);
|
||||
}
|
||||
Log::debug($message);
|
||||
}
|
||||
$imageData = (string)$xmlData->INCO->MEDIA_VISUEL_EAI->MEDIA_VISUEL_EAI;
|
||||
if ($imageData !== null) {
|
||||
$imageData = base64_decode($imageData);
|
||||
$imgName = strval($xmlData->INCO->INCO_COMPO->TBL_INCO_COMPO->ID_INCO);
|
||||
$imgNamePath = sprintf('%s/%s', rtrim($this->imagesPath, '/'), $imgName);
|
||||
|
||||
private function extract($sxe = null)
|
||||
{
|
||||
if (!$sxe instanceof SimpleXMLElement) {
|
||||
return array();
|
||||
}
|
||||
$disk = $this->getDisk();
|
||||
|
||||
$extract = array();
|
||||
|
||||
foreach ($sxe->children() as $key => $value) {
|
||||
if (array_key_exists($key, $extract)) {
|
||||
if (!isset($extract[$key][0])) {
|
||||
$tmp_extract = $extract[$key];
|
||||
$extract[$key] = array();
|
||||
$extract[$key][0] = $tmp_extract;
|
||||
} else {
|
||||
$extract[$key] = (array)$extract[$key];
|
||||
}
|
||||
$info = getimagesizefromstring($imageData);
|
||||
$mime = $info['mime'];
|
||||
switch ($mime) {
|
||||
case 'image/jpeg':
|
||||
$imgExtension = 'jpg';
|
||||
break;
|
||||
case 'image/png':
|
||||
$imgExtension = 'png';
|
||||
break;
|
||||
case 'image/gif':
|
||||
$imgExtension = 'gif';
|
||||
break;
|
||||
default:
|
||||
throw new AbortXmlImportException(sprintf('Unknown image type %s', $imgName));
|
||||
}
|
||||
|
||||
if ($value->count()) {
|
||||
if (isset($extract[$key]) && is_array($extract[$key])) {
|
||||
$extract[$key][] = $this->extract($value);
|
||||
} else {
|
||||
$extract[$key] = $this->extract($value);
|
||||
}
|
||||
} else {
|
||||
if (isset($extract[$key]) && is_array($extract[$key])) {
|
||||
$extract[$key][] = empty(strval($value)) ? null : strval($value);
|
||||
} else {
|
||||
$extract[$key] = empty(strval($value)) ? null : strval($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
$imgNamePath = sprintf('%s.%s', $imgNamePath, $imgExtension);
|
||||
|
||||
return $extract;
|
||||
}
|
||||
|
||||
private function processFields($fields, $body)
|
||||
{
|
||||
foreach ($fields as $key => $field) {
|
||||
if (is_array($field)) {
|
||||
$body = $this->processFields($field, $body);
|
||||
} else {
|
||||
$body[strtolower($key)] = $field;
|
||||
if (!$disk->put($imgNamePath, $imageData)) {
|
||||
throw new AbortXmlImportException(sprintf('The image %s could not be saved', $imgNamePath));
|
||||
}
|
||||
|
||||
return $imgNamePath;
|
||||
}
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ abstract class XmlJobs implements ShouldQueue
|
|||
* @param string $modelClass
|
||||
* @return void
|
||||
*/
|
||||
protected function parseData(string $entityName, string $modelClass): void
|
||||
protected function parseData(string $entityName, string $modelClass, callable $callback = null): void
|
||||
{
|
||||
/** @var Filesystem */
|
||||
$disk = $this->getDisk();
|
||||
|
|
@ -124,7 +124,7 @@ abstract class XmlJobs implements ShouldQueue
|
|||
$xmlContent = simplexml_load_string($data);
|
||||
} else {
|
||||
/** @var SimpleXMLElement|false */
|
||||
$xmlContent = simplexml_load_string($data, null, LIBXML_NOCDATA);
|
||||
$xmlContent = simplexml_load_string($data, null, LIBXML_NOCDATA | LIBXML_PARSEHUGE);
|
||||
}
|
||||
|
||||
if ($xmlContent === false) {
|
||||
|
|
@ -141,7 +141,11 @@ abstract class XmlJobs implements ShouldQueue
|
|||
$lines = [];
|
||||
|
||||
foreach ($data[$entityName] as $traca) {
|
||||
$lines[] = $this->processFields($traca);
|
||||
$line = $this->processFields($traca);
|
||||
if ($callback) {
|
||||
$callback($xmlContent, $line);
|
||||
}
|
||||
$lines[] = $line;
|
||||
}
|
||||
|
||||
// Mise en base des données
|
||||
|
|
@ -232,7 +236,7 @@ abstract class XmlJobs implements ShouldQueue
|
|||
// Flatten data
|
||||
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($field));
|
||||
foreach ($it as $k => $f) {
|
||||
$ko = sprintf('%s.%s', $key, $k);
|
||||
$ko = strtolower(sprintf('%s.%s', $key, $k));
|
||||
if (!$this->isFieldNullable($ko) && is_null($f)) {
|
||||
throw new AbortXmlImportException(sprintf('%s : The field %s is not allowed to be null', get_class($this), $ko));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class IncoDatas extends Model
|
||||
{
|
||||
|
||||
protected $table = "inco_datas";
|
||||
|
||||
protected $fillable = [
|
||||
"typemouvementinformatique",
|
||||
"no_art",
|
||||
"prod_saison",
|
||||
"id",
|
||||
"id_inco",
|
||||
"nom",
|
||||
"numero",
|
||||
"taille_ref",
|
||||
"val_energ_kj_nut",
|
||||
"val_energ_kj_r",
|
||||
"val_energ_kcal_r",
|
||||
"val_energ_kcal_nut",
|
||||
"mat_grass_r",
|
||||
"mat_grass_nut",
|
||||
"ags_r",
|
||||
"ags_nut",
|
||||
"glucides_r",
|
||||
"glucides_nut",
|
||||
"sucre_nut",
|
||||
"sucre_r",
|
||||
"fib_alim_nut",
|
||||
"fib_alim_r",
|
||||
"proteine_r",
|
||||
"proteine_nut",
|
||||
"sel_r",
|
||||
"sel_nut",
|
||||
"sodium_r",
|
||||
"ener_e14_r",
|
||||
"fruits_r",
|
||||
"fruits_coque_sech_r",
|
||||
"pourcent_apport_ref_e14_r",
|
||||
"ener_kjo_r",
|
||||
"pourcent_apport_ref_kjo_r",
|
||||
"pourcent_apport_ref_fat_r",
|
||||
"fatsat_r",
|
||||
"pourcent_apport_ref_fatsat_r",
|
||||
"choval_r",
|
||||
"pourcent_apport_ref_choval_r",
|
||||
"sugar_r",
|
||||
"pourcent_apport_ref_sugar_r",
|
||||
"fibtg_r",
|
||||
"pourcent_apport_ref_fibtg_r",
|
||||
"pro_r",
|
||||
"pourcent_apport_ref_pro_r",
|
||||
"salteq_r",
|
||||
"pourcent_apport_ref_salteq_r",
|
||||
"apport_ref",
|
||||
"taille_port",
|
||||
"val_energ_kj_p",
|
||||
"val_energ_kcal_p",
|
||||
"mat_grass_p",
|
||||
"ags_p",
|
||||
"glucides_p",
|
||||
"sucre_p",
|
||||
"fib_alim_p",
|
||||
"proteine_p",
|
||||
"sodium_p",
|
||||
"fruits_p",
|
||||
"sel_p",
|
||||
"fruits_coque_sech_p",
|
||||
"ener_e14_p",
|
||||
"pourcent_apport_ref_e14_p",
|
||||
"ener_kjo_p",
|
||||
"pourcent_apport_ref_kjo_p",
|
||||
"fat_p",
|
||||
"pourcent_apport_ref_fat_p",
|
||||
"fatsat_p",
|
||||
"pourcent_apport_ref_fatsat_p",
|
||||
"choval_p",
|
||||
"pourcent_apport_ref_choval_p",
|
||||
"sugar_p",
|
||||
"pourcent_apport_ref_sugar_p",
|
||||
"fibtg_p",
|
||||
"pourcent_apport_ref_fibtg_p",
|
||||
"pro_p",
|
||||
"pourcent_apport_ref_pro_p",
|
||||
"salteq_p",
|
||||
"pourcent_apport_ref_salteq_p",
|
||||
"apport_port",
|
||||
"part_p",
|
||||
"part_g",
|
||||
"part_l",
|
||||
"p_sur_l",
|
||||
"pourcent_ags",
|
||||
"pourcent_agi",
|
||||
"pourcent_ss",
|
||||
"pourcent_amidon",
|
||||
"composante_n",
|
||||
"composante_p",
|
||||
"pourcent_fruits",
|
||||
"score_nutri",
|
||||
"classement",
|
||||
"mat_grass_r_prec",
|
||||
"ags_r_prec",
|
||||
"glucides_r_prec",
|
||||
"sucre_r_prec",
|
||||
"fib_alim_r_prec",
|
||||
"proteine_r_prec",
|
||||
"sel_r_prec",
|
||||
"sodium_r_prec",
|
||||
"fruits_r_prec",
|
||||
"fruits_coque_sech_r_prec",
|
||||
"allegations",
|
||||
"conseil_prepa",
|
||||
"astuces",
|
||||
"descriptif",
|
||||
"arg_vente",
|
||||
"nb_portion",
|
||||
"typ_portion",
|
||||
"famille_vte",
|
||||
"id_societe",
|
||||
"nom_societe",
|
||||
"pds_moy_theo",
|
||||
"lib_conso",
|
||||
"lst_ing",
|
||||
"id_allergene",
|
||||
"lib_allergene",
|
||||
"allegat_sante",
|
||||
"allegat_nutrit",
|
||||
"allegat_autre",
|
||||
"picture",
|
||||
];
|
||||
}
|
||||
27
app/Models/XmlInco.php
Normal file
27
app/Models/XmlInco.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class XmlInco extends Model
|
||||
{
|
||||
|
||||
protected $fillable = [
|
||||
'id',
|
||||
'no_art',
|
||||
'prod_saison',
|
||||
'allegations',
|
||||
'conseil_prepa',
|
||||
'astuces',
|
||||
'descriptif',
|
||||
'arg_vente',
|
||||
'nb_portion',
|
||||
'typ_portion',
|
||||
'pds_moy_theo',
|
||||
'lib_conso',
|
||||
'lst_ing',
|
||||
'nutritional_values',
|
||||
'picture',
|
||||
];
|
||||
}
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
// @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
|
||||
class CreateIncoDatasTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('inco_datas', function (Blueprint $table) {
|
||||
$table->id('inco_data_id');
|
||||
|
||||
$table->bigInteger('numero')->unsigned()->nullable();
|
||||
$table->bigInteger('id_allergene')->unsigned()->nullable();
|
||||
$table->bigInteger('id_inco')->unsigned()->nullable();
|
||||
$table->bigInteger('id_societe')->unsigned()->nullable();
|
||||
$table->bigInteger('id')->unsigned()->nullable();
|
||||
|
||||
$table->double('ags_nut')->nullable();
|
||||
$table->double('ags_p')->nullable();
|
||||
$table->double('ags_r')->nullable();
|
||||
$table->double('choval_p')->nullable();
|
||||
$table->double('choval_r')->nullable();
|
||||
$table->double('ener_e14_p')->nullable();
|
||||
$table->double('ener_e14_r')->nullable();
|
||||
$table->double('ener_kjo_p')->nullable();
|
||||
$table->double('ener_kjo_r')->nullable();
|
||||
$table->double('fat_p')->nullable();
|
||||
$table->double('fatsat_p')->nullable();
|
||||
$table->double('fatsat_r')->nullable();
|
||||
$table->double('fib_alim_nut')->nullable();
|
||||
$table->double('fib_alim_p')->nullable();
|
||||
$table->double('fib_alim_r')->nullable();
|
||||
$table->double('fibtg_p')->nullable();
|
||||
$table->double('fibtg_r')->nullable();
|
||||
$table->double('fruits_coque_sech_p')->nullable();
|
||||
$table->double('fruits_coque_sech_r')->nullable();
|
||||
$table->double('fruits_p')->nullable();
|
||||
$table->double('fruits_r')->nullable();
|
||||
$table->double('glucides_nut')->nullable();
|
||||
$table->double('glucides_p')->nullable();
|
||||
$table->double('glucides_r')->nullable();
|
||||
$table->double('mat_grass_nut')->nullable();
|
||||
$table->double('mat_grass_p')->nullable();
|
||||
$table->double('mat_grass_r')->nullable();
|
||||
$table->double('pourcent_agi')->nullable();
|
||||
$table->double('pourcent_ags')->nullable();
|
||||
$table->double('pourcent_amidon')->nullable();
|
||||
$table->double('pourcent_apport_ref_choval_p')->nullable();
|
||||
$table->double('pourcent_apport_ref_choval_r')->nullable();
|
||||
$table->double('pourcent_apport_ref_e14_p')->nullable();
|
||||
$table->double('pourcent_apport_ref_e14_r')->nullable();
|
||||
$table->double('pourcent_apport_ref_fat_p')->nullable();
|
||||
$table->double('pourcent_apport_ref_fat_r')->nullable();
|
||||
$table->double('pourcent_apport_ref_fatsat_p')->nullable();
|
||||
$table->double('pourcent_apport_ref_fatsat_r')->nullable();
|
||||
$table->double('pourcent_apport_ref_fibtg_p')->nullable();
|
||||
$table->double('pourcent_apport_ref_fibtg_r')->nullable();
|
||||
$table->double('pourcent_apport_ref_kjo_p')->nullable();
|
||||
$table->double('pourcent_apport_ref_kjo_r')->nullable();
|
||||
$table->double('pourcent_apport_ref_pro_p')->nullable();
|
||||
$table->double('pourcent_apport_ref_pro_r')->nullable();
|
||||
$table->double('pourcent_apport_ref_salteq_p')->nullable();
|
||||
$table->double('pourcent_apport_ref_salteq_r')->nullable();
|
||||
$table->double('pourcent_apport_ref_sugar_p')->nullable();
|
||||
$table->double('pourcent_apport_ref_sugar_r')->nullable();
|
||||
$table->double('pourcent_fruits')->nullable();
|
||||
$table->double('pourcent_ss')->nullable();
|
||||
$table->double('pro_p')->nullable();
|
||||
$table->double('pro_r')->nullable();
|
||||
$table->double('proteine_nut')->nullable();
|
||||
$table->double('proteine_p')->nullable();
|
||||
$table->double('proteine_r')->nullable();
|
||||
$table->double('salteq_p')->nullable();
|
||||
$table->double('salteq_r')->nullable();
|
||||
$table->double('sel_nut')->nullable();
|
||||
$table->double('sel_p')->nullable();
|
||||
$table->double('sel_r')->nullable();
|
||||
$table->double('sodium_p')->nullable();
|
||||
$table->double('sodium_r')->nullable();
|
||||
$table->double('sucre_nut')->nullable();
|
||||
$table->double('sucre_p')->nullable();
|
||||
$table->double('sucre_r')->nullable();
|
||||
$table->double('sugar_p')->nullable();
|
||||
$table->double('sugar_r')->nullable();
|
||||
$table->double('val_energ_kcal_nut')->nullable();
|
||||
$table->double('val_energ_kcal_p')->nullable();
|
||||
$table->double('val_energ_kcal_r')->nullable();
|
||||
$table->double('val_energ_kj_nut')->nullable();
|
||||
$table->double('val_energ_kj_p')->nullable();
|
||||
$table->double('val_energ_kj_r')->nullable();
|
||||
|
||||
$table->integer('composante_n')->unsigned()->nullable();
|
||||
$table->integer('composante_p')->unsigned()->nullable();
|
||||
$table->integer('no_art')->unsigned()->nullable();
|
||||
$table->integer('p_sur_l')->unsigned()->nullable();
|
||||
$table->integer('part_g')->unsigned()->nullable();
|
||||
$table->integer('part_l')->unsigned()->nullable();
|
||||
$table->integer('part_p')->unsigned()->nullable();
|
||||
$table->integer('taille_ref')->unsigned()->nullable();
|
||||
|
||||
$table->text('ags_r_prec')->nullable();
|
||||
$table->text('allegat_autre')->nullable();
|
||||
$table->text('allegat_nutrit')->nullable();
|
||||
$table->text('allegat_sante')->nullable();
|
||||
$table->text('apport_port')->nullable();
|
||||
$table->text('apport_ref')->nullable();
|
||||
$table->text('arg_vente')->nullable();
|
||||
$table->text('astuces')->nullable();
|
||||
$table->text('classement')->nullable();
|
||||
$table->text('conseil_prepa')->nullable();
|
||||
$table->text('famille_vte')->nullable();
|
||||
$table->text('fib_alim_r_prec')->nullable();
|
||||
$table->text('fruits_coque_sech_r_prec')->nullable();
|
||||
$table->text('fruits_r_prec')->nullable();
|
||||
$table->text('glucides_r_prec')->nullable();
|
||||
$table->text('ldc_sable')->nullable();
|
||||
$table->text('lib_allergene')->nullable();
|
||||
$table->text('lib_conso')->nullable();
|
||||
$table->text('lst_ing')->nullable();
|
||||
$table->text('mat_grass_r_prec')->nullable();
|
||||
$table->text('nom_societe')->nullable();
|
||||
$table->text('nom')->nullable();
|
||||
$table->text('proteine_r_prec')->nullable();
|
||||
$table->text('score_nutri')->nullable();
|
||||
$table->text('sel_r_prec')->nullable();
|
||||
$table->text('sodium_r_prec')->nullable();
|
||||
$table->text('sucre_r_prec')->nullable();
|
||||
$table->text('taille_port')->nullable();
|
||||
$table->text('typemouvementinformatique')->nullable();
|
||||
|
||||
$table->string('allegations', 100)->nullable();
|
||||
$table->string('descriptif')->nullable();
|
||||
$table->string('nb_portion', 20)->nullable();
|
||||
$table->string('pds_moy_theo', 20)->nullable();
|
||||
$table->string('picture')->nullable();
|
||||
$table->string('prod_saison', 10)->nullable();
|
||||
$table->string('typ_portion', 200)->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('inco_datas');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
// @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
|
||||
class CreateXmlIncosTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('xml_incos', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->boolean('prod_saison');
|
||||
|
||||
$table->string('allegations')->nullable();
|
||||
$table->string('descriptif');
|
||||
$table->string('picture');
|
||||
$table->string('astuces')->nullable();
|
||||
$table->string('lib_conso')->nullable();
|
||||
$table->string('nb_portion', 20);
|
||||
$table->string('typ_portion', 200);
|
||||
$table->string('pds_moy_theo', 20)->nullable();
|
||||
|
||||
$table->string('conseil_prepa', 500);
|
||||
$table->string('arg_vente', 500);
|
||||
$table->text('lst_ing')->nullable();
|
||||
|
||||
$table->text('nutritional_values');
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('inco_datas');
|
||||
}
|
||||
}
|
||||
|
|
@ -52,8 +52,8 @@ class CreateProductsTable extends Migration
|
|||
|
||||
$table->foreignId('consumption_times_id');
|
||||
$table->foreign('consumption_times_id')->references('id')->on('consumption_times');
|
||||
$table->foreignId('inco_data_id')->nullable();
|
||||
$table->foreign('inco_data_id')->references('inco_data_id')->on('inco_datas');
|
||||
$table->foreignId('xml_inco_id')->nullable();
|
||||
$table->foreign('xml_inco_id')->references('id')->on('xml_incos');
|
||||
$table->foreignId('meat_type_id');
|
||||
$table->foreign('meat_type_id')->references('id')->on('meat_types');
|
||||
$table->foreignId('range_id');
|
||||
|
|
|
|||
|
|
@ -678,7 +678,7 @@ class MigrateOldDataBase extends LeGaulois2016Migration
|
|||
'order' => 'produit_position',
|
||||
'star' => 'produit_is_star',
|
||||
'status' => 'produit_is_published',
|
||||
// 'inco_data_id' => 'id', //! Missing Inco_data_id get from xml
|
||||
// 'xml_inco_id' => 'id', //! Missing xml_inco_id get from xml
|
||||
'created_at' => 'created_at',
|
||||
'updated_at' => 'updated_at'
|
||||
]
|
||||
|
|
|
|||
1
storage/.gitignore
vendored
1
storage/.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
eleveurs/*
|
||||
tracabilite/*
|
||||
inco/*
|
||||
|
|
|
|||
0
storage/eleveurs/.gitkeep
Normal file
0
storage/eleveurs/.gitkeep
Normal file
0
storage/eleveurs/archives/.gitkeep
Normal file
0
storage/eleveurs/archives/.gitkeep
Normal file
0
storage/tracabilite/.gitkeep
Normal file
0
storage/tracabilite/.gitkeep
Normal file
0
storage/tracabilite/archives/.gitkeep
Normal file
0
storage/tracabilite/archives/.gitkeep
Normal file
Loading…
Add table
Reference in a new issue