From 73ae94ca0a155cb0a3a56a14127f53b1c5543224 Mon Sep 17 00:00:00 2001 From: frid Date: Mon, 17 Feb 2025 09:42:35 +0100 Subject: [PATCH 01/38] add file .Ds_store --- .DS_Store | Bin 0 -> 6148 bytes src/.DS_Store | Bin 0 -> 8196 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .DS_Store create mode 100644 src/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9137ef2de7252204916eea383020815519a64a6b GIT binary patch literal 6148 zcmeHKO>WdM7=50E(X_PEs4F(8IYA_D5XyAHf=zn?=C5TSO`=SvrMv7ASKta2hv6_? zmG^zNs@O9~tWbsQk^K_qz0dZuV^08>lg;7{Fa|JU7EJe9>`3xl@>UwSnj@mo7}r?8 zFYEcNtY=&7SP>nF4m>vpr`0uZ53U8V7M@`!G1T(Bq_1?cPC086d?4m*XW=W-<3N$H2W=QP{l1%ZbKX{EU5P8)@o_-y9ox82AMV)R z&biW6^rN?mj_AUaqbYIAvZ4=pfA)CC)#CSC*H}Ns9%C%ocEcz#7t2tgYpbt#VOcDL z*t(Yb%mg=#+4?;&vWohwAz4f9eI0Uj-SoMXKD#6{$TWIsF_zJR=s;aM)S2av1mVVO-0` zeW4g_o$)iJ!xR={86AiY_zooYb}Z-r>7T#Epm6n>vKZEYtdPJfa(%9d?I)23-dpirfhlx>o2Q`(Zy{D+Eke@tU=yxX;R z(;y)t!2u9Z;J^imzY78+3dh2MQ!ju(LXn`NJ#c^v7cQWN3%v0-X+x4jFMy~=@_e2* z^E~!9-}CJA5)ryexx0w!iAd!`pi#-y3PtpbV_8u`ONk#NgGzRgeNORE(2vZD5ckWM!;f%=;XC+E^f^;W@Gb7AU5X{bgiC{P( zDKV-e1|kNQGaz#JwUi>4b9Z}b{vL~E3Xbjh?d{(~iB+swyH3^gN^@v#Vk7o03f6)^CBiX!DnsU7fqb8envLz?&TZbHHz<$#5Mt$p? zFV4oje9`X?29Db7D{K>nQSz*l7CYEwtB2h@1KLMbo8#M>Tifrw?}6?k$NT5j-&Sqt z`o>KoW!tkejy0C{vX+~(-7`becFvz1w{w>3+mG2+QPXN-)}Wi7vgT^*>J78OY+Ue? zW3T(FMw2>P%GhU1wm&21FcVuf)wpmf$2W2!pA%Qi1wE#j*siLN2zz(Y;#!(@vt<`M zqz65Imhd*)?$k8H8+^icLsxz8J*sBphMaW9a^%(Yj{R&pHd2;^hQ`i=s#b&DquKn_ z6hDu*FmsoxJ}PEDp7#8{g5~n$!LudI!>T$adJg4sY&7cXYkQBH=D0VRE?Rxlu2nR? zRn@?Lg6+Y&agVr8DrfuoV%Yg`&}rCi+4gxMcxf0)o~o)%$(>S40i{jJcG=SSC2mq1 zzp{Jj6j@ZD8G4akrt|bBy+;@5B7II@(3kWTeM56}m42dM=r{VEuF*UcRH6#&QH@4y zM>BR{FYZSt4kC#Z9zq}baS|gqjWjaI;cwkco4@hh+)>{7{-xhU0QHaz}Y~ZPq8*< z@eH12ZJx&~conbV4OZy;Qe}&(>s_ikeDHVGl?RHhlXuUseL<;9U7}XRz)dqC_I@t) zBL7dV{{24~6jc!e5d;5E22jy6(9^}ww%Xnza_s~kr}+>?jGL7hxNsGukkF0XP0GjrRXw5u)mrW8g2caI2XB literal 0 HcmV?d00001 From 4f8dc41fd84c6aa61a623ad3e62e156c3c9dde39 Mon Sep 17 00:00:00 2001 From: frid Date: Mon, 17 Feb 2025 23:20:31 +0100 Subject: [PATCH 02/38] phpoffice/phpspreadshee --- .DS_Store | Bin 6148 -> 10244 bytes composer.json | 1 + composer.lock | 665 ++++++++++++++++++++++++---- src/.DS_Store | Bin 8196 -> 10244 bytes src/Command/ImportThemesCommand.php | 4 + src/Script/IngestTheme.php | 4 + 6 files changed, 592 insertions(+), 82 deletions(-) create mode 100644 src/Command/ImportThemesCommand.php create mode 100644 src/Script/IngestTheme.php diff --git a/.DS_Store b/.DS_Store index 9137ef2de7252204916eea383020815519a64a6b..727b6d55c26cc5d1ec8bc2c4b6266a2b5b445634 100644 GIT binary patch literal 10244 zcmeHNO>0v@6upzAF|`d8LBxgh*_ErGIQ?Cxo76xzIiPXiSdQP z7?DRr30%?+4WbGuvR_^uC1P5}VFmoDTpG*f{YtF^=^C5@P64NYQ@|HnyoPdQgORh1IUY z95IA;N4sZmX=9t(YIhRm@FC31!kkcqnjPbN3Qi)et*e~^PJvDZw&DJsz%HLUY{ z?DGrWSMf8MT-nc|E%9LO`_#(DhYNOpp=W<>mc1KDeuXO?*_)SUX@PRq>@={|iQpWb zpQG*En&J=`r{cGnbVpfybh3%lG|l6uNDZ35RQB^GUYSy4tf^g9@QKL6+RVk>U_a-5 z<>TX%kF<8y7@T!CObF*F<*;(q0NZsi;A{=b=Gk`}DAU6k{=C{!w$2E9;EUH5S)q z`21>n^<<1@KE8V0Ft$$bF%2cbRjX9To~vO0)u2=j_e0H&AW{OFujo;r5hs+LhUc7O z64N1w>TCvY{jwj{R>i~gcmhxgASYB2Sp$mPLbPSr z0=~~v!`3Ex5k6d-IQ)@M;4w$}0CuAn*4CnL1Xyb!g#-GQu^?D)sOy{OnZ+j&>d+j)0VbHx% z7qj(h*{{l7D<9Yf!F35S&8U64g!n@5X_aN4uq=BFt3QoZEYnTYRjhxC-{ts*tYYg# zGWYQ@`Y}ckbJsA6yjO}r50!)V@v!wvJjEO#<8m#xAGY>KD6*|GTrgPP8b;}mcT96|*|ER)U17lHDq z7FHr?^l)9nC5yy0wN);tBt*8W*YWrwU&o&@ZLJ4b^ruZ*Q(NT@KjjYlkNz0I-{Y~f cJAb~%F>3@I-TA-uR@a^X_x`i(e>ngD0nI}=>Hq)$ delta 174 zcmZn(XfcprU|?W$DortDU=RQ@Ie-{Mvv5sJ6q~50C@KV!2aA<36np06Cnx3PCv8kz z&dew@Swv8O@@YY}$+Lw-CYK8JOzsrUn>=46XYypx_{m9PdndmX=bn5@Tz+!D1pDLy z3Hiy|lDw07B;_ZjODG#Us5xPgQ#$jXh0-&?bKX diff --git a/composer.json b/composer.json index 6483993..86eb592 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "doctrine/doctrine-bundle": "^2.13", "doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/orm": "^3.3", + "phpoffice/phpspreadsheet": "^4.0", "symfony/asset": "7.2.*", "symfony/console": "7.2.*", "symfony/dotenv": "7.2.*", diff --git a/composer.lock b/composer.lock index 0d925a5..c12d8f1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,87 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7ed4531db22012d48ac80870f73ea4ec", + "content-hash": "0af4d9851fba492801e7aa639896c4e7", "packages": [ + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, { "name": "composer/semver", "version": "3.4.3", @@ -1308,6 +1387,296 @@ }, "time": "2024-10-21T18:21:57+00:00" }, + { + "name": "maennchen/zipstream-php", + "version": "3.1.2", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.2" + }, + "require-dev": { + "brianium/paratest": "^7.7", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.16", + "guzzlehttp/guzzle": "^7.5", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^11.0", + "vimeo/psalm": "^6.0" + }, + "suggest": { + "guzzlehttp/psr7": "^2.4", + "psr/http-message": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": [ + "stream", + "zip" + ], + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.2" + }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], + "time": "2025-01-27T12:07:53+00:00" + }, + { + "name": "markbaker/complex", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", + "keywords": [ + "complex", + "mathematics" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" + }, + "time": "2022-12-06T16:21:08+00:00" + }, + { + "name": "markbaker/matrix", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" + }, + "time": "2022-12-02T22:17:43+00:00" + }, + { + "name": "phpoffice/phpspreadsheet", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "2d4b9f3582102aafb6b1378fff8a20eeeacfb31c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/2d4b9f3582102aafb6b1378fff8a20eeeacfb31c", + "reference": "2d4b9f3582102aafb6b1378fff8a20eeeacfb31c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1||^2||^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "maennchen/zipstream-php": "^2.1 || ^3.0", + "markbaker/complex": "^3.0", + "markbaker/matrix": "^3.0", + "php": "^8.1", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "dompdf/dompdf": "^2.0 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.3", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^10.5", + "squizlabs/php_codesniffer": "^3.7", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + } + ], + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", + "keywords": [ + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/4.0.0" + }, + "time": "2025-02-08T02:28:25+00:00" + }, { "name": "psr/cache", "version": "3.0.0", @@ -1460,6 +1829,166 @@ }, "time": "2019-01-08T18:20:26+00:00" }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, { "name": "psr/log", "version": "3.0.2", @@ -1510,6 +2039,57 @@ }, "time": "2024-09-11T13:17:53+00:00" }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, { "name": "symfony/asset", "version": "v7.2.0", @@ -4819,85 +5399,6 @@ ], "time": "2022-12-23T10:58:28+00:00" }, - { - "name": "composer/pcre", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<1.11.10" - }, - "require-dev": { - "phpstan/phpstan": "^1.12 || ^2", - "phpstan/phpstan-strict-rules": "^1 || ^2", - "phpunit/phpunit": "^8 || ^9" - }, - "type": "library", - "extra": { - "phpstan": { - "includes": [ - "extension.neon" - ] - }, - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Pcre\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" - ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.3.2" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2024-11-12T16:29:46+00:00" - }, { "name": "composer/xdebug-handler", "version": "3.0.5", @@ -8069,7 +8570,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { @@ -8077,6 +8578,6 @@ "ext-ctype": "*", "ext-iconv": "*" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/src/.DS_Store b/src/.DS_Store index 721e5db606bae6d5c86d2bc3f9ebcbbecfab0f83..e529ffba508c9fb0507dde522bfb66713e870fe5 100644 GIT binary patch delta 1100 zcmZp1XbF&DU|?W$DortDU{C-uIe-{M3-C-V6q~50C<>Mb@);P481fl%fH0MzXk+0t z_K6L=o7p*7IO;(vxEP#)vUv<8$f`VZ@{^Nt@{>Tu0kI-`42Pyu9>0==2aPz?59J;I0Ufr^oRh*Kk~FB$&gvjpK&1`b0F#xF^$!5)R1U61Z6 zWZz=bfUX|Z$Do*EU}JCvM`k9_ua&6L$S8~ENm&%ds6Jy0-OMAflr;j`4>LM4b}_C8 z`vFBKva@liL3irpg`)EHTnyfzGzoNYDlm2GF%&RlFchFUx*N^WTns@#)dj#bTMUfS z5+GfK;nAPO=*da1M@}z{i9k(Q4Mz@w74zUhfI}@Jy%JJez=x7x1vVD$W)oxvx&jCk yxPgQ#C^K&?{LVa?UnWq536jM%K+=p13Zo{JeCK`i+HNOoAYtKp?>lBwRrzZY=!HJegl5 YkOOEQ$R38t@jTNei-~VW)9cOz0Kc9eUjP6A diff --git a/src/Command/ImportThemesCommand.php b/src/Command/ImportThemesCommand.php new file mode 100644 index 0000000..d5d28b6 --- /dev/null +++ b/src/Command/ImportThemesCommand.php @@ -0,0 +1,4 @@ + Date: Wed, 19 Feb 2025 00:29:45 +0100 Subject: [PATCH 03/38] creation des fonctions pour la lecture et gestions de donnees excel --- .DS_Store | Bin 10244 -> 10244 bytes public/.DS_Store | Bin 0 -> 6148 bytes public/File/CITEPA.xlsx | Bin 0 -> 55840 bytes public/File/~$CITEPA.xlsx | Bin 0 -> 165 bytes src/Command/ImportThemesCommand.php | 4 - src/Controller/Api/ThemesController.php | 18 +++- src/Script/IngestTheme.php | 129 ++++++++++++++++++++++++ 7 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 public/.DS_Store create mode 100644 public/File/CITEPA.xlsx create mode 100644 public/File/~$CITEPA.xlsx delete mode 100644 src/Command/ImportThemesCommand.php diff --git a/.DS_Store b/.DS_Store index 727b6d55c26cc5d1ec8bc2c4b6266a2b5b445634..a6ff609d7717d6e9217863a16641269311b9c6d6 100644 GIT binary patch delta 490 zcmZn(XbG6$&*-u-U^hRb%Vr({9@hG#^5TM|octsP28JC;1v#0;B?bo97@3$^SlQS) z*g3d4VuLgC%Y#c2OG=BK5{sfiypa6-oFo`KF)1uFwLD%x#5q5&Br!8DwFs;sGbI(M zBqlsFFD1X+DZex?r5LO?7$U*J$-x;fAW`jFZD?SkqhMlaP^+U*ZE0ixWE-2+)^c)) zD(hPZ#b@W_=H+(*9R~!Aj1ZcE7fQpZ?#UdY-I8n!1q`JONenp*nGDHg!9{sF`FZIe UZJQ^EwsUS~R}dU2zHMOy0583St^fc4 delta 45 vcmZn(XbG6$&*-!1q_sv?}k5|86{_**%)Mdr3ME1vc zJ=#9^+jz`N!A~%sd`}O%z$v4g-cg5Y8d1HmBcCwp^p5ycQKDE4SR6j5A-oQ_4Pxaw zpWWEtqiOBjDqzlryMJyKunV^eSOvBzAioa*ip0oZt5JPAP^lCE=%8B}eD&)O3}go| zGT3TF4~%IlP*a&nF__65n5H8hnenYgO(&+FjCstHnaNO?NjzAfh7*f4+S)2$6U={eU6ktw2?)R`HbGM#ZoV;rTl-DTIRp75G@C$KSjNkwO literal 0 HcmV?d00001 diff --git a/public/File/CITEPA.xlsx b/public/File/CITEPA.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b2b5ec579f5ef5cf7389179b70aaee5b691e8753 GIT binary patch literal 55840 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 Ijson([ @@ -36,4 +37,19 @@ public function index(): JsonResponse ], ]); } + + #[Route('/test', name: 'app_test')] + public function testXls(): mixed + { + $ingestTheme = new IngestTheme(); + $file = $this->getParameter('kernel.project_dir') . '/public/File/CITEPA.xlsx'; + + if (!file_exists($file)) { + return new JsonResponse(["erreur"=>"File not found ".$file], 404); + } + $data = $ingestTheme->GetJsonDataFileXlsx($file); + + return new JsonResponse($data, 200, [], false); + + } } diff --git a/src/Script/IngestTheme.php b/src/Script/IngestTheme.php index e7a72bb..0991ff2 100644 --- a/src/Script/IngestTheme.php +++ b/src/Script/IngestTheme.php @@ -2,3 +2,132 @@ namespace App\Script; +use App\Entity\Theme; +use PhpOffice\PhpSpreadsheet\IOFactory; +use PhpOffice\PhpSpreadsheet\Spreadsheet; + +class IngestTheme +{ + + public function GetJsonDataFileXlsx(string $file): mixed + { + $data = []; + $code = ['code'=>null,'externalId'=>null]; + + if (!file_exists($file)) { + return ["File not found"]; + } + $spreadsheet = IOFactory::load($file); + $sheet = $spreadsheet->getActiveSheet(); + + $newSpreadsheet = new Spreadsheet(); + $newSheet = $newSpreadsheet->getActiveSheet(); + + foreach ($sheet->getRowIterator() as $row) { + $rowIndex = $row->getRowIndex(); + $cellA = $sheet->getCell('A' . $rowIndex)->getValue(); + $cellB = $sheet->getCell('B' . $rowIndex)->getValue(); + if($cellA !== null && $cellB !== null){ + if($rowIndex > 2){ + $data[] = [ + "categories_id" => $cellB, + "categories" => $this->makeSpace($cellA), + ]; + } + } + } + $theme = array(); + $code = $this->getCodeConcatenateByID($data, "V0.1"); + $externalId = $this::getIdByCategorie($data, "par_gaz_à_effet_de_serre"); + $parentId = $this->getParentIdByChildId("V0.1"); + + $theme = [ + 'id'=>1, + 'code' => $code, + 'externalId' => $externalId, + 'isSection' => true, + 'parentId' => $parentId, + ]; + + $external = []; + $external[] = $this->makeExternalValue($data, id: "V0.1.5"); + return [$external,$theme, $data]; + } + + private function makeSpace(string $word):string { + $word = str_replace('/', 'et ', $word); + $word = str_replace(' ', '_', $word); + return $word; + } + + public function makeExternalValue(array $data, string $id): mixed + { + //initialisation des variables + $hierarchie = []; + $hierarchie = $this->getParentIdByChildId($id); + //-cherche la valeur de V0 dans le tableau data + + return [$hierarchie]; + } + private static function gethierarchieLevels(string $level): mixed + { + $hierarchie = []; + $check_dot = strpos($level, '.'); + + if ($check_dot !== false) { + $level_array = explode('.', $level); + while (!empty($level_array)) { + $hierarchie[] = implode('.', $level_array); + array_pop($level_array); + } + return array_reverse($hierarchie); + } else { + return [$level]; + } + } + public static function getCategoriesById(array $data, string $id): string + { + foreach ($data as $item) { + if ($item['categories_id'] === $id) { + return $item['categories']; + } + } + return ''; + } + public static function getIdByCategorie(array $data, string $categorie): string + { + foreach ($data as $item) { + if ($item['categories'] === $categorie) { + return $item['categories_id']; + } + } + return ''; + } + public function getCodeConcatenateByID(array $data, string $id):string + { + $levels = []; + $hierarchie = []; + $levels = $this::gethierarchieLevels($id); + foreach ($levels as $value) { + $hierarchie[] = $this::getCategoriesById($data, $value); + } + return implode('.', $hierarchie); + } + public function getParentIdByChildId(string $ChildId): mixed + { + $check_dot = strpos($ChildId, '.'); + + if ($check_dot !== false) { + $level_array = explode('.', $ChildId); + while (!empty($level_array)) { + $hierarchie[] = implode('.', $level_array); + array_pop($level_array); + } + $level_array = array_reverse($hierarchie); + return $level_array[count($level_array) - 2]; + } else { + return "null"; + } + } + +} \ No newline at end of file From 3f0c7edfe33e98ddd4da57c77efa43408a57a7fa Mon Sep 17 00:00:00 2001 From: frid Date: Wed, 19 Feb 2025 02:59:28 +0100 Subject: [PATCH 04/38] save themes on themes.json file --- config/services.yaml | 4 + public/File/themes.json | 1 + src/Command/CommandIngestTheme.php | 68 +++++++++++++ src/Controller/Api/ThemesController.php | 7 +- src/Script/IngestTheme.php | 123 +++++++++++++++--------- 5 files changed, 152 insertions(+), 51 deletions(-) create mode 100644 public/File/themes.json create mode 100644 src/Command/CommandIngestTheme.php diff --git a/config/services.yaml b/config/services.yaml index 877186f..7556150 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -19,6 +19,10 @@ services: - '../src/DependencyInjection/' - '../src/Entity/' - '../src/Kernel.php' + App\Command\CommandIngestTheme: + bind: + $projectDir: '%kernel.project_dir%' + # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/public/File/themes.json b/public/File/themes.json new file mode 100644 index 0000000..d7b2cb4 --- /dev/null +++ b/public/File/themes.json @@ -0,0 +1 @@ +["id","code","externalId","isSection","parentId",{"id":0,"code":"Emissions_GES","externalId":"V0","isSection":true,"parentId":"null"},{"id":1,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre","externalId":"V0.1","isSection":true,"parentId":"V0"},{"id":2,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.CO2","externalId":"V0.1.1","isSection":true,"parentId":"V0.1"},{"id":3,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.M\u00e9thane","externalId":"V0.1.2","isSection":true,"parentId":"V0.1"},{"id":4,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.Protoxyde_d'azote","externalId":"V0.1.3","isSection":true,"parentId":"V0.1"},{"id":5,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.HFC","externalId":"V0.1.4","isSection":true,"parentId":"V0.1"},{"id":6,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.SF6","externalId":"V0.1.5","isSection":true,"parentId":"V0.1"},{"id":7,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.PFC","externalId":"V0.1.6","isSection":true,"parentId":"V0.1"},{"id":8,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.NF3","externalId":"V0.1.7","isSection":true,"parentId":"V0.1"},{"id":9,"code":"Emissions_GES.par_secteur","externalId":"V0.2","isSection":true,"parentId":"V0"},{"id":10,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie","externalId":"V0.2.1","isSection":true,"parentId":"V0.2"},{"id":11,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction","externalId":"V0.2.2","isSection":true,"parentId":"V0.2"},{"id":12,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets","externalId":"V0.2.3","isSection":true,"parentId":"V0.2"},{"id":13,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires","externalId":"V0.2.4","isSection":true,"parentId":"V0.2"},{"id":14,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture","externalId":"V0.2.5","isSection":true,"parentId":"V0.2"},{"id":15,"code":"Emissions_GES.par_secteur.Transports","externalId":"V0.2.6","isSection":true,"parentId":"V0.2"},{"id":16,"code":"Emissions_GES.par_secteur.UTCATF","externalId":"V0.2.7","isSection":true,"parentId":"V0.2"},{"id":17,"code":"Emissions_GES.par_secteur.Hors_total","externalId":"V0.2.8","isSection":true,"parentId":"V0.2"},{"id":18,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Production_d'\u00e9lectricit\u00e9","externalId":"V0.2.1.1","isSection":true,"parentId":"V0.2.1"},{"id":19,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Chauffage_urbain","externalId":"V0.2.1.2","isSection":true,"parentId":"V0.2.1"},{"id":20,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Raffinage_du_p\u00e9trole","externalId":"V0.2.1.3","isSection":true,"parentId":"V0.2.1"},{"id":21,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Transformation_des_combustibles_min\u00e9raux_solides","externalId":"V0.2.1.4","isSection":true,"parentId":"V0.2.1"},{"id":22,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Extraction_et_distribution_de_combustibles_solides","externalId":"V0.2.1.5","isSection":true,"parentId":"V0.2.1"},{"id":23,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Extraction_et_distribution_de_combustibles_liquides","externalId":"V0.2.1.6","isSection":true,"parentId":"V0.2.1"},{"id":24,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Extraction_et_distribution_de_combustibles_gazeux","externalId":"V0.2.1.7","isSection":true,"parentId":"V0.2.1"},{"id":25,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Fabrication_de_charbon_de_bois_par_pyrolyse","externalId":"V0.2.1.8","isSection":true,"parentId":"V0.2.1"},{"id":26,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Valorisation_\u00e9nerg\u00e9tique_des_d\u00e9chets","externalId":"V0.2.1.9","isSection":true,"parentId":"V0.2.1"},{"id":27,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Chimie","externalId":"V0.2.2.1","isSection":true,"parentId":"V0.2.2"},{"id":28,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Construction","externalId":"V0.2.2.2","isSection":true,"parentId":"V0.2.2"},{"id":29,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Biens_d'\u00e9quipements,_mat\u00e9riels_de_transport","externalId":"V0.2.2.3","isSection":true,"parentId":"V0.2.2"},{"id":30,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Agro-alimentaire","externalId":"V0.2.2.4","isSection":true,"parentId":"V0.2.2"},{"id":31,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.M\u00e9tallurgie_des_m\u00e9taux_ferreux","externalId":"V0.2.2.5","isSection":true,"parentId":"V0.2.2"},{"id":32,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.M\u00e9tallurgie_des_m\u00e9taux_non-ferreux","externalId":"V0.2.2.6","isSection":true,"parentId":"V0.2.2"},{"id":33,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Min\u00e9raux_non-m\u00e9talliques,_mat\u00e9riaux_de_construction","externalId":"V0.2.2.7","isSection":true,"parentId":"V0.2.2"},{"id":34,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Papier,_carton","externalId":"V0.2.2.8","isSection":true,"parentId":"V0.2.2"},{"id":35,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Autres_industries_manufacturi\u00e8res","externalId":"V0.2.2.9","isSection":true,"parentId":"V0.2.2"},{"id":36,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Stockage_des_d\u00e9chets","externalId":"V0.2.3.1","isSection":true,"parentId":"V0.2.3"},{"id":37,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Incin\u00e9ration_sans_r\u00e9cup\u00e9ration_d'\u00e9nergie","externalId":"V0.2.3.2","isSection":true,"parentId":"V0.2.3"},{"id":38,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Autres_traitements_des_d\u00e9chets_solides","externalId":"V0.2.3.3","isSection":true,"parentId":"V0.2.3"},{"id":39,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Traitement_des_eaux_us\u00e9es","externalId":"V0.2.3.4","isSection":true,"parentId":"V0.2.3"},{"id":40,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques","externalId":"V0.2.4.1","isSection":true,"parentId":"V0.2.4"},{"id":41,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires","externalId":"V0.2.4.2","isSection":true,"parentId":"V0.2.4"},{"id":42,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Chauffage,_eau_chaude_sanitaire_et_cuisson_domestique","externalId":"V0.2.4.1.1","isSection":true,"parentId":"V0.2.4.1"},{"id":43,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Climatisation_domestique","externalId":"V0.2.4.1.2","isSection":true,"parentId":"V0.2.4.1"},{"id":44,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.R\u00e9frig\u00e9ration_domestique","externalId":"V0.2.4.1.3","isSection":true,"parentId":"V0.2.4.1"},{"id":45,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Utilisation_de_produits_domestiques_(y.c._peintures,_a\u00e9rosols)","externalId":"V0.2.4.1.4","isSection":true,"parentId":"V0.2.4.1"},{"id":46,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Engins_(y.c._jardinage)_domestiques","externalId":"V0.2.4.1.5","isSection":true,"parentId":"V0.2.4.1"},{"id":47,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.D\u00e9chets_et_br\u00fblage_domestiques_et_eaux_us\u00e9es","externalId":"V0.2.4.1.6","isSection":true,"parentId":"V0.2.4.1"},{"id":48,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Autres_activit\u00e9s_domestiques_(tabac_et_feux_d\u2019artifices)","externalId":"V0.2.4.1.7","isSection":true,"parentId":"V0.2.4.1"},{"id":49,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Chauffage,_eau_chaude_sanitaire_et_cuisson_tertiaire","externalId":"V0.2.4.2.1","isSection":true,"parentId":"V0.2.4.2"},{"id":50,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Climatisation_tertiaire","externalId":"V0.2.4.2.2","isSection":true,"parentId":"V0.2.4.2"},{"id":51,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.R\u00e9frig\u00e9ration_tertiaire","externalId":"V0.2.4.2.3","isSection":true,"parentId":"V0.2.4.2"},{"id":52,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Utilisation_de_produits_tertiaires_(y.c._solvants,_peintures,_a\u00e9rosols,_anesth\u00e9sie)","externalId":"V0.2.4.2.4","isSection":true,"parentId":"V0.2.4.2"},{"id":53,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Autres_activit\u00e9s_tertiaires_(y.c._feux_d\u2019artifices,_activit\u00e9s_militaires,_cr\u00e9mation)","externalId":"V0.2.4.2.5","isSection":true,"parentId":"V0.2.4.2"},{"id":54,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage","externalId":"V0.2.5.1","isSection":true,"parentId":"V0.2.5"},{"id":55,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture","externalId":"V0.2.5.2","isSection":true,"parentId":"V0.2.5"},{"id":56,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Engins,_moteurs_et_chaudi\u00e8res","externalId":"V0.2.5.3","isSection":true,"parentId":"V0.2.5"},{"id":57,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Bovins","externalId":"V0.2.5.1.1","isSection":true,"parentId":"V0.2.5.1"},{"id":58,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Porcins","externalId":"V0.2.5.1.2","isSection":true,"parentId":"V0.2.5.1"},{"id":59,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Volailles","externalId":"V0.2.5.1.3","isSection":true,"parentId":"V0.2.5.1"},{"id":60,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Autres_\u00e9missions_de_l'\u00e9levage","externalId":"V0.2.5.1.4","isSection":true,"parentId":"V0.2.5.1"},{"id":61,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Engrais_et_amendements_min\u00e9raux","externalId":"V0.2.5.2.1","isSection":true,"parentId":"V0.2.5.2"},{"id":62,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Engrais_et_amendements_organiques","externalId":"V0.2.5.2.2","isSection":true,"parentId":"V0.2.5.2"},{"id":63,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.P\u00e2ture","externalId":"V0.2.5.2.3","isSection":true,"parentId":"V0.2.5.2"},{"id":64,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Br\u00fblage_de_r\u00e9sidus_agricoles","externalId":"V0.2.5.2.4","isSection":true,"parentId":"V0.2.5.2"},{"id":65,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Autres_\u00e9missions_des_cultures","externalId":"V0.2.5.2.5","isSection":true,"parentId":"V0.2.5.2"},{"id":66,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Engins,_moteurs_et_chaudi\u00e8res.Engins,_moteurs_et_chaudi\u00e8res_en_agriculture","externalId":"V0.2.5.3.1","isSection":true,"parentId":"V0.2.5.3"},{"id":67,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Engins,_moteurs_et_chaudi\u00e8res.Engins,_moteurs_et_chaudi\u00e8res_en_sylviculture","externalId":"V0.2.5.3.2","isSection":true,"parentId":"V0.2.5.3"},{"id":68,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier","externalId":"V0.2.6.1","isSection":true,"parentId":"V0.2.6"},{"id":69,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports","externalId":"V0.2.6.2","isSection":true,"parentId":"V0.2.6"},{"id":70,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP","externalId":"V0.2.6.1.1","isSection":true,"parentId":"V0.2.6.1"},{"id":71,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL","externalId":"V0.2.6.1.2","isSection":true,"parentId":"V0.2.6.1"},{"id":72,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises","externalId":"V0.2.6.1.3","isSection":true,"parentId":"V0.2.6.1"},{"id":73,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars","externalId":"V0.2.6.1.4","isSection":true,"parentId":"V0.2.6.1"},{"id":74,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues","externalId":"V0.2.6.1.5","isSection":true,"parentId":"V0.2.6.1"},{"id":75,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_diesel","externalId":"V0.2.6.1.1.1","isSection":true,"parentId":"V0.2.6.1.1"},{"id":76,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_essence","externalId":"V0.2.6.1.1.2","isSection":true,"parentId":"V0.2.6.1.1"},{"id":77,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_GPL","externalId":"V0.2.6.1.1.3","isSection":true,"parentId":"V0.2.6.1.1"},{"id":78,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_GNV","externalId":"V0.2.6.1.1.4","isSection":true,"parentId":"V0.2.6.1.1"},{"id":79,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_\u00e9lectriques","externalId":"V0.2.6.1.1.5","isSection":true,"parentId":"V0.2.6.1.1"},{"id":80,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_diesel","externalId":"V0.2.6.1.2.1","isSection":true,"parentId":"V0.2.6.1.2"},{"id":81,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_essence","externalId":"V0.2.6.1.2.2","isSection":true,"parentId":"V0.2.6.1.2"},{"id":82,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_GPL","externalId":"V0.2.6.1.2.3","isSection":true,"parentId":"V0.2.6.1.2"},{"id":83,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_GNV","externalId":"V0.2.6.1.2.4","isSection":true,"parentId":"V0.2.6.1.2"},{"id":84,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_\u00e9lectriques","externalId":"V0.2.6.1.2.5","isSection":true,"parentId":"V0.2.6.1.2"},{"id":85,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_diesel","externalId":"V0.2.6.1.3.1","isSection":true,"parentId":"V0.2.6.1.3"},{"id":86,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_essence","externalId":"V0.2.6.1.3.2","isSection":true,"parentId":"V0.2.6.1.3"},{"id":87,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_GNV","externalId":"V0.2.6.1.3.3","isSection":true,"parentId":"V0.2.6.1.3"},{"id":88,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_\u00e9lectriques","externalId":"V0.2.6.1.3.4","isSection":true,"parentId":"V0.2.6.1.3"},{"id":89,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_diesel","externalId":"V0.2.6.1.4.1","isSection":true,"parentId":"V0.2.6.1.4"},{"id":90,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_essence","externalId":"V0.2.6.1.4.2","isSection":true,"parentId":"V0.2.6.1.4"},{"id":91,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_GNV","externalId":"V0.2.6.1.4.3","isSection":true,"parentId":"V0.2.6.1.4"},{"id":92,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_\u00e9lectriques","externalId":"V0.2.6.1.4.4","isSection":true,"parentId":"V0.2.6.1.4"},{"id":93,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues.Deux_roues_essence","externalId":"V0.2.6.1.5.1","isSection":true,"parentId":"V0.2.6.1.5"},{"id":94,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues.Deux_roues_diesel","externalId":"V0.2.6.1.5.2","isSection":true,"parentId":"V0.2.6.1.5"},{"id":95,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues.Deux_roues_\u00e9lectriques","externalId":"V0.2.6.1.5.3","isSection":true,"parentId":"V0.2.6.1.5"},{"id":96,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_ferroviaire","externalId":"V0.2.6.2.1","isSection":true,"parentId":"V0.2.6.2"},{"id":97,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_fluvial_de_marchandises","externalId":"V0.2.6.2.2","isSection":true,"parentId":"V0.2.6.2"},{"id":98,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_maritime_domestique","externalId":"V0.2.6.2.3","isSection":true,"parentId":"V0.2.6.2"},{"id":99,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_autres_navigations","externalId":"V0.2.6.2.4","isSection":true,"parentId":"V0.2.6.2"},{"id":100,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_a\u00e9rien_fran\u00e7ais","externalId":"V0.2.6.2.5","isSection":true,"parentId":"V0.2.6.2"},{"id":101,"code":"Emissions_GES.par_secteur.UTCATF.For\u00eats","externalId":"V0.2.7.1","isSection":true,"parentId":"V0.2.7"},{"id":102,"code":"Emissions_GES.par_secteur.UTCATF.Terres_cultiv\u00e9es","externalId":"V0.2.7.2","isSection":true,"parentId":"V0.2.7"},{"id":103,"code":"Emissions_GES.par_secteur.UTCATF.Prairies","externalId":"V0.2.7.3","isSection":true,"parentId":"V0.2.7"},{"id":104,"code":"Emissions_GES.par_secteur.UTCATF.Zones_humides","externalId":"V0.2.7.4","isSection":true,"parentId":"V0.2.7"},{"id":105,"code":"Emissions_GES.par_secteur.UTCATF.Zones_artificialis\u00e9es","externalId":"V0.2.7.5","isSection":true,"parentId":"V0.2.7"},{"id":106,"code":"Emissions_GES.par_secteur.UTCATF.Autres_terres","externalId":"V0.2.7.6","isSection":true,"parentId":"V0.2.7"},{"id":107,"code":"Emissions_GES.par_secteur.UTCATF.Produits_bois","externalId":"V0.2.7.7","isSection":true,"parentId":"V0.2.7"},{"id":108,"code":"Emissions_GES.par_secteur.UTCATF.Barrages","externalId":"V0.2.7.8","isSection":true,"parentId":"V0.2.7"},{"id":109,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total","externalId":"V0.2.8.1","isSection":true,"parentId":"V0.2.8"},{"id":110,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles","externalId":"V0.2.8.2","isSection":true,"parentId":"V0.2.8"},{"id":111,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Transport_fluvial_international_-_hors_total_national","externalId":"V0.2.8.1.1","isSection":true,"parentId":"V0.2.8.1"},{"id":112,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Transport_maritime_international_-_hors_total_national","externalId":"V0.2.8.1.2","isSection":true,"parentId":"V0.2.8.1"},{"id":113,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Transport_a\u00e9rien_international_-_hors_total_national","externalId":"V0.2.8.1.3","isSection":true,"parentId":"V0.2.8.1"},{"id":114,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Autres_engins_hors_total_national","externalId":"V0.2.8.1.4","isSection":true,"parentId":"V0.2.8.1"},{"id":115,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles.V\u00e9g\u00e9tation_(dont_polluants_des_feux_de_for\u00eat)","externalId":"V0.2.8.2.1","isSection":true,"parentId":"V0.2.8.2"},{"id":116,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles.Eaux","externalId":"V0.2.8.2.2","isSection":true,"parentId":"V0.2.8.2"},{"id":117,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles.Autres_\u00e9missions_naturelles_(volcans,_foudre\u2026)","externalId":"V0.2.8.2.3","isSection":true,"parentId":"V0.2.8.2"}] \ No newline at end of file diff --git a/src/Command/CommandIngestTheme.php b/src/Command/CommandIngestTheme.php new file mode 100644 index 0000000..20381cc --- /dev/null +++ b/src/Command/CommandIngestTheme.php @@ -0,0 +1,68 @@ +projectDir = $projectDir; + parent::__construct(); + } + + protected function configure(): void + { + $this + ->addArgument('getjson', InputArgument::OPTIONAL, 'obtenir les données json') + ->addOption('option1', null, InputOption::VALUE_NONE, 'Option description') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $arg1 = $input->getArgument('getjson'); + + $IngestTheme = new IngestTheme(); + + if ($arg1) { + $io->note(sprintf('You passed an argument: %s', $arg1)); + } + + // Assurez-vous que le chemin du fichier est correct et que le fichier est au format .xlsx + $filePath = $this->projectDir.'/public/File/CITEPA.xlsx'; // Remplacez par le chemin correct + if (!file_exists($filePath)) { + $io->error('Le fichier n\'existe pas : '.$filePath); + + return Command::FAILURE; + } + + try { + $result = $IngestTheme->SaveThemesOnFileJson($IngestTheme->GetJsonDataFileXlsx($filePath), $this->projectDir.'/public/File/themes.json'); + $io->success('Résultat: '.json_encode($result)); + } catch (\Exception $e) { + $io->error('Erreur lors de la lecture du fichier : '.$e->getMessage()); + + return Command::FAILURE; + } + + $io->success('List how many themes have been added '. count($IngestTheme->GetJsonDataFileXlsx($filePath)) . ' themes'); + + return Command::SUCCESS; + } +} diff --git a/src/Controller/Api/ThemesController.php b/src/Controller/Api/ThemesController.php index 48d97d7..3ae8497 100644 --- a/src/Controller/Api/ThemesController.php +++ b/src/Controller/Api/ThemesController.php @@ -2,10 +2,10 @@ namespace App\Controller\Api; +use App\Script\IngestTheme; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; -use App\Script\IngestTheme; #[Route('/api')] final class ThemesController extends AbstractController @@ -42,14 +42,13 @@ public function index(): JsonResponse public function testXls(): mixed { $ingestTheme = new IngestTheme(); - $file = $this->getParameter('kernel.project_dir') . '/public/File/CITEPA.xlsx'; + $file = $this->getParameter('kernel.project_dir').'/public/File/CITEPA.xlsx'; if (!file_exists($file)) { - return new JsonResponse(["erreur"=>"File not found ".$file], 404); + return new JsonResponse(['erreur' => 'File not found '.$file], 404); } $data = $ingestTheme->GetJsonDataFileXlsx($file); return new JsonResponse($data, 200, [], false); - } } diff --git a/src/Script/IngestTheme.php b/src/Script/IngestTheme.php index 0991ff2..73a8ae2 100644 --- a/src/Script/IngestTheme.php +++ b/src/Script/IngestTheme.php @@ -1,21 +1,18 @@ -null,'externalId'=>null]; - + if (!file_exists($file)) { - return ["File not found"]; + return ['File not found']; } $spreadsheet = IOFactory::load($file); $sheet = $spreadsheet->getActiveSheet(); @@ -23,68 +20,59 @@ public function GetJsonDataFileXlsx(string $file): mixed $newSpreadsheet = new Spreadsheet(); $newSheet = $newSpreadsheet->getActiveSheet(); - foreach ($sheet->getRowIterator() as $row) { - $rowIndex = $row->getRowIndex(); - $cellA = $sheet->getCell('A' . $rowIndex)->getValue(); - $cellB = $sheet->getCell('B' . $rowIndex)->getValue(); - if($cellA !== null && $cellB !== null){ - if($rowIndex > 2){ + foreach ($sheet->getRowIterator() as $row) { + $rowIndex = $row->getRowIndex(); + $cellA = $sheet->getCell('A'.$rowIndex)->getValue(); + $cellB = $sheet->getCell('B'.$rowIndex)->getValue(); + if (null !== $cellA && null !== $cellB) { + if ($rowIndex > 2) { $data[] = [ - "categories_id" => $cellB, - "categories" => $this->makeSpace($cellA), - ]; - } - } + 'categories_id' => $cellB, + 'categories' => $this->makeSpace($cellA), + ]; + } + } } - $theme = array(); - $code = $this->getCodeConcatenateByID($data, "V0.1"); - $externalId = $this::getIdByCategorie($data, "par_gaz_à_effet_de_serre"); - $parentId = $this->getParentIdByChildId("V0.1"); - - $theme = [ - 'id'=>1, - 'code' => $code, - 'externalId' => $externalId, - 'isSection' => true, - 'parentId' => $parentId, - ]; - - $external = []; - $external[] = $this->makeExternalValue($data, id: "V0.1.5"); - return [$external,$theme, $data]; + + return $data; } - private function makeSpace(string $word):string { + private function makeSpace(string $word): string + { $word = str_replace('/', 'et ', $word); $word = str_replace(' ', '_', $word); + return $word; } public function makeExternalValue(array $data, string $id): mixed { - //initialisation des variables + // initialisation des variables $hierarchie = []; - $hierarchie = $this->getParentIdByChildId($id); - //-cherche la valeur de V0 dans le tableau data - - return [$hierarchie]; + + // -cherche la valeur de V0 dans le tableau data + + return [$hierarchie]; } + private static function gethierarchieLevels(string $level): mixed { $hierarchie = []; $check_dot = strpos($level, '.'); - if ($check_dot !== false) { + if (false !== $check_dot) { $level_array = explode('.', $level); while (!empty($level_array)) { $hierarchie[] = implode('.', $level_array); array_pop($level_array); } + return array_reverse($hierarchie); } else { return [$level]; } } + public static function getCategoriesById(array $data, string $id): string { foreach ($data as $item) { @@ -92,8 +80,10 @@ public static function getCategoriesById(array $data, string $id): string return $item['categories']; } } + return ''; } + public static function getIdByCategorie(array $data, string $categorie): string { foreach ($data as $item) { @@ -101,9 +91,11 @@ public static function getIdByCategorie(array $data, string $categorie): string return $item['categories_id']; } } + return ''; } - public function getCodeConcatenateByID(array $data, string $id):string + + public function getCodeConcatenateByID(array $data, string $id): string { $levels = []; $hierarchie = []; @@ -111,23 +103,60 @@ public function getCodeConcatenateByID(array $data, string $id):string foreach ($levels as $value) { $hierarchie[] = $this::getCategoriesById($data, $value); } - return implode('.', $hierarchie); + + return implode('.', $hierarchie); } + public function getParentIdByChildId(string $ChildId): mixed { $check_dot = strpos($ChildId, '.'); - if ($check_dot !== false) { + if (false !== $check_dot) { $level_array = explode('.', $ChildId); while (!empty($level_array)) { $hierarchie[] = implode('.', $level_array); array_pop($level_array); } - $level_array = array_reverse($hierarchie); + $level_array = array_reverse($hierarchie); + return $level_array[count($level_array) - 2]; } else { - return "null"; + return 'null'; } } -} \ No newline at end of file + private function getCategoriesId($data): array + { + $categories_id = []; + foreach ($data as $item) { + $categories_id[] = $item['categories_id']; + } + + return $categories_id; + } + + public function SaveThemesOnFileJson(array $data, ?string $filePath = null): mixed + { + $file = $filePath ?? '/default/path/to/Theme.json'; + $categories_id = $this->getCategoriesId($data); + + $themes = ['id', 'code', 'externalId', 'isSection', 'parentId']; + $dir = dirname($file); + if (!is_dir($dir)) { + mkdir($dir, 0777, true); + } + for ($i = 0; $i < count($categories_id); ++$i) { + $themes[] = [ + 'id' => $i, + 'code' => $this->getCodeConcatenateByID($data, $categories_id[$i]), + 'externalId' => $categories_id[$i], + 'isSection' => true, + 'parentId' => $this->getParentIdByChildId($categories_id[$i]), + ]; + } + $json = json_encode($themes); + file_put_contents($file, $json); + + return true; + } +} From 33598d3f3e29818ae1d069dcbd1579a77572c47f Mon Sep 17 00:00:00 2001 From: frid Date: Wed, 19 Feb 2025 03:05:23 +0100 Subject: [PATCH 05/38] fix code --- src/Command/CommandIngestTheme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Command/CommandIngestTheme.php b/src/Command/CommandIngestTheme.php index 20381cc..6e2e325 100644 --- a/src/Command/CommandIngestTheme.php +++ b/src/Command/CommandIngestTheme.php @@ -61,7 +61,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } - $io->success('List how many themes have been added '. count($IngestTheme->GetJsonDataFileXlsx($filePath)) . ' themes'); + $io->success('List how many themes have been added '.count($IngestTheme->GetJsonDataFileXlsx($filePath)).' themes'); return Command::SUCCESS; } From 9dc1744f2eadcb8c62b9dfd678ee8547db2cc498 Mon Sep 17 00:00:00 2001 From: frid Date: Wed, 19 Feb 2025 09:32:50 +0100 Subject: [PATCH 06/38] update composer and fix code IngestTheme --- composer.lock | 291 ++++++++++++++--------------- public/File/themes.json | 2 +- src/Command/CommandIngestTheme.php | 7 +- src/Script/IngestTheme.php | 17 +- 4 files changed, 150 insertions(+), 167 deletions(-) diff --git a/composer.lock b/composer.lock index c12d8f1..6ee1073 100644 --- a/composer.lock +++ b/composer.lock @@ -623,22 +623,22 @@ }, { "name": "doctrine/doctrine-migrations-bundle", - "version": "3.3.1", + "version": "3.4.1", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", - "reference": "715b62c31a5894afcb2b2cdbbc6607d7dd0580c0" + "reference": "e858ce0f5c12b266dce7dce24834448355155da7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/715b62c31a5894afcb2b2cdbbc6607d7dd0580c0", - "reference": "715b62c31a5894afcb2b2cdbbc6607d7dd0580c0", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/e858ce0f5c12b266dce7dce24834448355155da7", + "reference": "e858ce0f5c12b266dce7dce24834448355155da7", "shasum": "" }, "require": { "doctrine/doctrine-bundle": "^2.4", "doctrine/migrations": "^3.2", - "php": "^7.2|^8.0", + "php": "^7.2 || ^8.0", "symfony/deprecation-contracts": "^2.1 || ^3", "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" }, @@ -646,27 +646,21 @@ "composer/semver": "^3.0", "doctrine/coding-standard": "^12", "doctrine/orm": "^2.6 || ^3", - "doctrine/persistence": "^2.0 || ^3 ", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-deprecation-rules": "^1", - "phpstan/phpstan-phpunit": "^1", - "phpstan/phpstan-strict-rules": "^1.1", - "phpstan/phpstan-symfony": "^1.3", - "phpunit/phpunit": "^8.5|^9.5", - "psalm/plugin-phpunit": "^0.18.4", - "psalm/plugin-symfony": "^3 || ^5", + "doctrine/persistence": "^2.0 || ^3", + "phpstan/phpstan": "^1.4 || ^2", + "phpstan/phpstan-deprecation-rules": "^1 || ^2", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1.1 || ^2", + "phpstan/phpstan-symfony": "^1.3 || ^2", + "phpunit/phpunit": "^8.5 || ^9.5", "symfony/phpunit-bridge": "^6.3 || ^7", - "symfony/var-exporter": "^5.4 || ^6 || ^7", - "vimeo/psalm": "^4.30 || ^5.15" + "symfony/var-exporter": "^5.4 || ^6 || ^7" }, "type": "symfony-bundle", "autoload": { "psr-4": { - "Doctrine\\Bundle\\MigrationsBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Doctrine\\Bundle\\MigrationsBundle\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -695,7 +689,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", - "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.3.1" + "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.4.1" }, "funding": [ { @@ -711,7 +705,7 @@ "type": "tidelift" } ], - "time": "2024-05-14T20:32:18+00:00" + "time": "2025-01-27T22:48:22+00:00" }, { "name": "doctrine/event-manager", @@ -1147,16 +1141,16 @@ }, { "name": "doctrine/orm", - "version": "3.3.1", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "b1f8253105aa5382c495e5f9f8ef34e297775428" + "reference": "c9557c588b3a70ed93caff069d0aa75737f25609" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/b1f8253105aa5382c495e5f9f8ef34e297775428", - "reference": "b1f8253105aa5382c495e5f9f8ef34e297775428", + "url": "https://api.github.com/repos/doctrine/orm/zipball/c9557c588b3a70ed93caff069d0aa75737f25609", + "reference": "c9557c588b3a70ed93caff069d0aa75737f25609", "shasum": "" }, "require": { @@ -1231,9 +1225,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/3.3.1" + "source": "https://github.com/doctrine/orm/tree/3.3.2" }, - "time": "2024-12-19T07:08:14+00:00" + "time": "2025-02-04T19:43:15+00:00" }, { "name": "doctrine/persistence", @@ -1333,16 +1327,16 @@ }, { "name": "doctrine/sql-formatter", - "version": "1.5.1", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "b784cbde727cf806721451dde40eff4fec3bbe86" + "reference": "d6d00aba6fd2957fe5216fe2b7673e9985db20c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/b784cbde727cf806721451dde40eff4fec3bbe86", - "reference": "b784cbde727cf806721451dde40eff4fec3bbe86", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/d6d00aba6fd2957fe5216fe2b7673e9985db20c8", + "reference": "d6d00aba6fd2957fe5216fe2b7673e9985db20c8", "shasum": "" }, "require": { @@ -1352,8 +1346,7 @@ "doctrine/coding-standard": "^12", "ergebnis/phpunit-slow-test-detector": "^2.14", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "^5.24" + "phpunit/phpunit": "^10.5" }, "bin": [ "bin/sql-formatter" @@ -1383,9 +1376,9 @@ ], "support": { "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.5.1" + "source": "https://github.com/doctrine/sql-formatter/tree/1.5.2" }, - "time": "2024-10-21T18:21:57+00:00" + "time": "2025-01-24T11:45:48+00:00" }, { "name": "maennchen/zipstream-php", @@ -2240,16 +2233,16 @@ }, { "name": "symfony/cache", - "version": "v7.2.1", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "e7e983596b744c4539f31e79b0350a6cf5878a20" + "reference": "8d773a575e446de220dca03d600b2d8e1c1c10ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/e7e983596b744c4539f31e79b0350a6cf5878a20", - "reference": "e7e983596b744c4539f31e79b0350a6cf5878a20", + "url": "https://api.github.com/repos/symfony/cache/zipball/8d773a575e446de220dca03d600b2d8e1c1c10ec", + "reference": "8d773a575e446de220dca03d600b2d8e1c1c10ec", "shasum": "" }, "require": { @@ -2318,7 +2311,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.2.1" + "source": "https://github.com/symfony/cache/tree/v7.2.3" }, "funding": [ { @@ -2334,7 +2327,7 @@ "type": "tidelift" } ], - "time": "2024-12-07T08:08:50+00:00" + "time": "2025-01-27T11:08:17+00:00" }, { "name": "symfony/cache-contracts", @@ -2414,16 +2407,16 @@ }, { "name": "symfony/config", - "version": "v7.2.0", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "bcd3c4adf0144dee5011bb35454728c38adec055" + "reference": "7716594aaae91d9141be080240172a92ecca4d44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/bcd3c4adf0144dee5011bb35454728c38adec055", - "reference": "bcd3c4adf0144dee5011bb35454728c38adec055", + "url": "https://api.github.com/repos/symfony/config/zipball/7716594aaae91d9141be080240172a92ecca4d44", + "reference": "7716594aaae91d9141be080240172a92ecca4d44", "shasum": "" }, "require": { @@ -2469,7 +2462,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.2.0" + "source": "https://github.com/symfony/config/tree/v7.2.3" }, "funding": [ { @@ -2485,7 +2478,7 @@ "type": "tidelift" } ], - "time": "2024-11-04T11:36:24+00:00" + "time": "2025-01-22T12:07:01+00:00" }, { "name": "symfony/console", @@ -2582,16 +2575,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v7.2.0", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "a475747af1a1c98272a5471abc35f3da81197c5d" + "reference": "1d321c4bc3fe926fd4c38999a4c9af4f5d61ddfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a475747af1a1c98272a5471abc35f3da81197c5d", - "reference": "a475747af1a1c98272a5471abc35f3da81197c5d", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1d321c4bc3fe926fd4c38999a4c9af4f5d61ddfc", + "reference": "1d321c4bc3fe926fd4c38999a4c9af4f5d61ddfc", "shasum": "" }, "require": { @@ -2642,7 +2635,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.2.0" + "source": "https://github.com/symfony/dependency-injection/tree/v7.2.3" }, "funding": [ { @@ -2658,7 +2651,7 @@ "type": "tidelift" } ], - "time": "2024-11-25T15:45:00+00:00" + "time": "2025-01-17T10:56:55+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2729,21 +2722,21 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v7.2.2", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "f12195479a55b77bc8427b48443b966622f4a18b" + "reference": "7a183fdfb472c5487480baa128a41ed47367723e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/f12195479a55b77bc8427b48443b966622f4a18b", - "reference": "f12195479a55b77bc8427b48443b966622f4a18b", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/7a183fdfb472c5487480baa128a41ed47367723e", + "reference": "7a183fdfb472c5487480baa128a41ed47367723e", "shasum": "" }, "require": { "doctrine/event-manager": "^2", - "doctrine/persistence": "^3.1", + "doctrine/persistence": "^3.1|^4", "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "~1.8", @@ -2818,7 +2811,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v7.2.2" + "source": "https://github.com/symfony/doctrine-bridge/tree/v7.2.3" }, "funding": [ { @@ -2834,7 +2827,7 @@ "type": "tidelift" } ], - "time": "2024-12-19T14:25:03+00:00" + "time": "2025-01-27T11:08:17+00:00" }, { "name": "symfony/dotenv", @@ -2912,16 +2905,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.2.1", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "6150b89186573046167796fa5f3f76601d5145f8" + "reference": "959a74d044a6db21f4caa6d695648dcb5584cb49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/6150b89186573046167796fa5f3f76601d5145f8", - "reference": "6150b89186573046167796fa5f3f76601d5145f8", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/959a74d044a6db21f4caa6d695648dcb5584cb49", + "reference": "959a74d044a6db21f4caa6d695648dcb5584cb49", "shasum": "" }, "require": { @@ -2967,7 +2960,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.2.1" + "source": "https://github.com/symfony/error-handler/tree/v7.2.3" }, "funding": [ { @@ -2983,7 +2976,7 @@ "type": "tidelift" } ], - "time": "2024-12-07T08:50:44+00:00" + "time": "2025-01-07T09:39:55+00:00" }, { "name": "symfony/event-dispatcher", @@ -3341,16 +3334,16 @@ }, { "name": "symfony/framework-bundle", - "version": "v7.2.2", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "aaf86f38b483ce101c7e60be050bc0140431cfe2" + "reference": "d37a43dd0b2079605fcab3056dac71934f06dc0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/aaf86f38b483ce101c7e60be050bc0140431cfe2", - "reference": "aaf86f38b483ce101c7e60be050bc0140431cfe2", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/d37a43dd0b2079605fcab3056dac71934f06dc0f", + "reference": "d37a43dd0b2079605fcab3056dac71934f06dc0f", "shasum": "" }, "require": { @@ -3471,7 +3464,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.2.2" + "source": "https://github.com/symfony/framework-bundle/tree/v7.2.3" }, "funding": [ { @@ -3487,7 +3480,7 @@ "type": "tidelift" } ], - "time": "2024-12-19T14:25:03+00:00" + "time": "2025-01-29T07:13:55+00:00" }, { "name": "symfony/http-client", @@ -3664,16 +3657,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.2.2", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "62d1a43796ca3fea3f83a8470dfe63a4af3bc588" + "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/62d1a43796ca3fea3f83a8470dfe63a4af3bc588", - "reference": "62d1a43796ca3fea3f83a8470dfe63a4af3bc588", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ee1b504b8926198be89d05e5b6fc4c3810c090f0", + "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0", "shasum": "" }, "require": { @@ -3722,7 +3715,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.2.2" + "source": "https://github.com/symfony/http-foundation/tree/v7.2.3" }, "funding": [ { @@ -3738,20 +3731,20 @@ "type": "tidelift" } ], - "time": "2024-12-30T19:00:17+00:00" + "time": "2025-01-17T10:56:55+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.2.2", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "3c432966bd8c7ec7429663105f5a02d7e75b4306" + "reference": "caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/3c432966bd8c7ec7429663105f5a02d7e75b4306", - "reference": "3c432966bd8c7ec7429663105f5a02d7e75b4306", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b", + "reference": "caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b", "shasum": "" }, "require": { @@ -3836,7 +3829,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.2.2" + "source": "https://github.com/symfony/http-kernel/tree/v7.2.3" }, "funding": [ { @@ -3852,7 +3845,7 @@ "type": "tidelift" } ], - "time": "2024-12-31T14:59:40+00:00" + "time": "2025-01-29T07:40:13+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -4232,16 +4225,16 @@ }, { "name": "symfony/routing", - "version": "v7.2.0", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "e10a2450fa957af6c448b9b93c9010a4e4c0725e" + "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/e10a2450fa957af6c448b9b93c9010a4e4c0725e", - "reference": "e10a2450fa957af6c448b9b93c9010a4e4c0725e", + "url": "https://api.github.com/repos/symfony/routing/zipball/ee9a67edc6baa33e5fae662f94f91fd262930996", + "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996", "shasum": "" }, "require": { @@ -4293,7 +4286,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.2.0" + "source": "https://github.com/symfony/routing/tree/v7.2.3" }, "funding": [ { @@ -4309,20 +4302,20 @@ "type": "tidelift" } ], - "time": "2024-11-25T11:08:51+00:00" + "time": "2025-01-17T10:56:55+00:00" }, { "name": "symfony/runtime", - "version": "v7.2.0", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "2c350568f3eaccb25fbbbf962bd67cde273121a7" + "reference": "8e8d09bd69b7f6c0260dd3d58f37bd4fbdeab5ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/2c350568f3eaccb25fbbbf962bd67cde273121a7", - "reference": "2c350568f3eaccb25fbbbf962bd67cde273121a7", + "url": "https://api.github.com/repos/symfony/runtime/zipball/8e8d09bd69b7f6c0260dd3d58f37bd4fbdeab5ad", + "reference": "8e8d09bd69b7f6c0260dd3d58f37bd4fbdeab5ad", "shasum": "" }, "require": { @@ -4372,7 +4365,7 @@ "runtime" ], "support": { - "source": "https://github.com/symfony/runtime/tree/v7.2.0" + "source": "https://github.com/symfony/runtime/tree/v7.2.3" }, "funding": [ { @@ -4388,7 +4381,7 @@ "type": "tidelift" } ], - "time": "2024-11-06T11:43:25+00:00" + "time": "2024-12-29T21:39:47+00:00" }, { "name": "symfony/service-contracts", @@ -4896,16 +4889,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.2.0", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "c6a22929407dec8765d6e2b6ff85b800b245879c" + "reference": "82b478c69745d8878eb60f9a049a4d584996f73a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c6a22929407dec8765d6e2b6ff85b800b245879c", - "reference": "c6a22929407dec8765d6e2b6ff85b800b245879c", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/82b478c69745d8878eb60f9a049a4d584996f73a", + "reference": "82b478c69745d8878eb60f9a049a4d584996f73a", "shasum": "" }, "require": { @@ -4959,7 +4952,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.2.0" + "source": "https://github.com/symfony/var-dumper/tree/v7.2.3" }, "funding": [ { @@ -4975,7 +4968,7 @@ "type": "tidelift" } ], - "time": "2024-11-08T15:48:14+00:00" + "time": "2025-01-17T11:39:41+00:00" }, { "name": "symfony/var-exporter", @@ -5055,16 +5048,16 @@ }, { "name": "symfony/yaml", - "version": "v7.2.0", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "099581e99f557e9f16b43c5916c26380b54abb22" + "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/099581e99f557e9f16b43c5916c26380b54abb22", - "reference": "099581e99f557e9f16b43c5916c26380b54abb22", + "url": "https://api.github.com/repos/symfony/yaml/zipball/ac238f173df0c9c1120f862d0f599e17535a87ec", + "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec", "shasum": "" }, "require": { @@ -5107,7 +5100,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.2.0" + "source": "https://github.com/symfony/yaml/tree/v7.2.3" }, "funding": [ { @@ -5123,7 +5116,7 @@ "type": "tidelift" } ], - "time": "2024-10-23T06:56:12+00:00" + "time": "2025-01-07T12:55:42+00:00" }, { "name": "symfonycasts/tailwind-bundle", @@ -5467,20 +5460,20 @@ }, { "name": "doctrine/data-fixtures", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/doctrine/data-fixtures.git", - "reference": "2ae45139f148c9272c49a521d82cc50491355a99" + "reference": "f7f1e12d6bceb58c204b3e77210a103c1c57601e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/2ae45139f148c9272c49a521d82cc50491355a99", - "reference": "2ae45139f148c9272c49a521d82cc50491355a99", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/f7f1e12d6bceb58c204b3e77210a103c1c57601e", + "reference": "f7f1e12d6bceb58c204b3e77210a103c1c57601e", "shasum": "" }, "require": { - "doctrine/persistence": "^3.1", + "doctrine/persistence": "^3.1 || ^4.0", "php": "^8.1", "psr/log": "^1.1 || ^2 || ^3" }, @@ -5530,7 +5523,7 @@ ], "support": { "issues": "https://github.com/doctrine/data-fixtures/issues", - "source": "https://github.com/doctrine/data-fixtures/tree/2.0.1" + "source": "https://github.com/doctrine/data-fixtures/tree/2.0.2" }, "funding": [ { @@ -5546,7 +5539,7 @@ "type": "tidelift" } ], - "time": "2024-12-10T07:03:23+00:00" + "time": "2025-01-21T13:21:31+00:00" }, { "name": "doctrine/doctrine-fixtures-bundle", @@ -5744,16 +5737,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.68.1", + "version": "v3.69.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "b9db2b2ea3cdba7201067acee46f984ef2397cff" + "reference": "13b0c0eede38c11cd674b080f2b485d0f14ffa9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b9db2b2ea3cdba7201067acee46f984ef2397cff", - "reference": "b9db2b2ea3cdba7201067acee46f984ef2397cff", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/13b0c0eede38c11cd674b080f2b485d0f14ffa9f", + "reference": "13b0c0eede38c11cd674b080f2b485d0f14ffa9f", "shasum": "" }, "require": { @@ -5770,7 +5763,7 @@ "react/promise": "^2.0 || ^3.0", "react/socket": "^1.0", "react/stream": "^1.0", - "sebastian/diff": "^4.0 || ^5.1 || ^6.0", + "sebastian/diff": "^4.0 || ^5.1 || ^6.0 || ^7.0", "symfony/console": "^5.4 || ^6.4 || ^7.0", "symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0", "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", @@ -5783,18 +5776,18 @@ "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.4", - "infection/infection": "^0.29.8", + "facile-it/paraunit": "^1.3.1 || ^2.5", + "infection/infection": "^0.29.10", "justinrainbow/json-schema": "^5.3 || ^6.0", "keradus/cli-executor": "^2.1", "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.7", "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.5", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.5", - "phpunit/phpunit": "^9.6.22 || ^10.5.40 || ^11.5.2", - "symfony/var-dumper": "^5.4.48 || ^6.4.15 || ^7.2.0", - "symfony/yaml": "^5.4.45 || ^6.4.13 || ^7.2.0" + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", + "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.7", + "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.0", + "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.0" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -5835,7 +5828,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.68.1" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.69.1" }, "funding": [ { @@ -5843,7 +5836,7 @@ "type": "github" } ], - "time": "2025-01-17T09:20:36+00:00" + "time": "2025-02-18T23:57:43+00:00" }, { "name": "masterminds/html5", @@ -5914,16 +5907,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.1", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + "reference": "024473a478be9df5fdaca2c793f2232fe788e414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414", "shasum": "" }, "require": { @@ -5962,7 +5955,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" }, "funding": [ { @@ -5970,7 +5963,7 @@ "type": "tidelift" } ], - "time": "2024-11-08T17:47:46+00:00" + "time": "2025-02-12T12:17:51+00:00" }, { "name": "nikic/php-parser", @@ -6473,16 +6466,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.3", + "version": "11.5.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "30e319e578a7b5da3543073e30002bf82042f701" + "reference": "c9bd61aab12f0fc5e82ecfe621ff518a1d1f1049" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/30e319e578a7b5da3543073e30002bf82042f701", - "reference": "30e319e578a7b5da3543073e30002bf82042f701", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c9bd61aab12f0fc5e82ecfe621ff518a1d1f1049", + "reference": "c9bd61aab12f0fc5e82ecfe621ff518a1d1f1049", "shasum": "" }, "require": { @@ -6554,7 +6547,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.3" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.8" }, "funding": [ { @@ -6570,7 +6563,7 @@ "type": "tidelift" } ], - "time": "2025-01-13T09:36:00+00:00" + "time": "2025-02-18T06:26:59+00:00" }, { "name": "react/cache", @@ -8211,16 +8204,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v7.2.0", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "b176e1f1f550ef44c94eb971bf92488de08f7c6b" + "reference": "700a880e5089280c7cf3ca1ccf9d9de6630f5d25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b176e1f1f550ef44c94eb971bf92488de08f7c6b", - "reference": "b176e1f1f550ef44c94eb971bf92488de08f7c6b", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/700a880e5089280c7cf3ca1ccf9d9de6630f5d25", + "reference": "700a880e5089280c7cf3ca1ccf9d9de6630f5d25", "shasum": "" }, "require": { @@ -8258,7 +8251,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.2.0" + "source": "https://github.com/symfony/dom-crawler/tree/v7.2.3" }, "funding": [ { @@ -8274,7 +8267,7 @@ "type": "tidelift" } ], - "time": "2024-11-13T16:15:23+00:00" + "time": "2025-01-27T11:08:17+00:00" }, { "name": "symfony/maker-bundle", diff --git a/public/File/themes.json b/public/File/themes.json index d7b2cb4..46d5f05 100644 --- a/public/File/themes.json +++ b/public/File/themes.json @@ -1 +1 @@ -["id","code","externalId","isSection","parentId",{"id":0,"code":"Emissions_GES","externalId":"V0","isSection":true,"parentId":"null"},{"id":1,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre","externalId":"V0.1","isSection":true,"parentId":"V0"},{"id":2,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.CO2","externalId":"V0.1.1","isSection":true,"parentId":"V0.1"},{"id":3,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.M\u00e9thane","externalId":"V0.1.2","isSection":true,"parentId":"V0.1"},{"id":4,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.Protoxyde_d'azote","externalId":"V0.1.3","isSection":true,"parentId":"V0.1"},{"id":5,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.HFC","externalId":"V0.1.4","isSection":true,"parentId":"V0.1"},{"id":6,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.SF6","externalId":"V0.1.5","isSection":true,"parentId":"V0.1"},{"id":7,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.PFC","externalId":"V0.1.6","isSection":true,"parentId":"V0.1"},{"id":8,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.NF3","externalId":"V0.1.7","isSection":true,"parentId":"V0.1"},{"id":9,"code":"Emissions_GES.par_secteur","externalId":"V0.2","isSection":true,"parentId":"V0"},{"id":10,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie","externalId":"V0.2.1","isSection":true,"parentId":"V0.2"},{"id":11,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction","externalId":"V0.2.2","isSection":true,"parentId":"V0.2"},{"id":12,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets","externalId":"V0.2.3","isSection":true,"parentId":"V0.2"},{"id":13,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires","externalId":"V0.2.4","isSection":true,"parentId":"V0.2"},{"id":14,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture","externalId":"V0.2.5","isSection":true,"parentId":"V0.2"},{"id":15,"code":"Emissions_GES.par_secteur.Transports","externalId":"V0.2.6","isSection":true,"parentId":"V0.2"},{"id":16,"code":"Emissions_GES.par_secteur.UTCATF","externalId":"V0.2.7","isSection":true,"parentId":"V0.2"},{"id":17,"code":"Emissions_GES.par_secteur.Hors_total","externalId":"V0.2.8","isSection":true,"parentId":"V0.2"},{"id":18,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Production_d'\u00e9lectricit\u00e9","externalId":"V0.2.1.1","isSection":true,"parentId":"V0.2.1"},{"id":19,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Chauffage_urbain","externalId":"V0.2.1.2","isSection":true,"parentId":"V0.2.1"},{"id":20,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Raffinage_du_p\u00e9trole","externalId":"V0.2.1.3","isSection":true,"parentId":"V0.2.1"},{"id":21,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Transformation_des_combustibles_min\u00e9raux_solides","externalId":"V0.2.1.4","isSection":true,"parentId":"V0.2.1"},{"id":22,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Extraction_et_distribution_de_combustibles_solides","externalId":"V0.2.1.5","isSection":true,"parentId":"V0.2.1"},{"id":23,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Extraction_et_distribution_de_combustibles_liquides","externalId":"V0.2.1.6","isSection":true,"parentId":"V0.2.1"},{"id":24,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Extraction_et_distribution_de_combustibles_gazeux","externalId":"V0.2.1.7","isSection":true,"parentId":"V0.2.1"},{"id":25,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Fabrication_de_charbon_de_bois_par_pyrolyse","externalId":"V0.2.1.8","isSection":true,"parentId":"V0.2.1"},{"id":26,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Valorisation_\u00e9nerg\u00e9tique_des_d\u00e9chets","externalId":"V0.2.1.9","isSection":true,"parentId":"V0.2.1"},{"id":27,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Chimie","externalId":"V0.2.2.1","isSection":true,"parentId":"V0.2.2"},{"id":28,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Construction","externalId":"V0.2.2.2","isSection":true,"parentId":"V0.2.2"},{"id":29,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Biens_d'\u00e9quipements,_mat\u00e9riels_de_transport","externalId":"V0.2.2.3","isSection":true,"parentId":"V0.2.2"},{"id":30,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Agro-alimentaire","externalId":"V0.2.2.4","isSection":true,"parentId":"V0.2.2"},{"id":31,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.M\u00e9tallurgie_des_m\u00e9taux_ferreux","externalId":"V0.2.2.5","isSection":true,"parentId":"V0.2.2"},{"id":32,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.M\u00e9tallurgie_des_m\u00e9taux_non-ferreux","externalId":"V0.2.2.6","isSection":true,"parentId":"V0.2.2"},{"id":33,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Min\u00e9raux_non-m\u00e9talliques,_mat\u00e9riaux_de_construction","externalId":"V0.2.2.7","isSection":true,"parentId":"V0.2.2"},{"id":34,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Papier,_carton","externalId":"V0.2.2.8","isSection":true,"parentId":"V0.2.2"},{"id":35,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Autres_industries_manufacturi\u00e8res","externalId":"V0.2.2.9","isSection":true,"parentId":"V0.2.2"},{"id":36,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Stockage_des_d\u00e9chets","externalId":"V0.2.3.1","isSection":true,"parentId":"V0.2.3"},{"id":37,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Incin\u00e9ration_sans_r\u00e9cup\u00e9ration_d'\u00e9nergie","externalId":"V0.2.3.2","isSection":true,"parentId":"V0.2.3"},{"id":38,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Autres_traitements_des_d\u00e9chets_solides","externalId":"V0.2.3.3","isSection":true,"parentId":"V0.2.3"},{"id":39,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Traitement_des_eaux_us\u00e9es","externalId":"V0.2.3.4","isSection":true,"parentId":"V0.2.3"},{"id":40,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques","externalId":"V0.2.4.1","isSection":true,"parentId":"V0.2.4"},{"id":41,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires","externalId":"V0.2.4.2","isSection":true,"parentId":"V0.2.4"},{"id":42,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Chauffage,_eau_chaude_sanitaire_et_cuisson_domestique","externalId":"V0.2.4.1.1","isSection":true,"parentId":"V0.2.4.1"},{"id":43,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Climatisation_domestique","externalId":"V0.2.4.1.2","isSection":true,"parentId":"V0.2.4.1"},{"id":44,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.R\u00e9frig\u00e9ration_domestique","externalId":"V0.2.4.1.3","isSection":true,"parentId":"V0.2.4.1"},{"id":45,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Utilisation_de_produits_domestiques_(y.c._peintures,_a\u00e9rosols)","externalId":"V0.2.4.1.4","isSection":true,"parentId":"V0.2.4.1"},{"id":46,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Engins_(y.c._jardinage)_domestiques","externalId":"V0.2.4.1.5","isSection":true,"parentId":"V0.2.4.1"},{"id":47,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.D\u00e9chets_et_br\u00fblage_domestiques_et_eaux_us\u00e9es","externalId":"V0.2.4.1.6","isSection":true,"parentId":"V0.2.4.1"},{"id":48,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Autres_activit\u00e9s_domestiques_(tabac_et_feux_d\u2019artifices)","externalId":"V0.2.4.1.7","isSection":true,"parentId":"V0.2.4.1"},{"id":49,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Chauffage,_eau_chaude_sanitaire_et_cuisson_tertiaire","externalId":"V0.2.4.2.1","isSection":true,"parentId":"V0.2.4.2"},{"id":50,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Climatisation_tertiaire","externalId":"V0.2.4.2.2","isSection":true,"parentId":"V0.2.4.2"},{"id":51,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.R\u00e9frig\u00e9ration_tertiaire","externalId":"V0.2.4.2.3","isSection":true,"parentId":"V0.2.4.2"},{"id":52,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Utilisation_de_produits_tertiaires_(y.c._solvants,_peintures,_a\u00e9rosols,_anesth\u00e9sie)","externalId":"V0.2.4.2.4","isSection":true,"parentId":"V0.2.4.2"},{"id":53,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Autres_activit\u00e9s_tertiaires_(y.c._feux_d\u2019artifices,_activit\u00e9s_militaires,_cr\u00e9mation)","externalId":"V0.2.4.2.5","isSection":true,"parentId":"V0.2.4.2"},{"id":54,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage","externalId":"V0.2.5.1","isSection":true,"parentId":"V0.2.5"},{"id":55,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture","externalId":"V0.2.5.2","isSection":true,"parentId":"V0.2.5"},{"id":56,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Engins,_moteurs_et_chaudi\u00e8res","externalId":"V0.2.5.3","isSection":true,"parentId":"V0.2.5"},{"id":57,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Bovins","externalId":"V0.2.5.1.1","isSection":true,"parentId":"V0.2.5.1"},{"id":58,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Porcins","externalId":"V0.2.5.1.2","isSection":true,"parentId":"V0.2.5.1"},{"id":59,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Volailles","externalId":"V0.2.5.1.3","isSection":true,"parentId":"V0.2.5.1"},{"id":60,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Autres_\u00e9missions_de_l'\u00e9levage","externalId":"V0.2.5.1.4","isSection":true,"parentId":"V0.2.5.1"},{"id":61,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Engrais_et_amendements_min\u00e9raux","externalId":"V0.2.5.2.1","isSection":true,"parentId":"V0.2.5.2"},{"id":62,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Engrais_et_amendements_organiques","externalId":"V0.2.5.2.2","isSection":true,"parentId":"V0.2.5.2"},{"id":63,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.P\u00e2ture","externalId":"V0.2.5.2.3","isSection":true,"parentId":"V0.2.5.2"},{"id":64,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Br\u00fblage_de_r\u00e9sidus_agricoles","externalId":"V0.2.5.2.4","isSection":true,"parentId":"V0.2.5.2"},{"id":65,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Autres_\u00e9missions_des_cultures","externalId":"V0.2.5.2.5","isSection":true,"parentId":"V0.2.5.2"},{"id":66,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Engins,_moteurs_et_chaudi\u00e8res.Engins,_moteurs_et_chaudi\u00e8res_en_agriculture","externalId":"V0.2.5.3.1","isSection":true,"parentId":"V0.2.5.3"},{"id":67,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Engins,_moteurs_et_chaudi\u00e8res.Engins,_moteurs_et_chaudi\u00e8res_en_sylviculture","externalId":"V0.2.5.3.2","isSection":true,"parentId":"V0.2.5.3"},{"id":68,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier","externalId":"V0.2.6.1","isSection":true,"parentId":"V0.2.6"},{"id":69,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports","externalId":"V0.2.6.2","isSection":true,"parentId":"V0.2.6"},{"id":70,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP","externalId":"V0.2.6.1.1","isSection":true,"parentId":"V0.2.6.1"},{"id":71,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL","externalId":"V0.2.6.1.2","isSection":true,"parentId":"V0.2.6.1"},{"id":72,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises","externalId":"V0.2.6.1.3","isSection":true,"parentId":"V0.2.6.1"},{"id":73,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars","externalId":"V0.2.6.1.4","isSection":true,"parentId":"V0.2.6.1"},{"id":74,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues","externalId":"V0.2.6.1.5","isSection":true,"parentId":"V0.2.6.1"},{"id":75,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_diesel","externalId":"V0.2.6.1.1.1","isSection":true,"parentId":"V0.2.6.1.1"},{"id":76,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_essence","externalId":"V0.2.6.1.1.2","isSection":true,"parentId":"V0.2.6.1.1"},{"id":77,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_GPL","externalId":"V0.2.6.1.1.3","isSection":true,"parentId":"V0.2.6.1.1"},{"id":78,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_GNV","externalId":"V0.2.6.1.1.4","isSection":true,"parentId":"V0.2.6.1.1"},{"id":79,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_\u00e9lectriques","externalId":"V0.2.6.1.1.5","isSection":true,"parentId":"V0.2.6.1.1"},{"id":80,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_diesel","externalId":"V0.2.6.1.2.1","isSection":true,"parentId":"V0.2.6.1.2"},{"id":81,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_essence","externalId":"V0.2.6.1.2.2","isSection":true,"parentId":"V0.2.6.1.2"},{"id":82,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_GPL","externalId":"V0.2.6.1.2.3","isSection":true,"parentId":"V0.2.6.1.2"},{"id":83,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_GNV","externalId":"V0.2.6.1.2.4","isSection":true,"parentId":"V0.2.6.1.2"},{"id":84,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_\u00e9lectriques","externalId":"V0.2.6.1.2.5","isSection":true,"parentId":"V0.2.6.1.2"},{"id":85,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_diesel","externalId":"V0.2.6.1.3.1","isSection":true,"parentId":"V0.2.6.1.3"},{"id":86,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_essence","externalId":"V0.2.6.1.3.2","isSection":true,"parentId":"V0.2.6.1.3"},{"id":87,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_GNV","externalId":"V0.2.6.1.3.3","isSection":true,"parentId":"V0.2.6.1.3"},{"id":88,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_\u00e9lectriques","externalId":"V0.2.6.1.3.4","isSection":true,"parentId":"V0.2.6.1.3"},{"id":89,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_diesel","externalId":"V0.2.6.1.4.1","isSection":true,"parentId":"V0.2.6.1.4"},{"id":90,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_essence","externalId":"V0.2.6.1.4.2","isSection":true,"parentId":"V0.2.6.1.4"},{"id":91,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_GNV","externalId":"V0.2.6.1.4.3","isSection":true,"parentId":"V0.2.6.1.4"},{"id":92,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_\u00e9lectriques","externalId":"V0.2.6.1.4.4","isSection":true,"parentId":"V0.2.6.1.4"},{"id":93,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues.Deux_roues_essence","externalId":"V0.2.6.1.5.1","isSection":true,"parentId":"V0.2.6.1.5"},{"id":94,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues.Deux_roues_diesel","externalId":"V0.2.6.1.5.2","isSection":true,"parentId":"V0.2.6.1.5"},{"id":95,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues.Deux_roues_\u00e9lectriques","externalId":"V0.2.6.1.5.3","isSection":true,"parentId":"V0.2.6.1.5"},{"id":96,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_ferroviaire","externalId":"V0.2.6.2.1","isSection":true,"parentId":"V0.2.6.2"},{"id":97,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_fluvial_de_marchandises","externalId":"V0.2.6.2.2","isSection":true,"parentId":"V0.2.6.2"},{"id":98,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_maritime_domestique","externalId":"V0.2.6.2.3","isSection":true,"parentId":"V0.2.6.2"},{"id":99,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_autres_navigations","externalId":"V0.2.6.2.4","isSection":true,"parentId":"V0.2.6.2"},{"id":100,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_a\u00e9rien_fran\u00e7ais","externalId":"V0.2.6.2.5","isSection":true,"parentId":"V0.2.6.2"},{"id":101,"code":"Emissions_GES.par_secteur.UTCATF.For\u00eats","externalId":"V0.2.7.1","isSection":true,"parentId":"V0.2.7"},{"id":102,"code":"Emissions_GES.par_secteur.UTCATF.Terres_cultiv\u00e9es","externalId":"V0.2.7.2","isSection":true,"parentId":"V0.2.7"},{"id":103,"code":"Emissions_GES.par_secteur.UTCATF.Prairies","externalId":"V0.2.7.3","isSection":true,"parentId":"V0.2.7"},{"id":104,"code":"Emissions_GES.par_secteur.UTCATF.Zones_humides","externalId":"V0.2.7.4","isSection":true,"parentId":"V0.2.7"},{"id":105,"code":"Emissions_GES.par_secteur.UTCATF.Zones_artificialis\u00e9es","externalId":"V0.2.7.5","isSection":true,"parentId":"V0.2.7"},{"id":106,"code":"Emissions_GES.par_secteur.UTCATF.Autres_terres","externalId":"V0.2.7.6","isSection":true,"parentId":"V0.2.7"},{"id":107,"code":"Emissions_GES.par_secteur.UTCATF.Produits_bois","externalId":"V0.2.7.7","isSection":true,"parentId":"V0.2.7"},{"id":108,"code":"Emissions_GES.par_secteur.UTCATF.Barrages","externalId":"V0.2.7.8","isSection":true,"parentId":"V0.2.7"},{"id":109,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total","externalId":"V0.2.8.1","isSection":true,"parentId":"V0.2.8"},{"id":110,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles","externalId":"V0.2.8.2","isSection":true,"parentId":"V0.2.8"},{"id":111,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Transport_fluvial_international_-_hors_total_national","externalId":"V0.2.8.1.1","isSection":true,"parentId":"V0.2.8.1"},{"id":112,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Transport_maritime_international_-_hors_total_national","externalId":"V0.2.8.1.2","isSection":true,"parentId":"V0.2.8.1"},{"id":113,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Transport_a\u00e9rien_international_-_hors_total_national","externalId":"V0.2.8.1.3","isSection":true,"parentId":"V0.2.8.1"},{"id":114,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Autres_engins_hors_total_national","externalId":"V0.2.8.1.4","isSection":true,"parentId":"V0.2.8.1"},{"id":115,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles.V\u00e9g\u00e9tation_(dont_polluants_des_feux_de_for\u00eat)","externalId":"V0.2.8.2.1","isSection":true,"parentId":"V0.2.8.2"},{"id":116,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles.Eaux","externalId":"V0.2.8.2.2","isSection":true,"parentId":"V0.2.8.2"},{"id":117,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles.Autres_\u00e9missions_naturelles_(volcans,_foudre\u2026)","externalId":"V0.2.8.2.3","isSection":true,"parentId":"V0.2.8.2"}] \ No newline at end of file +[{"id":1,"code":"Emissions_GES","externalId":"V0","isSection":true,"parentId":"null"},{"id":2,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre","externalId":"V0.1","isSection":true,"parentId":"V0"},{"id":3,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.CO2","externalId":"V0.1.1","isSection":true,"parentId":"V0.1"},{"id":4,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.M\u00e9thane","externalId":"V0.1.2","isSection":true,"parentId":"V0.1"},{"id":5,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.Protoxyde_d'azote","externalId":"V0.1.3","isSection":true,"parentId":"V0.1"},{"id":6,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.HFC","externalId":"V0.1.4","isSection":true,"parentId":"V0.1"},{"id":7,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.SF6","externalId":"V0.1.5","isSection":true,"parentId":"V0.1"},{"id":8,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.PFC","externalId":"V0.1.6","isSection":true,"parentId":"V0.1"},{"id":9,"code":"Emissions_GES.par_gaz_\u00e0_effet_de_serre.NF3","externalId":"V0.1.7","isSection":true,"parentId":"V0.1"},{"id":10,"code":"Emissions_GES.par_secteur","externalId":"V0.2","isSection":true,"parentId":"V0"},{"id":11,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie","externalId":"V0.2.1","isSection":true,"parentId":"V0.2"},{"id":12,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction","externalId":"V0.2.2","isSection":true,"parentId":"V0.2"},{"id":13,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets","externalId":"V0.2.3","isSection":true,"parentId":"V0.2"},{"id":14,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires","externalId":"V0.2.4","isSection":true,"parentId":"V0.2"},{"id":15,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture","externalId":"V0.2.5","isSection":true,"parentId":"V0.2"},{"id":16,"code":"Emissions_GES.par_secteur.Transports","externalId":"V0.2.6","isSection":true,"parentId":"V0.2"},{"id":17,"code":"Emissions_GES.par_secteur.UTCATF","externalId":"V0.2.7","isSection":true,"parentId":"V0.2"},{"id":18,"code":"Emissions_GES.par_secteur.Hors_total","externalId":"V0.2.8","isSection":true,"parentId":"V0.2"},{"id":19,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Production_d'\u00e9lectricit\u00e9","externalId":"V0.2.1.1","isSection":true,"parentId":"V0.2.1"},{"id":20,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Chauffage_urbain","externalId":"V0.2.1.2","isSection":true,"parentId":"V0.2.1"},{"id":21,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Raffinage_du_p\u00e9trole","externalId":"V0.2.1.3","isSection":true,"parentId":"V0.2.1"},{"id":22,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Transformation_des_combustibles_min\u00e9raux_solides","externalId":"V0.2.1.4","isSection":true,"parentId":"V0.2.1"},{"id":23,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Extraction_et_distribution_de_combustibles_solides","externalId":"V0.2.1.5","isSection":true,"parentId":"V0.2.1"},{"id":24,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Extraction_et_distribution_de_combustibles_liquides","externalId":"V0.2.1.6","isSection":true,"parentId":"V0.2.1"},{"id":25,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Extraction_et_distribution_de_combustibles_gazeux","externalId":"V0.2.1.7","isSection":true,"parentId":"V0.2.1"},{"id":26,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Fabrication_de_charbon_de_bois_par_pyrolyse","externalId":"V0.2.1.8","isSection":true,"parentId":"V0.2.1"},{"id":27,"code":"Emissions_GES.par_secteur.Industrie_de_l'\u00e9nergie.Valorisation_\u00e9nerg\u00e9tique_des_d\u00e9chets","externalId":"V0.2.1.9","isSection":true,"parentId":"V0.2.1"},{"id":28,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Chimie","externalId":"V0.2.2.1","isSection":true,"parentId":"V0.2.2"},{"id":29,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Construction","externalId":"V0.2.2.2","isSection":true,"parentId":"V0.2.2"},{"id":30,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Biens_d'\u00e9quipements,_mat\u00e9riels_de_transport","externalId":"V0.2.2.3","isSection":true,"parentId":"V0.2.2"},{"id":31,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Agro-alimentaire","externalId":"V0.2.2.4","isSection":true,"parentId":"V0.2.2"},{"id":32,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.M\u00e9tallurgie_des_m\u00e9taux_ferreux","externalId":"V0.2.2.5","isSection":true,"parentId":"V0.2.2"},{"id":33,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.M\u00e9tallurgie_des_m\u00e9taux_non-ferreux","externalId":"V0.2.2.6","isSection":true,"parentId":"V0.2.2"},{"id":34,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Min\u00e9raux_non-m\u00e9talliques,_mat\u00e9riaux_de_construction","externalId":"V0.2.2.7","isSection":true,"parentId":"V0.2.2"},{"id":35,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Papier,_carton","externalId":"V0.2.2.8","isSection":true,"parentId":"V0.2.2"},{"id":36,"code":"Emissions_GES.par_secteur.Industrie_manufacturi\u00e8re_et_construction.Autres_industries_manufacturi\u00e8res","externalId":"V0.2.2.9","isSection":true,"parentId":"V0.2.2"},{"id":37,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Stockage_des_d\u00e9chets","externalId":"V0.2.3.1","isSection":true,"parentId":"V0.2.3"},{"id":38,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Incin\u00e9ration_sans_r\u00e9cup\u00e9ration_d'\u00e9nergie","externalId":"V0.2.3.2","isSection":true,"parentId":"V0.2.3"},{"id":39,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Autres_traitements_des_d\u00e9chets_solides","externalId":"V0.2.3.3","isSection":true,"parentId":"V0.2.3"},{"id":40,"code":"Emissions_GES.par_secteur.Traitement_centralis\u00e9_des_d\u00e9chets.Traitement_des_eaux_us\u00e9es","externalId":"V0.2.3.4","isSection":true,"parentId":"V0.2.3"},{"id":41,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques","externalId":"V0.2.4.1","isSection":true,"parentId":"V0.2.4"},{"id":42,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires","externalId":"V0.2.4.2","isSection":true,"parentId":"V0.2.4"},{"id":43,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Chauffage,_eau_chaude_sanitaire_et_cuisson_domestique","externalId":"V0.2.4.1.1","isSection":true,"parentId":"V0.2.4.1"},{"id":44,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Climatisation_domestique","externalId":"V0.2.4.1.2","isSection":true,"parentId":"V0.2.4.1"},{"id":45,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.R\u00e9frig\u00e9ration_domestique","externalId":"V0.2.4.1.3","isSection":true,"parentId":"V0.2.4.1"},{"id":46,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Utilisation_de_produits_domestiques_(y.c._peintures,_a\u00e9rosols)","externalId":"V0.2.4.1.4","isSection":true,"parentId":"V0.2.4.1"},{"id":47,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Engins_(y.c._jardinage)_domestiques","externalId":"V0.2.4.1.5","isSection":true,"parentId":"V0.2.4.1"},{"id":48,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.D\u00e9chets_et_br\u00fblage_domestiques_et_eaux_us\u00e9es","externalId":"V0.2.4.1.6","isSection":true,"parentId":"V0.2.4.1"},{"id":49,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_r\u00e9sidentiels_et_activit\u00e9s_domestiques.Autres_activit\u00e9s_domestiques_(tabac_et_feux_d\u2019artifices)","externalId":"V0.2.4.1.7","isSection":true,"parentId":"V0.2.4.1"},{"id":50,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Chauffage,_eau_chaude_sanitaire_et_cuisson_tertiaire","externalId":"V0.2.4.2.1","isSection":true,"parentId":"V0.2.4.2"},{"id":51,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Climatisation_tertiaire","externalId":"V0.2.4.2.2","isSection":true,"parentId":"V0.2.4.2"},{"id":52,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.R\u00e9frig\u00e9ration_tertiaire","externalId":"V0.2.4.2.3","isSection":true,"parentId":"V0.2.4.2"},{"id":53,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Utilisation_de_produits_tertiaires_(y.c._solvants,_peintures,_a\u00e9rosols,_anesth\u00e9sie)","externalId":"V0.2.4.2.4","isSection":true,"parentId":"V0.2.4.2"},{"id":54,"code":"Emissions_GES.par_secteur.Usage_des_b\u00e2timents_et_activit\u00e9s_r\u00e9sidentielset_tertiaires.sous-total_Usage_des_b\u00e2timents_tertiaires_et_activit\u00e9s_tertiaires.Autres_activit\u00e9s_tertiaires_(y.c._feux_d\u2019artifices,_activit\u00e9s_militaires,_cr\u00e9mation)","externalId":"V0.2.4.2.5","isSection":true,"parentId":"V0.2.4.2"},{"id":55,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage","externalId":"V0.2.5.1","isSection":true,"parentId":"V0.2.5"},{"id":56,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture","externalId":"V0.2.5.2","isSection":true,"parentId":"V0.2.5"},{"id":57,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Engins,_moteurs_et_chaudi\u00e8res","externalId":"V0.2.5.3","isSection":true,"parentId":"V0.2.5"},{"id":58,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Bovins","externalId":"V0.2.5.1.1","isSection":true,"parentId":"V0.2.5.1"},{"id":59,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Porcins","externalId":"V0.2.5.1.2","isSection":true,"parentId":"V0.2.5.1"},{"id":60,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Volailles","externalId":"V0.2.5.1.3","isSection":true,"parentId":"V0.2.5.1"},{"id":61,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total_Elevage.Autres_\u00e9missions_de_l'\u00e9levage","externalId":"V0.2.5.1.4","isSection":true,"parentId":"V0.2.5.1"},{"id":62,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Engrais_et_amendements_min\u00e9raux","externalId":"V0.2.5.2.1","isSection":true,"parentId":"V0.2.5.2"},{"id":63,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Engrais_et_amendements_organiques","externalId":"V0.2.5.2.2","isSection":true,"parentId":"V0.2.5.2"},{"id":64,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.P\u00e2ture","externalId":"V0.2.5.2.3","isSection":true,"parentId":"V0.2.5.2"},{"id":65,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Br\u00fblage_de_r\u00e9sidus_agricoles","externalId":"V0.2.5.2.4","isSection":true,"parentId":"V0.2.5.2"},{"id":66,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Culture.Autres_\u00e9missions_des_cultures","externalId":"V0.2.5.2.5","isSection":true,"parentId":"V0.2.5.2"},{"id":67,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Engins,_moteurs_et_chaudi\u00e8res.Engins,_moteurs_et_chaudi\u00e8res_en_agriculture","externalId":"V0.2.5.3.1","isSection":true,"parentId":"V0.2.5.3"},{"id":68,"code":"Emissions_GES.par_secteur.Agriculture_et__sylviculture.sous-total__Engins,_moteurs_et_chaudi\u00e8res.Engins,_moteurs_et_chaudi\u00e8res_en_sylviculture","externalId":"V0.2.5.3.2","isSection":true,"parentId":"V0.2.5.3"},{"id":69,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier","externalId":"V0.2.6.1","isSection":true,"parentId":"V0.2.6"},{"id":70,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports","externalId":"V0.2.6.2","isSection":true,"parentId":"V0.2.6"},{"id":71,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP","externalId":"V0.2.6.1.1","isSection":true,"parentId":"V0.2.6.1"},{"id":72,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL","externalId":"V0.2.6.1.2","isSection":true,"parentId":"V0.2.6.1"},{"id":73,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises","externalId":"V0.2.6.1.3","isSection":true,"parentId":"V0.2.6.1"},{"id":74,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars","externalId":"V0.2.6.1.4","isSection":true,"parentId":"V0.2.6.1"},{"id":75,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues","externalId":"V0.2.6.1.5","isSection":true,"parentId":"V0.2.6.1"},{"id":76,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_diesel","externalId":"V0.2.6.1.1.1","isSection":true,"parentId":"V0.2.6.1.1"},{"id":77,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_essence","externalId":"V0.2.6.1.1.2","isSection":true,"parentId":"V0.2.6.1.1"},{"id":78,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_GPL","externalId":"V0.2.6.1.1.3","isSection":true,"parentId":"V0.2.6.1.1"},{"id":79,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_GNV","externalId":"V0.2.6.1.1.4","isSection":true,"parentId":"V0.2.6.1.1"},{"id":80,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VP.VP_\u00e9lectriques","externalId":"V0.2.6.1.1.5","isSection":true,"parentId":"V0.2.6.1.1"},{"id":81,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_diesel","externalId":"V0.2.6.1.2.1","isSection":true,"parentId":"V0.2.6.1.2"},{"id":82,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_essence","externalId":"V0.2.6.1.2.2","isSection":true,"parentId":"V0.2.6.1.2"},{"id":83,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_GPL","externalId":"V0.2.6.1.2.3","isSection":true,"parentId":"V0.2.6.1.2"},{"id":84,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_GNV","externalId":"V0.2.6.1.2.4","isSection":true,"parentId":"V0.2.6.1.2"},{"id":85,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.VUL.VUL_\u00e9lectriques","externalId":"V0.2.6.1.2.5","isSection":true,"parentId":"V0.2.6.1.2"},{"id":86,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_diesel","externalId":"V0.2.6.1.3.1","isSection":true,"parentId":"V0.2.6.1.3"},{"id":87,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_essence","externalId":"V0.2.6.1.3.2","isSection":true,"parentId":"V0.2.6.1.3"},{"id":88,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_GNV","externalId":"V0.2.6.1.3.3","isSection":true,"parentId":"V0.2.6.1.3"},{"id":89,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.PL_de_marchandises.PL_de_marchandises_\u00e9lectriques","externalId":"V0.2.6.1.3.4","isSection":true,"parentId":"V0.2.6.1.3"},{"id":90,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_diesel","externalId":"V0.2.6.1.4.1","isSection":true,"parentId":"V0.2.6.1.4"},{"id":91,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_essence","externalId":"V0.2.6.1.4.2","isSection":true,"parentId":"V0.2.6.1.4"},{"id":92,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_GNV","externalId":"V0.2.6.1.4.3","isSection":true,"parentId":"V0.2.6.1.4"},{"id":93,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Bus_et_cars.Bus_et_cars_\u00e9lectriques","externalId":"V0.2.6.1.4.4","isSection":true,"parentId":"V0.2.6.1.4"},{"id":94,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues.Deux_roues_essence","externalId":"V0.2.6.1.5.1","isSection":true,"parentId":"V0.2.6.1.5"},{"id":95,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues.Deux_roues_diesel","externalId":"V0.2.6.1.5.2","isSection":true,"parentId":"V0.2.6.1.5"},{"id":96,"code":"Emissions_GES.par_secteur.Transports.sous-total_Transport_routier.Deux_roues.Deux_roues_\u00e9lectriques","externalId":"V0.2.6.1.5.3","isSection":true,"parentId":"V0.2.6.1.5"},{"id":97,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_ferroviaire","externalId":"V0.2.6.2.1","isSection":true,"parentId":"V0.2.6.2"},{"id":98,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_fluvial_de_marchandises","externalId":"V0.2.6.2.2","isSection":true,"parentId":"V0.2.6.2"},{"id":99,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_maritime_domestique","externalId":"V0.2.6.2.3","isSection":true,"parentId":"V0.2.6.2"},{"id":100,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_autres_navigations","externalId":"V0.2.6.2.4","isSection":true,"parentId":"V0.2.6.2"},{"id":101,"code":"Emissions_GES.par_secteur.Transports.sous-total_Autres_transports.Transport_a\u00e9rien_fran\u00e7ais","externalId":"V0.2.6.2.5","isSection":true,"parentId":"V0.2.6.2"},{"id":102,"code":"Emissions_GES.par_secteur.UTCATF.For\u00eats","externalId":"V0.2.7.1","isSection":true,"parentId":"V0.2.7"},{"id":103,"code":"Emissions_GES.par_secteur.UTCATF.Terres_cultiv\u00e9es","externalId":"V0.2.7.2","isSection":true,"parentId":"V0.2.7"},{"id":104,"code":"Emissions_GES.par_secteur.UTCATF.Prairies","externalId":"V0.2.7.3","isSection":true,"parentId":"V0.2.7"},{"id":105,"code":"Emissions_GES.par_secteur.UTCATF.Zones_humides","externalId":"V0.2.7.4","isSection":true,"parentId":"V0.2.7"},{"id":106,"code":"Emissions_GES.par_secteur.UTCATF.Zones_artificialis\u00e9es","externalId":"V0.2.7.5","isSection":true,"parentId":"V0.2.7"},{"id":107,"code":"Emissions_GES.par_secteur.UTCATF.Autres_terres","externalId":"V0.2.7.6","isSection":true,"parentId":"V0.2.7"},{"id":108,"code":"Emissions_GES.par_secteur.UTCATF.Produits_bois","externalId":"V0.2.7.7","isSection":true,"parentId":"V0.2.7"},{"id":109,"code":"Emissions_GES.par_secteur.UTCATF.Barrages","externalId":"V0.2.7.8","isSection":true,"parentId":"V0.2.7"},{"id":110,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total","externalId":"V0.2.8.1","isSection":true,"parentId":"V0.2.8"},{"id":111,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles","externalId":"V0.2.8.2","isSection":true,"parentId":"V0.2.8"},{"id":112,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Transport_fluvial_international_-_hors_total_national","externalId":"V0.2.8.1.1","isSection":true,"parentId":"V0.2.8.1"},{"id":113,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Transport_maritime_international_-_hors_total_national","externalId":"V0.2.8.1.2","isSection":true,"parentId":"V0.2.8.1"},{"id":114,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Transport_a\u00e9rien_international_-_hors_total_national","externalId":"V0.2.8.1.3","isSection":true,"parentId":"V0.2.8.1"},{"id":115,"code":"Emissions_GES.par_secteur.Hors_total.Transports_-_hors_total.Autres_engins_hors_total_national","externalId":"V0.2.8.1.4","isSection":true,"parentId":"V0.2.8.1"},{"id":116,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles.V\u00e9g\u00e9tation_(dont_polluants_des_feux_de_for\u00eat)","externalId":"V0.2.8.2.1","isSection":true,"parentId":"V0.2.8.2"},{"id":117,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles.Eaux","externalId":"V0.2.8.2.2","isSection":true,"parentId":"V0.2.8.2"},{"id":118,"code":"Emissions_GES.par_secteur.Hors_total.Emissions_naturelles.Autres_\u00e9missions_naturelles_(volcans,_foudre\u2026)","externalId":"V0.2.8.2.3","isSection":true,"parentId":"V0.2.8.2"}] \ No newline at end of file diff --git a/src/Command/CommandIngestTheme.php b/src/Command/CommandIngestTheme.php index 6e2e325..33e87f7 100644 --- a/src/Command/CommandIngestTheme.php +++ b/src/Command/CommandIngestTheme.php @@ -28,7 +28,7 @@ public function __construct(string $projectDir) protected function configure(): void { $this - ->addArgument('getjson', InputArgument::OPTIONAL, 'obtenir les données json') + ->addArgument('addThemes', InputArgument::OPTIONAL, 'ajouter les themes dans le fichier themes.json') ->addOption('option1', null, InputOption::VALUE_NONE, 'Option description') ; } @@ -36,7 +36,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - $arg1 = $input->getArgument('getjson'); + $arg1 = $input->getArgument('addThemes'); $IngestTheme = new IngestTheme(); @@ -44,8 +44,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->note(sprintf('You passed an argument: %s', $arg1)); } - // Assurez-vous que le chemin du fichier est correct et que le fichier est au format .xlsx - $filePath = $this->projectDir.'/public/File/CITEPA.xlsx'; // Remplacez par le chemin correct + $filePath = $this->projectDir.'/public/File/CITEPA.xlsx'; if (!file_exists($filePath)) { $io->error('Le fichier n\'existe pas : '.$filePath); diff --git a/src/Script/IngestTheme.php b/src/Script/IngestTheme.php index 73a8ae2..e3c2d6e 100644 --- a/src/Script/IngestTheme.php +++ b/src/Script/IngestTheme.php @@ -45,16 +45,6 @@ private function makeSpace(string $word): string return $word; } - public function makeExternalValue(array $data, string $id): mixed - { - // initialisation des variables - $hierarchie = []; - - // -cherche la valeur de V0 dans le tableau data - - return [$hierarchie]; - } - private static function gethierarchieLevels(string $level): mixed { $hierarchie = []; @@ -140,14 +130,15 @@ public function SaveThemesOnFileJson(array $data, ?string $filePath = null): mix $file = $filePath ?? '/default/path/to/Theme.json'; $categories_id = $this->getCategoriesId($data); - $themes = ['id', 'code', 'externalId', 'isSection', 'parentId']; + $themes = []; $dir = dirname($file); if (!is_dir($dir)) { mkdir($dir, 0777, true); } - for ($i = 0; $i < count($categories_id); ++$i) { + $y = 1; + for ($i = 0; $i < count($categories_id); ++$i, ++$y) { $themes[] = [ - 'id' => $i, + 'id' => $y, 'code' => $this->getCodeConcatenateByID($data, $categories_id[$i]), 'externalId' => $categories_id[$i], 'isSection' => true, From 02db4472885af9fb1f80e01d5246052c846b6f6c Mon Sep 17 00:00:00 2001 From: frid Date: Thu, 20 Feb 2025 01:09:16 +0100 Subject: [PATCH 07/38] Modify the parentId property in the Theme entity and change its type to string. --- .DS_Store | Bin 10244 -> 10244 bytes .gitignore | 1 + config/services.yaml | 3 ++ migrations/Version20250219234628.php | 43 +++++++++++++++++ src/Command/CommandIngestTheme.php | 30 ++++++------ src/Command/CommandSaveTheme.php | 61 ++++++++++++++++++++++++ src/Controller/Api/ThemesController.php | 20 ++++---- src/Entity/Theme.php | 6 +-- src/Script/SaveTheme.php | 33 +++++++++++++ tests/ThemeSaveTest.php | 55 +++++++++++++++++++++ 10 files changed, 223 insertions(+), 29 deletions(-) create mode 100644 migrations/Version20250219234628.php create mode 100644 src/Command/CommandSaveTheme.php create mode 100644 src/Script/SaveTheme.php create mode 100644 tests/ThemeSaveTest.php diff --git a/.DS_Store b/.DS_Store index a6ff609d7717d6e9217863a16641269311b9c6d6..1182f170b69e11d7fd8b4e78a56b9fb674d90d64 100644 GIT binary patch delta 70 zcmZn(XbG6$&*-)>U^hRb+h!htD$dCgq758IMg}?xMwTX%7l_(Vu9qwjW@ad3NMtA~ a3ogpb$cIQxvBF delta 36 scmZn(XbG6$&*-u-U^hRb%Vr*dD$dCvQkk1COPR1vEO@e+UEwb~0PZ6VH2?qr diff --git a/.gitignore b/.gitignore index 18ada98..18e4e01 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ script_memoire.txt /public/assets/ /assets/vendor/ ###< symfony/asset-mapper ### +.DS_Store diff --git a/config/services.yaml b/config/services.yaml index 7556150..ed753f2 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -22,6 +22,9 @@ services: App\Command\CommandIngestTheme: bind: $projectDir: '%kernel.project_dir%' + App\Command\CommandSaveTheme: + bind: + $projectDir: '%kernel.project_dir%' # add more service definitions when explicit configuration is needed diff --git a/migrations/Version20250219234628.php b/migrations/Version20250219234628.php new file mode 100644 index 0000000..1015fee --- /dev/null +++ b/migrations/Version20250219234628.php @@ -0,0 +1,43 @@ +addSql('CREATE TEMPORARY TABLE __temp__theme AS SELECT id, parent_id, code, is_section, external_id FROM theme'); + $this->addSql('DROP TABLE theme'); + $this->addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id VARCHAR(255) DEFAULT NULL, code VARCHAR(255) NOT NULL, is_section BOOLEAN NOT NULL, external_id VARCHAR(255) NOT NULL)'); + $this->addSql('INSERT INTO theme (id, parent_id, code, is_section, external_id) SELECT id, parent_id, code, is_section, external_id FROM __temp__theme'); + $this->addSql('DROP TABLE __temp__theme'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E7089F75D7B0 ON theme (external_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E70877153098 ON theme (code)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__theme AS SELECT id, parent_id, code, is_section, external_id FROM theme'); + $this->addSql('DROP TABLE theme'); + $this->addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, code VARCHAR(255) NOT NULL, is_section BOOLEAN NOT NULL, external_id VARCHAR(255) NOT NULL)'); + $this->addSql('INSERT INTO theme (id, parent_id, code, is_section, external_id) SELECT id, parent_id, code, is_section, external_id FROM __temp__theme'); + $this->addSql('DROP TABLE __temp__theme'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E70877153098 ON theme (code)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E7089F75D7B0 ON theme (external_id)'); + } +} diff --git a/src/Command/CommandIngestTheme.php b/src/Command/CommandIngestTheme.php index 33e87f7..ae9df9a 100644 --- a/src/Command/CommandIngestTheme.php +++ b/src/Command/CommandIngestTheme.php @@ -39,28 +39,28 @@ protected function execute(InputInterface $input, OutputInterface $output): int $arg1 = $input->getArgument('addThemes'); $IngestTheme = new IngestTheme(); - if ($arg1) { $io->note(sprintf('You passed an argument: %s', $arg1)); - } + $filePath = $this->projectDir.'/public/File/CITEPA.xlsx'; + if (!file_exists($filePath)) { + $io->error('Le fichier n\'existe pas : '.$filePath); - $filePath = $this->projectDir.'/public/File/CITEPA.xlsx'; - if (!file_exists($filePath)) { - $io->error('Le fichier n\'existe pas : '.$filePath); + return Command::FAILURE; + } - return Command::FAILURE; - } + try { + $result = $IngestTheme->SaveThemesOnFileJson($IngestTheme->GetJsonDataFileXlsx($filePath), $this->projectDir.'/public/File/themes.json'); + $io->success('Résultat: '.json_encode($result)); + } catch (\Exception $e) { + $io->error('Erreur lors de la lecture du fichier : '.$e->getMessage()); - try { - $result = $IngestTheme->SaveThemesOnFileJson($IngestTheme->GetJsonDataFileXlsx($filePath), $this->projectDir.'/public/File/themes.json'); - $io->success('Résultat: '.json_encode($result)); - } catch (\Exception $e) { - $io->error('Erreur lors de la lecture du fichier : '.$e->getMessage()); + return Command::FAILURE; + } - return Command::FAILURE; - } + $io->success('Congratulated, Themes have been added '.count($IngestTheme->GetJsonDataFileXlsx($filePath)).' themes'); - $io->success('List how many themes have been added '.count($IngestTheme->GetJsonDataFileXlsx($filePath)).' themes'); + return Command::SUCCESS; + } return Command::SUCCESS; } diff --git a/src/Command/CommandSaveTheme.php b/src/Command/CommandSaveTheme.php new file mode 100644 index 0000000..2874df2 --- /dev/null +++ b/src/Command/CommandSaveTheme.php @@ -0,0 +1,61 @@ +projectDir = $projectDir; + parent::__construct(); + } + + protected function configure(): void + { + $this + + ->addArgument('save', InputArgument::OPTIONAL, 'enregistrer les themes dans la base de données') + ->addOption('option1', null, InputOption::VALUE_NONE, 'Option description') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $arg1 = $input->getArgument('save'); + $saveTheme = new SaveTheme(); + + if ($arg1) { + $io->note(sprintf('You passed an argument: %s', $arg1)); + $filePath = $this->projectDir.'/public/File/themes.json'; + if (!file_exists($filePath)) { + $io->error('Le fichier n\'existe pas : '.$filePath); + + return Command::FAILURE; + } + + $file = $this->projectDir.'/public/File/themes.json'; + $result = $saveTheme->saveOnDatabase($file); + $io->info('le nombre des themes : '.count($result).' saved'); + + return Command::SUCCESS; + } + + return Command::SUCCESS; + } +} diff --git a/src/Controller/Api/ThemesController.php b/src/Controller/Api/ThemesController.php index 3ae8497..2c45bec 100644 --- a/src/Controller/Api/ThemesController.php +++ b/src/Controller/Api/ThemesController.php @@ -2,7 +2,7 @@ namespace App\Controller\Api; -use App\Script\IngestTheme; +use App\Script\SaveTheme; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; @@ -38,17 +38,15 @@ public function index(): JsonResponse ]); } - #[Route('/test', name: 'app_test')] - public function testXls(): mixed + #[Route('/test', name: 'app_themes', methods: ['GET'])] + public function test(): JsonResponse { - $ingestTheme = new IngestTheme(); - $file = $this->getParameter('kernel.project_dir').'/public/File/CITEPA.xlsx'; + // code... + $projectDir = $this->getParameter('kernel.project_dir'); + $file = $projectDir.'/public/File/themes.json'; + $saveTheme = new SaveTheme(); + $result = $saveTheme->saveOnDatabase($file); - if (!file_exists($file)) { - return new JsonResponse(['erreur' => 'File not found '.$file], 404); - } - $data = $ingestTheme->GetJsonDataFileXlsx($file); - - return new JsonResponse($data, 200, [], false); + return $this->json([count($result), $result]); } } diff --git a/src/Entity/Theme.php b/src/Entity/Theme.php index 8a32aad..ce072a2 100644 --- a/src/Entity/Theme.php +++ b/src/Entity/Theme.php @@ -14,7 +14,7 @@ class Theme private ?int $id = null; #[ORM\Column(nullable: true)] - private ?int $parentId = null; + private ?string $parentId = null; #[ORM\Column(length: 255, nullable: false, unique: true)] private ?string $code; @@ -37,12 +37,12 @@ public function setId(?int $id): static return $this; } - public function getParentId(): ?int + public function getParentId(): ?string { return $this->parentId; } - public function setParentId(?int $parentId): static + public function setParentId(?string $parentId): static { $this->parentId = $parentId; diff --git a/src/Script/SaveTheme.php b/src/Script/SaveTheme.php new file mode 100644 index 0000000..0e10b2a --- /dev/null +++ b/src/Script/SaveTheme.php @@ -0,0 +1,33 @@ +entityManager->persist( + (new Theme()) + ->setCode($theme['code']) + ->setId($theme['id']) + ->setParentId($theme['parentId']) + ->setExternalId($theme['externalId']) + ->setIsSection($theme['isSection']) + ); + } + $this->entityManager->flush(); + + return $data; + } +} diff --git a/tests/ThemeSaveTest.php b/tests/ThemeSaveTest.php new file mode 100644 index 0000000..8608f55 --- /dev/null +++ b/tests/ThemeSaveTest.php @@ -0,0 +1,55 @@ +entityManager = self::$kernel->getContainer()->get('doctrine.orm.entity_manager'); + $this->projectDir = self::$kernel->getContainer()->getParameter('kernel.project_dir'); + $this->entityManager = self::$kernel->getContainer()->get('doctrine.orm.entity_manager'); + $this->themeRepository = $this->entityManager->getRepository(Theme::class); + } + + protected function tearDown(): void + { + $themes = $this->themeRepository->findAll(); + foreach ($themes as $theme) { + $this->entityManager->remove($theme); + } + $this->entityManager->flush(); + parent::tearDown(); + } + + public function testSaveImportedThemes(): void + { + $theme = new Theme(); + $saveTheme = new SaveTheme(); + $file = $this->projectDir.'/public/File/themes.json'; + $themes = $saveTheme->saveOnDatabase($file); + + foreach ($themes as $theme) { + $this->entityManager->persist( + (new Theme()) + ->setCode($theme['code']) + ->setId($theme['id']) + ->setParentId($theme['parentId']) + ->setExternalId($theme['externalId']) + ->setIsSection($theme['isSection']) + ); + } + $this->entityManager->flush(); + $this->assertCount(count($themes), $this->themeRepository->findAll()); + } +} From 52b7f229e613d897a88d10d870fb21d0ebdb1224 Mon Sep 17 00:00:00 2001 From: frid Date: Thu, 20 Feb 2025 02:21:47 +0100 Subject: [PATCH 08/38] run testSaveImportedThemes on dataBase --- .gitignore | 2 ++ config/services.yaml | 1 + src/Command/CommandSaveTheme.php | 18 +++++++++++------- src/Controller/Api/ThemesController.php | 5 ++--- src/Script/SaveTheme.php | 13 +++++++++++++ tests/ThemeSaveTest.php | 10 ++++++---- 6 files changed, 35 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 18e4e01..6c7fc3b 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ script_memoire.txt /assets/vendor/ ###< symfony/asset-mapper ### .DS_Store + + diff --git a/config/services.yaml b/config/services.yaml index ed753f2..d1161e2 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -25,6 +25,7 @@ services: App\Command\CommandSaveTheme: bind: $projectDir: '%kernel.project_dir%' + # add more service definitions when explicit configuration is needed diff --git a/src/Command/CommandSaveTheme.php b/src/Command/CommandSaveTheme.php index 2874df2..b082703 100644 --- a/src/Command/CommandSaveTheme.php +++ b/src/Command/CommandSaveTheme.php @@ -3,13 +3,14 @@ namespace App\Command; use App\Script\SaveTheme; -use Symfony\Component\Console\Attribute\AsCommand; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; #[AsCommand( name: 'SaveThemes', @@ -19,16 +20,16 @@ class CommandSaveTheme extends Command { private $projectDir; - public function __construct(string $projectDir) + private $entityManager; + public function __construct(string $projectDir, EntityManagerInterface $entityManager) { $this->projectDir = $projectDir; + $this->entityManager = $entityManager; parent::__construct(); } - protected function configure(): void { $this - ->addArgument('save', InputArgument::OPTIONAL, 'enregistrer les themes dans la base de données') ->addOption('option1', null, InputOption::VALUE_NONE, 'Option description') ; @@ -51,7 +52,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int $file = $this->projectDir.'/public/File/themes.json'; $result = $saveTheme->saveOnDatabase($file); - $io->info('le nombre des themes : '.count($result).' saved'); + if ($result) { + $io->info('le nombre des themes enregistrés '); + $io->info('le nombre des themes : '.count($result).' enregistrés'); + } return Command::SUCCESS; } diff --git a/src/Controller/Api/ThemesController.php b/src/Controller/Api/ThemesController.php index 2c45bec..98d5425 100644 --- a/src/Controller/Api/ThemesController.php +++ b/src/Controller/Api/ThemesController.php @@ -41,12 +41,11 @@ public function index(): JsonResponse #[Route('/test', name: 'app_themes', methods: ['GET'])] public function test(): JsonResponse { - // code... $projectDir = $this->getParameter('kernel.project_dir'); $file = $projectDir.'/public/File/themes.json'; $saveTheme = new SaveTheme(); - $result = $saveTheme->saveOnDatabase($file); + $result = $saveTheme->saveDatabase($file); - return $this->json([count($result), $result]); + return $this->json($result); } } diff --git a/src/Script/SaveTheme.php b/src/Script/SaveTheme.php index 0e10b2a..828f445 100644 --- a/src/Script/SaveTheme.php +++ b/src/Script/SaveTheme.php @@ -7,6 +7,7 @@ class SaveTheme { private $entityManager; + private $themeRepository; public function saveOnDatabase(?string $filePath = null): mixed { @@ -27,6 +28,18 @@ public function saveOnDatabase(?string $filePath = null): mixed ); } $this->entityManager->flush(); + $themes = $this->themeRepository->findAll(); + + return count($themes) > 0 ? true : false; + } + + public function saveDatabase(?string $filePath = null): mixed + { + $file = $filePath ?? '/public/File/themes.json'; + if (!file_exists($file)) { + return ['File not found']; + } + $data = json_decode(file_get_contents($file), true); return $data; } diff --git a/tests/ThemeSaveTest.php b/tests/ThemeSaveTest.php index 8608f55..477c378 100644 --- a/tests/ThemeSaveTest.php +++ b/tests/ThemeSaveTest.php @@ -37,9 +37,10 @@ public function testSaveImportedThemes(): void $theme = new Theme(); $saveTheme = new SaveTheme(); $file = $this->projectDir.'/public/File/themes.json'; - $themes = $saveTheme->saveOnDatabase($file); - - foreach ($themes as $theme) { + $resultat = $saveTheme->saveDatabase($file); + $this->assertIsArray($resultat); + $this->assertNotEmpty($resultat); + foreach ($resultat as $theme) { $this->entityManager->persist( (new Theme()) ->setCode($theme['code']) @@ -50,6 +51,7 @@ public function testSaveImportedThemes(): void ); } $this->entityManager->flush(); - $this->assertCount(count($themes), $this->themeRepository->findAll()); + $themes = $this->themeRepository->findAll(); + $this->assertNotEmpty($themes); } } From 85ec700f613a8c3d2ad9dc9da87905bea9a486f2 Mon Sep 17 00:00:00 2001 From: frid Date: Thu, 20 Feb 2025 02:48:12 +0100 Subject: [PATCH 09/38] Import completed and theme saved to the database. --- src/Command/CommandSaveTheme.php | 11 ++++++----- src/Controller/Api/ThemesController.php | 12 ------------ src/Script/SaveTheme.php | 8 ++++++++ tests/ThemeSaveTest.php | 2 +- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/Command/CommandSaveTheme.php b/src/Command/CommandSaveTheme.php index b082703..d62a938 100644 --- a/src/Command/CommandSaveTheme.php +++ b/src/Command/CommandSaveTheme.php @@ -4,13 +4,13 @@ use App\Script\SaveTheme; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; #[AsCommand( name: 'SaveThemes', @@ -21,12 +21,14 @@ class CommandSaveTheme extends Command private $projectDir; private $entityManager; + public function __construct(string $projectDir, EntityManagerInterface $entityManager) { $this->projectDir = $projectDir; $this->entityManager = $entityManager; parent::__construct(); } + protected function configure(): void { $this @@ -39,7 +41,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $arg1 = $input->getArgument('save'); - $saveTheme = new SaveTheme(); + $saveTheme = new SaveTheme($this->entityManager); if ($arg1) { $io->note(sprintf('You passed an argument: %s', $arg1)); @@ -54,7 +56,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $result = $saveTheme->saveOnDatabase($file); if ($result) { $io->info('le nombre des themes enregistrés '); - $io->info('le nombre des themes : '.count($result).' enregistrés'); } return Command::SUCCESS; diff --git a/src/Controller/Api/ThemesController.php b/src/Controller/Api/ThemesController.php index 98d5425..996ee36 100644 --- a/src/Controller/Api/ThemesController.php +++ b/src/Controller/Api/ThemesController.php @@ -2,7 +2,6 @@ namespace App\Controller\Api; -use App\Script\SaveTheme; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; @@ -37,15 +36,4 @@ public function index(): JsonResponse ], ]); } - - #[Route('/test', name: 'app_themes', methods: ['GET'])] - public function test(): JsonResponse - { - $projectDir = $this->getParameter('kernel.project_dir'); - $file = $projectDir.'/public/File/themes.json'; - $saveTheme = new SaveTheme(); - $result = $saveTheme->saveDatabase($file); - - return $this->json($result); - } } diff --git a/src/Script/SaveTheme.php b/src/Script/SaveTheme.php index 828f445..f58d291 100644 --- a/src/Script/SaveTheme.php +++ b/src/Script/SaveTheme.php @@ -3,12 +3,20 @@ namespace App\Script; use App\Entity\Theme; +use Doctrine\ORM\EntityManagerInterface; class SaveTheme { private $entityManager; private $themeRepository; + // ajoute apres les test + public function __construct(EntityManagerInterface $entityManager) + { + $this->entityManager = $entityManager; + $this->themeRepository = $entityManager->getRepository(Theme::class); + } + public function saveOnDatabase(?string $filePath = null): mixed { $file = $filePath ?? '/public/File/themes.json'; diff --git a/tests/ThemeSaveTest.php b/tests/ThemeSaveTest.php index 477c378..28b9884 100644 --- a/tests/ThemeSaveTest.php +++ b/tests/ThemeSaveTest.php @@ -35,7 +35,7 @@ protected function tearDown(): void public function testSaveImportedThemes(): void { $theme = new Theme(); - $saveTheme = new SaveTheme(); + $saveTheme = new SaveTheme($this->entityManager); $file = $this->projectDir.'/public/File/themes.json'; $resultat = $saveTheme->saveDatabase($file); $this->assertIsArray($resultat); From e0253afffd0a911420eab7c34ae44ba418693e02 Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Fri, 21 Feb 2025 00:51:05 +0100 Subject: [PATCH 10/38] prepare theme for saving database --- public/File/emissions_GES_structure.xlsx | Bin 0 -> 55840 bytes src/Entity/Theme.php | 6 +- src/{Script => Import}/IngestTheme.php | 105 +++++++++++------------ src/Import/SaveTheme.php | 54 ++++++++++++ 4 files changed, 107 insertions(+), 58 deletions(-) create mode 100644 public/File/emissions_GES_structure.xlsx rename src/{Script => Import}/IngestTheme.php (52%) create mode 100644 src/Import/SaveTheme.php diff --git a/public/File/emissions_GES_structure.xlsx b/public/File/emissions_GES_structure.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b2b5ec579f5ef5cf7389179b70aaee5b691e8753 GIT binary patch literal 55840 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 IparentId; } - public function setParentId(?string $parentId): static + public function setParentId(?int $parentId): static { $this->parentId = $parentId; diff --git a/src/Script/IngestTheme.php b/src/Import/IngestTheme.php similarity index 52% rename from src/Script/IngestTheme.php rename to src/Import/IngestTheme.php index e3c2d6e..f387e13 100644 --- a/src/Script/IngestTheme.php +++ b/src/Import/IngestTheme.php @@ -1,20 +1,25 @@ getActiveSheet(); $newSpreadsheet = new Spreadsheet(); @@ -26,26 +31,31 @@ public function GetJsonDataFileXlsx(string $file): mixed $cellB = $sheet->getCell('B'.$rowIndex)->getValue(); if (null !== $cellA && null !== $cellB) { if ($rowIndex > 2) { - $data[] = [ + $themes[] = [ 'categories_id' => $cellB, - 'categories' => $this->makeSpace($cellA), + 'categories' => $this->replaceSpaceByUnderscore($cellA), ]; } } } - - return $data; + return $themes; } - - private function makeSpace(string $word): string + + /** + * + * replace the underscore + * to space between the word or word group + * @param string $word + * @return string + */ + private function replaceSpaceByUnderscore(string $word): string { $word = str_replace('/', 'et ', $word); $word = str_replace(' ', '_', $word); - return $word; } - private static function gethierarchieLevels(string $level): mixed + private static function getHierarchieLevels(string $level): mixed { $hierarchie = []; $check_dot = strpos($level, '.'); @@ -56,51 +66,47 @@ private static function gethierarchieLevels(string $level): mixed $hierarchie[] = implode('.', $level_array); array_pop($level_array); } - return array_reverse($hierarchie); } else { return [$level]; } } - public static function getCategoriesById(array $data, string $id): string + private static function getCategoriesByCategorieId(array $themes, string $categorie_id): string { - foreach ($data as $item) { - if ($item['categories_id'] === $id) { - return $item['categories']; + foreach ($themes as $theme) { + if ($theme['categories_id'] === $categorie_id) { + return $theme['categories']; } } - return ''; } - public static function getIdByCategorie(array $data, string $categorie): string + //cette function n'est pas appellé + /*private static function getCategorieIdByCategorie(array $themes, string $categorie): string { - foreach ($data as $item) { - if ($item['categories'] === $categorie) { - return $item['categories_id']; + foreach ($themes as $theme) { + if ($theme['categories'] === $categorie) { + return $theme['categories_id']; } } - return ''; - } + }*/ - public function getCodeConcatenateByID(array $data, string $id): string + private function getCodeConcatenateByCategorieId(array $themes, string $level_id): string { $levels = []; $hierarchie = []; - $levels = $this::gethierarchieLevels($id); - foreach ($levels as $value) { - $hierarchie[] = $this::getCategoriesById($data, $value); + $levels = $this::gethierarchieLevels($level_id); + foreach ($levels as $level) { + $hierarchie[] = $this::getCategoriesByCategorieId($themes, $level); } - return implode('.', $hierarchie); } - public function getParentIdByChildId(string $ChildId): mixed + private function getParentIdByChildId(string $ChildId): mixed { $check_dot = strpos($ChildId, '.'); - if (false !== $check_dot) { $level_array = explode('.', $ChildId); while (!empty($level_array)) { @@ -115,39 +121,28 @@ public function getParentIdByChildId(string $ChildId): mixed } } - private function getCategoriesId($data): array + private function getCategoriesId($themes): array { $categories_id = []; - foreach ($data as $item) { - $categories_id[] = $item['categories_id']; + foreach ($themes as $theme) { + $categories_id[] = $theme['categories_id']; } - return $categories_id; } - public function SaveThemesOnFileJson(array $data, ?string $filePath = null): mixed + public function PrepareThemesForDatabase(array $themes): mixed { - $file = $filePath ?? '/default/path/to/Theme.json'; - $categories_id = $this->getCategoriesId($data); - - $themes = []; - $dir = dirname($file); - if (!is_dir($dir)) { - mkdir($dir, 0777, true); - } + $categories_id = $this->getCategoriesId($themes); + $themes_json = []; $y = 1; for ($i = 0; $i < count($categories_id); ++$i, ++$y) { - $themes[] = [ - 'id' => $y, - 'code' => $this->getCodeConcatenateByID($data, $categories_id[$i]), + $themes_json[] = [ + 'code' => $this->getCodeConcatenateByCategorieId($themes, $categories_id[$i]), 'externalId' => $categories_id[$i], 'isSection' => true, 'parentId' => $this->getParentIdByChildId($categories_id[$i]), ]; } - $json = json_encode($themes); - file_put_contents($file, $json); - - return true; + return json_encode($themes_json); } } diff --git a/src/Import/SaveTheme.php b/src/Import/SaveTheme.php new file mode 100644 index 0000000..d9c6975 --- /dev/null +++ b/src/Import/SaveTheme.php @@ -0,0 +1,54 @@ +entityManager = $entityManager; + $this->themeRepository = $entityManager->getRepository(Theme::class); + } + + public function saveOnDatabase(?string $filePath = null): mixed + { + $file = $filePath ?? '/public/File/themes.json'; + if (!file_exists($file)) { + return ['File not found']; + } + $data = json_decode(file_get_contents($file), true); + + foreach ($data as $theme) { + $this->entityManager->persist( + (new Theme()) + ->setCode($theme['code']) + ->setId($theme['id']) + ->setParentId($theme['parentId']) + ->setExternalId($theme['externalId']) + ->setIsSection($theme['isSection']) + ); + } + $this->entityManager->flush(); + $themes = $this->themeRepository->findAll(); + + return count($themes) > 0 ? true : false; + } + + public function saveDatabase(?string $filePath = null): mixed + { + $file = $filePath ?? '/public/File/themes.json'; + if (!file_exists($file)) { + return ['File not found']; + } + $data = json_decode(file_get_contents($file), true); + + return $data; + } +} From 8fc9f0377d00228f910e57a8a6e978458f85541f Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Fri, 21 Feb 2025 01:17:18 +0100 Subject: [PATCH 11/38] it works ingest theme command --- src/Command/CommandIngestTheme.php | 30 ++++++++++--------------- src/Controller/Api/ThemesController.php | 12 ++++++++++ src/Import/IngestTheme.php | 25 ++++++++++++--------- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/Command/CommandIngestTheme.php b/src/Command/CommandIngestTheme.php index ae9df9a..6b87ed7 100644 --- a/src/Command/CommandIngestTheme.php +++ b/src/Command/CommandIngestTheme.php @@ -2,18 +2,17 @@ namespace App\Command; -use App\Script\IngestTheme; +use App\Import\IngestTheme; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; #[AsCommand( name: 'importThemes', - description: 'Parse themes from csvs and import them into the database', + description: 'Parse themes from excel and import them into the database', )] class CommandIngestTheme extends Command { @@ -28,40 +27,35 @@ public function __construct(string $projectDir) protected function configure(): void { $this - ->addArgument('addThemes', InputArgument::OPTIONAL, 'ajouter les themes dans le fichier themes.json') - ->addOption('option1', null, InputOption::VALUE_NONE, 'Option description') + ->addArgument('savethemes', InputArgument::OPTIONAL, 'save themes on database themes') ; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - $arg1 = $input->getArgument('addThemes'); + $arg1 = $input->getArgument('savethemes'); - $IngestTheme = new IngestTheme(); + $ingestTheme = new IngestTheme(); if ($arg1) { $io->note(sprintf('You passed an argument: %s', $arg1)); - $filePath = $this->projectDir.'/public/File/CITEPA.xlsx'; - if (!file_exists($filePath)) { - $io->error('Le fichier n\'existe pas : '.$filePath); + $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; + if (!file_exists($excel_file)) { + $io->error('file does not exist'); return Command::FAILURE; } - try { - $result = $IngestTheme->SaveThemesOnFileJson($IngestTheme->GetJsonDataFileXlsx($filePath), $this->projectDir.'/public/File/themes.json'); - $io->success('Résultat: '.json_encode($result)); + $themes = $ingestTheme->PrepareThemesForDatabase($ingestTheme->GetThemesFromExcelFile($excel_file)); + $themes = json_decode($themes); + $io->success('Résultat: '.json_encode($themes)); + } catch (\Exception $e) { $io->error('Erreur lors de la lecture du fichier : '.$e->getMessage()); - return Command::FAILURE; } - - $io->success('Congratulated, Themes have been added '.count($IngestTheme->GetJsonDataFileXlsx($filePath)).' themes'); - return Command::SUCCESS; } - return Command::SUCCESS; } } diff --git a/src/Controller/Api/ThemesController.php b/src/Controller/Api/ThemesController.php index 996ee36..e9d5824 100644 --- a/src/Controller/Api/ThemesController.php +++ b/src/Controller/Api/ThemesController.php @@ -2,6 +2,7 @@ namespace App\Controller\Api; +use App\Import\IngestTheme; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; @@ -36,4 +37,15 @@ public function index(): JsonResponse ], ]); } + + #[Route('/show', name: 'app_themes', methods: ['GET'])] + public function ShowThemeJson(): JsonResponse + { + $projectDir = $this->getParameter('kernel.project_dir'); + $excel_file = $projectDir.'/public/File/emissions_GES_structure.xlsx'; + $ingestTheme = new IngestTheme(); + $array_themes = $ingestTheme->PrepareThemesForDatabase($ingestTheme->GetThemesFromExcelFile($excel_file)); + + return $this->json(json_decode($array_themes), 200); + } } diff --git a/src/Import/IngestTheme.php b/src/Import/IngestTheme.php index f387e13..c02333e 100644 --- a/src/Import/IngestTheme.php +++ b/src/Import/IngestTheme.php @@ -6,11 +6,10 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; class IngestTheme -{ +{ /** - * Get Themes From Excel File + * Get Themes From Excel File. * - * @param mixed $excel file * @return array themes import from the excel file */ public function GetThemesFromExcelFile(string $excel_file): array @@ -38,20 +37,19 @@ public function GetThemesFromExcelFile(string $excel_file): array } } } + return $themes; } - + /** - * - * replace the underscore - * to space between the word or word group - * @param string $word - * @return string + * replace the underscore + * to space between the word or word group. */ private function replaceSpaceByUnderscore(string $word): string { $word = str_replace('/', 'et ', $word); $word = str_replace(' ', '_', $word); + return $word; } @@ -66,6 +64,7 @@ private static function getHierarchieLevels(string $level): mixed $hierarchie[] = implode('.', $level_array); array_pop($level_array); } + return array_reverse($hierarchie); } else { return [$level]; @@ -79,10 +78,11 @@ private static function getCategoriesByCategorieId(array $themes, string $catego return $theme['categories']; } } + return ''; } - //cette function n'est pas appellé + // cette function n'est pas appellé /*private static function getCategorieIdByCategorie(array $themes, string $categorie): string { foreach ($themes as $theme) { @@ -101,6 +101,7 @@ private function getCodeConcatenateByCategorieId(array $themes, string $level_id foreach ($levels as $level) { $hierarchie[] = $this::getCategoriesByCategorieId($themes, $level); } + return implode('.', $hierarchie); } @@ -127,13 +128,14 @@ private function getCategoriesId($themes): array foreach ($themes as $theme) { $categories_id[] = $theme['categories_id']; } + return $categories_id; } public function PrepareThemesForDatabase(array $themes): mixed { $categories_id = $this->getCategoriesId($themes); - $themes_json = []; + $themes_json = []; $y = 1; for ($i = 0; $i < count($categories_id); ++$i, ++$y) { $themes_json[] = [ @@ -143,6 +145,7 @@ public function PrepareThemesForDatabase(array $themes): mixed 'parentId' => $this->getParentIdByChildId($categories_id[$i]), ]; } + return json_encode($themes_json); } } From 644a987245e181394da65611c04afde9c78dd88c Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Fri, 21 Feb 2025 04:16:36 +0100 Subject: [PATCH 12/38] The test works correctly for the first theme added to the database. --- config/services.yaml | 4 +- migrations/Version20250219234628.php | 43 ------------ migrations/Version20250221003103.php | 33 +++++++++ public/File/CITEPA.xlsx | Bin 55840 -> 0 bytes public/File/themes.json | 1 - src/Command/CommandIngestTheme.php | 17 +++-- src/Command/CommandSaveTheme.php | 66 ------------------ src/Controller/Api/ThemesController.php | 15 +--- src/Import/IngestTheme.php | 87 ++++++++++++++++++++---- src/Import/SaveTheme.php | 54 --------------- src/Script/SaveTheme.php | 54 --------------- tests/Import/ThemeImportTest.php | 44 ++++++++++++ 12 files changed, 168 insertions(+), 250 deletions(-) delete mode 100644 migrations/Version20250219234628.php create mode 100644 migrations/Version20250221003103.php delete mode 100644 public/File/CITEPA.xlsx delete mode 100644 public/File/themes.json delete mode 100644 src/Command/CommandSaveTheme.php delete mode 100644 src/Import/SaveTheme.php delete mode 100644 src/Script/SaveTheme.php create mode 100644 tests/Import/ThemeImportTest.php diff --git a/config/services.yaml b/config/services.yaml index d1161e2..dc8c5e4 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -22,9 +22,7 @@ services: App\Command\CommandIngestTheme: bind: $projectDir: '%kernel.project_dir%' - App\Command\CommandSaveTheme: - bind: - $projectDir: '%kernel.project_dir%' + diff --git a/migrations/Version20250219234628.php b/migrations/Version20250219234628.php deleted file mode 100644 index 1015fee..0000000 --- a/migrations/Version20250219234628.php +++ /dev/null @@ -1,43 +0,0 @@ -addSql('CREATE TEMPORARY TABLE __temp__theme AS SELECT id, parent_id, code, is_section, external_id FROM theme'); - $this->addSql('DROP TABLE theme'); - $this->addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id VARCHAR(255) DEFAULT NULL, code VARCHAR(255) NOT NULL, is_section BOOLEAN NOT NULL, external_id VARCHAR(255) NOT NULL)'); - $this->addSql('INSERT INTO theme (id, parent_id, code, is_section, external_id) SELECT id, parent_id, code, is_section, external_id FROM __temp__theme'); - $this->addSql('DROP TABLE __temp__theme'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E7089F75D7B0 ON theme (external_id)'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E70877153098 ON theme (code)'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TEMPORARY TABLE __temp__theme AS SELECT id, parent_id, code, is_section, external_id FROM theme'); - $this->addSql('DROP TABLE theme'); - $this->addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, code VARCHAR(255) NOT NULL, is_section BOOLEAN NOT NULL, external_id VARCHAR(255) NOT NULL)'); - $this->addSql('INSERT INTO theme (id, parent_id, code, is_section, external_id) SELECT id, parent_id, code, is_section, external_id FROM __temp__theme'); - $this->addSql('DROP TABLE __temp__theme'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E70877153098 ON theme (code)'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E7089F75D7B0 ON theme (external_id)'); - } -} diff --git a/migrations/Version20250221003103.php b/migrations/Version20250221003103.php new file mode 100644 index 0000000..3358a6c --- /dev/null +++ b/migrations/Version20250221003103.php @@ -0,0 +1,33 @@ +addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, code VARCHAR(255) NOT NULL, is_section BOOLEAN NOT NULL, external_id VARCHAR(255) NOT NULL)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E70877153098 ON theme (code)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E7089F75D7B0 ON theme (external_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE theme'); + } +} diff --git a/public/File/CITEPA.xlsx b/public/File/CITEPA.xlsx deleted file mode 100644 index b2b5ec579f5ef5cf7389179b70aaee5b691e8753..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55840 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 IprojectDir = $projectDir; + $this->entityManager = $entityManager; + $this->themeRepository = $entityManager->getRepository(Theme::class); parent::__construct(); } @@ -35,27 +41,30 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $arg1 = $input->getArgument('savethemes'); - - $ingestTheme = new IngestTheme(); + $ingestTheme = new IngestTheme($this->entityManager, $this->projectDir); if ($arg1) { $io->note(sprintf('You passed an argument: %s', $arg1)); $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; if (!file_exists($excel_file)) { $io->error('file does not exist'); + return Command::FAILURE; } try { $themes = $ingestTheme->PrepareThemesForDatabase($ingestTheme->GetThemesFromExcelFile($excel_file)); $themes = json_decode($themes); - $io->success('Résultat: '.json_encode($themes)); + // $io->success('Résultat: '.$ingestTheme->checkThemesEmpty()); } catch (\Exception $e) { $io->error('Erreur lors de la lecture du fichier : '.$e->getMessage()); + return Command::FAILURE; } + return Command::SUCCESS; } + return Command::SUCCESS; } } diff --git a/src/Command/CommandSaveTheme.php b/src/Command/CommandSaveTheme.php deleted file mode 100644 index d62a938..0000000 --- a/src/Command/CommandSaveTheme.php +++ /dev/null @@ -1,66 +0,0 @@ -projectDir = $projectDir; - $this->entityManager = $entityManager; - parent::__construct(); - } - - protected function configure(): void - { - $this - ->addArgument('save', InputArgument::OPTIONAL, 'enregistrer les themes dans la base de données') - ->addOption('option1', null, InputOption::VALUE_NONE, 'Option description') - ; - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $io = new SymfonyStyle($input, $output); - $arg1 = $input->getArgument('save'); - $saveTheme = new SaveTheme($this->entityManager); - - if ($arg1) { - $io->note(sprintf('You passed an argument: %s', $arg1)); - $filePath = $this->projectDir.'/public/File/themes.json'; - if (!file_exists($filePath)) { - $io->error('Le fichier n\'existe pas : '.$filePath); - - return Command::FAILURE; - } - - $file = $this->projectDir.'/public/File/themes.json'; - $result = $saveTheme->saveOnDatabase($file); - if ($result) { - $io->info('le nombre des themes enregistrés '); - } - - return Command::SUCCESS; - } - - return Command::SUCCESS; - } -} diff --git a/src/Controller/Api/ThemesController.php b/src/Controller/Api/ThemesController.php index e9d5824..1d6972e 100644 --- a/src/Controller/Api/ThemesController.php +++ b/src/Controller/Api/ThemesController.php @@ -2,7 +2,6 @@ namespace App\Controller\Api; -use App\Import\IngestTheme; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; @@ -10,6 +9,9 @@ #[Route('/api')] final class ThemesController extends AbstractController { + private $entityManager; + private $projectDir; + #[Route('/themes', name: 'app_themes', methods: ['GET'])] public function index(): JsonResponse { @@ -37,15 +39,4 @@ public function index(): JsonResponse ], ]); } - - #[Route('/show', name: 'app_themes', methods: ['GET'])] - public function ShowThemeJson(): JsonResponse - { - $projectDir = $this->getParameter('kernel.project_dir'); - $excel_file = $projectDir.'/public/File/emissions_GES_structure.xlsx'; - $ingestTheme = new IngestTheme(); - $array_themes = $ingestTheme->PrepareThemesForDatabase($ingestTheme->GetThemesFromExcelFile($excel_file)); - - return $this->json(json_decode($array_themes), 200); - } } diff --git a/src/Import/IngestTheme.php b/src/Import/IngestTheme.php index c02333e..d3765e2 100644 --- a/src/Import/IngestTheme.php +++ b/src/Import/IngestTheme.php @@ -2,11 +2,24 @@ namespace App\Import; +use App\Entity\Theme; +use Doctrine\ORM\EntityManagerInterface; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Spreadsheet; class IngestTheme { + private $projectDir; + private $entityManager; + private $themeRepository; + + public function __construct(EntityManagerInterface $entityManager, string $projectDir) + { + $this->entityManager = $entityManager; + $this->themeRepository = $entityManager->getRepository(Theme::class); + $this->projectDir = $projectDir; + } + /** * Get Themes From Excel File. * @@ -82,17 +95,6 @@ private static function getCategoriesByCategorieId(array $themes, string $catego return ''; } - // cette function n'est pas appellé - /*private static function getCategorieIdByCategorie(array $themes, string $categorie): string - { - foreach ($themes as $theme) { - if ($theme['categories'] === $categorie) { - return $theme['categories_id']; - } - } - return ''; - }*/ - private function getCodeConcatenateByCategorieId(array $themes, string $level_id): string { $levels = []; @@ -136,8 +138,8 @@ public function PrepareThemesForDatabase(array $themes): mixed { $categories_id = $this->getCategoriesId($themes); $themes_json = []; - $y = 1; - for ($i = 0; $i < count($categories_id); ++$i, ++$y) { + + for ($i = 0; $i < count($categories_id); ++$i) { $themes_json[] = [ 'code' => $this->getCodeConcatenateByCategorieId($themes, $categories_id[$i]), 'externalId' => $categories_id[$i], @@ -148,4 +150,63 @@ public function PrepareThemesForDatabase(array $themes): mixed return json_encode($themes_json); } + + public function SaveThemesOnDatabase(): bool + { + $array_themes = []; + + $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; + $ingestTheme = new IngestTheme($this->entityManager, $this->projectDir); + $array_themes = $ingestTheme->PrepareThemesForDatabase($ingestTheme->GetThemesFromExcelFile($excel_file)); + $array_themes = json_decode($array_themes, true); + + if (null == $this->findParentId()) { + foreach ($array_themes as $theme) { + $this->entityManager->persist( + (new Theme()) + ->setCode($theme['code']) + // ->setParentId($theme['parentId']) + ->setExternalId($theme['externalId']) + ->setIsSection($theme['isSection']) + ); + $this->entityManager->flush(); + break; + } + + return true; + } + + return false; + } + + private function findParentId(string $externalId = ''): mixed + { + if (empty($this->themeRepository->findAll())) { + // la table est vide + // premier enregisterement + // parentId=null + return null; + } else { + $column = null; + + // donc on a le premier champs + // connaitre le nombre de champs + // count = ... + return $this->findParentIdByExternalId(); // return parentid + } + } + + private function findParentIdByExternalId(?string $externalId = ''): int + { + $value = $externalId; + // on recuperer le premier champs + // note champs courants + // note : connaitre le nombre total de champs + // note all = + // check if exist + // search it + // return its id + + return 0; + } } diff --git a/src/Import/SaveTheme.php b/src/Import/SaveTheme.php deleted file mode 100644 index d9c6975..0000000 --- a/src/Import/SaveTheme.php +++ /dev/null @@ -1,54 +0,0 @@ -entityManager = $entityManager; - $this->themeRepository = $entityManager->getRepository(Theme::class); - } - - public function saveOnDatabase(?string $filePath = null): mixed - { - $file = $filePath ?? '/public/File/themes.json'; - if (!file_exists($file)) { - return ['File not found']; - } - $data = json_decode(file_get_contents($file), true); - - foreach ($data as $theme) { - $this->entityManager->persist( - (new Theme()) - ->setCode($theme['code']) - ->setId($theme['id']) - ->setParentId($theme['parentId']) - ->setExternalId($theme['externalId']) - ->setIsSection($theme['isSection']) - ); - } - $this->entityManager->flush(); - $themes = $this->themeRepository->findAll(); - - return count($themes) > 0 ? true : false; - } - - public function saveDatabase(?string $filePath = null): mixed - { - $file = $filePath ?? '/public/File/themes.json'; - if (!file_exists($file)) { - return ['File not found']; - } - $data = json_decode(file_get_contents($file), true); - - return $data; - } -} diff --git a/src/Script/SaveTheme.php b/src/Script/SaveTheme.php deleted file mode 100644 index f58d291..0000000 --- a/src/Script/SaveTheme.php +++ /dev/null @@ -1,54 +0,0 @@ -entityManager = $entityManager; - $this->themeRepository = $entityManager->getRepository(Theme::class); - } - - public function saveOnDatabase(?string $filePath = null): mixed - { - $file = $filePath ?? '/public/File/themes.json'; - if (!file_exists($file)) { - return ['File not found']; - } - $data = json_decode(file_get_contents($file), true); - - foreach ($data as $theme) { - $this->entityManager->persist( - (new Theme()) - ->setCode($theme['code']) - ->setId($theme['id']) - ->setParentId($theme['parentId']) - ->setExternalId($theme['externalId']) - ->setIsSection($theme['isSection']) - ); - } - $this->entityManager->flush(); - $themes = $this->themeRepository->findAll(); - - return count($themes) > 0 ? true : false; - } - - public function saveDatabase(?string $filePath = null): mixed - { - $file = $filePath ?? '/public/File/themes.json'; - if (!file_exists($file)) { - return ['File not found']; - } - $data = json_decode(file_get_contents($file), true); - - return $data; - } -} diff --git a/tests/Import/ThemeImportTest.php b/tests/Import/ThemeImportTest.php new file mode 100644 index 0000000..5867177 --- /dev/null +++ b/tests/Import/ThemeImportTest.php @@ -0,0 +1,44 @@ +entityManager = $container->get('doctrine.orm.entity_manager'); + $this->themeRepository = $this->entityManager->getRepository(Theme::class); + $this->projectDir = $container->getParameter('kernel.project_dir'); + } + + protected function tearDown(): void + { + $themes = $this->themeRepository->findAll(); + foreach ($themes as $theme) { + $this->entityManager->remove($theme); + } + $this->entityManager->flush(); + parent::tearDown(); + } + + public function testImportThemeSave(): void + { + $ingestthemes = new IngestTheme($this->entityManager, $this->projectDir); + $check_table = $ingestthemes->SaveThemesOnDatabase(); + if ($check_table) { + $themes_table = $this->themeRepository->findAll(); + $this->assertNotEmpty($themes_table); + } + } +} From ec90a56d6da785fe843f47783b28a34b779a086c Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Fri, 21 Feb 2025 12:08:34 +0100 Subject: [PATCH 13/38] Some changes in the naming of directories and functions --- .gitignore | 3 +- config/services.yaml | 2 +- migrations/Version20250116221307.php | 27 --------- migrations/Version20250206105138.php | 39 ------------- ...21003103.php => Version20250221095203.php} | 4 +- ...estTheme.php => CommandExtractService.php} | 18 +++--- src/Controller/Api/ThemesController.php | 3 - .../Themes/ExtractService.php} | 19 +++---- .../Themes/ExtractServiceTest.php} | 8 +-- tests/ThemeSaveTest.php | 57 ------------------- tests/{ => Themes}/ThemeRepositoryTest.php | 2 +- tests/{ => Themes}/ThemeTest.php | 2 +- 12 files changed, 25 insertions(+), 159 deletions(-) delete mode 100644 migrations/Version20250116221307.php delete mode 100644 migrations/Version20250206105138.php rename migrations/{Version20250221003103.php => Version20250221095203.php} (89%) rename src/Command/{CommandIngestTheme.php => CommandExtractService.php} (75%) rename src/{Import/IngestTheme.php => Imports/Themes/ExtractService.php} (92%) rename tests/{Import/ThemeImportTest.php => Imports/Themes/ExtractServiceTest.php} (81%) delete mode 100644 tests/ThemeSaveTest.php rename tests/{ => Themes}/ThemeRepositoryTest.php (97%) rename tests/{ => Themes}/ThemeTest.php (94%) diff --git a/.gitignore b/.gitignore index 6c7fc3b..71d644d 100644 --- a/.gitignore +++ b/.gitignore @@ -25,12 +25,11 @@ ###< phpunit/phpunit ### phpunit.xml.old -script_memoire.txt .phpunit.cache ###> symfony/asset-mapper ### /public/assets/ /assets/vendor/ ###< symfony/asset-mapper ### -.DS_Store + diff --git a/config/services.yaml b/config/services.yaml index dc8c5e4..82707e3 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -19,7 +19,7 @@ services: - '../src/DependencyInjection/' - '../src/Entity/' - '../src/Kernel.php' - App\Command\CommandIngestTheme: + App\Command\CommandExtractService: bind: $projectDir: '%kernel.project_dir%' diff --git a/migrations/Version20250116221307.php b/migrations/Version20250116221307.php deleted file mode 100644 index bd2da51..0000000 --- a/migrations/Version20250116221307.php +++ /dev/null @@ -1,27 +0,0 @@ -addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_Id INTEGER DEFAULT NULL)'); - } - - public function down(Schema $schema): void - { - $this->addSql('DROP TABLE theme'); - } -} diff --git a/migrations/Version20250206105138.php b/migrations/Version20250206105138.php deleted file mode 100644 index caf8624..0000000 --- a/migrations/Version20250206105138.php +++ /dev/null @@ -1,39 +0,0 @@ -addSql('CREATE TEMPORARY TABLE __temp__theme AS SELECT id, parent_id FROM theme'); - $this->addSql('DROP TABLE theme'); - $this->addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, code VARCHAR(255) NOT NULL, is_section BOOLEAN NOT NULL, external_id VARCHAR(255) NOT NULL)'); - $this->addSql('INSERT INTO theme (id, parent_id) SELECT id, parent_id FROM __temp__theme'); - $this->addSql('DROP TABLE __temp__theme'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E70877153098 ON theme (code)'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E7089F75D7B0 ON theme (external_id)'); - } - - public function down(Schema $schema): void - { - - $this->addSql('CREATE TEMPORARY TABLE __temp__theme AS SELECT id, parent_id FROM theme'); - $this->addSql('DROP TABLE theme'); - $this->addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL)'); - $this->addSql('INSERT INTO theme (id, parent_id) SELECT id, parent_id FROM __temp__theme'); - $this->addSql('DROP TABLE __temp__theme'); - } -} diff --git a/migrations/Version20250221003103.php b/migrations/Version20250221095203.php similarity index 89% rename from migrations/Version20250221003103.php rename to migrations/Version20250221095203.php index 3358a6c..ea97518 100644 --- a/migrations/Version20250221003103.php +++ b/migrations/Version20250221095203.php @@ -10,11 +10,11 @@ /** * Auto-generated Migration: Please modify to your needs! */ -final class Version20250221003103 extends AbstractMigration +final class Version20250221095203 extends AbstractMigration { public function getDescription(): string { - return 'modify parent_id_column string by integer'; + return ''; } public function up(Schema $schema): void diff --git a/src/Command/CommandIngestTheme.php b/src/Command/CommandExtractService.php similarity index 75% rename from src/Command/CommandIngestTheme.php rename to src/Command/CommandExtractService.php index 51a5e1f..f411fae 100644 --- a/src/Command/CommandIngestTheme.php +++ b/src/Command/CommandExtractService.php @@ -3,7 +3,7 @@ namespace App\Command; use App\Entity\Theme; -use App\Import\IngestTheme; +use App\Imports\Themes\ExtractService; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; @@ -13,10 +13,10 @@ use Symfony\Component\Console\Style\SymfonyStyle; #[AsCommand( - name: 'importThemes', + name: 'ExtractService', description: 'Parse themes from excel and import them into the database', )] -class CommandIngestTheme extends Command +class CommandExtractService extends Command { private $projectDir; private $entityManager; @@ -33,15 +33,15 @@ public function __construct(string $projectDir, EntityManagerInterface $entityMa protected function configure(): void { $this - ->addArgument('savethemes', InputArgument::OPTIONAL, 'save themes on database themes') + ->addArgument('extracthemes', InputArgument::OPTIONAL, 'extract and save themes into database themes') ; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - $arg1 = $input->getArgument('savethemes'); - $ingestTheme = new IngestTheme($this->entityManager, $this->projectDir); + $arg1 = $input->getArgument('extracthemes'); + $ExtractService = new ExtractService($this->entityManager, $this->projectDir); if ($arg1) { $io->note(sprintf('You passed an argument: %s', $arg1)); $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; @@ -52,10 +52,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } try { - $themes = $ingestTheme->PrepareThemesForDatabase($ingestTheme->GetThemesFromExcelFile($excel_file)); - $themes = json_decode($themes); - - // $io->success('Résultat: '.$ingestTheme->checkThemesEmpty()); + $themes = []; + $themes = $ExtractService->PrepareThemesForDatabase($ExtractService->GetThemesFromExcelFile($excel_file)); } catch (\Exception $e) { $io->error('Erreur lors de la lecture du fichier : '.$e->getMessage()); diff --git a/src/Controller/Api/ThemesController.php b/src/Controller/Api/ThemesController.php index 1d6972e..996ee36 100644 --- a/src/Controller/Api/ThemesController.php +++ b/src/Controller/Api/ThemesController.php @@ -9,9 +9,6 @@ #[Route('/api')] final class ThemesController extends AbstractController { - private $entityManager; - private $projectDir; - #[Route('/themes', name: 'app_themes', methods: ['GET'])] public function index(): JsonResponse { diff --git a/src/Import/IngestTheme.php b/src/Imports/Themes/ExtractService.php similarity index 92% rename from src/Import/IngestTheme.php rename to src/Imports/Themes/ExtractService.php index d3765e2..2b35c4e 100644 --- a/src/Import/IngestTheme.php +++ b/src/Imports/Themes/ExtractService.php @@ -1,13 +1,13 @@ getCategoriesId($themes); - $themes_json = []; + $themes_array = []; for ($i = 0; $i < count($categories_id); ++$i) { - $themes_json[] = [ + $themes_array[] = [ 'code' => $this->getCodeConcatenateByCategorieId($themes, $categories_id[$i]), 'externalId' => $categories_id[$i], 'isSection' => true, @@ -148,7 +144,7 @@ public function PrepareThemesForDatabase(array $themes): mixed ]; } - return json_encode($themes_json); + return $themes_array; } public function SaveThemesOnDatabase(): bool @@ -156,9 +152,8 @@ public function SaveThemesOnDatabase(): bool $array_themes = []; $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; - $ingestTheme = new IngestTheme($this->entityManager, $this->projectDir); - $array_themes = $ingestTheme->PrepareThemesForDatabase($ingestTheme->GetThemesFromExcelFile($excel_file)); - $array_themes = json_decode($array_themes, true); + $ExtractService = new ExtractService($this->entityManager, $this->projectDir); + $array_themes = $ExtractService->PrepareThemesForDatabase($ExtractService->GetThemesFromExcelFile($excel_file)); if (null == $this->findParentId()) { foreach ($array_themes as $theme) { diff --git a/tests/Import/ThemeImportTest.php b/tests/Imports/Themes/ExtractServiceTest.php similarity index 81% rename from tests/Import/ThemeImportTest.php rename to tests/Imports/Themes/ExtractServiceTest.php index 5867177..672656a 100644 --- a/tests/Import/ThemeImportTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -3,10 +3,10 @@ namespace App\Tests; use App\Entity\Theme; -use App\Import\IngestTheme; +use App\Imports\Themes\ExtractService; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; -class ThemeImportTest extends KernelTestCase +class ExtractServiceTest extends KernelTestCase { private $entityManager; private $themeRepository; @@ -34,8 +34,8 @@ protected function tearDown(): void public function testImportThemeSave(): void { - $ingestthemes = new IngestTheme($this->entityManager, $this->projectDir); - $check_table = $ingestthemes->SaveThemesOnDatabase(); + $ExtractServices = new ExtractService($this->entityManager, $this->projectDir); + $check_table = $ExtractServices->SaveThemesOnDatabase(); if ($check_table) { $themes_table = $this->themeRepository->findAll(); $this->assertNotEmpty($themes_table); diff --git a/tests/ThemeSaveTest.php b/tests/ThemeSaveTest.php deleted file mode 100644 index 28b9884..0000000 --- a/tests/ThemeSaveTest.php +++ /dev/null @@ -1,57 +0,0 @@ -entityManager = self::$kernel->getContainer()->get('doctrine.orm.entity_manager'); - $this->projectDir = self::$kernel->getContainer()->getParameter('kernel.project_dir'); - $this->entityManager = self::$kernel->getContainer()->get('doctrine.orm.entity_manager'); - $this->themeRepository = $this->entityManager->getRepository(Theme::class); - } - - protected function tearDown(): void - { - $themes = $this->themeRepository->findAll(); - foreach ($themes as $theme) { - $this->entityManager->remove($theme); - } - $this->entityManager->flush(); - parent::tearDown(); - } - - public function testSaveImportedThemes(): void - { - $theme = new Theme(); - $saveTheme = new SaveTheme($this->entityManager); - $file = $this->projectDir.'/public/File/themes.json'; - $resultat = $saveTheme->saveDatabase($file); - $this->assertIsArray($resultat); - $this->assertNotEmpty($resultat); - foreach ($resultat as $theme) { - $this->entityManager->persist( - (new Theme()) - ->setCode($theme['code']) - ->setId($theme['id']) - ->setParentId($theme['parentId']) - ->setExternalId($theme['externalId']) - ->setIsSection($theme['isSection']) - ); - } - $this->entityManager->flush(); - $themes = $this->themeRepository->findAll(); - $this->assertNotEmpty($themes); - } -} diff --git a/tests/ThemeRepositoryTest.php b/tests/Themes/ThemeRepositoryTest.php similarity index 97% rename from tests/ThemeRepositoryTest.php rename to tests/Themes/ThemeRepositoryTest.php index f070edd..adc36c5 100644 --- a/tests/ThemeRepositoryTest.php +++ b/tests/Themes/ThemeRepositoryTest.php @@ -1,6 +1,6 @@ Date: Tue, 25 Feb 2025 11:14:15 +0100 Subject: [PATCH 14/38] Shell imports has successfully completed. --- src/Command/CommandExtractService.php | 6 + src/Imports/Themes/ExtractService.php | 122 +++++++++++--------- tests/Imports/Themes/ExtractServiceTest.php | 9 +- 3 files changed, 76 insertions(+), 61 deletions(-) diff --git a/src/Command/CommandExtractService.php b/src/Command/CommandExtractService.php index f411fae..3cc2d1a 100644 --- a/src/Command/CommandExtractService.php +++ b/src/Command/CommandExtractService.php @@ -54,6 +54,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int try { $themes = []; $themes = $ExtractService->PrepareThemesForDatabase($ExtractService->GetThemesFromExcelFile($excel_file)); + $io->info(count($themes).' themes extracted'); + + $saved = $ExtractService->SaveThemesOnDatabase(); + if ($saved) { + $io->info($this->themeRepository->count([]).' themes saved'); + } } catch (\Exception $e) { $io->error('Erreur lors de la lecture du fichier : '.$e->getMessage()); diff --git a/src/Imports/Themes/ExtractService.php b/src/Imports/Themes/ExtractService.php index 2b35c4e..dbfe587 100644 --- a/src/Imports/Themes/ExtractService.php +++ b/src/Imports/Themes/ExtractService.php @@ -103,20 +103,16 @@ private function getCodeConcatenateByCategorieId(array $themes, string $level_id return implode('.', $hierarchie); } - private function getParentIdByChildId(string $ChildId): mixed + private function getParentExternalId(string $externalId): mixed { - $check_dot = strpos($ChildId, '.'); + $check_dot = strpos($externalId, '.'); if (false !== $check_dot) { - $level_array = explode('.', $ChildId); - while (!empty($level_array)) { - $hierarchie[] = implode('.', $level_array); - array_pop($level_array); - } - $level_array = array_reverse($hierarchie); + $level_array = explode('.', $externalId); + array_pop($level_array); - return $level_array[count($level_array) - 2]; + return implode('.', $level_array); } else { - return 'null'; + return null; } } @@ -130,17 +126,19 @@ private function getCategoriesId($themes): array return $categories_id; } - public function PrepareThemesForDatabase(array $themes): mixed + public function PrepareThemesForDatabase(array $themes): array { $categories_id = $this->getCategoriesId($themes); $themes_array = []; + $size = count($categories_id); - for ($i = 0; $i < count($categories_id); ++$i) { + for ($i = 0; $i < $size; ++$i) { $themes_array[] = [ + 'id' => $i + 1, 'code' => $this->getCodeConcatenateByCategorieId($themes, $categories_id[$i]), 'externalId' => $categories_id[$i], 'isSection' => true, - 'parentId' => $this->getParentIdByChildId($categories_id[$i]), + 'parentExternalId' => $this->getParentExternalId($categories_id[$i]), ]; } @@ -149,59 +147,71 @@ public function PrepareThemesForDatabase(array $themes): mixed public function SaveThemesOnDatabase(): bool { - $array_themes = []; - - $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; - $ExtractService = new ExtractService($this->entityManager, $this->projectDir); - $array_themes = $ExtractService->PrepareThemesForDatabase($ExtractService->GetThemesFromExcelFile($excel_file)); - - if (null == $this->findParentId()) { - foreach ($array_themes as $theme) { - $this->entityManager->persist( - (new Theme()) - ->setCode($theme['code']) - // ->setParentId($theme['parentId']) - ->setExternalId($theme['externalId']) - ->setIsSection($theme['isSection']) - ); + $savedThemes = false; + $excelFile = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; + + $extractService = new ExtractService($this->entityManager, $this->projectDir); + $arrayThemes = $extractService->PrepareThemesForDatabase( + $extractService->GetThemesFromExcelFile($excelFile) + ); + + if (empty($arrayThemes)) { + return false; + } + + foreach ($arrayThemes as $theme) { + if (0 === $this->themeRepository->count([])) { + $newTheme = (new Theme()) + ->setCode($theme['code']) + ->setExternalId($theme['externalId']) + ->setIsSection($theme['isSection']) + ->setParentId(null); + $this->entityManager->persist($newTheme); + $this->entityManager->flush(); + $savedThemes = true; + } else { + $newTheme = (new Theme()) + ->setCode($theme['code']) + ->setExternalId($theme['externalId']) + ->setIsSection($theme['isSection']) + ->setParentId($this->getParentIdByparentExternalId($theme['parentExternalId'])); + $this->entityManager->persist($newTheme); $this->entityManager->flush(); - break; + $savedThemes = true; } - - return true; } - return false; + return $savedThemes; } - private function findParentId(string $externalId = ''): mixed + public function checkAllParentIdNotNull(): bool { - if (empty($this->themeRepository->findAll())) { - // la table est vide - // premier enregisterement - // parentId=null - return null; - } else { - $column = null; + $nullCount = $this->themeRepository->createQueryBuilder('t') + ->select('COUNT(t.id)') + ->where('t.id >= :startId') + ->andWhere('t.parentId IS NOT NULL') + ->setParameter('startId', 2) + ->getQuery() + ->getSingleScalarResult(); + + return 0 === $nullCount ? false : true; + } - // donc on a le premier champs - // connaitre le nombre de champs - // count = ... - return $this->findParentIdByExternalId(); // return parentid - } + private function getPreviousTheme(): ?Theme + { + return $this->themeRepository->findOneBy([], ['id' => 'DESC']); } - private function findParentIdByExternalId(?string $externalId = ''): int + private function getParentIdByparentExternalId(string $parentExternalId): ?int { - $value = $externalId; - // on recuperer le premier champs - // note champs courants - // note : connaitre le nombre total de champs - // note all = - // check if exist - // search it - // return its id - - return 0; + $result = $this->entityManager->getRepository(Theme::class) + ->createQueryBuilder('t') + ->select('t.id') + ->where('t.externalId = :externalId') + ->setParameter('externalId', $parentExternalId) + ->getQuery() + ->getOneOrNullResult(); + + return $result['id'] ?? null; } } diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index 672656a..935c9c8 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -35,10 +35,9 @@ protected function tearDown(): void public function testImportThemeSave(): void { $ExtractServices = new ExtractService($this->entityManager, $this->projectDir); - $check_table = $ExtractServices->SaveThemesOnDatabase(); - if ($check_table) { - $themes_table = $this->themeRepository->findAll(); - $this->assertNotEmpty($themes_table); - } + $saveThemes = $ExtractServices->SaveThemesOnDatabase(); + $this->assertTrue($saveThemes, 'themes are not saved'); + $this->assertEquals(118, $this->themeRepository->count([]), '118 themes not found ! juste '.$this->themeRepository->count([]).' theme(s) found '); + $this->assertTrue($ExtractServices->checkAllParentIdNotNull(), 'all parentId are Null'); } } From 6f25a444f192b34fe7fe2f5bb0e7ab4aa4a7e6e4 Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Thu, 27 Feb 2025 11:50:51 +0100 Subject: [PATCH 15/38] resolve all bug --- .env | 1 - src/Command/CommandExtractService.php | 15 ++++++---- src/Imports/Themes/ExtractService.php | 32 +-------------------- src/Repository/ThemeRepository.php | 28 ++++++++++++++++++ tests/Imports/Themes/ExtractServiceTest.php | 6 ++-- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/.env b/.env index 5992cf5..ad508a5 100644 --- a/.env +++ b/.env @@ -3,4 +3,3 @@ APP_SECRET= DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" - diff --git a/src/Command/CommandExtractService.php b/src/Command/CommandExtractService.php index 3cc2d1a..c049b51 100644 --- a/src/Command/CommandExtractService.php +++ b/src/Command/CommandExtractService.php @@ -33,17 +33,16 @@ public function __construct(string $projectDir, EntityManagerInterface $entityMa protected function configure(): void { $this - ->addArgument('extracthemes', InputArgument::OPTIONAL, 'extract and save themes into database themes') - ; + ->addArgument('extracthemes', InputArgument::OPTIONAL, 'extract and save themes into database themes'); } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - $arg1 = $input->getArgument('extracthemes'); + $extracthemes = $input->getArgument('extracthemes'); $ExtractService = new ExtractService($this->entityManager, $this->projectDir); - if ($arg1) { - $io->note(sprintf('You passed an argument: %s', $arg1)); + + if ($extracthemes) { $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; if (!file_exists($excel_file)) { @@ -52,7 +51,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } try { - $themes = []; $themes = $ExtractService->PrepareThemesForDatabase($ExtractService->GetThemesFromExcelFile($excel_file)); $io->info(count($themes).' themes extracted'); @@ -71,4 +69,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } + + private function resetThemetable() + { + $this->themeRepository->resetThemeTable(); + } } diff --git a/src/Imports/Themes/ExtractService.php b/src/Imports/Themes/ExtractService.php index dbfe587..546480b 100644 --- a/src/Imports/Themes/ExtractService.php +++ b/src/Imports/Themes/ExtractService.php @@ -5,7 +5,6 @@ use App\Entity\Theme; use Doctrine\ORM\EntityManagerInterface; use PhpOffice\PhpSpreadsheet\IOFactory; -use PhpOffice\PhpSpreadsheet\Spreadsheet; class ExtractService { @@ -34,9 +33,6 @@ public function GetThemesFromExcelFile(string $excel_file): array $spreadsheet = IOFactory::load($excel_file); $sheet = $spreadsheet->getActiveSheet(); - $newSpreadsheet = new Spreadsheet(); - $newSheet = $newSpreadsheet->getActiveSheet(); - foreach ($sheet->getRowIterator() as $row) { $rowIndex = $row->getRowIndex(); $cellA = $sheet->getCell('A'.$rowIndex)->getValue(); @@ -174,7 +170,7 @@ public function SaveThemesOnDatabase(): bool ->setCode($theme['code']) ->setExternalId($theme['externalId']) ->setIsSection($theme['isSection']) - ->setParentId($this->getParentIdByparentExternalId($theme['parentExternalId'])); + ->setParentId($this->themeRepository->getParentIdByparentExternalId($theme['parentExternalId'])); $this->entityManager->persist($newTheme); $this->entityManager->flush(); $savedThemes = true; @@ -184,34 +180,8 @@ public function SaveThemesOnDatabase(): bool return $savedThemes; } - public function checkAllParentIdNotNull(): bool - { - $nullCount = $this->themeRepository->createQueryBuilder('t') - ->select('COUNT(t.id)') - ->where('t.id >= :startId') - ->andWhere('t.parentId IS NOT NULL') - ->setParameter('startId', 2) - ->getQuery() - ->getSingleScalarResult(); - - return 0 === $nullCount ? false : true; - } - private function getPreviousTheme(): ?Theme { return $this->themeRepository->findOneBy([], ['id' => 'DESC']); } - - private function getParentIdByparentExternalId(string $parentExternalId): ?int - { - $result = $this->entityManager->getRepository(Theme::class) - ->createQueryBuilder('t') - ->select('t.id') - ->where('t.externalId = :externalId') - ->setParameter('externalId', $parentExternalId) - ->getQuery() - ->getOneOrNullResult(); - - return $result['id'] ?? null; - } } diff --git a/src/Repository/ThemeRepository.php b/src/Repository/ThemeRepository.php index 3e5711a..2d820a0 100644 --- a/src/Repository/ThemeRepository.php +++ b/src/Repository/ThemeRepository.php @@ -8,8 +8,36 @@ class ThemeRepository extends ServiceEntityRepository { + private $entityManager; + public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Theme::class); } + + public function checkAllParentIdNotNull(): bool + { + $nullCount = $this->createQueryBuilder('t') + ->select('COUNT(t.id)') + ->where('t.id >= :startId') + ->andWhere('t.parentId IS NOT NULL') + ->setParameter('startId', 2) + ->getQuery() + ->getSingleScalarResult(); + + return 0 === $nullCount ? false : true; + } + + public function getParentIdByparentExternalId(string $parentExternalId): ?int + { + $result = $this + ->createQueryBuilder('t') + ->select('t.id') + ->where('t.externalId = :externalId') + ->setParameter('externalId', $parentExternalId) + ->getQuery() + ->getOneOrNullResult(); + + return $result['id'] ?? null; + } } diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index 935c9c8..93ac49a 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -36,8 +36,8 @@ public function testImportThemeSave(): void { $ExtractServices = new ExtractService($this->entityManager, $this->projectDir); $saveThemes = $ExtractServices->SaveThemesOnDatabase(); - $this->assertTrue($saveThemes, 'themes are not saved'); - $this->assertEquals(118, $this->themeRepository->count([]), '118 themes not found ! juste '.$this->themeRepository->count([]).' theme(s) found '); - $this->assertTrue($ExtractServices->checkAllParentIdNotNull(), 'all parentId are Null'); + $this->assertTrue($saveThemes, 'themes are saved'); + $this->assertEquals(118, $this->themeRepository->count([]), $this->themeRepository->count([]).' theme(s) found '); + $this->assertTrue($this->themeRepository->checkAllParentIdNotNull(), 'all parentId are not Null'); } } From e7d364771cc25030183dce40d592aa8b6b33c04a Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Mon, 3 Mar 2025 23:05:27 +0100 Subject: [PATCH 16/38] Several functions in extraService.php have been modified and improved. --- .DS_Store | Bin 10244 -> 0 bytes public/.DS_Store | Bin 6148 -> 0 bytes public/File/~$CITEPA.xlsx | Bin 165 -> 0 bytes src/.DS_Store | Bin 10244 -> 0 bytes src/Imports/Themes/ExtractService.php | 13 +++++-------- tests/Imports/Themes/ExtractServiceTest.php | 7 ++++++- 6 files changed, 11 insertions(+), 9 deletions(-) delete mode 100644 .DS_Store delete mode 100644 public/.DS_Store delete mode 100644 public/File/~$CITEPA.xlsx delete mode 100644 src/.DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 1182f170b69e11d7fd8b4e78a56b9fb674d90d64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 zcmeHM&x;&I6n;H3JKN*#!Xk#~LDK5QL)gr2mO#j5+)M-yd&vYnXc(twCc9&Lx|{it zF<{#!0Rsx&G++#Zh+aH-G9ZFpauNIoL<1op#ILGrr(f0dYy>fzL)Fs#dg^_z z-g{qFzwYWKB4=aRS|##`$i*S#@*++dP3Xlj)pDxmMMwws6l|^5no&2NLfQ_?fMvik zU>UFsSOy*y2Jp-l&0R>OJzEAW1D1ga1EM~7IE1VyIZmW39XQD?0AvB(vf+AJTtIqC zR+Jnk5*jooF@;P_9ks=95_9x>3Rh8boXEtS9JTpylxIinP#ozU<9iZLP9>4{Y#Fc& zOfw+%?gd(*E$Yxf+rJln{+#Q&QMKv^k&iz2{p)|!uipIO1zq3TS08V)Zv#ue!Ib9r z{L(fJ$=AHo!tH4UkKwc9bjQCgV=%_)MrkCoQCd7nu_;o6`uJCe9T;EM0*1f@MslEk8^GpD5c>M z{&jxB>_nR~I<3u!YKfW@QI}$z1)n|4^c%Q}%t)Q5jYzXB1K2g`qMoDkwqaS6K0~?y zKDG?(z5TE{D~CN`wXrxiN7k>UvnO&?{`l<8!stAG4>z=gSQD0dSaV&hzZgu#xF2FY zgWML-d`2Gw8hL`*1}r-=5@XHge)%^xdiHJ&Hko5{7XGepf9u?k_udR_x?UcH)mic2 z9&Z4W0n~&LnGL|mo5(hRY~gpG20AzS)3D*(6k(5g0*ekc4eZVRu>PnYt9$+%KMp$m zLAm^^lgk(8iu3NgyWpO$ZAYWppfMQsHyXWH#jkTw6!?4B4gcj%dsJV3;&c=a8lAA+ zlLnnu8<5vt>4dFlv=I%%R$toI5`|lGOZDX^cXn3G>qouy)$;C9Z|CG%8P~^8?CzG_ zL(d&Mb+&#n=!Vf-f*`!!sMjE@AiasZLd##D-??W^J)hSZ zy)cMEv5rOTB|1xIP*tn63Auv#ZRs;rI06)()jVdtXU=Eg-E5Se^|8A^DSMvp?XQMQ zpC+|MuXL4{}iS_Uiw4}k%vP_0!r;An4CY5El3+C3bv;}A{Y#)*_BI0=a8m!ISD yLwt^ZUiD30cA&4QzHuU@Iqal41q_sv?}k5|86{_**%)Mdr3ME1vc zJ=#9^+jz`N!A~%sd`}O%z$v4g-cg5Y8d1HmBcCwp^p5ycQKDE4SR6j5A-oQ_4Pxaw zpWWEtqiOBjDqzlryMJyKunV^eSOvBzAioa*ip0oZt5JPAP^lCE=%8B}eD&)O3}go| zGT3TF4~%IlP*a&nF__65n5H8hnenYgO(&+FjCstHnaNO?NjzAfh7*f4+S)2$6U={eU6ktw2?)R`HbGM#ZoV;rTl-DTIRp75G@C$KSjNkwO diff --git a/public/File/~$CITEPA.xlsx b/public/File/~$CITEPA.xlsx deleted file mode 100644 index e14b1a34ed489a037d99ea74fb775d81fc5e0cfa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmd;0E6Pk!NJ`AB$}g?TR3H}cGNdsSF=R5NFeor2F(fkNF;p?+Gn4|!OrQw0Apovr B6v+Sp diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index e529ffba508c9fb0507dde522bfb66713e870fe5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 zcmeHMU2GIp6h3FRl$oK(l)rWeiwl*1s6~)~2rhIBMZh9$>5s&+yED?!>CCb-y9EYD0^WE>v-FpiFxT9dL14saXN)MAp9c5J#YiG}-q(n81B$4a^^5BC@e>T0zBoUAZ zNCYGT5&?<8e-Q!NvsqGS2q|-kfJ8tdFpdEGJ|yU2G8xGcA^E3+GJXUgSwq!4(L0?3 zxJ@FG$w-a}$xW$GaeBbW6r&>s3Ug{tFm;m2NR9|8%mIZtU^FsDClutPlU;(V115!( zxkNxBFc|@McGsbm&g~wA<>&X^uAj}geummvNNuO5`8rSZHieCQWL;<0AIfnJsNFD? z-jJV4MSZJHeW%S+=RRTmSDnX@Z{4zPZm@j(1e-=3q##Y6rC}#HaE#sxXRjgSomdUwJ5Vo{@gG#JsTJ3aI(`T672Yd7ZV|LN8hdXki<9oqDJ0Fl7 zFlGgV{_yUs@9odqti31eyM-Yy7%=LyX*XMN)1kfHbvvCW?VvZbkA>{5Kk)NmD_87w z$d=U&7)Bwm57=bj6zvq51qNy#)Xj@8S+=_6-uoVC-L-fBrI~lkGIV{;+@7KnI2qUO zO9xrovmEb8_poDygZ++Wd!h4~W9K!kK4y1$=^^{l{DwxuTwpFNSqa@6wyM#j_7*bE z(Sj2m7h{--#hPl29=)Oe|H^M_7Gp-lkks>*mT;WRUI($d_oE&39{> z5p+G_c#)~TeuJtRR=1nZ*skbJ-?W*k_Vg45pi$-4ZK_s}YkDc-hRC0wH1ZBreUy#7 zHywl>IoqSbxo-(`r>gd`mhF~Bl}1D3{Px|Z*&ht1^LEFuXXlNtRF&87qk6t>)WZ5{ zvz*Y+M~x5jM#J%nPDl$uOGBa%sH)nOTtU2{#35};wuq8Oo4X0kw3WBRAqu`69ETU- zWjG0E;2ju+^YAfz3ZKE}@FiS=tMEPi2*1Fua1F*#!5LVGGjSF!#HF|lm*aZ8AGhK* zOkx{8gdMma4`2@-#x!Qo!pAYd5DPerPvZ%E4xh)D@D#p*Z{l0{A)dzz_z_;jFYq#c zi&yX}{(#qT41ZH%%6z3kX;e&QiLzXYCay%kHlR;9Qpy#&I-L|AIT0R8f3@(4r`mS@ zOW|?3js-;h>^XDiEoxe^_8tn8an1;qm55bNSkBQ3W^<16g{?v_bAi4v-c2O7kKG6v z5kjSaGNv=Ss*H^0o%-UqF^y<0ig)SD;zlh|TohNvSH}&7B1;sT<1M-oqqxzytrlOc ztKxN|zDYM~D1Jn7i@sGifQ7UuCs{v4%fHUcx8W?DBVJx4UVaT%;5+yU#^5??#KQ(O zaRJ705pi)PuEI6A7B}K1+>Bd@nQgcW+wno%gI#!#c-e>jm?d7?=wS|za&|sN+#JDY z@LA&KNqhxg#n((hrXOqz!89R%|La zDopeFRO!1Mn|IHQV^eh=projectDir.'/public/File/emissions_GES_structure.xlsx'; + //$excelFile = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; - $extractService = new ExtractService($this->entityManager, $this->projectDir); + /*$extractService = new ExtractService($this->entityManager, $this->projectDir); $arrayThemes = $extractService->PrepareThemesForDatabase( $extractService->GetThemesFromExcelFile($excelFile) - ); + ); */ if (empty($arrayThemes)) { return false; @@ -180,8 +180,5 @@ public function SaveThemesOnDatabase(): bool return $savedThemes; } - private function getPreviousTheme(): ?Theme - { - return $this->themeRepository->findOneBy([], ['id' => 'DESC']); - } + } diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index 93ac49a..6c6d3b3 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -35,7 +35,12 @@ protected function tearDown(): void public function testImportThemeSave(): void { $ExtractServices = new ExtractService($this->entityManager, $this->projectDir); - $saveThemes = $ExtractServices->SaveThemesOnDatabase(); + $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; + $themes = $ExtractServices->GetThemesFromExcelFile($excel_file); + $preparedThemes = $ExtractServices->PrepareThemesForDatabase($themes); + $saveThemes = $ExtractServices->SaveThemesOnDatabase($preparedThemes); + + //$this->assertNull() $this->assertTrue($saveThemes, 'themes are saved'); $this->assertEquals(118, $this->themeRepository->count([]), $this->themeRepository->count([]).' theme(s) found '); $this->assertTrue($this->themeRepository->checkAllParentIdNotNull(), 'all parentId are not Null'); From ec167aada142af203d3829bd813bb325387a0ea7 Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Mon, 3 Mar 2025 23:39:01 +0100 Subject: [PATCH 17/38] The isFirstThemeParentIdNull test completed successfully --- src/Repository/ThemeRepository.php | 23 ++++++++++----------- tests/Imports/Themes/ExtractServiceTest.php | 7 +++++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Repository/ThemeRepository.php b/src/Repository/ThemeRepository.php index 2d820a0..03ac06e 100644 --- a/src/Repository/ThemeRepository.php +++ b/src/Repository/ThemeRepository.php @@ -15,18 +15,6 @@ public function __construct(ManagerRegistry $registry) parent::__construct($registry, Theme::class); } - public function checkAllParentIdNotNull(): bool - { - $nullCount = $this->createQueryBuilder('t') - ->select('COUNT(t.id)') - ->where('t.id >= :startId') - ->andWhere('t.parentId IS NOT NULL') - ->setParameter('startId', 2) - ->getQuery() - ->getSingleScalarResult(); - - return 0 === $nullCount ? false : true; - } public function getParentIdByparentExternalId(string $parentExternalId): ?int { @@ -40,4 +28,15 @@ public function getParentIdByparentExternalId(string $parentExternalId): ?int return $result['id'] ?? null; } + public function isFirstThemeParentIdNull(): bool + { + $firstTheme = $this->createQueryBuilder('theme') + ->select('theme.parentId') + ->orderBy('theme.id', 'ASC') + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult(); + + return $firstTheme !== null && $firstTheme['parentId'] === null ; + } } diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index 6c6d3b3..1dcaa33 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -41,8 +41,11 @@ public function testImportThemeSave(): void $saveThemes = $ExtractServices->SaveThemesOnDatabase($preparedThemes); //$this->assertNull() - $this->assertTrue($saveThemes, 'themes are saved'); + + $this->assertTrue($saveThemes, 'themes are saved'); + $this->assertTrue($this->themeRepository->isFirstThemeParentIdNull(), "first theme has exteranlId : null"); + /* $this->assertEquals(118, $this->themeRepository->count([]), $this->themeRepository->count([]).' theme(s) found '); - $this->assertTrue($this->themeRepository->checkAllParentIdNotNull(), 'all parentId are not Null'); + $this->assertTrue($this->themeRepository->checkAllParentIdNotNull(), 'all parentId are not Null'); */ } } From e91cd7dd7c8ec154f9e36a6d39b3eb548fcc7064 Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Tue, 4 Mar 2025 00:26:45 +0100 Subject: [PATCH 18/38] The test was completed successfully, and the parentId for all themes is not null, except for the first theme. --- src/Imports/Themes/ExtractService.php | 4 +--- src/Repository/ThemeRepository.php | 24 +++++++++++++++++++-- tests/Imports/Themes/ExtractServiceTest.php | 11 ++++------ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/Imports/Themes/ExtractService.php b/src/Imports/Themes/ExtractService.php index 2cbca0b..f2fe6f6 100644 --- a/src/Imports/Themes/ExtractService.php +++ b/src/Imports/Themes/ExtractService.php @@ -144,7 +144,7 @@ public function PrepareThemesForDatabase(array $themes): array public function SaveThemesOnDatabase(array $arrayThemes): bool { $savedThemes = false; - //$excelFile = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; + // $excelFile = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; /*$extractService = new ExtractService($this->entityManager, $this->projectDir); $arrayThemes = $extractService->PrepareThemesForDatabase( @@ -179,6 +179,4 @@ public function SaveThemesOnDatabase(array $arrayThemes): bool return $savedThemes; } - - } diff --git a/src/Repository/ThemeRepository.php b/src/Repository/ThemeRepository.php index 03ac06e..210a4a1 100644 --- a/src/Repository/ThemeRepository.php +++ b/src/Repository/ThemeRepository.php @@ -15,7 +15,6 @@ public function __construct(ManagerRegistry $registry) parent::__construct($registry, Theme::class); } - public function getParentIdByparentExternalId(string $parentExternalId): ?int { $result = $this @@ -28,6 +27,7 @@ public function getParentIdByparentExternalId(string $parentExternalId): ?int return $result['id'] ?? null; } + public function isFirstThemeParentIdNull(): bool { $firstTheme = $this->createQueryBuilder('theme') @@ -37,6 +37,26 @@ public function isFirstThemeParentIdNull(): bool ->getQuery() ->getOneOrNullResult(); - return $firstTheme !== null && $firstTheme['parentId'] === null ; + return null !== $firstTheme && null === $firstTheme['parentId']; + } + public function isAllThemesParentIdAreNotNull(): bool + { + $isAllThemesParentId = true; + + $parentIds = $this->createQueryBuilder('theme') + ->select('theme.parentId') + ->orderBy('theme.id', 'ASC') + ->setFirstResult(1) + ->getQuery() + ->getResult(); + + foreach ($parentIds as $parentId) { + if ($parentId['parentId'] === null) { + $isAllThemesParentId = false; + break; + } + } + + return $isAllThemesParentId; } } diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index 1dcaa33..7fc1f17 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -40,12 +40,9 @@ public function testImportThemeSave(): void $preparedThemes = $ExtractServices->PrepareThemesForDatabase($themes); $saveThemes = $ExtractServices->SaveThemesOnDatabase($preparedThemes); - //$this->assertNull() - - $this->assertTrue($saveThemes, 'themes are saved'); - $this->assertTrue($this->themeRepository->isFirstThemeParentIdNull(), "first theme has exteranlId : null"); - /* - $this->assertEquals(118, $this->themeRepository->count([]), $this->themeRepository->count([]).' theme(s) found '); - $this->assertTrue($this->themeRepository->checkAllParentIdNotNull(), 'all parentId are not Null'); */ + + $this->assertTrue($saveThemes, 'themes are saved'); + $this->assertTrue($this->themeRepository->isFirstThemeParentIdNull(), 'first theme has ParentId : null'); + $this->assertTrue($this->themeRepository->isAllThemesParentIdAreNotNull(), "The parentId for all themes is not null, except for the first theme."); } } From 6fed003f652f35acd6b75ac95152451625a25acf Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Tue, 4 Mar 2025 00:43:17 +0100 Subject: [PATCH 19/38] The CommandExtractService command was completed successfully. --- src/Command/CommandExtractService.php | 15 ++++++--------- src/Repository/ThemeRepository.php | 11 ++++++----- tests/Imports/Themes/ExtractServiceTest.php | 3 +-- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Command/CommandExtractService.php b/src/Command/CommandExtractService.php index c049b51..27da6e5 100644 --- a/src/Command/CommandExtractService.php +++ b/src/Command/CommandExtractService.php @@ -53,13 +53,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int try { $themes = $ExtractService->PrepareThemesForDatabase($ExtractService->GetThemesFromExcelFile($excel_file)); $io->info(count($themes).' themes extracted'); + $get_themes_from_file = $ExtractService->GetThemesFromExcelFile($excel_file); + $prepared_themes = $ExtractService->PrepareThemesForDatabase($get_themes_from_file); + $save_themes = $ExtractService->SaveThemesOnDatabase($prepared_themes); - $saved = $ExtractService->SaveThemesOnDatabase(); - if ($saved) { - $io->info($this->themeRepository->count([]).' themes saved'); + if ($save_themes) { + $io->info($this->themeRepository->count([]).' themes are saved successfuly'); } } catch (\Exception $e) { - $io->error('Erreur lors de la lecture du fichier : '.$e->getMessage()); + $io->error('File Excel failed to read : '.$e->getMessage()); return Command::FAILURE; } @@ -69,9 +71,4 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } - - private function resetThemetable() - { - $this->themeRepository->resetThemeTable(); - } } diff --git a/src/Repository/ThemeRepository.php b/src/Repository/ThemeRepository.php index 210a4a1..46ce922 100644 --- a/src/Repository/ThemeRepository.php +++ b/src/Repository/ThemeRepository.php @@ -39,24 +39,25 @@ public function isFirstThemeParentIdNull(): bool return null !== $firstTheme && null === $firstTheme['parentId']; } + public function isAllThemesParentIdAreNotNull(): bool { $isAllThemesParentId = true; $parentIds = $this->createQueryBuilder('theme') ->select('theme.parentId') - ->orderBy('theme.id', 'ASC') - ->setFirstResult(1) + ->orderBy('theme.id', 'ASC') + ->setFirstResult(1) ->getQuery() ->getResult(); foreach ($parentIds as $parentId) { - if ($parentId['parentId'] === null) { + if (null === $parentId['parentId']) { $isAllThemesParentId = false; break; } } - - return $isAllThemesParentId; + + return $isAllThemesParentId; } } diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index 7fc1f17..ddc8735 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -40,9 +40,8 @@ public function testImportThemeSave(): void $preparedThemes = $ExtractServices->PrepareThemesForDatabase($themes); $saveThemes = $ExtractServices->SaveThemesOnDatabase($preparedThemes); - $this->assertTrue($saveThemes, 'themes are saved'); $this->assertTrue($this->themeRepository->isFirstThemeParentIdNull(), 'first theme has ParentId : null'); - $this->assertTrue($this->themeRepository->isAllThemesParentIdAreNotNull(), "The parentId for all themes is not null, except for the first theme."); + $this->assertTrue($this->themeRepository->isAllThemesParentIdAreNotNull(), 'The parentId for all themes is not null, except for the first theme.'); } } From 9a9a96441a2e9dc92206748fd18ab4eedc4d4f7d Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Tue, 4 Mar 2025 01:25:24 +0100 Subject: [PATCH 20/38] The migration Version20250116221307 has been successfully added. --- migrations/Version20250116221307.php | 27 +++++++++++++++++++++++++++ migrations/Version20250221095203.php | 22 +++++++++++++++------- 2 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 migrations/Version20250116221307.php diff --git a/migrations/Version20250116221307.php b/migrations/Version20250116221307.php new file mode 100644 index 0000000..bd2da51 --- /dev/null +++ b/migrations/Version20250116221307.php @@ -0,0 +1,27 @@ +addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_Id INTEGER DEFAULT NULL)'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP TABLE theme'); + } +} diff --git a/migrations/Version20250221095203.php b/migrations/Version20250221095203.php index ea97518..ee052dc 100644 --- a/migrations/Version20250221095203.php +++ b/migrations/Version20250221095203.php @@ -14,20 +14,28 @@ final class Version20250221095203 extends AbstractMigration { public function getDescription(): string { - return ''; + return 'Add columns and indexes into theme table'; } public function up(Schema $schema): void { - // this up() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, code VARCHAR(255) NOT NULL, is_section BOOLEAN NOT NULL, external_id VARCHAR(255) NOT NULL)'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E70877153098 ON theme (code)'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E7089F75D7B0 ON theme (external_id)'); + + $this->addSql('ALTER TABLE theme ADD COLUMN is_section BOOLEAN NOT NULL'); + $this->addSql('ALTER TABLE theme ADD COLUMN code VARCHAR(255) NOT NULL'); + $this->addSql('ALTER TABLE theme ADD COLUMN external_id VARCHAR(255) NOT NULL'); + + $this->addSql('CREATE UNIQUE INDEX IF NOT EXISTS UNIQ_9775E70877153098 ON theme (code)'); + $this->addSql('CREATE UNIQUE INDEX IF NOT EXISTS UNIQ_9775E7089F75D7B0 ON theme (external_id)'); } public function down(Schema $schema): void { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('DROP TABLE theme'); + $this->addSql('ALTER TABLE theme DROP COLUMN parent_id'); + $this->addSql('ALTER TABLE theme DROP COLUMN is_section'); + $this->addSql('ALTER TABLE theme DROP COLUMN code'); + + $this->addSql('DROP INDEX IF EXISTS UNIQ_9775E70877153098'); + $this->addSql('DROP INDEX IF EXISTS UNIQ_9775E7089F75D7B0'); } } + From 2310e5ec34bb9af146cb5f9cd067f49dd4596d20 Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Thu, 6 Mar 2025 07:15:54 +0100 Subject: [PATCH 21/38] entity theme modify code by name --- src/Entity/Theme.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Entity/Theme.php b/src/Entity/Theme.php index 8a32aad..8e0762f 100644 --- a/src/Entity/Theme.php +++ b/src/Entity/Theme.php @@ -17,7 +17,7 @@ class Theme private ?int $parentId = null; #[ORM\Column(length: 255, nullable: false, unique: true)] - private ?string $code; + private ?string $name; #[ORM\Column(nullable: false)] private ?bool $isSection; @@ -49,14 +49,14 @@ public function setParentId(?int $parentId): static return $this; } - public function getCode(): ?string + public function getName(): ?string { - return $this->code; + return $this->name; } - public function setCode(string $code): static + public function setName(string $name): static { - $this->code = $code; + $this->name = $name; return $this; } From 2efe854151ab37c98c807243338afefe255a281f Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Thu, 6 Mar 2025 07:43:39 +0100 Subject: [PATCH 22/38] creation migrations/Version20250306062918.php --- migrations/Version20250306062918.php | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 migrations/Version20250306062918.php diff --git a/migrations/Version20250306062918.php b/migrations/Version20250306062918.php new file mode 100644 index 0000000..91fdf2f --- /dev/null +++ b/migrations/Version20250306062918.php @@ -0,0 +1,43 @@ +addSql('CREATE TEMPORARY TABLE __temp__theme AS SELECT id, parent_Id, is_section, code, external_id FROM theme'); + $this->addSql('DROP TABLE theme'); + $this->addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_Id INTEGER DEFAULT NULL, is_section BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, external_id VARCHAR(255) NOT NULL)'); + $this->addSql('INSERT INTO theme (id, parent_Id, is_section, name, external_id) SELECT id, parent_Id, is_section, code, external_id FROM __temp__theme'); + $this->addSql('DROP TABLE __temp__theme'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E7089F75D7B0 ON theme (external_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E7085E237E06 ON theme (name)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__theme AS SELECT id, parent_id, name, is_section, external_id FROM theme'); + $this->addSql('DROP TABLE theme'); + $this->addSql('CREATE TABLE theme (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, code VARCHAR(255) NOT NULL, is_section BOOLEAN NOT NULL, external_id VARCHAR(255) NOT NULL)'); + $this->addSql('INSERT INTO theme (id, parent_id, code, is_section, external_id) SELECT id, parent_id, name, is_section, external_id FROM __temp__theme'); + $this->addSql('DROP TABLE __temp__theme'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E7089F75D7B0 ON theme (external_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9775E70877153098 ON theme (code)'); + } +} From 612b04a500ea19dc0560ab97df684ecda82a40bb Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Thu, 6 Mar 2025 07:55:29 +0100 Subject: [PATCH 23/38] Every occurrence of 'code' has been replaced with 'name' in the ExtractService.php file. --- src/Imports/Themes/ExtractService.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Imports/Themes/ExtractService.php b/src/Imports/Themes/ExtractService.php index f2fe6f6..f1a3e38 100644 --- a/src/Imports/Themes/ExtractService.php +++ b/src/Imports/Themes/ExtractService.php @@ -87,7 +87,7 @@ private static function getCategoriesByCategorieId(array $themes, string $catego return ''; } - private function getCodeConcatenateByCategorieId(array $themes, string $level_id): string + private function getNameConcatenateByCategorieId(array $themes, string $level_id): string { $levels = []; $hierarchie = []; @@ -131,7 +131,7 @@ public function PrepareThemesForDatabase(array $themes): array for ($i = 0; $i < $size; ++$i) { $themes_array[] = [ 'id' => $i + 1, - 'code' => $this->getCodeConcatenateByCategorieId($themes, $categories_id[$i]), + 'name' => $this->getNameConcatenateByCategorieId($themes, $categories_id[$i]), 'externalId' => $categories_id[$i], 'isSection' => true, 'parentExternalId' => $this->getParentExternalId($categories_id[$i]), @@ -144,12 +144,6 @@ public function PrepareThemesForDatabase(array $themes): array public function SaveThemesOnDatabase(array $arrayThemes): bool { $savedThemes = false; - // $excelFile = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; - - /*$extractService = new ExtractService($this->entityManager, $this->projectDir); - $arrayThemes = $extractService->PrepareThemesForDatabase( - $extractService->GetThemesFromExcelFile($excelFile) - ); */ if (empty($arrayThemes)) { return false; @@ -158,7 +152,7 @@ public function SaveThemesOnDatabase(array $arrayThemes): bool foreach ($arrayThemes as $theme) { if (0 === $this->themeRepository->count([])) { $newTheme = (new Theme()) - ->setCode($theme['code']) + ->setName($theme['name']) ->setExternalId($theme['externalId']) ->setIsSection($theme['isSection']) ->setParentId(null); @@ -167,7 +161,7 @@ public function SaveThemesOnDatabase(array $arrayThemes): bool $savedThemes = true; } else { $newTheme = (new Theme()) - ->setCode($theme['code']) + ->setName($theme['name']) ->setExternalId($theme['externalId']) ->setIsSection($theme['isSection']) ->setParentId($this->themeRepository->getParentIdByparentExternalId($theme['parentExternalId'])); From 75775b4a99ba103a65097adbb8b31ba70ab51e95 Mon Sep 17 00:00:00 2001 From: magrigsdev Date: Thu, 6 Mar 2025 07:59:08 +0100 Subject: [PATCH 24/38] Every occurrence of 'code' has been replaced with 'name' in the ThemeRepositoryTest.phand ThemeTest.php file --- tests/Themes/ThemeRepositoryTest.php | 4 ++-- tests/Themes/ThemeTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Themes/ThemeRepositoryTest.php b/tests/Themes/ThemeRepositoryTest.php index adc36c5..9b84cde 100644 --- a/tests/Themes/ThemeRepositoryTest.php +++ b/tests/Themes/ThemeRepositoryTest.php @@ -32,7 +32,7 @@ protected function tearDown(): void public function testAddParentTheme(): void { $theme = new Theme(); - $theme->setCode('environnement'); + $theme->setName('environnement'); $theme->setIsSection(true); $theme->setParentId(null); $theme->setExternalId('1024'); @@ -44,7 +44,7 @@ public function testAddParentTheme(): void public function testAddChildTheme(): void { $child = new Theme(); - $child->setCode('Emissions GES'); + $child->setName('Emissions GES'); $child->setIsSection(true); $child->setParentId(1); $child->setExternalId('2981'); diff --git a/tests/Themes/ThemeTest.php b/tests/Themes/ThemeTest.php index 757defa..ebf66c8 100644 --- a/tests/Themes/ThemeTest.php +++ b/tests/Themes/ThemeTest.php @@ -10,11 +10,11 @@ class ThemeTest extends TestCase public function testAddEntityTheme(): void { $theme = new Theme(); - $theme->setCode('environement'); + $theme->setName('environement'); $theme->setIsSection(true); $theme->setParentId(null); $theme->setExternalId('2980'); - $this->assertSame('environement', $theme->getCode()); + $this->assertSame('environement', $theme->getName()); $this->assertSame(true, $theme->getIsSection()); $this->assertSame(null, $theme->getParentId()); $this->assertSame('2980', $theme->getExternalId()); From ebcb682c77272adcf29fe2ec37a118e4db1c9caa Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Thu, 6 Mar 2025 10:46:23 +0100 Subject: [PATCH 25/38] Remove unused code --- src/Command/CommandExtractService.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Command/CommandExtractService.php b/src/Command/CommandExtractService.php index 27da6e5..a08dae2 100644 --- a/src/Command/CommandExtractService.php +++ b/src/Command/CommandExtractService.php @@ -40,7 +40,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $extracthemes = $input->getArgument('extracthemes'); - $ExtractService = new ExtractService($this->entityManager, $this->projectDir); + $extract_service = new ExtractService($this->entityManager, $this->projectDir); if ($extracthemes) { $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; @@ -50,12 +50,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } + try { - $themes = $ExtractService->PrepareThemesForDatabase($ExtractService->GetThemesFromExcelFile($excel_file)); - $io->info(count($themes).' themes extracted'); - $get_themes_from_file = $ExtractService->GetThemesFromExcelFile($excel_file); - $prepared_themes = $ExtractService->PrepareThemesForDatabase($get_themes_from_file); - $save_themes = $ExtractService->SaveThemesOnDatabase($prepared_themes); + $extracted_themes = $extract_service->GetThemesFromExcelFile($excel_file); + $io->info(count($extracted_themes).' themes extracted'); + $prepared_themes = $extract_service->PrepareThemesForDatabase($extracted_themes); + $save_themes = $extract_service->SaveThemesOnDatabase($prepared_themes); if ($save_themes) { $io->info($this->themeRepository->count([]).' themes are saved successfuly'); From f4be30b75bb1caab17accdf5fde35f21f4842888 Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Thu, 6 Mar 2025 10:59:13 +0100 Subject: [PATCH 26/38] Remove unused parameter --- src/Command/CommandExtractService.php | 2 +- src/Imports/Themes/ExtractService.php | 4 +--- tests/Imports/Themes/ExtractServiceTest.php | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Command/CommandExtractService.php b/src/Command/CommandExtractService.php index a08dae2..d983d33 100644 --- a/src/Command/CommandExtractService.php +++ b/src/Command/CommandExtractService.php @@ -40,7 +40,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $extracthemes = $input->getArgument('extracthemes'); - $extract_service = new ExtractService($this->entityManager, $this->projectDir); + $extract_service = new ExtractService($this->entityManager); if ($extracthemes) { $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; diff --git a/src/Imports/Themes/ExtractService.php b/src/Imports/Themes/ExtractService.php index f1a3e38..026422a 100644 --- a/src/Imports/Themes/ExtractService.php +++ b/src/Imports/Themes/ExtractService.php @@ -8,15 +8,13 @@ class ExtractService { - private $projectDir; private $entityManager; private $themeRepository; - public function __construct(EntityManagerInterface $entityManager, string $projectDir) + public function __construct(EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; $this->themeRepository = $entityManager->getRepository(Theme::class); - $this->projectDir = $projectDir; } /** diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index ddc8735..6ed4df8 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -34,7 +34,7 @@ protected function tearDown(): void public function testImportThemeSave(): void { - $ExtractServices = new ExtractService($this->entityManager, $this->projectDir); + $ExtractServices = new ExtractService($this->entityManager); $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; $themes = $ExtractServices->GetThemesFromExcelFile($excel_file); $preparedThemes = $ExtractServices->PrepareThemesForDatabase($themes); From 6b229be3f817db9ee01d54dfa736d54337aa26df Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Thu, 6 Mar 2025 11:32:08 +0100 Subject: [PATCH 27/38] Remove non-needed functions for theme processing --- src/Imports/Themes/ExtractService.php | 110 ++++---------------------- src/Repository/ThemeRepository.php | 2 +- 2 files changed, 18 insertions(+), 94 deletions(-) diff --git a/src/Imports/Themes/ExtractService.php b/src/Imports/Themes/ExtractService.php index 026422a..d5226f5 100644 --- a/src/Imports/Themes/ExtractService.php +++ b/src/Imports/Themes/ExtractService.php @@ -33,13 +33,13 @@ public function GetThemesFromExcelFile(string $excel_file): array foreach ($sheet->getRowIterator() as $row) { $rowIndex = $row->getRowIndex(); - $cellA = $sheet->getCell('A'.$rowIndex)->getValue(); - $cellB = $sheet->getCell('B'.$rowIndex)->getValue(); - if (null !== $cellA && null !== $cellB) { + $name = $sheet->getCell('A'.$rowIndex)->getValue(); + $external_id = $sheet->getCell('B'.$rowIndex)->getValue(); + if (null !== $name && null !== $external_id) { if ($rowIndex > 2) { $themes[] = [ - 'categories_id' => $cellB, - 'categories' => $this->replaceSpaceByUnderscore($cellA), + 'categories_id' => $external_id, + 'categories' => $name, ]; } } @@ -48,55 +48,6 @@ public function GetThemesFromExcelFile(string $excel_file): array return $themes; } - private function replaceSpaceByUnderscore(string $word): string - { - $word = str_replace('/', 'et ', $word); - $word = str_replace(' ', '_', $word); - - return $word; - } - - private static function getHierarchieLevels(string $level): mixed - { - $hierarchie = []; - $check_dot = strpos($level, '.'); - - if (false !== $check_dot) { - $level_array = explode('.', $level); - while (!empty($level_array)) { - $hierarchie[] = implode('.', $level_array); - array_pop($level_array); - } - - return array_reverse($hierarchie); - } else { - return [$level]; - } - } - - private static function getCategoriesByCategorieId(array $themes, string $categorie_id): string - { - foreach ($themes as $theme) { - if ($theme['categories_id'] === $categorie_id) { - return $theme['categories']; - } - } - - return ''; - } - - private function getNameConcatenateByCategorieId(array $themes, string $level_id): string - { - $levels = []; - $hierarchie = []; - $levels = $this::gethierarchieLevels($level_id); - foreach ($levels as $level) { - $hierarchie[] = $this::getCategoriesByCategorieId($themes, $level); - } - - return implode('.', $hierarchie); - } - private function getParentExternalId(string $externalId): mixed { $check_dot = strpos($externalId, '.'); @@ -110,33 +61,16 @@ private function getParentExternalId(string $externalId): mixed } } - private function getCategoriesId($themes): array - { - $categories_id = []; - foreach ($themes as $theme) { - $categories_id[] = $theme['categories_id']; - } - - return $categories_id; - } - public function PrepareThemesForDatabase(array $themes): array { - $categories_id = $this->getCategoriesId($themes); - $themes_array = []; - $size = count($categories_id); - - for ($i = 0; $i < $size; ++$i) { - $themes_array[] = [ - 'id' => $i + 1, - 'name' => $this->getNameConcatenateByCategorieId($themes, $categories_id[$i]), - 'externalId' => $categories_id[$i], + return array_map(function($theme) { + return [ + 'name' => $theme['categories'], + 'externalId' => $theme['categories_id'], 'isSection' => true, - 'parentExternalId' => $this->getParentExternalId($categories_id[$i]), + 'parentExternalId' => $this->getParentExternalId($theme['categories_id']), ]; - } - - return $themes_array; + }, $themes); } public function SaveThemesOnDatabase(array $arrayThemes): bool @@ -148,25 +82,15 @@ public function SaveThemesOnDatabase(array $arrayThemes): bool } foreach ($arrayThemes as $theme) { - if (0 === $this->themeRepository->count([])) { - $newTheme = (new Theme()) + $newTheme = (new Theme()) ->setName($theme['name']) ->setExternalId($theme['externalId']) ->setIsSection($theme['isSection']) - ->setParentId(null); - $this->entityManager->persist($newTheme); - $this->entityManager->flush(); - $savedThemes = true; - } else { - $newTheme = (new Theme()) - ->setName($theme['name']) - ->setExternalId($theme['externalId']) - ->setIsSection($theme['isSection']) - ->setParentId($this->themeRepository->getParentIdByparentExternalId($theme['parentExternalId'])); - $this->entityManager->persist($newTheme); - $this->entityManager->flush(); - $savedThemes = true; - } + ->setParentId($this->themeRepository->getParentIdByparentExternalId($theme['parentExternalId'])); + $this->entityManager->persist($newTheme); + $this->entityManager->flush(); + $savedThemes = true; + } return $savedThemes; diff --git a/src/Repository/ThemeRepository.php b/src/Repository/ThemeRepository.php index 46ce922..32f19f7 100644 --- a/src/Repository/ThemeRepository.php +++ b/src/Repository/ThemeRepository.php @@ -15,7 +15,7 @@ public function __construct(ManagerRegistry $registry) parent::__construct($registry, Theme::class); } - public function getParentIdByparentExternalId(string $parentExternalId): ?int + public function getParentIdByparentExternalId(?string $parentExternalId): ?int { $result = $this ->createQueryBuilder('t') From 3b351151dd14b8b03b8a6d9e96d83e17bd67e949 Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Thu, 6 Mar 2025 11:53:18 +0100 Subject: [PATCH 28/38] Add ability to upsert themes --- src/Imports/Themes/ExtractService.php | 17 +++++++++++------ src/Repository/ThemeRepository.php | 4 ++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Imports/Themes/ExtractService.php b/src/Imports/Themes/ExtractService.php index d5226f5..f107009 100644 --- a/src/Imports/Themes/ExtractService.php +++ b/src/Imports/Themes/ExtractService.php @@ -48,7 +48,7 @@ public function GetThemesFromExcelFile(string $excel_file): array return $themes; } - private function getParentExternalId(string $externalId): mixed + private function getParentExternalId(string $externalId): ?string { $check_dot = strpos($externalId, '.'); if (false !== $check_dot) { @@ -82,15 +82,20 @@ public function SaveThemesOnDatabase(array $arrayThemes): bool } foreach ($arrayThemes as $theme) { - $newTheme = (new Theme()) + $existing_theme = $this->themeRepository->findOneBy(['externalId' => $theme['externalId']]); + $theme_to_write = $existing_theme ?? (new Theme())->setExternalId($theme['externalId']); + $external_id = $theme_to_write->getExternalId(); + $parent_external_id = $this->getParentExternalId($external_id); + $parent_id = $this->themeRepository->getIdByExternalId($parent_external_id); + + $theme_to_write ->setName($theme['name']) - ->setExternalId($theme['externalId']) ->setIsSection($theme['isSection']) - ->setParentId($this->themeRepository->getParentIdByparentExternalId($theme['parentExternalId'])); - $this->entityManager->persist($newTheme); + ->setParentId($parent_id); + + $this->entityManager->persist($theme_to_write); $this->entityManager->flush(); $savedThemes = true; - } return $savedThemes; diff --git a/src/Repository/ThemeRepository.php b/src/Repository/ThemeRepository.php index 32f19f7..1ac57f5 100644 --- a/src/Repository/ThemeRepository.php +++ b/src/Repository/ThemeRepository.php @@ -15,13 +15,13 @@ public function __construct(ManagerRegistry $registry) parent::__construct($registry, Theme::class); } - public function getParentIdByparentExternalId(?string $parentExternalId): ?int + public function getIdByExternalId(?string $externalId): ?int { $result = $this ->createQueryBuilder('t') ->select('t.id') ->where('t.externalId = :externalId') - ->setParameter('externalId', $parentExternalId) + ->setParameter('externalId', $externalId) ->getQuery() ->getOneOrNullResult(); From 0b5a359fb72c219284732cfdb6da824f31485b09 Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Thu, 6 Mar 2025 11:59:37 +0100 Subject: [PATCH 29/38] Remove xlsx de repo --- .gitignore | 5 ++++- public/File/emissions_GES_structure.xlsx | Bin 55840 -> 0 bytes var/import-data/.gitkeep | 0 3 files changed, 4 insertions(+), 1 deletion(-) delete mode 100644 public/File/emissions_GES_structure.xlsx create mode 100644 var/import-data/.gitkeep diff --git a/.gitignore b/.gitignore index 71d644d..e5ab8f8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,8 @@ /.env.*.local /config/secrets/prod/prod.decrypt.private.php /public/bundles/ -/var/ +/var/* +!/var/import-data/.gitkeep /vendor/ ###< symfony/framework-bundle ### @@ -31,5 +32,7 @@ phpunit.xml.old /assets/vendor/ ###< symfony/asset-mapper ### +public/File/emissions_GES_structure.xlsx + diff --git a/public/File/emissions_GES_structure.xlsx b/public/File/emissions_GES_structure.xlsx deleted file mode 100644 index b2b5ec579f5ef5cf7389179b70aaee5b691e8753..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55840 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 I Date: Thu, 6 Mar 2025 12:48:57 +0100 Subject: [PATCH 30/38] Update code to use built in repo methods --- src/Imports/Themes/ExtractService.php | 10 +++-- src/Repository/ThemeRepository.php | 43 --------------------- tests/Imports/Themes/ExtractServiceTest.php | 9 +++-- 3 files changed, 12 insertions(+), 50 deletions(-) diff --git a/src/Imports/Themes/ExtractService.php b/src/Imports/Themes/ExtractService.php index f107009..27e78bb 100644 --- a/src/Imports/Themes/ExtractService.php +++ b/src/Imports/Themes/ExtractService.php @@ -5,7 +5,7 @@ use App\Entity\Theme; use Doctrine\ORM\EntityManagerInterface; use PhpOffice\PhpSpreadsheet\IOFactory; - +use Symfony\Component\Filesystem\Exception\FileNotFoundException; class ExtractService { private $entityManager; @@ -26,7 +26,9 @@ public function GetThemesFromExcelFile(string $excel_file): array { $themes = []; if (!file_exists($excel_file)) { - return ['Excel File not found']; + throw new FileNotFoundException( + sprintf('Excel file "%s" not found', $excel_file) + ); } $spreadsheet = IOFactory::load($excel_file); $sheet = $spreadsheet->getActiveSheet(); @@ -86,12 +88,12 @@ public function SaveThemesOnDatabase(array $arrayThemes): bool $theme_to_write = $existing_theme ?? (new Theme())->setExternalId($theme['externalId']); $external_id = $theme_to_write->getExternalId(); $parent_external_id = $this->getParentExternalId($external_id); - $parent_id = $this->themeRepository->getIdByExternalId($parent_external_id); + $parent_theme = $this->themeRepository->findOneBy(['externalId' => $parent_external_id]); $theme_to_write ->setName($theme['name']) ->setIsSection($theme['isSection']) - ->setParentId($parent_id); + ->setParentId($parent_theme ? $parent_theme->getId() : null); $this->entityManager->persist($theme_to_write); $this->entityManager->flush(); diff --git a/src/Repository/ThemeRepository.php b/src/Repository/ThemeRepository.php index 1ac57f5..d183968 100644 --- a/src/Repository/ThemeRepository.php +++ b/src/Repository/ThemeRepository.php @@ -15,49 +15,6 @@ public function __construct(ManagerRegistry $registry) parent::__construct($registry, Theme::class); } - public function getIdByExternalId(?string $externalId): ?int - { - $result = $this - ->createQueryBuilder('t') - ->select('t.id') - ->where('t.externalId = :externalId') - ->setParameter('externalId', $externalId) - ->getQuery() - ->getOneOrNullResult(); - - return $result['id'] ?? null; - } - public function isFirstThemeParentIdNull(): bool - { - $firstTheme = $this->createQueryBuilder('theme') - ->select('theme.parentId') - ->orderBy('theme.id', 'ASC') - ->setMaxResults(1) - ->getQuery() - ->getOneOrNullResult(); - - return null !== $firstTheme && null === $firstTheme['parentId']; - } - public function isAllThemesParentIdAreNotNull(): bool - { - $isAllThemesParentId = true; - - $parentIds = $this->createQueryBuilder('theme') - ->select('theme.parentId') - ->orderBy('theme.id', 'ASC') - ->setFirstResult(1) - ->getQuery() - ->getResult(); - - foreach ($parentIds as $parentId) { - if (null === $parentId['parentId']) { - $isAllThemesParentId = false; - break; - } - } - - return $isAllThemesParentId; - } } diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index 6ed4df8..899881d 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -35,13 +35,16 @@ protected function tearDown(): void public function testImportThemeSave(): void { $ExtractServices = new ExtractService($this->entityManager); - $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; + $excel_file = $this->projectDir.'/var/import-data/emissions_GES_structure.xlsx'; $themes = $ExtractServices->GetThemesFromExcelFile($excel_file); $preparedThemes = $ExtractServices->PrepareThemesForDatabase($themes); $saveThemes = $ExtractServices->SaveThemesOnDatabase($preparedThemes); $this->assertTrue($saveThemes, 'themes are saved'); - $this->assertTrue($this->themeRepository->isFirstThemeParentIdNull(), 'first theme has ParentId : null'); - $this->assertTrue($this->themeRepository->isAllThemesParentIdAreNotNull(), 'The parentId for all themes is not null, except for the first theme.'); + $this->assertEquals( + 1, + count($this->themeRepository->findBy(['parentId' => null])), + "One import doesn't have a parentId" + ); } } From 7091bc65a0981f7d7600c68bd12c81369d1f47d8 Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Thu, 6 Mar 2025 13:08:51 +0100 Subject: [PATCH 31/38] Update names of array keys --- src/Command/CommandExtractService.php | 8 +++---- src/Imports/Themes/ExtractService.php | 23 ++++++++------------- tests/Imports/Themes/ExtractServiceTest.php | 9 ++++++-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Command/CommandExtractService.php b/src/Command/CommandExtractService.php index d983d33..d1cb8e7 100644 --- a/src/Command/CommandExtractService.php +++ b/src/Command/CommandExtractService.php @@ -55,11 +55,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int $extracted_themes = $extract_service->GetThemesFromExcelFile($excel_file); $io->info(count($extracted_themes).' themes extracted'); $prepared_themes = $extract_service->PrepareThemesForDatabase($extracted_themes); - $save_themes = $extract_service->SaveThemesOnDatabase($prepared_themes); + $saved_themes_count = $extract_service->SaveThemesOnDatabase($prepared_themes); + + + $io->info("$saved_themes_count themes were upserted successfuly"); - if ($save_themes) { - $io->info($this->themeRepository->count([]).' themes are saved successfuly'); - } } catch (\Exception $e) { $io->error('File Excel failed to read : '.$e->getMessage()); diff --git a/src/Imports/Themes/ExtractService.php b/src/Imports/Themes/ExtractService.php index 27e78bb..8e11174 100644 --- a/src/Imports/Themes/ExtractService.php +++ b/src/Imports/Themes/ExtractService.php @@ -40,13 +40,15 @@ public function GetThemesFromExcelFile(string $excel_file): array if (null !== $name && null !== $external_id) { if ($rowIndex > 2) { $themes[] = [ - 'categories_id' => $external_id, - 'categories' => $name, + 'externalId' => $external_id, + 'name' => $name, ]; } } } + + return $themes; } @@ -67,22 +69,16 @@ public function PrepareThemesForDatabase(array $themes): array { return array_map(function($theme) { return [ - 'name' => $theme['categories'], - 'externalId' => $theme['categories_id'], + 'name' => $theme['name'], + 'externalId' => $theme['externalId'], 'isSection' => true, - 'parentExternalId' => $this->getParentExternalId($theme['categories_id']), + 'parentExternalId' => $this->getParentExternalId($theme['externalId']), ]; }, $themes); } - public function SaveThemesOnDatabase(array $arrayThemes): bool + public function SaveThemesOnDatabase(array $arrayThemes): int { - $savedThemes = false; - - if (empty($arrayThemes)) { - return false; - } - foreach ($arrayThemes as $theme) { $existing_theme = $this->themeRepository->findOneBy(['externalId' => $theme['externalId']]); $theme_to_write = $existing_theme ?? (new Theme())->setExternalId($theme['externalId']); @@ -97,9 +93,8 @@ public function SaveThemesOnDatabase(array $arrayThemes): bool $this->entityManager->persist($theme_to_write); $this->entityManager->flush(); - $savedThemes = true; } - return $savedThemes; + return count($arrayThemes); } } diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index 899881d..f0d1822 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -38,9 +38,14 @@ public function testImportThemeSave(): void $excel_file = $this->projectDir.'/var/import-data/emissions_GES_structure.xlsx'; $themes = $ExtractServices->GetThemesFromExcelFile($excel_file); $preparedThemes = $ExtractServices->PrepareThemesForDatabase($themes); - $saveThemes = $ExtractServices->SaveThemesOnDatabase($preparedThemes); + $savedThemesCount = $ExtractServices->SaveThemesOnDatabase($preparedThemes); + + $this->assertEquals( + $savedThemesCount, + count($preparedThemes), + "Themes are saved" + ); - $this->assertTrue($saveThemes, 'themes are saved'); $this->assertEquals( 1, count($this->themeRepository->findBy(['parentId' => null])), From ad25c1b447eaf4347b042cab8aa080de33668da9 Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Thu, 6 Mar 2025 14:08:40 +0100 Subject: [PATCH 32/38] Add gd extension for ci --- .github/workflows/pre-merge-validation.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pre-merge-validation.yml b/.github/workflows/pre-merge-validation.yml index dc2f8e7..efc149b 100644 --- a/.github/workflows/pre-merge-validation.yml +++ b/.github/workflows/pre-merge-validation.yml @@ -12,6 +12,7 @@ jobs: uses: php-actions/composer@v6 with: php_version: 8.2 + php_extensions: gd # Fix permissions before composer operations # If not some composer operations that need to update autoloader files will fail From ad7bdfa20b84175ebdc6b6a384996fdf7099d36e Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Thu, 6 Mar 2025 14:13:58 +0100 Subject: [PATCH 33/38] Add another lib --- .github/workflows/pre-merge-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-merge-validation.yml b/.github/workflows/pre-merge-validation.yml index efc149b..377099b 100644 --- a/.github/workflows/pre-merge-validation.yml +++ b/.github/workflows/pre-merge-validation.yml @@ -12,7 +12,7 @@ jobs: uses: php-actions/composer@v6 with: php_version: 8.2 - php_extensions: gd + php_extensions: gd ext-zip # Fix permissions before composer operations # If not some composer operations that need to update autoloader files will fail From f0eeb49ded940d6156d74959524380bc35c895b1 Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Thu, 6 Mar 2025 14:16:56 +0100 Subject: [PATCH 34/38] Remove prefix from extension --- .github/workflows/pre-merge-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-merge-validation.yml b/.github/workflows/pre-merge-validation.yml index 377099b..33925b4 100644 --- a/.github/workflows/pre-merge-validation.yml +++ b/.github/workflows/pre-merge-validation.yml @@ -12,7 +12,7 @@ jobs: uses: php-actions/composer@v6 with: php_version: 8.2 - php_extensions: gd ext-zip + php_extensions: gd zip # Fix permissions before composer operations # If not some composer operations that need to update autoloader files will fail From 5e489cc816a8d564682c2aaed3af4e8d373a9171 Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Mon, 10 Mar 2025 09:48:54 +0100 Subject: [PATCH 35/38] Fix linter --- src/Command/CommandExtractService.php | 2 -- src/Imports/Themes/ExtractService.php | 9 +++------ src/Repository/ThemeRepository.php | 3 --- tests/Imports/Themes/ExtractServiceTest.php | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/Command/CommandExtractService.php b/src/Command/CommandExtractService.php index d1cb8e7..deb53c7 100644 --- a/src/Command/CommandExtractService.php +++ b/src/Command/CommandExtractService.php @@ -57,9 +57,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $prepared_themes = $extract_service->PrepareThemesForDatabase($extracted_themes); $saved_themes_count = $extract_service->SaveThemesOnDatabase($prepared_themes); - $io->info("$saved_themes_count themes were upserted successfuly"); - } catch (\Exception $e) { $io->error('File Excel failed to read : '.$e->getMessage()); diff --git a/src/Imports/Themes/ExtractService.php b/src/Imports/Themes/ExtractService.php index 8e11174..55f040f 100644 --- a/src/Imports/Themes/ExtractService.php +++ b/src/Imports/Themes/ExtractService.php @@ -6,6 +6,7 @@ use Doctrine\ORM\EntityManagerInterface; use PhpOffice\PhpSpreadsheet\IOFactory; use Symfony\Component\Filesystem\Exception\FileNotFoundException; + class ExtractService { private $entityManager; @@ -26,9 +27,7 @@ public function GetThemesFromExcelFile(string $excel_file): array { $themes = []; if (!file_exists($excel_file)) { - throw new FileNotFoundException( - sprintf('Excel file "%s" not found', $excel_file) - ); + throw new FileNotFoundException(sprintf('Excel file "%s" not found', $excel_file)); } $spreadsheet = IOFactory::load($excel_file); $sheet = $spreadsheet->getActiveSheet(); @@ -47,8 +46,6 @@ public function GetThemesFromExcelFile(string $excel_file): array } } - - return $themes; } @@ -67,7 +64,7 @@ private function getParentExternalId(string $externalId): ?string public function PrepareThemesForDatabase(array $themes): array { - return array_map(function($theme) { + return array_map(function ($theme) { return [ 'name' => $theme['name'], 'externalId' => $theme['externalId'], diff --git a/src/Repository/ThemeRepository.php b/src/Repository/ThemeRepository.php index d183968..8d8d09c 100644 --- a/src/Repository/ThemeRepository.php +++ b/src/Repository/ThemeRepository.php @@ -14,7 +14,4 @@ public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Theme::class); } - - - } diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index f0d1822..42dc775 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -43,7 +43,7 @@ public function testImportThemeSave(): void $this->assertEquals( $savedThemesCount, count($preparedThemes), - "Themes are saved" + 'Themes are saved' ); $this->assertEquals( From 3949ed45c876f386214127ce0c0abb0d6e9e35d6 Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Mon, 10 Mar 2025 10:06:00 +0100 Subject: [PATCH 36/38] Add test file --- tests/Imports/Themes/ExtractServiceTest.php | 8 +++++++- tests/Imports/Themes/test-themes.xlsx | Bin 0 -> 6447 bytes 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tests/Imports/Themes/test-themes.xlsx diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index 42dc775..71b6e20 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -35,7 +35,7 @@ protected function tearDown(): void public function testImportThemeSave(): void { $ExtractServices = new ExtractService($this->entityManager); - $excel_file = $this->projectDir.'/var/import-data/emissions_GES_structure.xlsx'; + $excel_file = $this->projectDir.'/tests/Imports/Themes/test-themes.xlsx'; $themes = $ExtractServices->GetThemesFromExcelFile($excel_file); $preparedThemes = $ExtractServices->PrepareThemesForDatabase($themes); $savedThemesCount = $ExtractServices->SaveThemesOnDatabase($preparedThemes); @@ -46,6 +46,12 @@ public function testImportThemeSave(): void 'Themes are saved' ); + $imported_theme = $this->themeRepository->findOneBy(['externalId'=> 'V0.1']); + + $this->assertEquals( + $imported_theme->getName(),'par gaz à effet de serre' + ); + $this->assertEquals( 1, count($this->themeRepository->findBy(['parentId' => null])), diff --git a/tests/Imports/Themes/test-themes.xlsx b/tests/Imports/Themes/test-themes.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f3f2238faf734685c0a240237f5fc032b940741c GIT binary patch literal 6447 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 Date: Mon, 10 Mar 2025 10:07:15 +0100 Subject: [PATCH 37/38] Add comment --- tests/Imports/Themes/ExtractServiceTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Imports/Themes/ExtractServiceTest.php b/tests/Imports/Themes/ExtractServiceTest.php index 71b6e20..4364af9 100644 --- a/tests/Imports/Themes/ExtractServiceTest.php +++ b/tests/Imports/Themes/ExtractServiceTest.php @@ -35,6 +35,7 @@ protected function tearDown(): void public function testImportThemeSave(): void { $ExtractServices = new ExtractService($this->entityManager); + // TODO: change this to inject the spreadsheet so that we can test the integration more easily. $excel_file = $this->projectDir.'/tests/Imports/Themes/test-themes.xlsx'; $themes = $ExtractServices->GetThemesFromExcelFile($excel_file); $preparedThemes = $ExtractServices->PrepareThemesForDatabase($themes); @@ -46,10 +47,10 @@ public function testImportThemeSave(): void 'Themes are saved' ); - $imported_theme = $this->themeRepository->findOneBy(['externalId'=> 'V0.1']); + $imported_theme = $this->themeRepository->findOneBy(['externalId' => 'V0.1']); $this->assertEquals( - $imported_theme->getName(),'par gaz à effet de serre' + $imported_theme->getName(), 'par gaz à effet de serre' ); $this->assertEquals( From 870798b28e18d06bad8c3f78554ef1dcdbd9737b Mon Sep 17 00:00:00 2001 From: Austin Flores Date: Mon, 10 Mar 2025 10:27:20 +0100 Subject: [PATCH 38/38] Update themes location --- src/Command/CommandExtractService.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Command/CommandExtractService.php b/src/Command/CommandExtractService.php index deb53c7..a9ebbbc 100644 --- a/src/Command/CommandExtractService.php +++ b/src/Command/CommandExtractService.php @@ -2,7 +2,6 @@ namespace App\Command; -use App\Entity\Theme; use App\Imports\Themes\ExtractService; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Attribute\AsCommand; @@ -20,13 +19,11 @@ class CommandExtractService extends Command { private $projectDir; private $entityManager; - private $themeRepository; public function __construct(string $projectDir, EntityManagerInterface $entityManager) { $this->projectDir = $projectDir; $this->entityManager = $entityManager; - $this->themeRepository = $entityManager->getRepository(Theme::class); parent::__construct(); } @@ -43,7 +40,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $extract_service = new ExtractService($this->entityManager); if ($extracthemes) { - $excel_file = $this->projectDir.'/public/File/emissions_GES_structure.xlsx'; + $excel_file = $this->projectDir.'/var/import-data/emissions_GES_structure.xlsx'; if (!file_exists($excel_file)) { $io->error('file does not exist');