Skip to content

Commit 7c5c254

Browse files
rootroot
authored andcommitted
added transformers for list fields mappings and symfony console component
+ refactor.
1 parent 8cbadac commit 7c5c254

File tree

14 files changed

+1248
-67
lines changed

14 files changed

+1248
-67
lines changed

Command/ImportCommand.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace Command;
4+
5+
use Symfony\Component\Console\Command\Command;
6+
use Symfony\Component\Console\Input\InputArgument;
7+
use Symfony\Component\Console\Input\InputInterface;
8+
use Symfony\Component\Console\Input\InputOption;
9+
use Symfony\Component\Console\Output\OutputInterface;
10+
11+
/**
12+
* Symfony Command component used for easing CLI arguments treatment.
13+
*
14+
* @author Juan Manuel Fernandez <[email protected]>
15+
*/
16+
class ImportCommand extends Command
17+
{
18+
/**
19+
* {@hinheritdoc}
20+
*/
21+
protected function configure()
22+
{
23+
$this
24+
->setName('import')
25+
->setDescription('Imports or delete issues in a Redmine project, from a data sheet')
26+
->addArgument(
27+
'dataFile',
28+
InputArgument::REQUIRED,
29+
'absolute file path to the data sheet'
30+
)
31+
->addOption(
32+
'sheet',
33+
null,
34+
InputOption::VALUE_REQUIRED,
35+
'The sheet name config to be used to interpret data in input sheet file.'
36+
)
37+
->addOption(
38+
'record',
39+
null,
40+
InputOption::VALUE_REQUIRED,
41+
'The default record name config to be used to interpret data in input sheet file.'
42+
)
43+
->addOption(
44+
'delimiter',
45+
null,
46+
InputOption::VALUE_OPTIONAL,
47+
'The sheet field delimiter, for CSV, defaults to ";"',
48+
null
49+
)
50+
->addOption(
51+
'fileType',
52+
null,
53+
InputOption::VALUE_OPTIONAL,
54+
"The default file Format to be used, its case sensitive as the parser class name "
55+
. "beggins with this string and ends with 'SheetRecordParser' i.e. 'CsvSheetRecordParser'.",
56+
'Csv'
57+
);
58+
}
59+
60+
/**
61+
* {@hinheritdoc}
62+
*/
63+
protected function execute(InputInterface $input, OutputInterface $output)
64+
{
65+
$fileName = $input->getArgument('dataFile');
66+
$sheet = $input->getOption('sheet') ? : null;
67+
$record = $input->getOption('record') ? : null;
68+
$delimiter = $input->getOption('delimiter') ? : null;
69+
$fileType = $input->getOption('fileType') ? : null;
70+
71+
$import = \ImportService::getInstance(
72+
$fileName, $sheet, $delimiter, $fileType, $record
73+
);
74+
$import->createTickets();
75+
//$import->deleteIssuesInProject('demandas');
76+
}
77+
}

Command/deleteCommand.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace Command;
4+
5+
use Symfony\Component\Console\Command\Command;
6+
use Symfony\Component\Console\Input\InputArgument;
7+
use Symfony\Component\Console\Input\InputInterface;
8+
use Symfony\Component\Console\Input\InputOption;
9+
use Symfony\Component\Console\Output\OutputInterface;
10+
11+
/**
12+
* Symfony Command component used for easing CLI arguments treatment.
13+
*
14+
* @author Juan Manuel Fernandez <[email protected]>
15+
*/
16+
class deleteCommand extends Command
17+
{
18+
/**
19+
* {@hinheritdoc}
20+
*/
21+
protected function configure()
22+
{
23+
$this
24+
->setName('delete')
25+
->setDescription(
26+
'Deletes all issues (only if --all) in a project or issued created in last import '
27+
. 'command run. Does nothing if cant find serielized created issues ids file'
28+
)
29+
->addOption(
30+
'all',
31+
null,
32+
InputOption::VALUE_OPTIONAL,
33+
'if Specified, deletes all issues in a project. WARNING! all issues, not just the imported ones.'
34+
)
35+
->addOption(
36+
'project',
37+
null,
38+
InputOption::VALUE_REQUIRED,
39+
'This must be set either if --all is set or just last Run Ids need to be deleted.'
40+
. ' Its the project identifier, the one that appears in the URL. i.e. '
41+
. ' <RedmineDomain>/projects/<projectIdentifier>/issues?...'
42+
);
43+
}
44+
45+
/**
46+
* {@hinheritdoc}
47+
*/
48+
protected function execute(InputInterface $input, OutputInterface $output)
49+
{
50+
$import = \ImportService::getInstance();
51+
$progectidentifier = $input->getOption('project') ? : null;
52+
if (null === $progectidentifier) {
53+
throw new \Exception('You must use --project="RedmineProectIdentifier"');
54+
}
55+
$all = $input->getOption('all') ? : null;
56+
if (null === $all) {
57+
$import->deleteLastRunCreatedIssues($progectidentifier);
58+
} else {
59+
$import->deleteIssuesInProject($progectidentifier);
60+
}
61+
}
62+
}

Config/config.demo.yml

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
################################################################################
2+
# Run customization
3+
################################################################################
4+
redmine_account:
5+
# http://www.redmine.org/projects/redmine/wiki/Rest_api#Authentication # show API Key.
6+
api_key: 'fc..8' # siup
7+
host: 'http://redmine.myserver.com.ar/'
8+
# values that determines the config that is to be used for next run
9+
sheet_selection:
10+
sheet: 'demandas' # sheets => demandas
11+
record: 'demanda' # sheets => demandas => record => demanda
12+
input_format:
13+
file_type: 'Csv' # maps to CsvSheetRecordParser
14+
delimiter: '#' # field delimiter
15+
16+
# false Makes everithing but call save on entities, for tests before hitting API
17+
save_records: true
18+
################################################################################
19+
# Sheets mapping configuration
20+
################################################################################
21+
on_error:
22+
behavior: continue # [continue, stop, rollback]
23+
display_exceptions: false # [true | false]
24+
25+
# redmine custom fields settings <objectType> => <customFieldName> => id => <idValue>
26+
custom_fields:
27+
issue:
28+
sprint:
29+
id: 1 # for issues, Intermediario field
30+
Localidad:
31+
id: 19 # for issues, Localidad field
32+
"Fecha Comprometido":
33+
id: 21 # for issues, Fecha Comprometido field
34+
Telefono:
35+
id: 20 # for issues, Telefono field
36+
Beneficiario:
37+
id: 18 # for issues, Beneficiario field
38+
Intermediario:
39+
id: 17 # for issues, Intermediario field
40+
41+
# all sheets that can be importes should be mapped here
42+
sheets:
43+
#1st sheet definition
44+
demandas:
45+
# sheet Name, not used unless you add a web form that might use this name.
46+
name: Planilla de Relevamiento de Demandas Area de Nuevos Medios
47+
# each sheet could host several record types, here's each definition
48+
records:
49+
# recordName use as index to select this config in [Run customization]
50+
demanda:
51+
# record Label, not used.
52+
name: Demanda
53+
# Redmine Objects/Entities that are related to sheet's data.
54+
entities:
55+
# Issue object
56+
Issue:
57+
# previously relatd to Doctrien 1.X adapted to Redmine issue types.
58+
# Not used schema_entity: ~ # si entities[entName] no coincide con el esquema setear este valor
59+
# object deefault values (in this case Issue) can be [callbackClass, Callbackmethod]
60+
# these default need not to be in the sheet, but might be mandatory for the API.
61+
# you could also use defaults for cusotm fields here, just with the name.
62+
defaults:
63+
project: A Nuevos Medios, Seguimiento de Demandas
64+
status: Nueva
65+
priority: Normal
66+
assigned_to_id: 92
67+
author: 'juan'
68+
due_date: ~
69+
start_date: [Defaults\Defaults, startDate]
70+
tracker: Demanda
71+
"Fecha Comprometido": '2013-11-01'
72+
Beneficiario: N/N
73+
subject: 'Sin Asunto'
74+
Intermediario: '-----------------'
75+
76+
# These are the fields expected to be present in the CSV or any other sheet like input
77+
# next definition matched a CSV as follows (showing two records):
78+
# [Subject;Description;Sprint] the rest of mandatory values are defined above for "Issue"
79+
# subject1;Description1;8
80+
# subject2;Description2;8
81+
fields:
82+
# field name
83+
beneficiario:
84+
# this key relates this field with Redmine's model described above
85+
model: {entity: 'Issue', column: 'Beneficiario'}
86+
# This are the coordinates, where the parser tries to find the 1st occurrence of the field.
87+
# Zero based. from the upper-left corner
88+
coord: {x: 1, y: 0}
89+
# default value for this field. ovverides other defaults possibly defined above.
90+
default: ~ # Si !== ~ pisa al default del schema y al default en [entities]
91+
# necesary moves to reach next instance of this field, e.i. next record. Normally it'll be just
92+
# one step down. (Y+1, X+0). But for values in headers, that appears only once, might be
93+
# (Y+0, X+0) so, the same value is used for every record.
94+
increment: {x: 0, y: 1}
95+
# callback that might be needed to transform input data before being persisted.
96+
transform: ~ # a callback method
97+
98+
subject:
99+
model: {entity: 'Issue', column: 'subject'} # entity referencia entities[entity] no al schema. Para eso está entities[entity][schema_entity], en caso de que difiera.
100+
coord: {x: 3, y: 0}
101+
default: ~ # Si !== ~ pisa al default del schema y al default en [entities]
102+
increment: {x: 0, y: 1} # ~ = {x: 0, y: 0} if field is recurrent increment determines the relative loction of the next sibling. ~ means the field is no recurrent, only appears once in a sheet-
103+
transform: [Transformers\Transformer, asunto] # a callback method
104+
105+
intermediario:
106+
model: {entity: 'Issue', column: 'Intermediario'} # entity referencia entities[entity] no al schema. Para eso está entities[entity][schema_entity], en caso de que difiera.
107+
coord: {x: 2, y: 0}
108+
default: ~ # Si !== ~ pisa al default del schema y al default en [entities]
109+
increment: {x: 0, y: 1} # ~ = {x: 0, y: 0} if field is recurrent increment determines the relative loction of the next sibling. ~ means the field is no recurrent, only appears once in a sheet-
110+
transform: [Transformers\Transformer, intermediario] # a callback method
111+
112+
description:
113+
model: {entity: 'Issue', column: 'description', glue: '| '} # entity referencia entities[entity] no al schema. Para eso está entities[entity][schema_entity], en caso de que difiera.
114+
coord: {x: 3, y: 0}
115+
default: ~ # Si !== ~ pisa al default del schema y al default en [entities]
116+
increment: {x: 0, y: 1} # ~ = {x: 0, y: 0} if field is recurrent increment determines the relative loction of the next sibling. ~ means the field is no recurrent, only appears once in a sheet-
117+
transform: ~ # a callback method
118+
119+
localidad:
120+
model: { entity: 'Issue', column: 'Localidad'} # entity referencia entities[entity] no al schema. Para eso está entities[entity][schema_entity], en caso de que difiera.
121+
coord: {x: 0, y: 0}
122+
default: ~ # Si !== ~ pisa al default del schema y al default en [entities]
123+
increment: {x: 0, y: 1} # ~ = {x: 0, y: 0} if field is recurrent increment determines the relative loction of the next sibling. ~ means the field is no recurrent, only appears once in a sheet-
124+
transform: [Transformers\Transformer, localidad] # a callback method
125+
126+
estado:
127+
model: { entity: 'Issue', column: 'status'} # entity referencia entities[entity] no al schema. Para eso está entities[entity][schema_entity], en caso de que difiera.
128+
coord: {x: 4, y: 0}
129+
default: ~ # Si !== ~ pisa al default del schema y al default en [entities]
130+
increment: {x: 0, y: 1} # ~ = {x: 0, y: 0} if field is recurrent increment determines the relative loction of the next sibling. ~ means the field is no recurrent, only appears once in a sheet-
131+
transform: [Transformers\Transformer, estado] # a callback method
132+
133+
observaciones:
134+
model: { entity: 'Issue', column: 'description', glue: '| '} # entity referencia entities[entity] no al schema. Para eso está entities[entity][schema_entity], en caso de que difiera.
135+
coord: {x: 5, y: 0}
136+
default: ~ # Si !== ~ pisa al default del schema y al default en [entities]
137+
increment: {x: 0, y: 1} # ~ = {x: 0, y: 0} if field is recurrent increment determines the relative loction of the next sibling. ~ means the field is no recurrent, only appears once in a sheet-
138+
transform: ~ # a callback method
139+
140+
fecha_inicio:
141+
model: { entity: 'Issue', column: 'start_date'} # entity referencia entities[entity] no al schema. Para eso está entities[entity][schema_entity], en caso de que difiera.
142+
coord: {x: 6, y: 0}
143+
default: ~ # Si !== ~ pisa al default del schema y al default en [entities]
144+
increment: {x: 0, y: 1} # ~ = {x: 0, y: 0} if field is recurrent increment determines the relative loction of the next sibling. ~ means the field is no recurrent, only appears once in a sheet-
145+
transform: [Transformers\Transformer, fecha] # a callback method
146+
147+
localidad_en_descripcion:
148+
model: { entity: 'Issue', column: 'description', glue: '| '} # entity referencia entities[entity] no al schema. Para eso está entities[entity][schema_entity], en caso de que difiera.
149+
coord: {x: 0, y: 0}
150+
default: ~ # Si !== ~ pisa al default del schema y al default en [entities]
151+
increment: {x: 0, y: 1} # ~ = {x: 0, y: 0} if field is recurrent increment determines the relative loction of the next sibling. ~ means the field is no recurrent, only appears once in a sheet-
152+
transform: ~ # a callback method
153+
#2st sheet definition
154+
iprodich:
155+
# sheet Name, not used unless you add a web form that might use this name.
156+
name: iprodich
157+
# each sheet could host several record types, here's each definition
158+
records:
159+
# recordName use as index to select this config in [Run customization]
160+
iprodich:
161+
# record Label, not used.
162+
name: iprodich
163+
# Redmine Objects/Entities that are related to sheet's data.
164+
entities:
165+
# Issue object
166+
Issue:
167+
# previously relatd to Doctrien 1.X adapted to Redmine issue types.
168+
# Not used schema_entity: ~ # si entities[entName] no coincide con el esquema setear este valor
169+
# object deefault values (in this case Issue) can be [callbackClass, Callbackmethod]
170+
# these default need not to be in the sheet, but might be mandatory for the API.
171+
# you could also use defaults for cusotm fields here, just with the name.
172+
defaults:
173+
project: Gestión I.Pro.Di.Ch
174+
status: Nueva
175+
priority: Normal
176+
assigned_to: juanmf
177+
author: 'juan'
178+
due_date: ~
179+
start_date: [Defaults\Defaults, startDate]
180+
tracker: Demanda
181+
"Fecha Comprometido": '2013-11-01'
182+
subject: 'Sin Asunto'
183+
sprint: 8
184+
185+
# These are the fields expected to be present in the CSV or any other sheet like input
186+
# next definition matched a CSV as follows (showing two records):
187+
# [Subject] the rest of mandatory values are defined above for "Issue"
188+
# subject1
189+
fields:
190+
# field name
191+
subject:
192+
model: {entity: 'Issue', column: 'subject'} # entity referencia entities[entity] no al schema. Para eso está entities[entity][schema_entity], en caso de que difiera.
193+
coord: {x: 0, y: 0}
194+
default: ~ # Si !== ~ pisa al default del schema y al default en [entities]
195+
increment: {x: 0, y: 1} # ~ = {x: 0, y: 0} if field is recurrent increment determines the relative loction of the next sibling. ~ means the field is no recurrent, only appears once in a sheet-
196+
transform: [Transformers\Transformer, asunto] # a callback method
197+

0 commit comments

Comments
 (0)