diff --git a/app/Resources/views/brickset/instructions.html.twig b/app/Resources/views/brickset/instructions.html.twig index 43c983d..5e64733 100644 --- a/app/Resources/views/brickset/instructions.html.twig +++ b/app/Resources/views/brickset/instructions.html.twig @@ -17,7 +17,7 @@ {% for instruction in instructions %} {{ instruction.description }} - {{ utils.bytesToSize( remoteSize(instruction.uRL)) }} + {{ remoteSize(instruction.uRL) | bytesToSize }} {{ remoteFilename(instruction.uRL) }} {% endfor %} diff --git a/app/Resources/views/macros/utils.html.twig b/app/Resources/views/macros/utils.html.twig index 36850dd..e69de29 100644 --- a/app/Resources/views/macros/utils.html.twig +++ b/app/Resources/views/macros/utils.html.twig @@ -1,21 +0,0 @@ -{#http://stackoverflow.com/a/15303004#} -{% macro bytesToSize(bytes) %} - {% spaceless %} - {% set kilobyte = 1024 %} - {% set megabyte = kilobyte * 1024 %} - {% set gigabyte = megabyte * 1024 %} - {% set terabyte = gigabyte * 1024 %} - - {% if bytes < kilobyte %} - {{ bytes ~ ' B' }} - {% elseif bytes < megabyte %} - {{ (bytes / kilobyte)|number_format(2, '.') ~ ' KB' }} - {% elseif bytes < gigabyte %} - {{ (bytes / megabyte)|number_format(2, '.') ~ ' MB' }} - {% elseif bytes < terabyte %} - {{ (bytes / gigabyte)|number_format(2, '.') ~ ' GB' }} - {% else %} - {{ (bytes / terabyte)|number_format(2, '.') ~ ' TB' }} - {% endif %} - {% endspaceless %} -{% endmacro %} \ No newline at end of file diff --git a/app/config/config.yml b/app/config/config.yml index 9bf88c9..ab7ca80 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -8,7 +8,7 @@ imports: parameters: locale: en # rebrickable csv files root URL (http://rebrickable.com/media/downloads/ or local dir containing csv files) - rebrickable_csv_url: 'http://rebrickable.com/media/downloads/' + rebrickable_downloads_url: 'http://rebrickable.com/media/downloads/' framework: #esi: ~ @@ -65,13 +65,13 @@ doctrine: naming_strategy: doctrine.orm.naming_strategy.underscore auto_mapping: true -# Swiftmailer Configuration -swiftmailer: - transport: "%mailer_transport%" - host: "%mailer_host%" - username: "%mailer_user%" - password: "%mailer_password%" - spool: { type: memory } +## Swiftmailer Configuration +#swiftmailer: +# transport: "%mailer_transport%" +# host: "%mailer_host%" +# username: "%mailer_user%" +# password: "%mailer_password%" +# spool: { type: memory } knp_menu: twig: @@ -97,7 +97,7 @@ liip_imagine: filesystem_service: oneup_flysystem.media_filesystem rebrickable: stream: - wrapper: 'http://m.rebrickable.com/media/' + wrapper: 'http://rebrickable.com/media/' resolvers: default: diff --git a/app/config/service/manager.yml b/app/config/service/manager.yml deleted file mode 100644 index 9e4a3f5..0000000 --- a/app/config/service/manager.yml +++ /dev/null @@ -1 +0,0 @@ -services: \ No newline at end of file diff --git a/app/config/service/service.yml b/app/config/service/service.yml index 7eb05a6..f800491 100644 --- a/app/config/service/service.yml +++ b/app/config/service/service.yml @@ -2,15 +2,10 @@ services: app.twig_extension: class: AppBundle\Twig\AppExtension public: false - arguments: ['@api.manager.rebrickable'] + arguments: ['@api.manager.rebrickable', '@app.transformer.format'] tags: - { name: twig.extension } - app.relation.mapper: - class: AppBundle\Utils\RelationMapper - arguments: - - ['%kernel.root_dir%/Resources/relations'] - service.renderer.stl: class: AppBundle\Service\StlRendererService arguments: ['%kernel.root_dir%/Resources/povray_layout/layout.tmpl', '%povray_bin%','%stl2pov_bin%'] diff --git a/app/config/service/util.yml b/app/config/service/util.yml new file mode 100644 index 0000000..b044eed --- /dev/null +++ b/app/config/service/util.yml @@ -0,0 +1,8 @@ +services: + app.transformer.format: + class: AppBundle\Transformer\FormatTransformer + + app.relation.mapper: + class: AppBundle\Util\RelationMapper + arguments: + - ['%kernel.root_dir%/Resources/relations'] diff --git a/app/config/services.yml b/app/config/services.yml index ed103c2..5125d4d 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -2,7 +2,7 @@ imports: - { resource: service/repository.yml } - { resource: service/api.yml } - { resource: service/loader.yml } - - { resource: service/manager.yml } + - { resource: service/util.yml } - { resource: service/service.yml } - { resource: service/form.yml } - { resource: service/menu.yml } \ No newline at end of file diff --git a/src/AppBundle/Transformer/FormatTransformer.php b/src/AppBundle/Transformer/FormatTransformer.php new file mode 100644 index 0000000..0691092 --- /dev/null +++ b/src/AppBundle/Transformer/FormatTransformer.php @@ -0,0 +1,18 @@ +rebrickableAPIManager = $rebrickableAPIManager; + $this->formatTransformer = $formatTransformer; } public function getFilters() @@ -24,6 +29,7 @@ class AppExtension extends \Twig_Extension return [ new \Twig_SimpleFilter('partImage', [$this, 'partImage']), new \Twig_SimpleFilter('setImage', [$this, 'setImage']), + new \Twig_SimpleFilter('bytesToSize', [$this, 'bytesToSize']), ]; } @@ -37,7 +43,7 @@ class AppExtension extends \Twig_Extension public function partImage($number, $color = null) { - return '/parts/ldraw/'.($color ? $color : '-1').'/'.$number.'.png'; + return '/parts/ldraw/'.($color !== null ? $color : '-1').'/'.$number.'.png'; } public function setImage($number) @@ -45,6 +51,10 @@ class AppExtension extends \Twig_Extension return '/sets/'.strtolower($number).'.jpg'; } + public function bytesToSize($bytes, $precision = 2) { + return $this->formatTransformer->bytesToSize($bytes, $precision); + } + public function remoteSize($url) { $ch = curl_init($url); diff --git a/src/AppBundle/Util/LDModelParser.php b/src/AppBundle/Util/LDModelParser.php new file mode 100644 index 0000000..adcddeb --- /dev/null +++ b/src/AppBundle/Util/LDModelParser.php @@ -0,0 +1,206 @@ + string + * 'name' => string + * 'category' => string + * 'keywords' => [] + * 'author' => string + * 'modified' => DateTime + * 'type' => string + * 'subparts' => [], + * 'licence' => string + * ]. + * + * LDraw.org Standards: Official Library Header Specification (http://www.ldraw.org/article/398.html) + * + * @throws ErrorParsingLineException + * + * @return array + */ + public function parse($string) + { + $model = [ + 'id' => null, + 'name' => null, + 'category' => null, + 'keywords' => [], + 'author' => null, + 'modified' => null, + 'type' => null, + 'subparts' => [], + 'parent' => null, + 'license' => null, + ]; + + $firstLine = false; + foreach(explode("\n", $string) as $line) { + $line = trim($line); + + // Comments or META Commands + if (strpos($line, '0 ') === 0) { + $line = preg_replace('/^0 /', '', $line); + + // 0 + if (!$firstLine) { + $array = explode(' ', ltrim(trim($line, 2), '=_~')); + $model['category'] = isset($array[0]) ? $array[0] : ''; + $model['name'] = preg_replace('/ {2,}/', ' ', ltrim($line, '=_')); + + $firstLine = true; + } + // 0 !CATEGORY + elseif (strpos($line, '!CATEGORY ') === 0) { + $model['category'] = trim(preg_replace('/^!CATEGORY /', '', $line)); + } + // 0 !KEYWORDS , , ..., + elseif (strpos($line, '!KEYWORDS ') === 0) { + $model['keywords'] = explode(', ', preg_replace('/^!KEYWORDS /', '', $line)); + } + // 0 Name: .dat + elseif (strpos($line, 'Name: ') === 0 && !isset($header['id'])) { + $model['id'] = preg_replace('/(^Name: )(.*)(.dat)/', '$2', $line); + } + // 0 Author: [] + elseif (strpos($line, 'Author: ') === 0) { + $model['author'] = preg_replace('/^Author: /', '', $line); + } + // 0 !LDRAW_ORG Part|Subpart|Primitive|48_Primitive|Shortcut (optional qualifier(s)) ORIGINAL|UPDATE YYYY-RR + elseif (strpos($line, '!LDRAW_ORG ') === 0) { + $type = preg_replace('/(^!LDRAW_ORG )(.*)( UPDATE| ORIGINAL)(.*)/', '$2', $line); + + $model['type'] = $type; + + // Last modification date in format YYYY-RR + $date = preg_replace('/(^!LDRAW_ORG )(.*)( UPDATE | ORIGINAL )(.*)/', '$4', $line); + if (preg_match('/^[1-2][0-9]{3}-[0-9]{2}$/', $date)) { + $model['modified'] = \DateTime::createFromFormat('Y-m-d H:i:s', $date.'-01 00:00:00'); + } + } + // 0 !LICENSE Redistributable under CCAL version 2.0 : see CAreadme.txt | 0 !LICENSE Not redistributable : see NonCAreadme.txt + elseif (strpos($line, '!LICENSE ') === 0) { + $model['license'] = preg_replace('/(^!LICENSE )(.*) : (.*)$/', '$2', $line); + } + } elseif (strpos($line, '1 ') === 0) { + $id = strtolower($this->getReferencedModelNumber($line)); + + if (isset($model['subparts'][$id])) { + $model['subparts'][$id] = $model['subparts'][$id] + 1; + } else { + $model['subparts'][$id] = 1; + } + } elseif (!empty($line) && !in_array($line[0], ['2', '3', '4', '5'])) { + throw new ErrorParsingLineException($model['id'],$line); + } + } + + if ($this->isSticker($model['name'], $model['id'])) { + $model['type'] = 'Sticker'; + } elseif (count($model['subparts']) == 1 && in_array($model['type'], ['Part Alias', 'Shortcut Physical_Colour', 'Shortcut Alias', 'Part Physical_Colour'])) { + $model['parent'] = array_keys($model['subparts'])[0]; + } elseif (($parent = $this->getPrintedModelParentNumber($model['id'])) && !in_array($model['type'], ['48_Primitive', '8_Primitive', 'Primitive', 'Subpart'])) { + $model['type'] = 'Printed'; + $model['parent'] = $parent; + } elseif ($parent = $this->getObsoleteModelParentNumber($model['name'])) { + $model['parent'] = $parent; + } + + return $model; + } + + /** + * Get file reference from part line. + * + * Line type 1 is a sub-file reference. The generic format is: + * 1 x y z a b c d e f g h i + * + * LDraw.org Standards: File Format 1.0.2 (http://www.ldraw.org/article/218.html) + * + * @param $line + * + * @return string|null Filename of referenced part + */ + public function getReferencedModelNumber($line) + { + $line = ($line); + + if (preg_match('/^1(.*) (.*)\.dat$/', strtolower($line), $matches)) { + return str_replace('\\', DIRECTORY_SEPARATOR, $matches[2]); + } + + return null; + } + + /** + * Get printed part parent id. + * + * part name in format: + * nnnPxx, nnnnPxx, nnnnnPxx, nnnaPxx, nnnnaPxx (a = alpha, n= numeric, x = alphanumeric) + * + * http://www.ldraw.org/library/tracker/ref/numberfaq/ + * + * @param $id + * + * @return string|null LDraw number of printed part parent + */ + public function getPrintedModelParentNumber($id) + { + if (preg_match('/(^.*)(p[0-9a-z]{2,3})$/', $id, $matches)) { + return $matches[1]; + } + + return null; + } + + /** + * Check if part is shortcut part of stricker and part. + * + * part name in format: + * nnnDnn, nnnnDnn, nnnnnDnn (a = alpha, n= numeric, x = alphanumeric) + * + * http://www.ldraw.org/library/tracker/ref/numberfaq/ + * + * @param $name + * @param $number + * + * @return bool + */ + public function isSticker($name, $number) + { + if (strpos($name, 'Sticker') === 0) { + return true; + } + + // Check if in format n*Daa == sticker + return preg_match('/(^.*)(d[0-9]{2})$/', $number); + } + + /** + * Get parent of obsolete part kept for reference. + * + * part description in format: + * ~Moved to {new_number} + * + * http://www.ldraw.org/article/398.html (Appendix II (02-Oct-06)) + * + * @param $name + * + * @return string|null Filename of referenced part + */ + public function getObsoleteModelParentNumber($name) + { + if (preg_match('/^(~Moved to )(.*)$/', $name, $matches)) { + return str_replace('\\', DIRECTORY_SEPARATOR, $matches[2]); + } + + return null; + } +} diff --git a/src/AppBundle/Utils/RelationMapper.php b/src/AppBundle/Util/RelationMapper.php similarity index 98% rename from src/AppBundle/Utils/RelationMapper.php rename to src/AppBundle/Util/RelationMapper.php index 257a8d4..940493a 100644 --- a/src/AppBundle/Utils/RelationMapper.php +++ b/src/AppBundle/Util/RelationMapper.php @@ -1,6 +1,6 @@ string - * 'name' => string - * 'category' => string - * 'keywords' => [] - * 'author' => string - * 'modified' => DateTime - * 'type' => string - * 'subparts' => [], - * 'licence' => string - * ]. - * - * LDraw.org Standards: Official Library Header Specification (http://www.ldraw.org/article/398.html) - * - * @throws FileNotFoundException|ParseErrorException - * - * @return array - */ - public function parse($file) - { - if (file_exists($file)) { - $model = [ - 'id' => null, - 'name' => null, - 'category' => null, - 'keywords' => [], - 'author' => null, - 'modified' => null, - 'type' => null, - 'subparts' => [], - 'parent' => null, - 'license' => null, - ]; - - try { - $handle = fopen($file, 'r'); - - if ($handle) { - $firstLine = false; - - while (($line = fgets($handle)) !== false) { - $line = trim($line); - - // Comments or META Commands - if (strpos($line, '0 ') === 0) { - $line = preg_replace('/^0 /', '', $line); - - // 0 - if (!$firstLine) { - $array = explode(' ', ltrim(trim($line, 2), '=_~')); - $model['category'] = isset($array[0]) ? $array[0] : ''; - $model['name'] = preg_replace('/ {2,}/', ' ', ltrim($line, '=_')); - - $firstLine = true; - } - // 0 !CATEGORY - elseif (strpos($line, '!CATEGORY ') === 0) { - $model['category'] = trim(preg_replace('/^!CATEGORY /', '', $line)); - } - // 0 !KEYWORDS , , ..., - elseif (strpos($line, '!KEYWORDS ') === 0) { - $model['keywords'] = explode(', ', preg_replace('/^!KEYWORDS /', '', $line)); - } - // 0 Name: .dat - elseif (strpos($line, 'Name: ') === 0 && !isset($header['id'])) { - $model['id'] = preg_replace('/(^Name: )(.*)(.dat)/', '$2', $line); - } - // 0 Author: [] - elseif (strpos($line, 'Author: ') === 0) { - $model['author'] = preg_replace('/^Author: /', '', $line); - } - // 0 !LDRAW_ORG Part|Subpart|Primitive|48_Primitive|Shortcut (optional qualifier(s)) ORIGINAL|UPDATE YYYY-RR - elseif (strpos($line, '!LDRAW_ORG ') === 0) { - $type = preg_replace('/(^!LDRAW_ORG )(.*)( UPDATE| ORIGINAL)(.*)/', '$2', $line); - - $model['type'] = $type; - - // Last modification date in format YYYY-RR - $date = preg_replace('/(^!LDRAW_ORG )(.*)( UPDATE | ORIGINAL )(.*)/', '$4', $line); - if (preg_match('/^[1-2][0-9]{3}-[0-9]{2}$/', $date)) { - $model['modified'] = \DateTime::createFromFormat('Y-m-d H:i:s', $date.'-01 00:00:00'); - } - } - // 0 !LICENSE Redistributable under CCAL version 2.0 : see CAreadme.txt | 0 !LICENSE Not redistributable : see NonCAreadme.txt - elseif (strpos($line, '!LICENSE ') === 0) { - $model['license'] = preg_replace('/(^!LICENSE )(.*) : (.*)$/', '$2', $line); - } - } elseif (strpos($line, '1 ') === 0) { - $id = strtolower($this->getReferencedModelNumber($line)); - - if (isset($model['subparts'][$id])) { - $model['subparts'][$id] = $model['subparts'][$id] + 1; - } else { - $model['subparts'][$id] = 1; - } - } elseif (!empty($line) && !in_array($line[0], ['2', '3', '4', '5'])) { - throw new ErrorParsingLineException($file,$line); - } - } - - if ($this->isSticker($model['name'], $model['id'])) { - $model['type'] = 'Sticker'; - } elseif (count($model['subparts']) == 1 && in_array($model['type'], ['Part Alias', 'Shortcut Physical_Colour', 'Shortcut Alias', 'Part Physical_Colour'])) { - $model['parent'] = array_keys($model['subparts'])[0]; - } elseif (($parent = $this->getPrintedModelParentNumber($model['id'])) && !in_array($model['type'], ['48_Primitive', '8_Primitive', 'Primitive', 'Subpart'])) { - $model['type'] = 'Printed'; - $model['parent'] = $parent; - } elseif ($parent = $this->getObsoleteModelParentNumber($model['name'])) { - $model['type'] = 'Alias'; - $model['parent'] = $parent; - } - - fclose($handle); - - return $model; - } - } catch (\Exception $exception) { - throw new ParseErrorException($file); - } - } - throw new FileNotFoundException($file); - } - - /** - * Get file reference from part line. - * - * Line type 1 is a sub-file reference. The generic format is: - * 1 x y z a b c d e f g h i - * - * LDraw.org Standards: File Format 1.0.2 (http://www.ldraw.org/article/218.html) - * - * @param $line - * - * @return string|null Filename of referenced part - */ - public function getReferencedModelNumber($line) - { - $line = ($line); - - if (preg_match('/^1(.*) (.*)\.dat$/', strtolower($line), $matches)) { - return str_replace('\\', DIRECTORY_SEPARATOR, $matches[2]); - } - - return null; - } - - /** - * Get printed part parent id. - * - * part name in format: - * nnnPxx, nnnnPxx, nnnnnPxx, nnnaPxx, nnnnaPxx (a = alpha, n= numeric, x = alphanumeric) - * - * http://www.ldraw.org/library/tracker/ref/numberfaq/ - * - * @param $id - * - * @return string|null LDraw number of printed part parent - */ - public function getPrintedModelParentNumber($id) - { - if (preg_match('/(^.*)(p[0-9a-z]{2,3})$/', $id, $matches)) { - return $matches[1]; - } - - return null; - } - - /** - * Check if part is shortcut part of stricker and part. - * - * part name in format: - * nnnDnn, nnnnDnn, nnnnnDnn (a = alpha, n= numeric, x = alphanumeric) - * - * http://www.ldraw.org/library/tracker/ref/numberfaq/ - * - * @param $name - * @param $number - * - * @return bool - */ - public function isSticker($name, $number) - { - if (strpos($name, 'Sticker') === 0) { - return true; - } - - // Check if in format n*Daa == sticker - return preg_match('/(^.*)(d[0-9]{2})$/', $number); - } - - /** - * Get parent of obsolete part kept for reference. - * - * part description in format: - * ~Moved to {new_number} - * - * http://www.ldraw.org/article/398.html (Appendix II (02-Oct-06)) - * - * @param $name - * - * @return string|null Filename of referenced part - */ - public function getObsoleteModelParentNumber($name) - { - if (preg_match('/^(~Moved to )(.*)$/', $name, $matches)) { - return str_replace('\\', DIRECTORY_SEPARATOR, $matches[2]); - } - - return null; - } -}