1
0
mirror of https://github.com/ToxicCrack/PrintABrick.git synced 2025-05-17 21:00:09 -07:00

Change ZipStream to ZipArchive

This commit is contained in:
David Hübner 2017-05-05 21:04:25 +02:00
parent 6bf6752818
commit a20e31da9b
9 changed files with 241 additions and 51 deletions

View File

@ -0,0 +1,19 @@
<div class="ui modal">
<i class="close icon"></i>
{% if block('title') is not empty %}
<div class="header">
{% block title %}{% endblock %}
</div>
{% endif %}
<div class="content">
{% block content %}{% endblock %}
</div>
{% if block('actions') is not empty %}
<div class="actions">
{% block actions %}{% endblock %}
</div>
{% endif %}
</div>

View File

@ -17,11 +17,15 @@ services:
service.zip: service.zip:
class: AppBundle\Service\ZipService class: AppBundle\Service\ZipService
arguments: ['@oneup_flysystem.media_filesystem', '@service.set'] arguments: ['@oneup_flysystem.media_filesystem', '@service.set', '@service.model']
service.set: service.set:
class: AppBundle\Service\SetService class: AppBundle\Service\SetService
arguments: ['@repository.rebrickable.inventorypart'] arguments: ['@repository.rebrickable.inventorypart'] app.part_image_loader: arguments: ['@repository.rebrickable.inventorypart']
service.model:
class: AppBundle\Service\ModelService
app.part_image_loader: app.part_image_loader:
class: AppBundle\Imagine\PartImageLoader class: AppBundle\Imagine\PartImageLoader
arguments: ['@api.manager.rebrickable', '@oneup_flysystem.media_filesystem'] arguments: ['@api.manager.rebrickable', '@oneup_flysystem.media_filesystem']

View File

@ -9,7 +9,9 @@ use AppBundle\Form\Filter\Model\ModelFilterType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
/** /**
* Part controller. * Part controller.
@ -59,11 +61,11 @@ class ModelController extends Controller
*/ */
public function detailAction($number) public function detailAction($number)
{ {
$em = $this->getDoctrine()->getManager();
/** @var Model $model */ /** @var Model $model */
if ($model = $this->get('repository.ldraw.model')->findOneByNumber($number)) { if ($model = $this->get('repository.ldraw.model')->findOneByNumber($number)) {
try { try {
$subparts = $this->get('service.model')->getAllSubparts($model);
$rbParts = $model != null ? $this->get('repository.rebrickable.part')->findAllByModel($model) : null; $rbParts = $model != null ? $this->get('repository.rebrickable.part')->findAllByModel($model) : null;
$sets = $model != null ? $this->get('repository.rebrickable.set')->findAllByModel($model) : null; $sets = $model != null ? $this->get('repository.rebrickable.set')->findAllByModel($model) : null;
@ -74,6 +76,7 @@ class ModelController extends Controller
'rbParts' => $rbParts, 'rbParts' => $rbParts,
'sets' => $sets, 'sets' => $sets,
'related' => $related, 'related' => $related,
'subparts' => $subparts,
]); ]);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->addFlash('error', $e->getMessage()); $this->addFlash('error', $e->getMessage());
@ -87,8 +90,21 @@ class ModelController extends Controller
* @Route("/{number}/zip", name="model_zip") * @Route("/{number}/zip", name="model_zip")
* @Method("GET") * @Method("GET")
*/ */
public function zipAction(Model $model) public function zipAction(Request $request, Model $model)
{ {
$zip = $this->get('service.zip')->createFromModel($model); $zip = $this->get('service.zip')->createFromModel($model, true);
$response = new BinaryFileResponse($zip);
$response->headers->set('Content-Type', 'application/zip');
// Create the disposition of the file
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
"model_{$model->getNumber()}_{$model->getName()}.zip"
);
$response->headers->set('Content-Disposition', $disposition);
return $response;
} }
} }

View File

@ -3,14 +3,15 @@
namespace AppBundle\Controller; namespace AppBundle\Controller;
use AppBundle\Api\Exception\ApiException; use AppBundle\Api\Exception\ApiException;
use AppBundle\Api\Exception\EmptyResponseException;
use AppBundle\Entity\Rebrickable\Inventory_Set; use AppBundle\Entity\Rebrickable\Inventory_Set;
use AppBundle\Entity\Rebrickable\Set; use AppBundle\Entity\Rebrickable\Set;
use AppBundle\Form\Filter\Set\SetFilterType; use AppBundle\Form\Filter\Set\SetFilterType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
/** /**
* @Route("/sets") * @Route("/sets")
@ -55,10 +56,12 @@ class SetController extends Controller
public function detailAction(Request $request, Set $set) public function detailAction(Request $request, Set $set)
{ {
$bricksetSet = null; $bricksetSet = null;
$colors = null; $partCount = $this->get('repository.rebrickable.inventoryPart')->getPartCount($set->getNumber(), false);
try { try {
$bricksetSet = $this->get('api.manager.brickset')->getSetByNumber($set->getNumber()); if (!($bricksetSet = $this->get('api.manager.brickset')->getSetByNumber($set->getNumber()))) {
$this->addFlash('warning', "{$set->getNumber()} not found in Brickset database");
}
} catch (ApiException $e) { } catch (ApiException $e) {
$this->addFlash('error', $e->getService()); $this->addFlash('error', $e->getService());
} catch (\Exception $e) { } catch (\Exception $e) {
@ -68,6 +71,7 @@ class SetController extends Controller
return $this->render('set/detail.html.twig', [ return $this->render('set/detail.html.twig', [
'set' => $set, 'set' => $set,
'brset' => $bricksetSet, 'brset' => $bricksetSet,
'partCount' => $partCount,
]); ]);
} }
@ -113,7 +117,8 @@ class SetController extends Controller
try { try {
$models = $this->get('service.set')->getModels($set, false); $models = $this->get('service.set')->getModels($set, false);
$spareModels = $this->get('service.set')->getModels($set, true); $spareModels = $this->get('service.set')->getModels($set, true);
$missing = $this->get('repository.rebrickable.inventorypart')->findAllBySetNumber($set->getNumber(), false, false); $missing = $this->get('service.set')->getParts($set, false, false);
// $missing = $this->get('repository.rebrickable.inventorypart')->findAllBySetNumber($set->getNumber(), false, false);
$missingSpare = $this->get('repository.rebrickable.inventorypart')->findAllBySetNumber($set->getNumber(), true, false); $missingSpare = $this->get('repository.rebrickable.inventorypart')->findAllBySetNumber($set->getNumber(), true, false);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->addFlash('error', $e->getMessage()); $this->addFlash('error', $e->getMessage());
@ -197,6 +202,22 @@ class SetController extends Controller
public function zipAction(Request $request, Set $set) public function zipAction(Request $request, Set $set)
{ {
$sorted = $request->query->get('sorted') == 1 ? true : false; $sorted = $request->query->get('sorted') == 1 ? true : false;
$this->get('service.zip')->createFromSet($set, $sorted);
$sort = $sorted ? 'sorted' : 'unsorted';
$zip = $this->get('service.zip')->createFromSet($set, $sorted);
$response = new BinaryFileResponse($zip);
$response->headers->set('Content-Type', 'application/zip');
// Create the disposition of the file
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
"set_{$set->getNumber()}_{$set->getName()}({$sort}).zip"
);
$response->headers->set('Content-Disposition', $disposition);
return $response;
} }
} }

View File

@ -26,7 +26,9 @@ class Inventory_PartRepository extends BaseRepository
$queryBuilder = $this->createQueryBuilder('inventory_part') $queryBuilder = $this->createQueryBuilder('inventory_part')
->where('inventory_part.inventory = :inventory') ->where('inventory_part.inventory = :inventory')
->setParameter('inventory', $inventory); ->setParameter('inventory', $inventory)
->join(Part::class, 'part', JOIN::WITH, 'inventory_part.part = part')
->andWhere('part.category != 17');
if ($spare !== null) { if ($spare !== null) {
$queryBuilder $queryBuilder
@ -35,8 +37,6 @@ class Inventory_PartRepository extends BaseRepository
} }
if ($model !== null) { if ($model !== null) {
$queryBuilder
->join(Part::class, 'part', JOIN::WITH, 'inventory_part.part = part');
if ($model === true) { if ($model === true) {
$queryBuilder->andWhere('part.model IS NOT NULL'); $queryBuilder->andWhere('part.model IS NOT NULL');
} else { } else {
@ -59,4 +59,32 @@ class Inventory_PartRepository extends BaseRepository
return $queryBuilder->getQuery()->getResult(); return $queryBuilder->getQuery()->getResult();
} }
public function getPartCount($number, $spare = null, $model = null)
{
$inventory = $this->getEntityManager()->getRepository(Inventory::class)->findNewestInventoryBySetNumber($number);
$queryBuilder = $this->createQueryBuilder('inventory_part')
->where('inventory_part.inventory = :inventory')
->setParameter('inventory', $inventory)
->join(Part::class, 'part', JOIN::WITH, 'inventory_part.part = part')
->andWhere('part.category != 17')
->select('SUM(inventory_part.quantity) as parts');
if ($spare !== null) {
$queryBuilder
->andWhere('inventory_part.spare = :spare')
->setParameter('spare', $spare);
}
if ($model !== null) {
if ($model === true) {
$queryBuilder->andWhere('part.model IS NOT NULL');
} else {
$queryBuilder->andWhere('part.model IS NULL');
}
}
return $queryBuilder->getQuery()->getSingleScalarResult();
}
} }

View File

@ -19,4 +19,12 @@ class ThemeRepository extends BaseRepository
return $queryBuilder->getQuery()->getResult(); return $queryBuilder->getQuery()->getResult();
} }
public function findAllMain()
{
$queryBuilder = $this->createQueryBuilder('theme')
->where('theme.parent IS NULL');
return $queryBuilder->getQuery()->getResult();
}
} }

View File

@ -0,0 +1,35 @@
<?php
namespace AppBundle\Service;
use AppBundle\Entity\LDraw\Model;
class ModelService
{
private $models = [];
public function getAllSubparts(Model $model)
{
foreach ($model->getSubparts() as $subpart) {
$this->resursiveLoadModels($subpart->getSubpart(), $subpart->getCount());
}
return $this->models;
}
private function resursiveLoadModels(Model $model, $quantity = 1)
{
if (($model->getSubparts()->count() !== 0)) {
foreach ($model->getSubparts() as $subpart) {
$this->resursiveLoadModels($subpart->getSubpart(), $subpart->getCount());
}
} else {
$q = isset($this->models[$model->getNumber()]['quantity']) ? $this->models[$model->getNumber()]['quantity'] : 0;
$this->models[$model->getNumber()] = [
'quantity' => $q + $quantity,
'model' => $model,
];
}
}
}

View File

@ -3,12 +3,12 @@
namespace AppBundle\Service; namespace AppBundle\Service;
use AppBundle\Entity\LDraw\Model; use AppBundle\Entity\LDraw\Model;
use AppBundle\Entity\Rebrickable\Inventory_Part; use AppBundle\Entity\Rebrickable\Inventory_Part;
use AppBundle\Entity\Rebrickable\Set; use AppBundle\Entity\Rebrickable\Set;
use AppBundle\Repository\Rebrickable\Inventory_PartRepository; use AppBundle\Repository\Rebrickable\Inventory_PartRepository;
class SetService class SetService
{ {
/** @var Inventory_PartRepository */ /** @var Inventory_PartRepository */
private $inventoryPartRepository; private $inventoryPartRepository;
@ -110,7 +110,6 @@ use AppBundle\Entity\LDraw\Model;
return $models; return $models;
} }
/** /**
* Get array models grouped by color. * Get array models grouped by color.
* [ * [
@ -163,4 +162,31 @@ use AppBundle\Entity\LDraw\Model;
return $colors; return $colors;
} }
/*
* @param Set $set
* @param bool $spare If true - add only spare parts, false - add only regular parts, null - add all parts
*
* @return array
*/
public function getParts(Set $set, $spare = null, $model = false)
{
$parts = [];
$inventoryParts = $this->inventoryPartRepository->findAllBySetNumber($set->getNumber(), $spare, $model);
/** @var Inventory_Part $inventoryPart */
foreach ($inventoryParts as $inventoryPart) {
if (isset($parts[$inventoryPart->getPart()->getNumber()])) {
$parts[$inventoryPart->getPart()->getNumber()]['quantity'] += $inventoryPart->getQuantity();
} else {
$parts[$inventoryPart->getPart()->getNumber()] = [
'part' => $inventoryPart->getPart(),
'quantity' => $inventoryPart->getQuantity(),
];
} }
}
return $parts;
}
}

View File

@ -5,11 +5,10 @@ namespace AppBundle\Service;
use AppBundle\Entity\LDraw\Model; use AppBundle\Entity\LDraw\Model;
use AppBundle\Entity\Rebrickable\Set; use AppBundle\Entity\Rebrickable\Set;
use League\Flysystem\Filesystem; use League\Flysystem\Filesystem;
use ZipStream\ZipStream;
class ZipService class ZipService
{ {
/** @var ZipStream */ /** @var \ZipArchive */
private $archive; private $archive;
/** @var Filesystem */ /** @var Filesystem */
@ -18,26 +17,41 @@ class ZipService
/** @var SetService */ /** @var SetService */
private $setService; private $setService;
/** @var ModelService */
private $modelService;
private $zipName;
private $models;
/** /**
* ZipService constructor. * ZipService constructor.
* *
* @param $mediaFilesystem * @param $mediaFilesystem
* @param $setService * @param $setService
*/ */
public function __construct($mediaFilesystem, $setService) public function __construct($mediaFilesystem, $setService, $modelService)
{ {
$this->mediaFilesystem = $mediaFilesystem; $this->mediaFilesystem = $mediaFilesystem;
$this->setService = $setService; $this->setService = $setService;
$this->modelService = $modelService;
}
private function createZip($path)
{
$archive = new \ZipArchive();
$archive->open($path, \ZipArchive::CREATE);
return $archive;
} }
public function createFromSet(Set $set, $sorted = false) public function createFromSet(Set $set, $sorted = false)
{ {
$sort = $sorted ? 'sorted' : 'unsorted'; $sort = $sorted ? 'sorted' : 'unsorted';
$this->zipName = "set_{$set->getNumber()}_{$set->getName()}({$sort})";
$filename = "set_{$set->getNumber()}_{$set->getName()}({$sort}).zip"; $zipPath = tempnam(sys_get_temp_dir(), 'printabrick');
$this->archive = $this->createZip($zipPath);
// Initialize zip stream
$this->archive = new ZipStream($filename);
if ($sorted) { if ($sorted) {
$this->addSetGroupedByColor($set); $this->addSetGroupedByColor($set);
@ -45,23 +59,35 @@ class ZipService
$this->addSet($set); $this->addSet($set);
} }
$this->archive->finish(); $this->addLicense();
$this->archive->close();
return $this->archive; return $zipPath;
} }
public function createFromModel(Model $model, $subparts = false) public function createFromModel(Model $model, $subparts = false)
{ {
$filename = "model_{$model->getNumber()}.zip"; $this->zipName = "model_{$model->getNumber()}";
// Initialize zip stream $zipPath = tempnam(sys_get_temp_dir(), 'printabrick');
$this->archive = new ZipStream($filename); $this->archive = $this->createZip($zipPath);
$this->addModel($model); $filename = "{$this->zipName}/{$model->getNumber()}.stl";
$this->addModel($filename, $model);
$this->archive->finish(); if ($subparts) {
foreach ($this->modelService->getAllSubparts($model) as $subpart) {
$submodel = $subpart['model'];
$filename = "{$this->zipName}/submodels/{$submodel->getNumber()}_({$subpart['quantity']}x).stl";
return $this->archive; $this->addModel($filename, $submodel);
}
}
$this->addLicense();
$this->archive->close();
return $zipPath;
} }
/** /**
@ -81,9 +107,9 @@ class ZipService
$model = $modelArray['model']; $model = $modelArray['model'];
$quantity = $modelArray['quantity']; $quantity = $modelArray['quantity'];
$filename = "{$color->getName()}/{$model->getNumber()}_({$quantity}x).stl"; $filename = "{$this->zipName}/{$color->getName()}/{$model->getNumber()}_({$quantity}x).stl";
$this->archive->addFile($filename, $this->mediaFilesystem->read($model->getPath())); $this->addModel($filename, $model);
} }
} }
} }
@ -99,26 +125,33 @@ class ZipService
$models = $this->setService->getModels($set, $spare); $models = $this->setService->getModels($set, $spare);
foreach ($models as $number => $array) { foreach ($models as $number => $array) {
$model = $array['model'];
$quantity = $array['quantity']; $quantity = $array['quantity'];
$filename = $number."_({$quantity}x).stl"; $filename = "{$this->zipName}/{$number}_({$quantity}x).stl";
$this->models[$number] = $array['model'];
$this->archive->addFile($filename, $this->mediaFilesystem->read($array['model']->getPath())); $this->addModel($filename, $model);
} }
} }
public function addModel(Model $model, $count = 1, $folder = '') private function addModel($path, $model)
{ {
$filename = $folder.$model->getNumber()."_({$count}x).stl"; $this->archive->addFromString($path, $this->mediaFilesystem->read($model->getPath()));
$this->models[$model->getNumber()] = $model;
$this->archive->addFile($filename, $this->mediaFilesystem->read($model->getPath()));
foreach ($model->getSubparts() as $subpart) {
$this->addModel($subpart->getSubpart(), $subpart->getCount(), $folder.$model->getNumber().'_subparts/');
}
} }
// TODO add licence file and information to zip file private function addLicense()
public function addLicence()
{ {
$text = sprintf('All stl files in this archive were converted by LDView from LDraw Library http://www.ldraw.org/'."\n\n");
$text .= sprintf('Files are licensed under the Creative Commons - Attribution license.'."\n");
$text .= sprintf('http://creativecommons.org/licenses/by/2.0/'."\n\n");
$text .= sprintf('Attribution:'."\n"."\n");
foreach ($this->models as $model) {
$text .= sprintf('%s - "%s" by %s'."\n", $model->getNumber(), $model->getName(), $model->getAuthor()->getName());
}
$this->archive->addFromString("{$this->zipName}/LICENSE.txt", $text);
} }
} }