1
0
mirror of https://github.com/ToxicCrack/PrintABrick.git synced 2025-05-18 05:10:07 -07:00

Add RelationLoader service

Service for loading relation between rebrickable parts and ldraw models
This commit is contained in:
David Hübner 2017-04-05 19:28:19 +02:00
parent df1053dd92
commit 649121f1dc
11 changed files with 617 additions and 3 deletions

View File

@ -31,4 +31,5 @@ For full requirements see Symfony 3.2 [docs](http://symfony.com/doc/3.2/referenc
1. Set application parameters in *app/config/parameters.yml* 1. Set application parameters in *app/config/parameters.yml*
2. Generate empty database by running command `$ php bin/console doctrine:database:create` 2. Generate empty database by running command `$ php bin/console doctrine:database:create`
3. Load LDraw models into database by running commad `$ php bin/console app:load:ldraw [ldraw_dir_path]` 3. Load LDraw models into database by running commad `$ php bin/console app:load:ldraw [ldraw_dir_path]`
4. Load Rebrickable data into database by running command `$ php bin/console app:load:rebrickable` 4. Load Rebrickable data into database by running command `$ php bin/console app:load:rebrickable`
5. Load relations between LDraw models and Rebrickable parts by running command `$ php bin/console app:load:relation`

View File

@ -0,0 +1,167 @@
# Define the relationship between LDraw models that are not automatically recognizabe to eliminate duplication of models
#
# format:
# alias_ID: model_ID
# Minifig Hips and Legs
3815c3j: 3815c01
3815c4f: 3815c01
3815c63: 3815c01
3815c6g: 3815c01
3815c6u: 3815c01
3815c6v: 3815c01
3815c6w: 3815c01
3815c89: 3815c01
3815c8h: 3815c01
3815c8i: 3815c01
3815ca2: 3815c01
3815ca3: 3815c01
3815ca9: 3815c01
3815caw: 3815c01
3815cba: 3815c01
3815cbb: 3815c01
3815cbc: 3815c01
3815cbd: 3815c01
3815cbe: 3815c01
3815cbf: 3815c01
3815cbg: 3815c01
3815cc44: 3815c01
3815cc67: 3815c01
3815cde: 3815c01
3815chb: 3815c01
3815cm0: 3815c01
3815cm1: 3815c01
3815cm2: 3815c01
3815cq0: 3815c01
3815cq1: 3815c01
3815cq2: 3815c01
3815cs5: 3815c01
3815csk: 3815c01
3815cw1: 3815c01
3815cw2: 3815c01
3815cw3: 3815c01
4214921: 3815c01
4221858: 3815c01
4225787: 3815c01
4168950: 3815c01
73418: 3815c01
122c02: 122c01
# Bucket 1 x 1 x 1 Cylindrical
12884c02: 12884c01
11244p02c01: 11244p01c01
16529p02c01: 16529p01c01
# Figure Friends Hips and Legs
11202p02c01: 11202p01c01
11202p03c01: 11202p01c01
# Figure Friends Hips and Legs with Long Skirt
92249p02c01: 92249p01c01
# Figure Friends Hips and Legs with Layered Skirt
92250p02c01: 92250p01c01
92250p03c01: 92250p01c01
92250p04c01: 92250p01c01
92250p05c01: 92250p01c01
92250p06c01: 92250p01c01
92250p07c01: 92250p01c01
92250p08c01: 92250p01c01
# Figure Friends Hips and Legs with Cropped Trousers
92251p02c01: 92251p01c01
92251p03c01: 92251p01c01
92251p04c01: 92251p01c01
92251p05c01: 92251p01c01
# Figure Friends Hips and Legs with Pleated Skirt
92252p02c01: 92252p01c01
92252p03c01: 92252p01c01
92252p04c01: 92252p01c01
92252p05c01: 92252p01c01
92252p06c01: 92252p01c01
92252p07c01: 92252p01c01
# Figure Friends Hips and Legs with Trousers
92253p02c01: 92253p01c01
92253p03c01: 92253p01c01
92253p04c01: 92253p01c01
# Figure Friends Hips and Legs
15680p01c01: 15680c01
# Figure Friends Girl Torso with Arms
92241p02c01: 92241p01c01
92241p03c01: 92241p01c01
92241p04c01: 92241p01c01
92241p05c01: 92241p01c01
92241p06c01: 92241p01c01
92241p07c01: 92241p01c01
92241p08c01: 92241p01c01
92241p09c01: 92241p01c01
92241p11c01: 92241p01c01
92241p12c01: 92241p01c01
92241p13c01: 92241p01c01
92241p14c01: 92241p01c01
92241p15c01: 92241p01c01
92241p16c01: 92241p01c01
92241p17c01: 92241p01c01
92241p19c01: 92241p01c01
# Figure Fabuland Pig 3 w Body
u9147p01c03: u9147p01c02
u9147p01c04: u9147p01c02
u9147p01c05: u9147p01c02
u9147p01c06: u9147p01c02
u9147p02c02: u9147p01c02
u9147p03c02: u9147p01c02
# Figure Fabuland Elephant Head
u588p02c02: u588p01c02
30461c02: 30461c01
10350p01c01: 10350c01
10350p02c01: 10350c01
# Minifig Headdress
30168a: 30168
30168b: 30168
# Boat Section Stern 6 x 6 x 3.333
164c02: 164c01
90391p02: 90391p01
4493c01: 4493c00
4493c02: 4493c00
4493c03: 4493c00
4493c04: 4493c00
50231c01: 50231
50231c02: 50231
50231p01c01: 50231
56630c01: 56630
86038c01: 86038
99464c01: 99464
2453a: 2453
# Brick 1 x 1 with Orange Slices Pattern
3005bpf1: 3005
11146: 55805
4116604: 3009
56204: 45411
# Hinge Control Stick and Base
73587: 4592c01
4296152: 4592c01

View File

@ -0,0 +1,155 @@
# Define the relationship between LDraw models that are not automatically recognizabe to eliminate duplication of models
#
# To load changes `run app:load:relation` command
#
# format:
# rebrickable_part_ID: ldraw_model_ID
970d00: 970c00
970d03: 970c00
970d06: 970c00
970d08: 970c00
970d10: 970c00
970d14: 970c00
970d18: 970c00
970d19: 970c00
970d29: 970c00
970d30: 970c00
970x021: 970c00
970d01: 3815c36
970d05: 3815c36
970d09: 3815c36
970d12: 3815c36
57503: 70501a
90391: 90391p01
2453a: 2453
2454a: 2454
2454b: 2454
4493cx6: 4493c00
298c02: 4592c01
298c03: 4592c01
298c04: 4592c01
298c05: 4592c01
73983: 2429c01
# Tubes
75c03: 76263
75c04: 76250
75c05: 76307
75c06: 76279
75c07: 76289
75c08: 76260
75c09: 76324
75c10: 76348
75c11: 71505
75c12: 71175
75c13: 71551
75c14: 71177
75c15: 71194
75c16: 71192
75c17: 76270
75c18: 71582
75c19: 22463
75c20: 76276
75c21: 70978
75c22: 76252
75c23: 76254
75c24: 76277
75c26: 53475
75c28: 76280
75c29: 76389
75c30: 76282
75c31: 76283
75c32: 57274
75c33: 57274
75c34: 22461
75c40: 46305
75c45: 76281
75c53: 22296
78c02: 72504
78c03: 72706
78c04: 71952
78c06: 71944
78c08: 71951
78c11: 71986
78c19: 43675
30361c: 30361d
30361b: 30361d
3048c: 3048
98560: 3684c
10119: 51704
3149: 3149d
75998: 4493c00
10119: 51704
30658: 3404
59275: 2599
3008a03: 925
# Part molds fetched from rebrickable
73590c01a: 73590a
3847a: 3847
44301a: 44301
44567a: 44567
44302a: 44302
3001a: 3001
3002a: 3002
3947a: 3947
30187a: 30187
3046a: 3046
18868a: 18868
45707a: 45707
2635a: 2635
2714a: 2714
73590c02a: 73590b
wheel2a: 568c01
6014a: 6014
18979a: 18978b
132a: u9131
16816pr0001a: u9209p01
57909a: 57909
9244a: 575c01
32005a: 2739a
2850a: 2850
6048a: 6048
30151a: 30151
3035a: u8202
30350a: 30350
4856a: 4856
6153a: 6153
60583a: 60583
30390a: 30390
30237a: 30237
4739a: 4739
4476b: 4476
30237b: 95820
44301b: 44301
44302b: 44302
32005b: 32005
61927b: 61927
3852b: 3852
6228b: 6228
18979b: 18979a
3634b: 574
6014b: 6014
3940b: 3940
58123b: 58123p01
6216b: 6216m
32064c: 32064a
3062c: u9026
40344c01: 43123
11895pr0001c01: 11895
92456pr0021c01: 92241p03c01

View File

@ -22,3 +22,8 @@ services:
class: AppBundle\Service\Loader\LDrawLoaderService class: AppBundle\Service\Loader\LDrawLoaderService
arguments: ['@service.ldview', '%ldraw_url%', '@manager.ldraw', '@util.dat.parser'] arguments: ['@service.ldview', '%ldraw_url%', '@manager.ldraw', '@util.dat.parser']
parent: service.loader parent: service.loader
service.loader.relation:
class: AppBundle\Service\Loader\RelationLoader
arguments: ['@manager.ldraw.model', '@repository.rebrickable.part', '@api.manager.rebrickable']
parent: service.loader

View File

@ -13,4 +13,8 @@ services:
class: AppBundle\Form\FilterSetType class: AppBundle\Form\FilterSetType
arguments: ['@manager.brickset'] arguments: ['@manager.brickset']
tags: tags:
- { name: form.type } - { name: form.type } app.relation.mapper:
app.relation.mapper:
class: AppBundle\Utils\RelationMapper
arguments:
- ['%kernel.root_dir%/Resources/relations']

View File

@ -0,0 +1,27 @@
<?php
namespace AppBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class LoadRelationCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('app:load:relations')
->setDescription('Loads relations between LDraw models and Rebrickable parts')
->setHelp('This command allows you to..');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$relationLoader = $this->getContainer()->get('service.loader.relation');
$relationLoader->setOutput($output);
//TODO log errors
$relationLoader->loadNotPaired();
}
}

View File

@ -2,6 +2,7 @@
namespace AppBundle\Entity\Rebrickable; namespace AppBundle\Entity\Rebrickable;
use AppBundle\Entity\LDraw\Model;
use AppBundle\Entity\Traits\NameTrait; use AppBundle\Entity\Traits\NameTrait;
use AppBundle\Entity\Traits\NumberTrait; use AppBundle\Entity\Traits\NumberTrait;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
@ -33,6 +34,13 @@ class Part
*/ */
protected $inventoryParts; protected $inventoryParts;
/**
* @var Model
*
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Model", inversedBy="parts")
*/
private $model;
/** /**
* Part constructor. * Part constructor.
*/ */
@ -92,4 +100,24 @@ class Part
return $this; return $this;
} }
/**
* @return Model
*/
public function getModel()
{
return $this->model;
}
/**
* @param Model $model
*
* @return Part
*/
public function setModel($model)
{
$this->model = $model;
return $this;
}
} }

View File

@ -2,6 +2,9 @@
namespace AppBundle\Repository\Rebrickable; namespace AppBundle\Repository\Rebrickable;
use AppBundle\Entity\LDraw\Model;
use AppBundle\Entity\LDraw\Part;
use AppBundle\Entity\Rebrickable\Category;
use AppBundle\Entity\Rebrickable\Inventory; use AppBundle\Entity\Rebrickable\Inventory;
use AppBundle\Entity\Rebrickable\Inventory_Part; use AppBundle\Entity\Rebrickable\Inventory_Part;
use AppBundle\Entity\Rebrickable\Set; use AppBundle\Entity\Rebrickable\Set;
@ -10,6 +13,29 @@ use Doctrine\ORM\Query\Expr\Join;
class PartRepository extends BaseRepository class PartRepository extends BaseRepository
{ {
public function findAllByModel(Model $model)
{
$queryBuilder = $this->createQueryBuilder('part');
$queryBuilder
->where('part.model = :model')
->setParameter('model', $model);
return $queryBuilder->getQuery()->getResult();
}
public function findAllNotPaired()
{
$queryBuilder = $this->createQueryBuilder('part')
->leftJoin(Category::class, 'category', JOIN::WITH, 'part.category = category.id')
->where('category.name NOT LIKE :categoryName')
->andWhere('part.model IS NULL')
->setParameter('categoryName', 'Non-LEGO')
->distinct(true);
return $queryBuilder->getQuery()->getResult();
}
public function findAllBySetNumber($number) public function findAllBySetNumber($number)
{ {
$queryBuilder = $this->createQueryBuilder('part'); $queryBuilder = $this->createQueryBuilder('part');

View File

@ -2,6 +2,7 @@
namespace AppBundle\Repository\Rebrickable; namespace AppBundle\Repository\Rebrickable;
use AppBundle\Entity\LDraw\Model;
use AppBundle\Entity\Rebrickable\Inventory; use AppBundle\Entity\Rebrickable\Inventory;
use AppBundle\Entity\Rebrickable\Inventory_Part; use AppBundle\Entity\Rebrickable\Inventory_Part;
use AppBundle\Entity\Rebrickable\Part; use AppBundle\Entity\Rebrickable\Part;
@ -19,7 +20,22 @@ class SetRepository extends BaseRepository
->join(Inventory_Part::class, 'inventory_part', JOIN::WITH, 'inventory.id = inventory_part.inventory') ->join(Inventory_Part::class, 'inventory_part', JOIN::WITH, 'inventory.id = inventory_part.inventory')
->join(Part::class, 'part', Join::WITH, 'inventory_part.part = part.number') ->join(Part::class, 'part', Join::WITH, 'inventory_part.part = part.number')
->where('part.number LIKE :number') ->where('part.number LIKE :number')
->setParameter('number', $number.'%') ->setParameter('number', $number)
->distinct(true);
return $queryBuilder->getQuery()->getResult();
}
public function findAllByModel(Model $model)
{
$queryBuilder = $this->createQueryBuilder('s');
$queryBuilder
->join(Inventory::class, 'inventory', JOIN::WITH, 'inventory.set = s.number')
->join(Inventory_Part::class, 'inventory_part', JOIN::WITH, 'inventory.id = inventory_part.inventory')
->join(Part::class, 'part', Join::WITH, 'inventory_part.part = part.number')
->where('part.model = :model')
->setParameter('model', $model->getNumber())
->distinct(true); ->distinct(true);
return $queryBuilder->getQuery()->getResult(); return $queryBuilder->getQuery()->getResult();

View File

@ -0,0 +1,119 @@
<?php
namespace AppBundle\Service\Loader;
use AppBundle\Api\Manager\RebrickableManager;
use AppBundle\Entity\LDraw\Model;
use AppBundle\Entity\Rebrickable\Part;
use AppBundle\Manager\LDraw\ModelManager;
use AppBundle\Repository\Rebrickable\PartRepository;
class RelationLoader extends BaseLoaderService
{
/** @var ModelManager */
private $modelManager;
/** @var PartRepository */
private $partRepository;
/** @var RebrickableManager */
private $rebrickableAPIManager;
/**
* RelationLoader constructor.
*
* @param $ldrawPartManager
* @param $rebrickablePartRepository
*/
public function __construct($modelManager, $partRepository, $rebrickableApiManager)
{
$this->modelManager = $modelManager;
$this->partRepository = $partRepository;
$this->rebrickableAPIManager = $rebrickableApiManager;
}
/**
*
*/
public function loadAll()
{
$parts = $this->partRepository->findAll();
$this->initProgressBar(count($parts));
/** @var Part $part */
foreach ($parts as $part) {
$this->load($part);
$this->progressBar->advance();
}
$this->progressBar->finish();
}
/**
*
*/
public function loadNotPaired()
{
$parts = $this->partRepository->findAllNotPaired();
$this->initProgressBar(count($parts));
/** @var Part $part */
foreach ($parts as $part) {
$this->load($part);
$this->progressBar->advance();
}
$this->progressBar->finish();
}
/**
* Loads relations between Rebrickable part and ldraw models for $parts
*
* @param Part $part
*
* @return Model $m
*/
private function load($part)
{
$number = $part->getNumber();
$model = $this->modelManager->findByNumber($number);
if (!$model) {
$number = $this->relationMapper->find($this->getPrintedParentId($number), 'part_model');
$model = $this->modelManager->findByNumber($number);
if (!$model) {
$model = $this->modelManager->findByName($part->getName());
}
}
if ($model) {
$part->setModel($model);
$this->partRepository->save($part);
}
}
/**
* Get printed part parent number.
*
* @param $id
*
* @return string|null LDraw number of printed part parent
*/
private function getPrintedParentId($number)
{
if (preg_match('/(^970[c,x])([0-9a-z]*)$/', $number, $matches)) {
return '970c00';
} elseif (preg_match('/(^973)([c,p][0-9a-z]*)$/', $number, $matches)) {
return '973c00';
} elseif (preg_match('/(^.*)((pr[x]{0,1}[0-9]{1,7}[a-z]{0,1})|(pat[[0-9]{1,4}[a-z]{0,1}))$/', $number, $matches)) {
return $matches[1];
} elseif (preg_match('/(^.*)((pb[0-9]{1,4}[a-z]{0,1}))$/', $number, $matches)) {
return $matches[1];
} elseif (preg_match('/(^.*)(p[x]{0,1}[0-9a-z]{2,4})$/', $number, $matches)) {
return $matches[1];
}
return $number;
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace AppBundle\Utils;
use Symfony\Component\Finder\Finder;
use Symfony\Component\OptionsResolver\Exception\InvalidArgumentException;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\Translation\Exception\InvalidResourceException;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;
class RelationMapper
{
/**
* @var array
*/
private $relations;
/**
* RelationMapper constructor.
*
* @param $resourcesDir
*/
public function __construct($resourcesDir)
{
$finder = new Finder();
$files = $finder->files()->name('*.yml')->in($resourcesDir);
foreach ($files as $file) {
$domain = substr($file->getFilename(), 0, -1 * strlen('yml') - 1);
$this->loadResource($file, $domain);
}
}
/**
* Finds related part/model number to given $number in $domain resource or returns original $number if not found.
*
* @param string $number The part/model number
* @param string $domain The domain of relation type
*
* @throws InvalidArgumentException If the domain not found
*
* @return string The mapped string
*/
public function find($number, $domain)
{
if (isset($this->relations[$domain])) {
return isset($this->relations[$domain][$number]) ? $this->relations[$domain][$number] : $number;
}
throw new InvalidOptionsException();
}
/**
* Adds a Resource.
*
* @param $file
* @param $domain
*/
private function loadResource($file, $domain)
{
try {
$this->relations[$domain] = Yaml::parse(file_get_contents($file->getPathname()));
} catch (ParseException $e) {
throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $file->getPathname()), 0, $e);
}
}
}