Completed
Pull Request — master (#8)
by
unknown
06:16
created

ImportCommand   C

Complexity

Total Complexity 25

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 20

Test Coverage

Coverage 73.63%

Importance

Changes 21
Bugs 5 Features 4
Metric Value
wmc 25
c 21
b 5
f 4
lcom 1
cbo 20
dl 0
loc 178
ccs 67
cts 91
cp 0.7363
rs 6.4705

6 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 13 1
A validateInput() 0 7 3
A execute() 0 19 3
C import() 0 74 8
C writeValidationViolations() 0 40 8
A writeStatistics() 0 13 2
1
<?php
2
namespace Mathielen\ImportEngineBundle\Command;
3
4
use Ddeboer\DataImport\Filter\OffsetFilter;
5
use Mathielen\DataImport\Event\ImportItemEvent;
6
use Mathielen\ImportEngine\Event\ImportConfigureEvent;
7
use Mathielen\ImportEngine\Event\ImportRequestEvent;
8
use Mathielen\ImportEngine\Exception\InvalidConfigurationException;
9
use Mathielen\ImportEngine\Import\ImportBuilder;
10
use Mathielen\ImportEngine\Import\Run\ImportRunner;
11
use Mathielen\ImportEngine\ValueObject\ImportRequest;
12
use Mathielen\ImportEngine\ValueObject\ImportRun;
13
use Mathielen\ImportEngineBundle\Utils;
14
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
15
use Symfony\Component\Console\Helper\ProgressBar;
16
use Symfony\Component\Console\Helper\Table;
17
use Symfony\Component\Console\Helper\TableSeparator;
18
use Symfony\Component\Console\Input\InputArgument;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Input\InputOption;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\Validator\ConstraintViolation;
23
24
class ImportCommand extends ContainerAwareCommand
25
{
26
27
    const MAX_VIOLATION_ERRORS = 10;
28
29 4
    protected function configure()
30
    {
31 4
        $this->setName('importengine:import')
32 4
            ->setDescription('Imports data with a definied importer')
33 4
            ->addArgument('source_id', InputArgument::REQUIRED, "id of source. Different StorageProviders need different id data.\n- upload, directory: \"<path/to/file>\"\n- doctrine: \"<id of query>\"\n- service: \"<service>.<method>[?arguments_like_url_query]\"")
34 4
            ->addArgument('source_provider', InputArgument::OPTIONAL, 'id of source provider', 'default')
35 4
            ->addOption('importer', 'i', InputOption::VALUE_REQUIRED, 'id/name of importer')
36 4
            ->addOption('context', 'c', InputOption::VALUE_REQUIRED, 'Supply optional context information to import. Supply key-value data in query style: key=value&otherkey=othervalue&...')
37 4
            ->addOption('offset', 'o', InputOption::VALUE_REQUIRED, 'Offset imported rows', 0)
38 4
            ->addOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Limit imported rows')
39 4
            ->addOption('dryrun', 'd', InputOption::VALUE_NONE, 'Do not import - Validation only')
40
        ;
41 4
    }
42
43 4
    protected function validateInput(InputInterface $input)
0 ignored issues
show
Unused Code introduced by
The parameter $input is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
44
    {
45 4
        if (!$this->getContainer()->has('mathielen_importengine.import.builder') ||
46 4
            !$this->getContainer()->has('mathielen_importengine.import.runner')) {
47
            throw new InvalidConfigurationException("No importengine services have been found. Did you register the bundle in AppKernel and configured at least one importer in config?");
48
        }
49 4
    }
50
51 4
    protected function execute(InputInterface $input, OutputInterface $output)
52
    {
53 4
        $this->validateInput($input);
54
55 4
        $importerId = $input->getOption('importer');
56 4
        $sourceProviderId = $input->getArgument('source_provider');
57 4
        $sourceId = $input->getArgument('source_id');
58 4
        $isDryrun = $input->getOption('dryrun');
59 4
        if ($context = $input->getOption('context')) {
60
            //parse key=value&key=value string to array
61
            if (strpos($context, '=') !== false) {
62
                parse_str($input->getOption('context'), $context);
63
            }
64
        }
65 4
        $limit = $input->getOption('limit');
66 4
        $offset = $input->getOption('offset');
67
68 4
        $this->import($output, $importerId, $sourceProviderId, $sourceId, $context, $offset, $limit, $isDryrun);
69 4
    }
70
71 4
    protected function import(OutputInterface $output, $importerId, $sourceProviderId, $sourceId, $context=null, $offset=0, $limit=null, $isDryrun=false)
72
    {
73 4
        $output->writeln("Commencing ".($isDryrun?'<comment>dry-run</comment> ':'')."import using importer ".(empty($importerId)?'<comment>unknown</comment>':"<info>$importerId</info>")." with source provider <info>$sourceProviderId</info> and source id <info>$sourceId</info>");
74
75 4
        $sourceId = Utils::parseSourceId($sourceId);
76 4
        $progress = new ProgressBar($output);
77
78
        //set limit
79 4
        if ($limit) {
80 1
            $output->writeln("Limiting import to <info>$limit</info> rows.");
81
82
            $this->getContainer()->get('event_dispatcher')->addListener(ImportConfigureEvent::AFTER_BUILD, function (ImportConfigureEvent $event) use ($offset, $limit) {
83
                $event->getImport()->importer()->filters()->add(new OffsetFilter($offset, $limit));
84 1
            });
85
        }
86
87
        //show discovered importer id
88 4
        if (empty($importerId)) {
89
            $this->getContainer()->get('event_dispatcher')->addListener(ImportRequestEvent::DISCOVERED, function (ImportRequestEvent $event) use ($output) {
90
                $importerId = $event->getImportRequest()->getImporterId();
91
                $output->writeln("Importer discovered: <info>$importerId</info>");
92 2
            });
93
        }
94
95
        /** @var ImportBuilder $importBuilder */
96 4
        $importBuilder = $this->getContainer()->get('mathielen_importengine.import.builder');
97
98 4
        $importRequest = new ImportRequest($sourceId, $sourceProviderId, $importerId, Utils::whoAmI().'@CLI', $context);
0 ignored issues
show
Unused Code introduced by
The call to ImportRequest::__construct() has too many arguments starting with $context.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
99
100 4
        $import = $importBuilder->buildFromRequest($importRequest);
101
102
        //apply context info from commandline
103 4
        $importRun = $import->getRun();
104
105
        //status callback
106 4
        $this->getContainer()->get('event_dispatcher')->addListener(ImportItemEvent::AFTER_READ, function (ImportItemEvent $event) use ($output, &$progress) {
107
            /** @var ImportRun $importRun */
108
            $importRun = $event->getContext()->getRun();
109
            $stats = $importRun->getStatistics();
110
            $processed = isset($stats['processed'])?$stats['processed']:0;
111
            $max = $importRun->getInfo()['count'];
112
113
            if ($progress->getMaxSteps() != $max) {
114
                $progress = new ProgressBar($output, $max);
115
                $progress->start();
116
            }
117
118
            $progress->setProgress($processed);
119 4
        });
120
121
        /** @var ImportRunner $importRunner */
122 4
        $importRunner = $this->getContainer()->get('mathielen_importengine.import.runner');
123 4
        if ($isDryrun) {
124 1
            $importRunner->dryRun($import);
125
        } else {
126 3
            $importRunner->run($import);
127
        }
128
129 4
        $progress->finish();
130 4
        $output->writeln('');
131 4
        $output->writeln("<info>Import done</info>");
132 4
        $output->writeln('');
133
134 4
        $this->writeStatistics($importRun->getStatistics(), new Table($output));
135
136 4
        $this->writeValidationViolations(
137
            $import
138 4
                ->importer()
139 4
                ->validation()
140 4
                ->getViolations(),
141 4
            new Table($output));
142
143 4
        $output->writeln('');
144 4
    }
145
146 4
    protected function writeValidationViolations(array $violations, Table $table)
147
    {
148 4
        if (empty($violations)) {
149 4
            return;
150
        }
151
        $violations = $violations['source'] + $violations['target'];
152
153
        $table
154
            ->setHeaders(array('Constraint', 'Occurrences (lines)'))
155
        ;
156
157
        $tree = [];
158
        foreach ($violations as $line=>$validations) {
159
            /** @var ConstraintViolation $validation */
160
            foreach ($validations as $validation) {
161
                $key = $validation->__toString();
162
                if (!isset($tree[$key])) {
163
                    $tree[$key] = [];
164
                }
165
                $tree[$key][] = $line;
166
            }
167
        }
168
169
        $i = 0;
170
        foreach ($tree as $violation=>$lines) {
171
            $table->addRow([$violation, join(', ', Utils::numbersToRangeText($lines))]);
172
            ++$i;
173
174
            if ($i === self::MAX_VIOLATION_ERRORS) {
175
                $table->addRow(new TableSeparator());
176
                $table->addRow(array(null, 'There are more errors...'));
177
178
                break;
179
            }
180
        }
181
182
        if ($i > 0) {
183
            $table->render();
184
        }
185
    }
186
187 4
    protected function writeStatistics(array $statistics, Table $table)
188
    {
189 4
        $rows = [];
190 4
        foreach ($statistics as $k=>$v) {
191
            $rows[] = [$k, $v];
192
        }
193
194
        $table
195 4
            ->setHeaders(array('Statistics'))
196 4
            ->setRows($rows)
197
        ;
198 4
        $table->render();
199 4
    }
200
201
}
202