importation initiale

This commit is contained in:
Jephté Clain 2024-11-27 13:49:42 +04:00
commit 80850f0540
21 changed files with 5128 additions and 0 deletions

6
.composer.yaml Normal file
View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 mode: yaml -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8
require:
nulib/php: ^7.4-dev
branch:
develop:
master:

0
.dockerignore Normal file
View File

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.~lock*#
.*.swp
/vendor/

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

17
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State />
</expanded-state>
<selected-state>
<State>
<id>Angular</id>
</State>
</selected-state>
</profile-state>
</entry>
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/nulib-spout.iml" filepath="$PROJECT_DIR$/.idea/nulib-spout.iml" />
</modules>
</component>
</project>

13
.idea/nulib-spout.iml generated Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="nulib\ext\" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="nulib\ext\" />
<sourceFolder url="file://$MODULE_DIR$/upstream/src" isTestSource="false" packagePrefix="OpenSpout\" />
<excludeFolder url="file://$MODULE_DIR$/vendor" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

23
.idea/php-docker-settings.xml generated Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpDockerContainerSettings">
<list>
<map>
<entry key="125ffb9d-fd5f-4e71-8182-94191665795a">
<value>
<DockerContainerSettings>
<option name="version" value="1" />
<option name="volumeBindings">
<list>
<DockerVolumeBindingImpl>
<option name="containerPath" value="/opt/project" />
</DockerVolumeBindingImpl>
</list>
</option>
</DockerContainerSettings>
</value>
</entry>
</map>
</list>
</component>
</project>

14
.idea/php-test-framework.xml generated Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpTestFrameworkVersionCache">
<tools_cache>
<tool tool_name="PHPUnit">
<cache>
<versions>
<info id="Local/vendor/autoload.php" version="9.6.21" />
</versions>
</cache>
</tool>
</tools_cache>
</component>
</project>

106
.idea/php.xml generated Normal file
View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MessDetectorOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCSFixerOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCodeSnifferOptionsConfiguration">
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpCSFixer">
<phpcsfixer_settings>
<PhpCSFixerConfiguration tool_path="$PROJECT_DIR$/vendor/bin/php-cs-fixer" />
</phpcsfixer_settings>
</component>
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
<path value="$PROJECT_DIR$/vendor/sebastian/type" />
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
<path value="$PROJECT_DIR$/vendor/sebastian/version" />
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
<path value="$PROJECT_DIR$/vendor/sebastian/comparator" />
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
<path value="$PROJECT_DIR$/vendor/symfony/string" />
<path value="$PROJECT_DIR$/vendor/symfony/process" />
<path value="$PROJECT_DIR$/vendor/sebastian/resource-operations" />
<path value="$PROJECT_DIR$/vendor/sebastian/complexity" />
<path value="$PROJECT_DIR$/vendor/sebastian/cli-parser" />
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
<path value="$PROJECT_DIR$/vendor/friendsofphp/php-cs-fixer" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-code-coverage" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-text-template" />
<path value="$PROJECT_DIR$/vendor/nikic/php-parser" />
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-phpunit" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan" />
<path value="$PROJECT_DIR$/vendor/psr/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/psr/container" />
<path value="$PROJECT_DIR$/vendor/composer" />
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit" />
<path value="$PROJECT_DIR$/vendor/sebastian/lines-of-code" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-invoker" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
<path value="$PROJECT_DIR$/vendor/psr/log" />
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
<path value="$PROJECT_DIR$/vendor/clue/ndjson-react" />
<path value="$PROJECT_DIR$/vendor/evenement/evenement" />
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
<path value="$PROJECT_DIR$/vendor/react/promise" />
<path value="$PROJECT_DIR$/vendor/react/cache" />
<path value="$PROJECT_DIR$/vendor/fidry/cpu-core-counter" />
<path value="$PROJECT_DIR$/vendor/react/dns" />
<path value="$PROJECT_DIR$/vendor/react/child-process" />
<path value="$PROJECT_DIR$/vendor/react/event-loop" />
<path value="$PROJECT_DIR$/vendor/react/socket" />
<path value="$PROJECT_DIR$/vendor/react/stream" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-grapheme" />
<path value="$PROJECT_DIR$/vendor/symfony/console" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php81" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php73" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-normalizer" />
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/finder" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
<path value="$PROJECT_DIR$/vendor/symfony/filesystem" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/options-resolver" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/symfony/stopwatch" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
<path value="$PROJECT_DIR$/vendor/nulib/tests" />
<path value="$PROJECT_DIR$/vendor/nulib/php" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.4">
<option name="suggestChangeDefaultLanguageLevel" value="false" />
</component>
<component name="PhpStan">
<PhpStan_settings>
<PhpStanConfiguration tool_path="$PROJECT_DIR$/vendor/bin/phpstan" />
</PhpStan_settings>
</component>
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PhpUnit">
<phpunit_settings>
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" phpunit_phar_path="" />
</phpunit_settings>
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>
</project>

10
.idea/phpunit.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PHPUnit">
<option name="directories">
<list>
<option value="$PROJECT_DIR$/tests" />
</list>
</option>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

8
.runphp.conf Normal file
View File

@ -0,0 +1,8 @@
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
# Chemin vers runphp, e.g sbin/runphp
RUNPHP=
# Si RUNPHP n'est pas défini, les variables suivantes peuvent être définies
DIST=d11
#REGISTRY=pubdocker.univ-reunion.fr

52
composer.json Normal file
View File

@ -0,0 +1,52 @@
{
"name": "nulib/spout",
"type": "library",
"description": "wrapper pour openspout/openspout",
"repositories": [
{
"type": "composer",
"url": "https://repos.univ-reunion.fr/composer"
}
],
"extra": {
"branch-alias": {
"dev-master": "7.4.x-dev"
}
},
"replace": {
"openspout/openspout": "v3.7.4"
},
"require": {
"nulib/php": "^7.4-dev",
"ext-dom": "*",
"ext-filter": "*",
"ext-libxml": "*",
"ext-xmlreader": "*",
"ext-zip": "*",
"php": "^7.4"
},
"require-dev": {
"nulib/tests": "^7.4",
"friendsofphp/php-cs-fixer": "^3.4",
"phpstan/phpstan": "^1.4",
"phpstan/phpstan-phpunit": "^1.0",
"ext-zlib": "*"
},
"autoload": {
"psr-4": {
"nulib\\ext\\": "src",
"OpenSpout\\": "upstream-3.x/src"
}
},
"autoload-dev": {
"psr-4": {
"nulib\\ext\\": "tests"
}
},
"authors": [
{
"name": "Jephte Clain",
"email": "Jephte.Clain@univ-reunion.fr"
}
]
}

4507
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

0
src/.gitignore vendored Normal file
View File

206
src/spout/SpoutBuilder.php Normal file
View File

@ -0,0 +1,206 @@
<?php
namespace nulib\ext\spout;
use nulib\file\tab\AbstractBuilder;
use nulib\file\tab\TAbstractBuilder;
use nulib\os\path;
use nulib\php\nur_func;
use nulib\php\time\Date;
use nulib\php\time\DateTime;
use nulib\web\http;
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Common\Helper\CellTypeHelper;
use OpenSpout\Writer\Common\Creator\WriterEntityFactory;
use OpenSpout\Writer\WriterMultiSheetsAbstract;
use OpenSpout\Writer\XLSX\Entity\SheetView;
class SpoutBuilder extends AbstractBuilder {
use TAbstractBuilder;
const DATE_FORMAT = "mm/dd/yyyy";
const DATETIME_FORMAT = "mm/dd/yyyy hh:mm:ss";
/** @var bool faut-il choisir le type numérique pour une chaine numérique? */
const TYPE_NUMERIC = true;
/** @var bool faut-il choisir le type date pour une chaine au bon format? */
const TYPE_DATE = true;
/** @var string|int|null nom de la feuille dans laquelle écrire */
const WSNAME = null;
/** @var array|null paramètres de la feuille dans laquelle écrire */
const WSPARAMS = null;
function __construct(?string $output, ?array $params=null) {
parent::__construct($output, $params);
$ssType = $params["ss_type"] ?? null;
if ($ssType === null) {
switch (path::ext($this->output)) {
case ".ods":
$ssType = "ods";
break;
case ".xlsx":
default:
$ssType = "xlsx";
break;
}
}
switch ($ssType) {
case "ods":
$ss = WriterEntityFactory::createODSWriter();
break;
case "xlsx":
default:
$ss = WriterEntityFactory::createXLSXWriter();
break;
}
$ss->setDefaultColumnWidth(10.5);
$ss->writeToStream($this->getResource());
$this->ss = $ss;
$this->typeNumeric = boolval($params["type_numeric"] ?? static::TYPE_NUMERIC);
$this->typeDate = boolval($params["type_date"] ?? static::TYPE_DATE);
$this->firstSheet = true;
$wsparams = $params["wsparams"] ?? static::WSPARAMS;
$this->setWsname($params["wsname"] ?? static::WSNAME, $wsparams);
}
protected WriterMultiSheetsAbstract $ss;
protected bool $typeNumeric;
protected bool $typeDate;
const STYLE_ROW = 0, STYLE_HEADER = 1;
protected int $rowStyle;
protected bool $firstSheet;
/**
* @param string|int|null $wsname
*/
function setWsname($wsname, ?array $params=null): self {
$ss = $this->ss;
$this->rowStyle = self::STYLE_ROW;
if ($this->firstSheet) {
$this->firstSheet = false;
$ws = $ss->getCurrentSheet();
} else {
$ws = $ss->addNewSheetAndMakeItCurrent();
$this->wroteHeaders = false;
$this->built = false;
}
$wsname ??= $params["wsname"] ?? null;
if ($wsname !== null) $ws->setName($wsname);
$sheetView = (new SheetView())
->setFreezeRow($params["sheetView_freezeRow"] ?? 2);
$ws->setSheetView($sheetView);
if ($params !== null) {
if (array_key_exists("schema", $params)) {
$this->schema = $params["schema"] ?? null;
}
if (array_key_exists("headers", $params)) {
$this->headers = $params["headers"] ?? null;
}
if (array_key_exists("rows", $params)) {
$rows = $params["rows"] ?? null;
if (is_callable($rows)) $rows = $rows();
$this->rows = $rows;
}
if (array_key_exists("cook_func", $params)) {
$cookFunc = $params["cook_func"] ?? null;
$cookCtx = $cookArgs = null;
if ($cookFunc !== null) {
nur_func::ensure_func($cookFunc, $this, $cookArgs);
$cookCtx = nur_func::_prepare($cookFunc);
}
$this->cookCtx = $cookCtx;
$this->cookArgs = $cookArgs;
}
if (array_key_exists("type_numeric", $params)) {
$this->typeNumeric = boolval($params["type_numeric"] ?? static::TYPE_NUMERIC);
}
if (array_key_exists("type_date", $params)) {
$this->typeDate = boolval($params["type_date"] ?? static::TYPE_DATE);
}
}
return $this;
}
protected function isNumeric($value): bool {
if ($this->typeNumeric && is_numeric($value)) return true;
if (!is_string($value) && is_numeric($value)) return true;
return false;
}
protected function isDate(&$value, &$style): bool {
if (CellTypeHelper::isDateTimeOrDateInterval($value)) {
$style = (new Style())->setFormat(self::DATE_FORMAT);
return true;
}
if (!is_string($value) || !$this->typeDate) return false;
if (DateTime::isa_datetime($value, true)) {
$value = new DateTime($value);
$style = (new Style())->setFormat(self::DATETIME_FORMAT);
return true;
}
if (DateTime::isa_date($value, true)) {
$value = new Date($value);
$style = (new Style())->setFormat(self::DATE_FORMAT);
return true;
}
return false;
}
function _write(array $row): void {
$cells = [];
$rowStyle = null;
foreach ($row as $col) {
$style = null;
if ($col === null || $col === "") {
$type = Cell::TYPE_EMPTY;
} elseif ($this->isNumeric($col)) {
$type = Cell::TYPE_NUMERIC;
} elseif ($this->isDate($col, $style)) {
$type = Cell::TYPE_DATE;
} else {
$type = Cell::TYPE_STRING;
}
$cell = WriterEntityFactory::createCell($col, $style);
$cell->setType($type);
$cells[] = $cell;
}
if ($this->rowStyle === self::STYLE_HEADER) {
$rowStyle = (new Style())->setFontBold();
}
$this->ss->addRow(WriterEntityFactory::createRow($cells, $rowStyle));
}
function writeHeaders(?array $headers=null): void {
$this->rowStyle = self::STYLE_HEADER;
parent::writeHeaders($headers);
$this->rowStyle = self::STYLE_ROW;
}
function _sendContentType(): void {
switch (path::ext($this->output)) {
case ".ods":
$contentType = "application/vnd.oasis.opendocument.spreadsheet";
break;
case ".xlsx":
default:
$contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
break;
}
http::content_type($contentType);
}
protected function _checkOk(): bool {
$this->ss->close();
$this->rewind();
return true;
}
}

121
src/spout/SpoutReader.php Normal file
View File

@ -0,0 +1,121 @@
<?php
namespace nulib\ext\spout;
use nulib\cl;
use nulib\file\tab\AbstractReader;
use OpenSpout\Reader\Common\Creator\ReaderEntityFactory;
class SpoutReader extends AbstractReader {
/** @var string|int|null nom de la feuille depuis laquelle lire */
const WSNAME = null;
function __construct($input, ?array $params=null) {
parent::__construct($input, $params);
$this->ssType = $params["ss_type"] ?? null;
$this->allSheets = $params["all_sheets"] ?? true;
$wsname = static::WSNAME;
if ($params !== null && array_key_exists("wsname", $params)) {
# spécifié par l'utilisateur: $allSheets = false
$this->setWsname($params["wsname"]);
} elseif ($wsname !== null) {
# valeur non nulle de la classe: $allSheets = false
$this->setWsname($wsname);
} else {
# pas de valeur définie dans la classe, laisser $allSheets à sa valeur
# actuelle
$this->wsname = null;
}
$this->includeWsnames = cl::withn($params["include_wsnames"] ?? null);
$this->excludeWsnames = cl::withn($params["exclude_wsnames"] ?? null);
}
protected ?string $ssType;
/** @var bool faut-il retourner les lignes de toutes les feuilles? */
protected bool $allSheets;
function setAllSheets(bool $allSheets=true): self {
$this->allSheets = $allSheets;
return $this;
}
/**
* @var array|null si non null, liste de feuilles à inclure. n'est pris en
* compte que si $allSheets===true
*/
protected ?array $includeWsnames;
/**
* @var array|null si non null, liste de feuilles à exclure. n'est pris en
* compte que si $allSheets===true
*/
protected ?array $excludeWsnames;
protected $wsname;
/**
* @param string|int|null $wsname l'unique feuille à sélectionner
*
* NB: appeler cette méthode réinitialise $allSheets à false
*/
function setWsname($wsname): self {
$this->wsname = $wsname;
$this->allSheets = true;
return $this;
}
function getIterator() {
switch ($this->ssType) {
case "ods":
$ss = ReaderEntityFactory::createODSReader();
break;
case "xlsx":
$ss = ReaderEntityFactory::createXLSXReader();
break;
default:
$ss = ReaderEntityFactory::createReaderFromFile($this->input);
break;
}
$ss->open($this->input);
try {
$allSheets = $this->allSheets;
$includeWsnames = $this->includeWsnames;
$excludeWsnames = $this->excludeWsnames;
$wsname = $this->wsname;
$first = true;
foreach ($ss->getSheetIterator() as $ws) {
if ($allSheets) {
$wsname = $ws->getName();
$found = ($includeWsnames === null || in_array($wsname, $includeWsnames))
&& ($excludeWsnames === null || !in_array($wsname, $excludeWsnames));
} else {
$found = $wsname === null || $wsname === $ws->getName();
}
if ($found) {
if ($first) {
$first = false;
} else {
yield null;
# on garde le même schéma le cas échéant, mais supprimer headers
# pour permettre son recalcul
$this->headers = null;
}
$this->isrc = $this->idest = 0;
foreach ($ws->getRowIterator() as $row) {
$row = $row->toArray();
foreach ($row as &$col) {
$this->verifixCol($col);
}; unset($col);
if ($this->cookRow($row)) {
yield $row;
$this->idest++;
}
$this->isrc++;
}
}
}
} finally {
$ss->close();
}
}
}

11
src/tab/SsBuilder.php Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace nulib\ext\tab;
use nulib\ext\spout\SpoutBuilder;
/**
* Class SsBuilder: construction d'une feuille de calcul, pour envoi à
* l'utilisateur
*/
class SsBuilder extends SpoutBuilder {
}

9
src/tab/SsReader.php Normal file
View File

@ -0,0 +1,9 @@
<?php
namespace nulib\ext\tab;
use nulib\ext\spout\SpoutReader;
use nulib\file\tab\TAbstractReader;
class SsReader extends SpoutReader {
use TAbstractReader;
}

0
tests/.gitignore vendored Normal file
View File