1 package SGN
::Controller
::AJAX
::LabelDesigner
;
5 use CXGN
::List
::Transform
;
14 use Tie
::UrlEncoder
; our(%urlencode);
15 use CXGN
::Trial
::TrialLayout
;
17 BEGIN { extends
'Catalyst::Controller::REST' }
20 default => 'application/json',
22 map => { 'application/json' => 'JSON', 'text/html' => 'JSON' },
25 sub retrieve_longest_fields
:Path
('/tools/label_designer/retrieve_longest_fields') {
28 my $schema = $c->dbic_schema('Bio::Chado::Schema');
29 my $data_type = $c->req->param("data_type");
30 my $value = $c->req->param("value");
32 print STDERR
"Data type is $data_type and id is $value\n";
33 my ($trial_num, $trial_id, $plot_design, $plant_design) = get_plot_data
($c, $schema, $data_type, $value);
35 #if plant ids exist, use plant design
36 my %plot_design = %{$plot_design};
37 my @plot_ids = keys %plot_design;
38 my $plant_ids = $plot_design{$plot_ids[0]}->{'plant_ids'};
39 my @plant_ids = @
{$plant_ids};
42 if (scalar(@plant_ids) > 0) {
43 $design = $plant_design;
45 $design = $plot_design;
49 print STDERR
"Num plants 3: " . scalar(keys %{$design});
50 print STDERR
"AFTER SUB: \nTrial_id is $trial_id and design is ". Dumper
($design) ."\n";
52 $c->stash->{rest
} = { error
=> "The selected list contains plots from more than one trial. This is not supported. Please select a different data source." };
56 my $trial_name = $schema->resultset("Project::Project")->search({ project_id
=> $trial_id })->first->name();
58 $c->stash->{rest
} = { error
=> "Trial with id $trial_id does not exist. Can't create labels." };
62 my %design = %{$design};
64 $c->stash->{rest
} = { error
=> "Trial $trial_name does not have a valid field design. Can't create labels." };
67 $longest_hash{'trial_name'} = $trial_name;
69 my $year_cvterm_id = $schema->resultset("Cv::Cvterm")->search({name
=> 'project year' })->first->cvterm_id();
70 my $year = $schema->resultset("Project::Projectprop")->search({ project_id
=> $trial_id, type_id
=> $year_cvterm_id } )->first->value();
71 $longest_hash{'year'} = $year;
73 my $design_cvterm_id = $schema->resultset("Cv::Cvterm")->search({name
=> 'design' })->first->cvterm_id();
74 my $design_value = $schema->resultset("Project::Projectprop")->search({ project_id
=> $trial_id, type_id
=> $design_cvterm_id } )->first->value();
75 if ($design_value eq "genotyping_plate") { # for genotyping trials, get "Genotyping Facility" and "Genotyping Project Name"
76 my $genotyping_facility_cvterm_id = $schema->resultset("Cv::Cvterm")->search({name
=> 'genotyping_facility' })->first->cvterm_id();
77 my $geno_project_name_cvterm_id = $schema->resultset("Cv::Cvterm")->search({name
=> 'genotyping_project_name' })->first->cvterm_id();
78 my $genotyping_facility = $schema->resultset("Project::Projectprop")->search({ project_id
=> $trial_id, type_id
=> $genotyping_facility_cvterm_id } )->first->value();
79 my $genotyping_project_name = $schema->resultset("NaturalDiversity::NdExperimentProject")->search({
80 project_id
=> $trial_id
81 })->search_related('nd_experiment')->search_related('nd_experimentprops',{
82 'nd_experimentprops.type_id' => $geno_project_name_cvterm_id
85 $longest_hash{'genotyping_project_name'} = $genotyping_project_name;
86 $longest_hash{'genotyping_facility'} = $genotyping_facility;
89 #get all fields in this trials design
90 my $random_plot = $design{(keys %design)[rand keys %design]};
92 my @keys = keys %{$random_plot};
93 foreach my $field (@keys) {
95 # if rep_number, find unique options and return them
96 if ($field eq 'rep_number') {
97 print STDERR
"Searching for unique rep numbers.\n";
98 # foreach my $key (keys %design) {
99 $reps{$_->{'rep_number'}}++ foreach values %design;
100 print STDERR
"Reps: ".Dumper
(%reps);
104 print STDERR
" Searching for longest $field\n";
105 #for each field order values by descending length, then save the first one
106 foreach my $key ( sort { length($design{$b}{$field}) <=> length($design{$a}{$field}) or versioncmp
($a, $b) } keys %design) {
107 print STDERR
"Longest $field is: ".$design{$key}{$field}."\n";
108 my $longest = $design{$key}{$field};
109 unless (ref($longest) || length($longest) < 1) { # skip if not scalar or undefined
110 $longest_hash{$field} = $longest;
111 } elsif (ref($longest) eq 'ARRAY') { # if array (ex. plants), sort array by length and take longest
112 print STDERR
"Processing array " . Dumper
($longest) . "\n";
113 # my @array = @{$longest};
114 my @sorted = sort { length $a <=> length $b } @
{$longest};
115 if (length($sorted[0]) > 0) {
116 $longest_hash{$field} = $sorted[0];
118 } elsif (ref($longest) eq 'HASH') {
119 print STDERR
"Not handling hashes yet\n";
125 # save longest pedigree string
126 my $pedigree_strings = get_all_pedigrees
($schema, \
%design);
127 my %pedigree_strings = %{$pedigree_strings};
129 foreach my $key ( sort { length($pedigree_strings{$b}) <=> length($pedigree_strings{$a}) } keys %pedigree_strings) {
130 $longest_hash{'pedigree_string'} = $pedigree_strings{$key};
134 #print STDERR "Dumped data is: ".Dumper(%longest_hash);
135 $c->stash->{rest
} = {
136 fields
=> \
%longest_hash,
141 sub label_designer_download
: Path
('/tools/label_designer/download') : ActionClass
('REST') { }
143 sub label_designer_download_GET
: Args
(0) {
146 $c->forward('label_designer_download_POST');
149 sub label_designer_download_POST
: Args
(0) {
152 my $schema = $c->dbic_schema('Bio::Chado::Schema');
153 my $download_type = $c->req->param("download_type");
154 # my $trial_id = $c->req->param("trial_id");
155 my $data_type = $c->req->param("data_type");
156 my $value = $c->req->param("value");
157 my $design_json = $c->req->param("design_json");
158 my $conversion_factor = 2.83; # for converting from 8 dots per mmm to 2.83 per mm (72 per inch)
162 my $design_params = $json->allow_nonref->utf8->relaxed->escape_slash->loose->allow_singlequote->allow_barekey->decode($design_json);
164 my ($trial_num, $trial_id, $plot_design, $plant_design) = get_plot_data
($c, $schema, $data_type, $value);
166 #if plant ids or names are used in design params, use plant design
168 my $design = $plot_design;
169 my $label_params = $design_params->{'label_elements'};
170 foreach my $element (@
$label_params) {
171 my %element = %{$element};
172 my $filled_value = $element{'value'};
173 print STDERR
"Filled value is $filled_value\n";
174 if ($filled_value eq '{plant_id}' || $filled_value eq '{plant_name}') {
175 $design = $plant_design;
179 if ($trial_num > 1) {
180 $c->stash->{rest
} = { error
=> "The selected list contains plots from more than one trial. This is not supported. Please select a different data source." };
184 my $trial_name = $schema->resultset("Project::Project")->search({ project_id
=> $trial_id })->first->name();
186 $c->stash->{rest
} = { error
=> "Trial with id $trial_id does not exist. Can't create labels." };
189 my %design = %{$design};
191 $c->stash->{rest
} = { error
=> "Trial $trial_name does not have a valid field design. Can't create labels." };
195 my $design_cvterm_id = $schema->resultset("Cv::Cvterm")->search({name
=> 'design' })->first->cvterm_id();
196 my $design_value = $schema->resultset("Project::Projectprop")->search({ project_id
=> $trial_id, type_id
=> $design_cvterm_id } )->first->value();
198 my ($genotyping_facility, $genotyping_project_name);
199 if ($design_value eq "genotyping_plate") { # for genotyping trials, get "Genotyping Facility" and "Genotyping Project Name"
200 my $genotyping_facility_cvterm_id = $schema->resultset("Cv::Cvterm")->search({name
=> 'genotyping_facility' })->first->cvterm_id();
201 my $geno_project_name_cvterm_id = $schema->resultset("Cv::Cvterm")->search({name
=> 'genotyping_project_name' })->first->cvterm_id();
202 $genotyping_facility = $schema->resultset("Project::Projectprop")->search({ project_id
=> $trial_id, type_id
=> $genotyping_facility_cvterm_id } )->first->value();
203 $genotyping_project_name = $schema->resultset("NaturalDiversity::NdExperimentProject")->search({
204 project_id
=> $trial_id
205 })->search_related('nd_experiment')->search_related('nd_experimentprops',{
206 'nd_experimentprops.type_id' => $geno_project_name_cvterm_id
210 my $year_cvterm_id = $schema->resultset("Cv::Cvterm")->search({name
=> 'project year' })->first->cvterm_id();
211 my $year = $schema->resultset("Project::Projectprop")->search({ project_id
=> $trial_id, type_id
=> $year_cvterm_id } )->first->value();
213 # if needed retrieve pedigrees in bulk
214 my $pedigree_strings;
215 foreach my $element (@
$label_params) {
216 if ($element->{'value'} eq '{pedigree_string}') {
217 $pedigree_strings = get_all_pedigrees
($schema, $design);
221 # Create a blank PDF file
222 my $dir = $c->tempfiles_subdir('labels');
223 my $file_prefix = $trial_name;
224 $file_prefix =~ s/[^a-zA-Z0-9-_]//g;
226 my ($FH, $filename) = $c->tempfile(TEMPLATE
=>"labels/$file_prefix-XXXXX", SUFFIX
=>".$download_type");
228 # initialize loop variables
232 my $sort_order = $design_params->{'sort_order'};
234 if ($download_type eq 'pdf') {
236 print STDERR
"Creating the PDF . . .\n";
237 my $pdf = PDF
::API2
->new(-file
=> $FH);
238 my $page = $pdf->page();
239 my $text = $page->text();
240 my $gfx = $page->gfx();
241 $page->mediabox($design_params->{'page_width'}, $design_params->{'page_height'});
243 # loop through plot data in design hash
244 foreach my $key ( sort { versioncmp
( $design{$a}{$sort_order} , $design{$b}{$sort_order} ) or $a <=> $b } keys %design) {
246 #print STDERR "Design key is $key\n";
247 my %design_info = %{$design{$key}};
248 $design_info{'trial_name'} = $trial_name;
249 $design_info{'year'} = $year;
250 $design_info{'genotyping_facility'} = $genotyping_facility;
251 $design_info{'genotyping_project_name'} = $genotyping_project_name;
252 $design_info{'pedigree_string'} = $pedigree_strings->{$design_info{'accession_name'}};
253 #print STDERR "Design info: " . Dumper(%design_info);
255 if ( $design_params->{'plot_filter'} eq 'all' || $design_params->{'plot_filter'} eq $design_info{'rep_number'}) { # filter by rep if needed
257 for (my $i=0; $i < $design_params->{'copies_per_plot'}; $i++) {
258 #print STDERR "Working on label num $i\n";
259 my $label_x = $design_params->{'left_margin'} + ($design_params->{'label_width'} + $design_params->{'horizontal_gap'}) * ($col_num-1);
260 my $label_y = $design_params->{'page_height'} - $design_params->{'top_margin'} - ($design_params->{'label_height'} + $design_params->{'vertical_gap'}) * ($row_num-1);
262 foreach my $element (@
$label_params) {
263 #print STDERR "Element Dumper\n" . Dumper($element);
264 my %element = %{$element};
265 my $elementx = $label_x + ( $element{'x'} / $conversion_factor );
266 my $elementy = $label_y - ( $element{'y'} / $conversion_factor );
268 my $filled_value = $element{'value'};
269 $filled_value =~ s/\{(.*?)\}/process_field($1,$key_number,\%design_info)/ge;
270 #print STDERR "Element ".$element{'type'}."_".$element{'size'}." filled value is ".$filled_value." and coords are $elementx and $elementy\n";
271 #print STDERR "Writing to the PDF . . .\n";
272 if ( $element{'type'} eq "Code128" || $element{'type'} eq "QRCode" ) {
274 if ( $element{'type'} eq "Code128" ) {
276 my $barcode_object = Barcode
::Code128
->new();
278 my ($png_location, $png_uri) = $c->tempfile( TEMPLATE
=> [ 'barcode', 'bc-XXXXX'], SUFFIX
=>'.png');
279 open(PNG
, ">", $png_location) or die "Can't write $png_location: $!\n";
282 $barcode_object->option("scale", $element{'size'}, "font_align", "center", "padding", 5, "show_text", 0);
283 $barcode_object->barcode($filled_value);
284 my $barcode = $barcode_object->gd_image();
285 print PNG
$barcode->png();
288 my $image = $pdf->image_png($png_location);
289 my $height = $element{'height'} / $conversion_factor ; # scale to 72 pts per inch
290 my $width = $element{'width'} / $conversion_factor ; # scale to 72 pts per inch
291 my $elementy = $elementy - ($height/2); # adjust for img position sarting at bottom
292 my $elementx = $elementx - ($width/2);
293 #print STDERR 'adding Code 128 params $image, $elementx, $elementy, $width, $height with: '."$image, $elementx, $elementy, $width, $height\n";
294 $gfx->image($image, $elementx, $elementy, $width, $height);
299 my ($jpeg_location, $jpeg_uri) = $c->tempfile( TEMPLATE
=> [ 'barcode', 'bc-XXXXX'], SUFFIX
=>'.jpg');
300 my $barcode_generator = CXGN
::QRcode
->new(
301 text
=> $filled_value,
302 size
=> $element{'size'},
307 my $barcode_file = $barcode_generator->get_barcode_file($jpeg_location);
309 my $image = $pdf->image_jpeg($jpeg_location);
310 my $height = $element{'height'} / $conversion_factor ; # scale to 72 pts per inch
311 my $width = $element{'width'} / $conversion_factor ; # scale to 72 pts per inch
312 my $elementy = $elementy - ($height/2); # adjust for img position sarting at bottom
313 my $elementx = $elementx - ($width/2);
314 $gfx->image($image, $elementx, $elementy, $width, $height);
320 my $font = $pdf->corefont($element{'font'}); # Add a built-in font to the PDF
321 # Add text to the page
322 my $adjusted_size = $element{'size'} / $conversion_factor; # scale to 72 pts per inch
323 $text->font($font, $adjusted_size);
324 my $height = $element{'height'} / $conversion_factor ; # scale to 72 pts per inch
325 my $elementy = $elementy - ($height/4); # adjust for img position starting at bottom
326 $text->translate($elementx, $elementy);
327 $text->text_center($filled_value);
331 if ($col_num < $design_params->{'number_of_columns'}) { #next column
333 } else { #new row, reset col num
338 if ($row_num > $design_params->{'number_of_rows'}) { #create new page and reset row and col num
339 $pdf->finishobjects($page, $gfx, $text); #flush the page to save memory on big PDFs
340 $page = $pdf->page();
341 $text = $page->text();
343 $page->mediabox($design_params->{'page_width'}, $design_params->{'page_height'});
351 print STDERR
"Saving the PDF . . .\n";
354 } elsif ($download_type eq 'zpl') {
356 print STDERR
"Generating zpl . . .\n";
357 my $zpl_obj = CXGN
::ZPL
->new(
358 print_width
=> $design_params->{'label_width'} * $conversion_factor,
359 label_length
=> $design_params->{'label_height'} * $conversion_factor
361 $zpl_obj->start_sequence();
362 $zpl_obj->label_format();
363 foreach my $element (@
$label_params) {
364 my $x = $element->{'x'} - ($element->{'width'}/2);
365 my $y = $element->{'y'} - ($element->{'height'}/2);
366 $zpl_obj->new_element($element->{'type'}, $x, $y, $element->{'size'}, $element->{'value'});
368 $zpl_obj->end_sequence();
369 my $zpl_template = $zpl_obj->render();
370 foreach my $key ( sort { versioncmp
( $design{$a}{$sort_order} , $design{$b}{$sort_order} ) or $a <=> $b } keys %design) {
371 # print STDERR "Design key is $key\n";
372 my %design_info = %{$design{$key}};
373 $design_info{'trial_name'} = $trial_name;
374 $design_info{'year'} = $year;
375 $design_info{'pedigree_string'} = $pedigree_strings->{$design_info{'accession_name'}};
377 my $zpl = $zpl_template;
378 $zpl =~ s/\{(.*?)\}/process_field($1,$key_number,\%design_info)/ge;
379 for (my $i=0; $i < $design_params->{'copies_per_plot'}; $i++) {
387 print STDERR
"Returning with filename . . .\n";
388 $c->stash->{rest
} = {
389 filename
=> $urlencode{$filename},
390 filepath
=> $c->config->{basepath
}."/".$filename
397 my $key_number = shift;
398 my $design_info = shift;
399 my %design_info = %{$design_info};
400 #print STDERR "Field is $field\n";
401 if ($field =~ m/Number:/) {
402 our ($placeholder, $start_num, $increment) = split ':', $field;
403 my $length = length($start_num);
404 #print STDERR "Increment is $increment\nKey Number is $key_number\n";
405 my $custom_num = $start_num + ($increment * $key_number);
406 return sprintf("%0${length}d", $custom_num);
408 return $design_info{$field};
414 my $key_number = shift;
415 my $design_info = shift;
416 my %design_info = %{$design_info};
417 #print STDERR "Field is $field\n";
418 if ($field =~ m/Number:/) {
419 our ($placeholder, $start_num, $increment) = split ':', $field;
420 my $length = length($start_num);
421 #print STDERR "Increment is $increment\nKey Number is $key_number\n";
422 my $custom_num = $start_num + ($increment * $key_number);
423 return sprintf("%0${length}d", $custom_num);
425 return $design_info{$field};
429 sub get_all_pedigrees
{
432 my %design = %{$design};
434 # collect all unique accession ids for pedigree retrieval
435 my %accession_id_hash;
436 foreach my $key (keys %design) {
437 $accession_id_hash{$design{$key}{'accession_id'}} = $design{$key}{'accession_name'};
439 my @accession_ids = keys %accession_id_hash;
441 # retrieve pedigree info using batch download (fastest method), then extract pedigree strings from download rows.
442 my $stock = CXGN
::Stock
->new ( schema
=> $schema);
443 my $pedigree_rows = $stock->get_pedigree_rows(\
@accession_ids, 'parents_only');
444 my %pedigree_strings;
445 foreach my $row (@
$pedigree_rows) {
446 my ($progeny, $female_parent, $male_parent, $cross_type) = split "\t", $row;
447 my $string = join ('/', $female_parent ?
$female_parent : 'NA', $male_parent ?
$male_parent : 'NA');
448 $pedigree_strings{$progeny} = $string;
450 return \
%pedigree_strings;
456 my $data_type = shift;
459 my ($trial_id, $plot_design, $plant_design);
461 # print STDERR "Data type is $data_type and value is $value\n";
463 if ($data_type =~ m/Plant List/) {
465 elsif ($data_type =~ m/Plot List/) {
466 # get items from list, get trial id from plot id. Or, get plot data one by one
467 my $plot_data = SGN
::Controller
::AJAX
::List
->retrieve_list($c, $value);
468 my @plot_list = map { $_->[1] } @
$plot_data;
469 my $t = CXGN
::List
::Transform
->new();
470 my $acc_t = $t->can_transform("plots", "plot_ids");
471 my $plot_id_hash = $t->transform($schema, $acc_t, \
@plot_list);
472 my @plot_ids = @
{$plot_id_hash->{transform
}};
473 my $trial_rs = $schema->resultset("NaturalDiversity::NdExperimentStock")->search({
474 stock_id
=> { -in => \
@plot_ids }
475 })->search_related('nd_experiment')->search_related('nd_experiment_projects');
477 while (my $row = $trial_rs->next()) {
478 print STDERR
"Looking at id ".$row->project_id()."\n";
479 my $id = $row->project_id();
482 $num_trials = scalar keys %trials;
483 print STDERR
"Count is $num_trials\n";
484 $trial_id = $trial_rs->first->project_id();
485 my $full_design = CXGN
::Trial
::TrialLayout
->new({schema
=> $schema, trial_id
=> $trial_id, experiment_type
=>'field_layout' })->get_design();
486 print STDERR
"Full Design is: ".Dumper
($full_design);
487 # reduce design hash, removing plots that aren't in list
488 my %full_design = %{$full_design};
490 foreach my $i (0 .. $#plot_ids) {
491 foreach my $key (keys %full_design) {
492 if ($full_design{$key}->{'plot_id'} eq $plot_ids[$i]) {
493 print STDERR
"Plot name is ".$full_design{$key}->{'plot_name'}."\n";
494 $plot_design->{$key} = $full_design{$key};
495 $plot_design->{$key}->{'list_order'} = $i;
501 elsif ($data_type =~ m/Genotyping Trial/) {
503 $plot_design = CXGN
::Trial
::TrialLayout
->new({schema
=> $schema, trial_id
=> $trial_id, experiment_type
=>'field_layout' })->get_design();
505 elsif ($data_type =~ m/Field Trials/) {
507 $plot_design = CXGN
::Trial
::TrialLayout
->new({schema
=> $schema, trial_id
=> $trial_id, experiment_type
=>'field_layout' })->get_design();
508 my %plot_design = %{$plot_design};
509 my @plot_ids = keys %plot_design;
510 my $plant_ids = $plot_design{$plot_ids[0]}->{'plant_ids'};
511 my @plant_ids = @
{$plant_ids};
512 #check if there are plant ids
514 if (scalar(@plant_ids) > 0) {
515 foreach my $plot_id (keys %plot_design) {
516 #print STDERR "Working on key $plot_id and value: " . Dumper($design{$plot_id});
517 my $plant_ids = $plot_design{$plot_id}->{'plant_ids'};
518 my @plant_ids = @
{$plant_ids};
519 my $plant_names = $plot_design{$plot_id}->{'plant_names'};
520 my @plant_names = @
{$plant_names};
521 for (my $i=0; $i < scalar(@plant_ids); $i++) {
522 my $plant_id = $plant_ids[$i];
523 my $plant_name = $plant_names[$i];
524 #print STDERR "plant id is $plant_id and name is $plant_name\n";
525 foreach my $property (keys %{$plot_design{$plot_id}}) { $plant_design{$plant_id}->{$property} = $plot_design{$plot_id}->{$property}; }
526 $plant_design{$plant_id}->{'plant_id'} = $plant_id;
527 $plant_design{$plant_id}->{'plant_name'} = $plant_name;
528 #print STDERR "Added key " . $plant_id . " and value: " . Dumper($plant_design{$plant_id});
531 $plant_design = \
%plant_design;
534 # elsif ($data_type =~ m/Field Trial Plots/) {
535 # $trial_id = $value;
536 # $design = CXGN::Trial::TrialLayout->new({schema => $schema, trial_id => $trial_id, experiment_type=>'field_layout' })->get_design();
538 return ($num_trials, $trial_id, $plot_design, $plant_design);