mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-13 21:59:34 +00:00
Projekt-Importer um JSON/CSV Importer analog zu Assemblies erweitern
This commit is contained in:
parent
688e2f87a3
commit
a47f6c22e6
18 changed files with 3124 additions and 62 deletions
|
|
@ -46,14 +46,16 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function Symfony\Component\Translation\t;
|
||||
|
||||
#[Route(path: '/project')]
|
||||
class ProjectController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly DataTableFactory $dataTableFactory)
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DataTableFactory $dataTableFactory,
|
||||
private readonly TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route(path: '/{id}/info', name: 'project_info', requirements: ['id' => '\d+'])]
|
||||
|
|
@ -147,6 +149,8 @@ class ProjectController extends AbstractController
|
|||
'label' => 'project.bom_import.type',
|
||||
'required' => true,
|
||||
'choices' => [
|
||||
'project.bom_import.type.json' => 'json',
|
||||
'project.bom_import.type.csv' => 'csv',
|
||||
'project.bom_import.type.kicad_pcbnew' => 'kicad_pcbnew',
|
||||
'project.bom_import.type.kicad_schematic' => 'kicad_schematic',
|
||||
'project.bom_import.type.generic_csv' => 'generic_csv',
|
||||
|
|
@ -193,13 +197,20 @@ class ProjectController extends AbstractController
|
|||
'type' => $import_type,
|
||||
]);
|
||||
|
||||
$importerResult = $BOMImporter->importFileIntoProject($form->get('file')->getData(), $project, [
|
||||
'type' => $import_type,
|
||||
]);
|
||||
|
||||
// Validate the project entries
|
||||
$errors = $validator->validateProperty($project, 'bom_entries');
|
||||
|
||||
// If no validation errors occurred, save the changes and redirect to edit page
|
||||
if (count($errors) === 0) {
|
||||
//If no validation errors occurred, save the changes and redirect to edit page
|
||||
if (count($errors) === 0 && $importerResult->getViolations()->count() === 0) {
|
||||
$entries = $importerResult->getBomEntries();
|
||||
|
||||
$this->addFlash('success', t('project.bom_import.flash.success', ['%count%' => count($entries)]));
|
||||
$entityManager->flush();
|
||||
|
||||
return $this->redirectToRoute('project_edit', ['id' => $project->getID()]);
|
||||
}
|
||||
|
||||
|
|
@ -211,10 +222,29 @@ class ProjectController extends AbstractController
|
|||
}
|
||||
}
|
||||
|
||||
$jsonTemplate = [
|
||||
[
|
||||
"quantity" => 1.0,
|
||||
"name" => $this->translator->trans('project.bom_import.template.entry.name'),
|
||||
"part" => [
|
||||
"id" => null,
|
||||
"ipn" => $this->translator->trans('project.bom_import.template.entry.part.ipn'),
|
||||
"mpnr" => $this->translator->trans('project.bom_import.template.entry.part.mpnr'),
|
||||
"name" => $this->translator->trans('project.bom_import.template.entry.part.name'),
|
||||
"manufacturer" => [
|
||||
"id" => null,
|
||||
"name" => $this->translator->trans('project.bom_import.template.entry.part.manufacturer.name')
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
return $this->render('projects/import_bom.html.twig', [
|
||||
'project' => $project,
|
||||
'jsonTemplate' => $jsonTemplate,
|
||||
'form' => $form,
|
||||
'errors' => $errors ?? null,
|
||||
'validationErrors' => $errors ?? null,
|
||||
'importerErrors' => isset($importerResult) ? $importerResult->getViolations() : null,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ use ApiPlatform\OpenApi\Model\Operation;
|
|||
use ApiPlatform\Serializer\Filter\PropertyFilter;
|
||||
use App\ApiPlatform\Filter\LikeFilter;
|
||||
use App\Entity\Contracts\TimeStampableInterface;
|
||||
use App\Repository\DBElementRepository;
|
||||
use App\Validator\UniqueValidatableInterface;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use App\Entity\Base\AbstractDBElement;
|
||||
|
|
@ -54,7 +55,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|||
* The ProjectBOMEntry class represents an entry in a project's BOM.
|
||||
*/
|
||||
#[ORM\HasLifecycleCallbacks]
|
||||
#[ORM\Entity]
|
||||
#[ORM\Entity(repositoryClass: DBElementRepository::class)]
|
||||
#[ORM\Table('project_bom_entries')]
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ class BOMImporter
|
|||
private const IMPORT_TYPE_JSON = 'json';
|
||||
private const IMPORT_TYPE_CSV = 'csv';
|
||||
private const IMPORT_TYPE_KICAD_PCB = 'kicad_pcbnew';
|
||||
private const IMPORT_TYPE_KICAD_SCHEMATIC = 'kicad_schematic';
|
||||
|
||||
private const MAP_KICAD_PCB_FIELDS = [
|
||||
0 => 'Id',
|
||||
|
|
@ -80,7 +81,7 @@ class BOMImporter
|
|||
protected function configureOptions(OptionsResolver $resolver): OptionsResolver
|
||||
{
|
||||
$resolver->setRequired('type');
|
||||
$resolver->setAllowedValues('type', ['kicad_pcbnew', 'kicad_schematic', 'json']);
|
||||
$resolver->setAllowedValues('type', [self::IMPORT_TYPE_KICAD_PCB, self::IMPORT_TYPE_KICAD_SCHEMATIC, self::IMPORT_TYPE_JSON, self::IMPORT_TYPE_CSV]);
|
||||
|
||||
// For flexible schematic import with field mapping
|
||||
$resolver->setDefined(['field_mapping', 'field_priorities', 'delimiter']);
|
||||
|
|
@ -96,18 +97,19 @@ class BOMImporter
|
|||
/**
|
||||
* Converts the given file into an array of BOM entries using the given options and save them into the given project.
|
||||
* The changes are not saved into the database yet.
|
||||
* @return ProjectBOMEntry[]
|
||||
*/
|
||||
public function importFileIntoProject(File $file, Project $project, array $options): array
|
||||
public function importFileIntoProject(UploadedFile $file, Project $project, array $options): ImporterResult
|
||||
{
|
||||
$bom_entries = $this->fileToBOMEntries($file, $options);
|
||||
$importerResult = $this->fileToImporterResult($file, $options);
|
||||
|
||||
//Assign the bom_entries to the project
|
||||
foreach ($bom_entries as $bom_entry) {
|
||||
$project->addBomEntry($bom_entry);
|
||||
if ($importerResult->getViolations()->count() === 0) {
|
||||
//Assign the bom_entries to the project
|
||||
foreach ($importerResult->getBomEntries() as $bomEntry) {
|
||||
$project->addBomEntry($bomEntry);
|
||||
}
|
||||
}
|
||||
|
||||
return $bom_entries;
|
||||
return $importerResult;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -170,7 +172,7 @@ class BOMImporter
|
|||
$fileExtension,
|
||||
[
|
||||
'%extension%' => $fileExtension,
|
||||
'%importType%' => $this->translator->trans('assembly.bom_import.type.'.$options['type']),
|
||||
'%importType%' => $this->translator->trans($objectType === ProjectBOMEntry::class ? 'project.bom_import.type.'.$options['type'] : 'assembly.bom_import.type.'.$options['type']),
|
||||
'%allowedExtensions%' => implode(', ', $validExtensions),
|
||||
]
|
||||
));
|
||||
|
|
@ -211,9 +213,9 @@ class BOMImporter
|
|||
$options = $resolver->resolve($options);
|
||||
|
||||
return match ($options['type']) {
|
||||
'kicad_pcbnew' => $this->parseKiCADPCB($data, $objectType),
|
||||
'kicad_schematic' => $this->parseKiCADSchematic($data, $options),
|
||||
default => throw new InvalidArgumentException('Invalid import type!'),
|
||||
self::IMPORT_TYPE_KICAD_PCB => $this->parseKiCADPCB($data, $objectType)->getBomEntries(),
|
||||
self::IMPORT_TYPE_KICAD_SCHEMATIC => $this->parseKiCADSchematic($data, $options),
|
||||
default => throw new InvalidArgumentException($this->translator->trans('validator.bom_importer.invalid_import_type', [], 'validators')),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -697,7 +699,7 @@ class BOMImporter
|
|||
$category = $categoryIdValid ? $this->categoryRepository->findOneBy(['id' => $entry['part']['category']['id']]) : null;
|
||||
$category = $category ?? ($categoryNameValid ? $this->categoryRepository->findOneBy(['name' => trim($entry['part']['category']['name'])]) : null);
|
||||
|
||||
if (($categoryIdValid || $categoryNameValid) && $category === null) {
|
||||
if (($categoryIdValid || $categoryNameValid)) {
|
||||
$value = sprintf(
|
||||
'category.id: %s, category.name: %s',
|
||||
isset($entry['part']['category']['id']) && $entry['part']['category']['id'] !== null ? '<strong>' . $entry['part']['category']['id'] . '</strong>' : '-',
|
||||
|
|
@ -734,12 +736,12 @@ class BOMImporter
|
|||
$part->setDescription($partDescription);
|
||||
}
|
||||
|
||||
if ($manufacturer !== null && $manufacturer->getID() !== $part->getManufacturerID()) {
|
||||
if ($manufacturer !== null && $manufacturer->getID() !== $part->getManufacturer()->getID()) {
|
||||
//When updating the associated parts, take over to a assembly of the manufacturer of the part.
|
||||
$part->setManufacturer($manufacturer);
|
||||
}
|
||||
|
||||
if ($category !== null && $category->getID() !== $part->getCategoryID()) {
|
||||
if ($category !== null && $category->getID() !== $part->getCategory()->getID()) {
|
||||
//When updating the associated parts to a assembly, take over the category of the part.
|
||||
$part->setCategory($category);
|
||||
}
|
||||
|
|
@ -757,11 +759,26 @@ class BOMImporter
|
|||
}
|
||||
}
|
||||
} else {
|
||||
$bomEntry = new ProjectBOMEntry();
|
||||
$bomEntry = $this->projectBOMEntryRepository->findOneBy(['part' => $part]);
|
||||
|
||||
if ($bomEntry === null) {
|
||||
if (isset($entry['name']) && $entry['name'] !== '') {
|
||||
$bomEntry = $this->projectBOMEntryRepository->findOneBy(['name' => $entry['name']]);
|
||||
}
|
||||
|
||||
if ($bomEntry === null) {
|
||||
$bomEntry = new ProjectBOMEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$bomEntry->setQuantity((float) $entry['quantity']);
|
||||
$bomEntry->setName($entry['name'] ?? '');
|
||||
|
||||
if (isset($entry['name'])) {
|
||||
$bomEntry->setName(trim($entry['name']) === '' ? null : trim ($entry['name']));
|
||||
} else {
|
||||
$bomEntry->setName(null);
|
||||
}
|
||||
|
||||
$bomEntry->setPart($part);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue