diff --git a/src/Command/Migrations/ImportPartKeeprCommand.php b/src/Command/Migrations/ImportPartKeeprCommand.php index 53aecc04..22ac1ef3 100644 --- a/src/Command/Migrations/ImportPartKeeprCommand.php +++ b/src/Command/Migrations/ImportPartKeeprCommand.php @@ -24,10 +24,12 @@ use App\Services\ImportExportSystem\PartKeeprImporter\PKDatastructureImporter; use App\Services\ImportExportSystem\PartKeeprImporter\MySQLDumpXMLConverter; use App\Services\ImportExportSystem\PartKeeprImporter\PKImportHelper; use App\Services\ImportExportSystem\PartKeeprImporter\PKPartImporter; +use App\Services\ImportExportSystem\PartKeeprImporter\PKOptionalImporter; use Doctrine\ORM\EntityManagerInterface; 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; @@ -41,9 +43,11 @@ class ImportPartKeeprCommand extends Command protected PKDatastructureImporter $datastructureImporter; protected PKImportHelper $importHelper; protected PKPartImporter $partImporter; + protected PKOptionalImporter $optionalImporter; public function __construct(EntityManagerInterface $em, MySQLDumpXMLConverter $xml_converter, - PKDatastructureImporter $datastructureImporter, PKPartImporter $partImporter, PKImportHelper $importHelper) + PKDatastructureImporter $datastructureImporter, PKPartImporter $partImporter, PKImportHelper $importHelper, + PKOptionalImporter $optionalImporter) { parent::__construct(self::$defaultName); $this->em = $em; @@ -51,6 +55,7 @@ class ImportPartKeeprCommand extends Command $this->importHelper = $importHelper; $this->partImporter = $partImporter; $this->xml_converter = $xml_converter; + $this->optionalImporter = $optionalImporter; } protected function configure() @@ -58,6 +63,9 @@ class ImportPartKeeprCommand extends Command $this->setDescription('Import a PartKeepr database dump into Part-DB'); $this->addArgument('file', InputArgument::REQUIRED, 'The file to which should be imported.'); + + $this->addOption('--no-projects', null, InputOption::VALUE_NONE, 'Do not import projects.'); + $this->addOption('--import-users', null, InputOption::VALUE_NONE, 'Import users (passwords will not be imported).'); } public function execute(InputInterface $input, OutputInterface $output) @@ -65,6 +73,8 @@ class ImportPartKeeprCommand extends Command $io = new SymfonyStyle($input, $output); $input_path = $input->getArgument('file'); + $no_projects_import = $input->getOption('no-projects'); + $import_users = $input->getOption('import-users'); //Make more checks here //$io->confirm('This will delete all data in the database. Do you want to continue?', false); @@ -76,9 +86,21 @@ class ImportPartKeeprCommand extends Command $xml = file_get_contents($input_path); $data = $this->xml_converter->convertMySQLDumpXMLDataToArrayStructure($xml); - //Import the data + //Import the mandatory data $this->doImport($io, $data); + if (!$no_projects_import) { + $io->info('Importing projects...'); + $count = $this->optionalImporter->importProjects($data); + $io->success('Imported '.$count.' projects.'); + } + + if ($import_users) { + $io->info('Importing users...'); + $count = $this->optionalImporter->importUsers($data); + $io->success('Imported '.$count.' users.'); + } + return 0; } diff --git a/src/Entity/ProjectSystem/ProjectBOMEntry.php b/src/Entity/ProjectSystem/ProjectBOMEntry.php index 7fefb1fe..b48b3f50 100644 --- a/src/Entity/ProjectSystem/ProjectBOMEntry.php +++ b/src/Entity/ProjectSystem/ProjectBOMEntry.php @@ -58,7 +58,7 @@ class ProjectBOMEntry extends AbstractDBElement * @var string A comma separated list of the names, where this parts should be placed * @ORM\Column(type="text", name="mountnames") */ - protected string $mountnames; + protected string $mountnames = ''; /** * @var string An optional name describing this BOM entry (useful for non-part entries) diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKOptionalImporter.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKOptionalImporter.php new file mode 100644 index 00000000..e0ac9532 --- /dev/null +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKOptionalImporter.php @@ -0,0 +1,146 @@ +. + */ + +namespace App\Services\ImportExportSystem\PartKeeprImporter; + +use App\Entity\Parts\Part; +use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; +use App\Entity\UserSystem\Group; +use App\Entity\UserSystem\User; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * This service is used to other non mandatory data from a PartKeepr export. + * You have to import the datastructures and parts first to use project import! + */ +class PKOptionalImporter +{ + use PKImportHelperTrait; + + public function __construct(EntityManagerInterface $em, PropertyAccessorInterface $propertyAccessor) + { + $this->em = $em; + $this->propertyAccessor = $propertyAccessor; + } + + /** + * Import the projects from the given data. + * @param array $data + * @return int The number of imported projects + */ + public function importProjects(array $data): int + { + if (!isset($data['project'])) { + throw new \RuntimeException('$data must contain a "project" key!'); + } + if (!isset($data['projectpart'])) { + throw new \RuntimeException('$data must contain a "projectpart" key!'); + } + + $projects_data = $data['project']; + $projectparts_data = $data['projectpart']; + + //First import the projects + foreach ($projects_data as $project_data) { + $project = new Project(); + $project->setName($project_data['name']); + $project->setDescription($project_data['description'] ?? ''); + + $this->setIDOfEntity($project, $project_data['id']); + $this->em->persist($project); + } + $this->em->flush(); + + //Then the project BOM entries + foreach ($projectparts_data as $projectpart_data) { + /** @var Project $project */ + $project = $this->em->find(Project::class, (int) $projectpart_data['project_id']); + if (!$project) { + throw new \RuntimeException('Could not find project with ID '.$projectpart_data['project_id']); + } + + $bom_entry = new ProjectBOMEntry(); + $bom_entry->setQuantity((float) $projectpart_data['quantity']); + $bom_entry->setName($projectpart_data['remarks']); + $this->setAssociationField($bom_entry, 'part', Part::class, $projectpart_data['part_id']); + + $comments = []; + if (!empty($projectpart_data['lotNumber'])) { + $comments[] = 'Lot number: '.$projectpart_data['lotNumber']; + } + if (!empty($projectpart_data['overage'])) { + $comments[] = 'Overage: '.$projectpart_data['overage'].($projectpart_data['overageType'] ? ' %' : ' pcs'); + } + $bom_entry->setComment(implode(',', $comments)); + + $project->addBomEntry($bom_entry); + } + $this->em->flush(); + + return count($projects_data); + } + + /** + * Import the users from the given data. + * @param array $data + * @return int The number of imported users + */ + public function importUsers(array $data): int + { + if (!isset($data['fosuser'])) { + throw new \RuntimeException('$data must contain a "fosuser" key!'); + } + + //All imported users get assigned to the "PartKeepr Users" group + $group_users = $this->em->find(Group::class, 3); + $group = $this->em->getRepository(Group::class)->findOneBy(['name' => 'PartKeepr Users', 'parent' => $group_users]); + if (!$group) { + $group = new Group(); + $group->setName('PartKeepr Users'); + $group->setParent($group_users); + $this->em->persist($group); + } + + + $users_data = $data['fosuser']; + foreach ($users_data as $user_data) { + if (in_array($user_data['username'], ['admin', 'anonymous'], true)) { + continue; + } + + $user = new User(); + $user->setName($user_data['username']); + $user->setEmail($user_data['email']); + $user->setGroup($group); + + //User is disabled by default + $user->setDisabled(true); + + //We let doctrine generate a new ID for the user + $this->em->persist($user); + } + + $this->em->flush(); + + return count($users_data); + } +} \ No newline at end of file