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

Refactor Utils

This commit is contained in:
David Hübner 2017-04-21 02:20:34 +02:00
parent efde576d1c
commit 2a02cd19ef
12 changed files with 257 additions and 265 deletions

View File

@ -17,7 +17,7 @@
{% for instruction in instructions %} {% for instruction in instructions %}
<tr> <tr>
<td>{{ instruction.description }}</td> <td>{{ instruction.description }}</td>
<td>{{ utils.bytesToSize( remoteSize(instruction.uRL)) }}</td> <td>{{ remoteSize(instruction.uRL) | bytesToSize }}</td>
<td><a href="{{ instruction.uRL }}">{{ remoteFilename(instruction.uRL) }}</a></td> <td><a href="{{ instruction.uRL }}">{{ remoteFilename(instruction.uRL) }}</a></td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -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 %}

View File

@ -8,7 +8,7 @@ imports:
parameters: parameters:
locale: en locale: en
# rebrickable csv files root URL (http://rebrickable.com/media/downloads/ or local dir containing csv files) # 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: framework:
#esi: ~ #esi: ~
@ -65,13 +65,13 @@ doctrine:
naming_strategy: doctrine.orm.naming_strategy.underscore naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true auto_mapping: true
# Swiftmailer Configuration ## Swiftmailer Configuration
swiftmailer: #swiftmailer:
transport: "%mailer_transport%" # transport: "%mailer_transport%"
host: "%mailer_host%" # host: "%mailer_host%"
username: "%mailer_user%" # username: "%mailer_user%"
password: "%mailer_password%" # password: "%mailer_password%"
spool: { type: memory } # spool: { type: memory }
knp_menu: knp_menu:
twig: twig:
@ -97,7 +97,7 @@ liip_imagine:
filesystem_service: oneup_flysystem.media_filesystem filesystem_service: oneup_flysystem.media_filesystem
rebrickable: rebrickable:
stream: stream:
wrapper: 'http://m.rebrickable.com/media/' wrapper: 'http://rebrickable.com/media/'
resolvers: resolvers:
default: default:

View File

@ -1 +0,0 @@
services:

View File

@ -2,15 +2,10 @@ services:
app.twig_extension: app.twig_extension:
class: AppBundle\Twig\AppExtension class: AppBundle\Twig\AppExtension
public: false public: false
arguments: ['@api.manager.rebrickable'] arguments: ['@api.manager.rebrickable', '@app.transformer.format']
tags: tags:
- { name: twig.extension } - { name: twig.extension }
app.relation.mapper:
class: AppBundle\Utils\RelationMapper
arguments:
- ['%kernel.root_dir%/Resources/relations']
service.renderer.stl: service.renderer.stl:
class: AppBundle\Service\StlRendererService class: AppBundle\Service\StlRendererService
arguments: ['%kernel.root_dir%/Resources/povray_layout/layout.tmpl', '%povray_bin%','%stl2pov_bin%'] arguments: ['%kernel.root_dir%/Resources/povray_layout/layout.tmpl', '%povray_bin%','%stl2pov_bin%']

View File

@ -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']

View File

@ -2,7 +2,7 @@ imports:
- { resource: service/repository.yml } - { resource: service/repository.yml }
- { resource: service/api.yml } - { resource: service/api.yml }
- { resource: service/loader.yml } - { resource: service/loader.yml }
- { resource: service/manager.yml } - { resource: service/util.yml }
- { resource: service/service.yml } - { resource: service/service.yml }
- { resource: service/form.yml } - { resource: service/form.yml }
- { resource: service/menu.yml } - { resource: service/menu.yml }

View File

@ -0,0 +1,18 @@
<?php
namespace AppBundle\Transformer;
class FormatTransformer
{
function bytesToSize($bytes, $precision = 2)
{
if ($bytes == 0)
return "0.00 B";
$suffix = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
$exponent = floor(log($bytes, 1024));
return round($bytes/pow(1024, $exponent), $precision).' '.$suffix[(int)$exponent];
}
}

View File

@ -3,20 +3,25 @@
namespace AppBundle\Twig; namespace AppBundle\Twig;
use AppBundle\Api\Manager\RebrickableManager; use AppBundle\Api\Manager\RebrickableManager;
use AppBundle\Transformer\FormatTransformer;
class AppExtension extends \Twig_Extension class AppExtension extends \Twig_Extension
{ {
/** @var RebrickableManager */ /** @var RebrickableManager */
private $rebrickableAPIManager; private $rebrickableAPIManager;
/** @var FormatTransformer */
private $formatTransformer;
/** /**
* AppExtension constructor. * AppExtension constructor.
* *
* @param RebrickableManager $rebrickableAPIManager * @param RebrickableManager $rebrickableAPIManager
*/ */
public function __construct($rebrickableAPIManager) public function __construct($rebrickableAPIManager, $formatTransformer)
{ {
$this->rebrickableAPIManager = $rebrickableAPIManager; $this->rebrickableAPIManager = $rebrickableAPIManager;
$this->formatTransformer = $formatTransformer;
} }
public function getFilters() public function getFilters()
@ -24,6 +29,7 @@ class AppExtension extends \Twig_Extension
return [ return [
new \Twig_SimpleFilter('partImage', [$this, 'partImage']), new \Twig_SimpleFilter('partImage', [$this, 'partImage']),
new \Twig_SimpleFilter('setImage', [$this, 'setImage']), 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) 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) public function setImage($number)
@ -45,6 +51,10 @@ class AppExtension extends \Twig_Extension
return '/sets/'.strtolower($number).'.jpg'; return '/sets/'.strtolower($number).'.jpg';
} }
public function bytesToSize($bytes, $precision = 2) {
return $this->formatTransformer->bytesToSize($bytes, $precision);
}
public function remoteSize($url) public function remoteSize($url)
{ {
$ch = curl_init($url); $ch = curl_init($url);

View File

@ -0,0 +1,206 @@
<?php
namespace AppBundle\Util;
use AppBundle\Exception\ErrorParsingLineException;
class LDModelParser
{
/**
* Parse LDraw model .dat file content and return associative array in format:
* [
* 'id' => 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 <CategoryName> <PartDescription>
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 <CategoryName>
elseif (strpos($line, '!CATEGORY ') === 0) {
$model['category'] = trim(preg_replace('/^!CATEGORY /', '', $line));
}
// 0 !KEYWORDS <first keyword>, <second keyword>, ..., <last keyword>
elseif (strpos($line, '!KEYWORDS ') === 0) {
$model['keywords'] = explode(', ', preg_replace('/^!KEYWORDS /', '', $line));
}
// 0 Name: <Filename>.dat
elseif (strpos($line, 'Name: ') === 0 && !isset($header['id'])) {
$model['id'] = preg_replace('/(^Name: )(.*)(.dat)/', '$2', $line);
}
// 0 Author: <Realname> [<Username>]
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 <colour> x y z a b c d e f g h i <file>
*
* 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;
}
}

View File

@ -1,6 +1,6 @@
<?php <?php
namespace AppBundle\Utils; namespace AppBundle\Util;
use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Finder;
use Symfony\Component\OptionsResolver\Exception\InvalidArgumentException; use Symfony\Component\OptionsResolver\Exception\InvalidArgumentException;

View File

@ -1,223 +0,0 @@
<?php
namespace AppBundle\Utils;
use AppBundle\Exception\FileNotFoundException;
use AppBundle\Exception\ParseErrorException;
use League\Flysystem\File;
class LDModelParser
{
/**
* Parse LDraw model .dat file and return associative array in format:
* [
* 'id' => 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 <CategoryName> <PartDescription>
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 <CategoryName>
elseif (strpos($line, '!CATEGORY ') === 0) {
$model['category'] = trim(preg_replace('/^!CATEGORY /', '', $line));
}
// 0 !KEYWORDS <first keyword>, <second keyword>, ..., <last keyword>
elseif (strpos($line, '!KEYWORDS ') === 0) {
$model['keywords'] = explode(', ', preg_replace('/^!KEYWORDS /', '', $line));
}
// 0 Name: <Filename>.dat
elseif (strpos($line, 'Name: ') === 0 && !isset($header['id'])) {
$model['id'] = preg_replace('/(^Name: )(.*)(.dat)/', '$2', $line);
}
// 0 Author: <Realname> [<Username>]
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 <colour> x y z a b c d e f g h i <file>
*
* 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;
}
}