5 SGN::Image - SGN Images
9 This class provides database access and store functions as well as
10 image upload and certain image manipulation functions, such as image
11 file type conversion and image resizing; and functions to associate
12 tags with the image. Note that this was forked off from the insitu
13 image object. The insitu database needs to be re-factored to use this
16 The philosophy of the image object has changed slightly from the
17 Insitu::Image object. It now stores the images in a directory
18 specified by the conf object parameter "static_datasets_dir" plus
19 the conf parameter "image_dir" plus the directory name "image_files"
20 for the production server and the directory "image_files_sandbox" for
21 the test server. In those directories, it creates a subdirectory for
22 each image, with the subdirectory name being the corresponding image
23 id. In that directory are then several files, the originial image file
24 with the orignial name, the converted image into jpg in the standard
25 image sizes: large, medium, small and thumbnail with the names:
26 large.jpg, medium.jpg, small.jpg and thumbnail.jpg . All other
27 metadata about the image is stored in the database.
31 Lukas Mueller (lam87@cornell.edu)
32 Naama Menda (nm249@cornell.edu)
35 =head1 MEMBER FUNCTIONS
37 The following functions are provided in this class:
44 use File
::Path
'make_path';
45 use File
::Temp qw
/ tempfile tempdir /;
46 use File
::Copy qw
/ copy move /;
47 use File
::Basename qw
/ basename /;
49 use CXGN
::DB
::Connection
;
51 use CXGN
::Metadata
::Metadbdata
;
53 use CatalystX
::GlobalContext
'$c';
55 use base qw
| CXGN
::Image
|;
59 Usage: my $image = SGN::Image->new($dbh)
62 Args: a database handle, optional identifier
63 Side Effects: an empty object is returned.
64 a database connection is established.
70 my ( $class, $dbh, $image_id, $context ) = @_;
73 my $self = $class->SUPER::new
(
74 dbh
=> $dbh || $context->dbc->dbh,
75 image_id
=> $image_id,
76 image_dir
=> $context->get_conf('static_datasets_path')."/".$context->get_conf('image_dir'),
79 $self->config( $context );
88 Usage: $self->get_image_url($size)
89 Desc: get the url for the image with a given size
90 Ret: a url for the image
91 Args: size (large, medium, small, thumbnail, original)
101 if( $self->config->test_mode && ! -e
$self->get_filename($size) ) {
102 # for performance, only try to stat the file if running in
103 # test mode. doing lots of file stats over NFS can actually be
105 return '/img/image_temporarily_unavailable.png';
108 my $url = join '/', (
110 $self->config()->get_conf('static_datasets_url'),
111 $self->config()->get_conf('image_dir'),
112 $self->get_filename($size, 'partial'),
120 Usage: $image->process_image($filename, "stock", 234);
121 Desc: creates the image and associates it to the type and type_id
123 Args: filename, type (experiment, stock, fish, locus, organism) , type_id
124 Side Effects: Calls the relevant $image->associate_$type function
131 my ($filename, $type, $type_id) = @_;
133 $self->SUPER::process_image
($filename);
135 if ( $type eq "experiment" ) {
136 #print STDERR "Associating experiment $type_id...\n";
137 $self->associate_experiment($type_id);
139 elsif ( $type eq "stock" ) {
140 #print STDERR "Associating stock $type_id...\n";
141 $self->associate_stock($type_id);
143 elsif ( $type eq "fish" ) {
144 #print STDERR "Associating to fish experiment $type_id\n";
145 $self->associate_fish_result($type_id);
147 elsif ( $type eq "locus" ) {
148 #print STDERR "Associating to locus $type_id\n";
149 $self->associate_locus($type_id);
151 elsif ( $type eq "organism" ) {
152 $self->associate_organism($type_id);
154 elsif ( $type eq "cvterm" ) {
155 $self->associate_cvterm($type_id);
158 elsif ( $type eq "test") {
159 # need to return something to make this function happy
164 warn "type $type is not recognized as one of the legal types. Not associating image with any object. Please check if your loading script links the image with an sgn object! \n";
169 =head2 config, context, _app
171 Get the Catalyst context object we are running with.
176 my ($self,$obj) = @_;
178 $self->{configuration_object
} = $obj if $obj;
180 return $self->{configuration_object
};
185 =head2 get_img_src_tag
190 Args: "large" | "medium" | "small" | "thumbnail" | "original" | "tiny"
197 sub get_img_src_tag
{
200 my $url = $self->get_image_url($size);
201 my $name = $self->get_name();
202 if ( $size && $size eq "original" ) {
204 my $static = $self->config()->get_conf("static_datasets_url");
209 . "\"><img src=\"$static/images/download_icon.png\" border=\"0\" alt=\""
213 elsif ( $size && $size eq "tiny" ) {
217 . "\" width=\"20\" height=\"15\" border=\"0\" alt=\""
225 . "\" border=\"0\" alt=\""
231 =head2 get_temp_filename
242 sub get_temp_filename
{
244 return $self->{temp_filename
};
248 =head2 set_temp_filename
259 sub set_temp_filename
{
261 $self->{temp_filename
} = shift;
264 =head2 apache_upload_image
268 Usage: my $temp_file_name = $image->apache_upload_image($apache_upload_object);
270 Ret: the name of the intermediate tempfile that can be
271 used to access down the road.
272 Args: an apache upload object
273 Side Effects: generates an intermediate temp file from an apache request
274 that can be handled more easily. Adds the remote IP addr to the
275 filename so that different uploaders don\'t clobber but
276 allows only one upload per remote addr at a time.
277 Errors: change 11/30/07 - removes temp file if already exists
278 # returns -1 if the intermediate temp file already exists.
279 # this probably means that the submission button was hit twice
280 # and that an upload is already in progress.
285 sub apache_upload_image
{
288 ### deanx jan 03 2007
289 # Adjust File name if using Windows IE - it sends whole paht; drive letter, path, and filename
291 if ( $ENV{HTTP_USER_AGENT
} =~ /msie/i ) {
292 my ( $directory, $filename ) = $upload->filename =~ m/(.*\\)(.*)$/;
293 $upload_filename = $filename;
296 $upload_filename = $upload->filename;
299 my $upload_fh = $upload->fh;
302 $self->config()->get_conf("basepath") . "/"
303 . $self->config()->get_conf("tempfiles_subdir")
305 . $ENV{REMOTE_ADDR
} . "-"
308 my $ret_temp_file = $self->upload_image($temp_file, $upload_fh);
309 return $ret_temp_file;
313 sub upload_fieldbook_zipfile
{
315 my $image_zip = shift;
317 my $c = $self->config();
318 my $schema = $c->dbic_schema("Bio::Chado::Schema");
319 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
320 my $dbh = $schema->storage->dbh;
321 my $archived_zip = CXGN
::ZipFile
->new(archived_zipfile_path
=>$image_zip);
322 my $file_members = $archived_zip->file_members();
323 my $plot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'plot', 'stock_type')->cvterm_id();
324 my $plant_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'plant', 'stock_type')->cvterm_id();
327 foreach (@
$file_members) {
328 my $image = SGN
::Image
->new( $dbh, undef, $c );
329 #print STDERR Dumper $_;
330 my $img_name = substr($_->fileName(), 0, -24);
331 $img_name =~ s/^.*photos\///;
332 my $stock = $schema->resultset("Stock::Stock")->find( { uniquename
=> $img_name, 'me.type_id' => [$plot_cvterm_id, $plant_cvterm_id] } );
333 my $stock_id = $stock->stock_id;
335 my $temp_file = $image->upload_zipfile_images($_);
337 #Check if image already stored in database
338 my $md5checksum = $image->calculate_md5sum($temp_file);
339 #print STDERR "MD5: $md5checksum\n";
340 my $md_image = $metadata_schema->resultset("MdImage")->search({md5sum
=>$md5checksum})->count();
341 #print STDERR "Count: $md_image\n";
343 $error_status .= "Image $temp_file has already been added to the database and will not be added again.<br/><br/>";
345 $image->set_sp_person_id($user_id);
346 my $ret = $image->process_image($temp_file, 'stock', $stock_id);
348 $error_status .= "Image processing for $temp_file did not work. Image not associated to stock_id $stock_id.<br/><br/>";
352 return $error_status;
355 sub upload_zipfile_images
{
357 my $file_member = shift;
359 my $filename = $file_member->fileName();
361 my $zipfile_image_temp_path = $self->config()->get_conf("basepath") . $self->config()->get_conf("tempfiles_subdir") . "/temp_images/photos";
362 make_path
($zipfile_image_temp_path);
364 $self->config()->get_conf("basepath")
365 . $self->config()->get_conf("tempfiles_subdir")
368 system("chmod 775 $zipfile_image_temp_path");
369 $file_member->extractToFileNamed($temp_file);
370 print STDERR
"Temp Image: ".$temp_file."\n";
377 my $temp_file = shift;
378 my $upload_fh = shift;
380 ### 11/30/07 - change this so it removes existing file
382 # # only copy file if it doesn't already exist
384 if ( -e
$temp_file ) {
388 open UPLOADFILE
, '>', $temp_file or die "Could not write to $temp_file: $!\n";
391 while (<$upload_fh>) {
393 #warn "Read another chunk...\n";
397 warn "Done uploading.\n";
402 =head2 associate_stock
404 Usage: $image->associate_stock($stock_id);
405 Desc: associate a Bio::Chado::Schema::Result::Stock::Stock object with this image
406 Ret: a database id (stock_image_id)
413 sub associate_stock
{
415 my $stock_id = shift;
417 my $username = $self->config->can('user_exists') ?
$self->config->user->get_object->get_username : $self->config->username;
419 my $metadata_schema = $self->config->dbic_schema('CXGN::Metadata::Schema');
420 my $metadata = CXGN
::Metadata
::Metadbdata
->new($metadata_schema, $username);
421 my $metadata_id = $metadata->store()->get_metadata_id();
423 my $q = "INSERT INTO phenome.stock_image (stock_id, image_id, metadata_id) VALUES (?,?,?) RETURNING stock_image_id";
424 my $sth = $self->get_dbh->prepare($q);
425 $sth->execute($stock_id, $self->get_image_id, $metadata_id);
426 my ($stock_image_id) = $sth->fetchrow_array;
427 return $stock_image_id;
435 Usage: $image->get_stocks
436 Desc: find all stock objects linked with this image
437 Ret: a list of Bio::Chado::Schema::Result::Stock::Stock
444 my $schema = $self->config->dbic_schema('Bio::Chado::Schema' , 'sgn_chado');
446 my $q = "SELECT stock_id FROM phenome.stock_image WHERE image_id = ? ";
447 my $sth = $self->get_dbh->prepare($q);
448 $sth->execute($self->get_image_id);
449 while (my ($stock_id) = $sth->fetchrow_array) {
450 my $stock = $schema->resultset("Stock::Stock")->find( { stock_id
=> $stock_id } ) ;
451 push @stocks, $stock;
455 =head2 associate_individual
457 Usage: DEPRECATED, Individual table is not used any more . Please use stock instead
458 $image->associate_individual($individual_id)
459 Desc: associate a CXGN::Phenome::Individual with this image
460 Ret: a database id (individual_image)
467 sub associate_individual
{
469 my $individual_id = shift;
470 warn "DEPRECATED. Individual table is not used any more . Please use stock instead";
471 my $query = "INSERT INTO phenome.individual_image
472 (individual_id, image_id) VALUES (?, ?)";
473 my $sth = $self->get_dbh()->prepare($query);
474 $sth->execute($individual_id, $self->get_image_id());
476 my $id= $self->get_currval("phenome.individual_image_individual_image_id_seq");
481 =head2 get_individuals
483 Usage: DEPRECATED. Use the stock table .
484 $self->get_individuals()
485 Desc: find associated individuals with the image
486 Ret: list of 'Individual' objects
493 sub get_individuals
{
495 warn "DEPRECATED. Individual table is not used any more . Please use stock instead";
496 my $query = "SELECT individual_id FROM phenome.individual_image WHERE individual_image.image_id=?";
497 my $sth = $self->get_dbh()->prepare($query);
498 $sth->execute($self->get_image_id());
500 while (my ($individual_id) = $sth->fetchrow_array()) {
501 my $i = CXGN
::Phenome
::Individual
->new($self->get_dbh(), $individual_id);
502 if ( $i->get_individual_id() ) { push @individuals, $i; } #obsolete individuals should be ignored!
508 =head2 associate_experiment
510 Usage: $image->associate_experiment($experiment_id);
511 Desc: associate and image with and insitu experiment
519 sub associate_experiment
{
521 my $experiment_id = shift;
522 my $query = "INSERT INTO insitu.experiment_image
523 (image_id, experiment_id)
525 my $sth = $self->get_dbh()->prepare($query);
526 $sth->execute($self->get_image_id(), $experiment_id);
527 my $id= $self->get_currval("insitu.experiment_image_experiment_image_id_seq");
532 =head2 get_experiments
536 Ret: a list of CXGN::Insitu::Experiment objects associated
544 sub get_experiments
{
546 my $query = "SELECT experiment_id FROM insitu.experiment_image
548 my $sth = $self->get_dbh()->prepare($query);
549 $sth->execute($self->get_image_id());
550 my @experiments = ();
551 while (my ($experiment_id) = $sth->fetchrow_array()) {
552 push @experiments, CXGN
::Insitu
::Experiment
->new($self->get_dbh(), $experiment_id);
557 =head2 associate_fish_result
559 Usage: $image->associate_fish_result($fish_result_id)
560 Desc: associate a CXGN::Phenome::Individual with this image
568 sub associate_fish_result
{
570 my $fish_result_id = shift;
571 my $query = "INSERT INTO sgn.fish_result_image
572 (fish_result_id, image_id) VALUES (?, ?)";
573 my $sth = $self->get_dbh()->prepare($query);
574 $sth->execute($fish_result_id, $self->get_image_id());
575 my $id= $self->get_currval("sgn.fish_result_image_fish_result_image_id_seq");
579 =head2 get_fish_result_clone_ids
581 Usage: my @clone_ids = $image->get_fish_result_clones();
582 Desc: because fish results are associated with genomic
583 clones, this function returns the genomic clone ids
584 that are associated through the fish results to
585 this image. The clone ids can be used to construct
586 links to the BAC detail page.
587 Ret: A list of clone_ids
594 sub get_fish_result_clone_ids
{
596 my $query = "SELECT distinct(clone_id) FROM sgn.fish_result_image join sgn.fish_result using(fish_result_id) WHERE fish_result_image.image_id=?";
597 my $sth = $self->get_dbh()->prepare($query);
598 $sth->execute($self->get_image_id());
599 my @fish_result_clone_ids = ();
600 while (my ($fish_result_clone_id) = $sth->fetchrow_array()) {
601 push @fish_result_clone_ids, $fish_result_clone_id;
603 return @fish_result_clone_ids;
606 =head2 get_associated_objects
616 sub get_associated_objects
{
618 my @associations = ();
619 my @stocks=$self->get_stocks();
620 foreach my $stock (@stocks) {
621 my $stock_id = $stock->stock_id();
622 my $stock_name = $stock->name();
623 push @associations, [ "stock", $stock_id, $stock_name ];
626 foreach my $exp ($self->get_experiments()) {
627 my $experiment_id = $exp->get_experiment_id();
628 my $experiment_name = $exp->get_name();
630 push @associations, [ "experiment", $experiment_id, $experiment_name ];
632 #print "<a href=\"/insitu/detail/experiment.pl?experiment_id=$experiment_id&action=view\">".($exp->get_name())."</a>";
635 foreach my $fish_result_clone_id ($self->get_fish_result_clone_ids()) {
636 push @associations, [ "fished_clone", $fish_result_clone_id ];
638 foreach my $locus ($self->get_loci() ) {
639 push @associations, ["locus", $locus->get_locus_id(), $locus->get_locus_name];
641 foreach my $o ($self->get_organisms ) {
642 push @associations, ["organism", $o->organism_id, $o->species];
645 foreach my $cvterm ( $self->get_cvterms ) {
646 push @associations, ["cvterm" , $cvterm->cvterm_id, $cvterm->name];
648 return @associations;
651 =head2 associate_locus
653 Usage: $image->associate_locus($locus_id)
654 Desc: associate a locus with this image
662 sub associate_locus
{
664 my $locus_id = shift;
665 my $sp_person_id= $self->get_sp_person_id();
666 my $query = "INSERT INTO phenome.locus_image
671 my $sth = $self->get_dbh()->prepare($query);
675 $self->get_image_id()
678 my $locus_image_id= $self->get_currval("phenome.locus_image_locus_image_id_seq");
679 return $locus_image_id;
685 Usage: $self->get_loci
686 Desc: find the locus objects asociated with this image
687 Ret: a list of locus objects
696 my $query = "SELECT locus_id FROM phenome.locus_image WHERE locus_image.obsolete = 'f' and locus_image.image_id=?";
697 my $sth = $self->get_dbh()->prepare($query);
698 $sth->execute($self->get_image_id());
701 while (my ($locus_id) = $sth->fetchrow_array()) {
702 $locus = CXGN
::Phenome
::Locus
->new($self->get_dbh(), $locus_id);
709 =head2 associate_organism
711 Usage: $image->associate_organism($organism_id)
720 sub associate_organism
{
722 my $organism_id = shift;
723 my $sp_person_id= $self->get_sp_person_id();
724 my $query = "INSERT INTO metadata.md_image_organism
728 VALUES (?, ?, ?) RETURNING md_image_organism_id";
729 my $sth = $self->get_dbh()->prepare($query);
735 my ($image_organism_id) = $sth->fetchrow_array;
736 return $image_organism_id;
741 Usage: $self->get_organisms
742 Desc: find the organism objects asociated with this image
743 Ret: a list of BCS Organism objects
752 my $schema = $self->config->dbic_schema('Bio::Chado::Schema' , 'sgn_chado');
753 my $query = "SELECT organism_id FROM metadata.md_image_organism WHERE md_image_organism.obsolete != 't' and md_image_organism.image_id=?";
754 my $sth = $self->get_dbh()->prepare($query);
755 $sth->execute($self->get_image_id());
757 while (my ($o_id) = $sth->fetchrow_array ) {
758 push @organisms, $schema->resultset("Organism::Organism")->find(
759 { organism_id
=> $o_id } );
765 =head2 get_associated_object_links
771 Description: gets the associated objects as links in tabular format
775 sub get_associated_object_links
{
778 foreach my $assoc ($self->get_associated_objects()) {
780 if ($assoc->[0] eq "stock") {
781 $s .= "<a href=\"/stock/$assoc->[1]/view\">Stock name: $assoc->[2].</a>";
784 if ($assoc->[0] eq "experiment") {
785 $s .= "<a href=\"/insitu/detail/experiment.pl?experiment_id=$assoc->[1]&action=view\">insitu experiment $assoc->[2]</a>";
788 if ($assoc->[0] eq "fished_clone") {
789 $s .= qq { <a href
="/maps/physical/clone_info.pl?id=$assoc->[1]">FISHed clone id
:$assoc->[1]</a
> };
791 if ($assoc->[0] eq "locus" ) {
792 $s .= qq { <a href
="/phenome/locus_display.pl?locus_id=$assoc->[1]">Locus name
:$assoc->[2]</a
> };
794 if ($assoc->[0] eq "organism" ) {
795 $s .= qq { <a href
="/organism/$assoc->[1]/view/">Organism name
:$assoc->[2]</a
> };
797 if ($assoc->[0] eq "cvterm" ) {
798 $s .= qq { <a href
="/cvterm/$assoc->[1]/view/">Cvterm
: $assoc->[2]</a
> };
805 =head2 associate_cvterm
807 Usage: $image->associate_cvterm($cvterm_id)
808 Desc: link uploaded image with a cvterm
809 Ret: database ID md_image_cvterm_id
811 Side Effects: Insert database row
816 sub associate_cvterm
{
818 my $cvterm_id = shift;
819 my $sp_person_id= $self->get_sp_person_id();
820 my $query = "INSERT INTO metadata.md_image_cvterm
824 VALUES (?, ?, ?) RETURNING md_image_cvterm_id";
825 my $sth = $self->get_dbh()->prepare($query);
831 my ($image_cvterm_id) = $sth->fetchrow_array;
832 return $image_cvterm_id;
837 Usage: $self->get_cvterms
838 Desc: find the cvterm objects asociated with this image
839 Ret: a list of BCS Cvterm objects
848 my $schema = $self->config->dbic_schema('Bio::Chado::Schema' , 'sgn_chado');
849 my $query = "SELECT cvterm_id FROM metadata.md_image_cvterm WHERE md_image_cvterm.obsolete != 't' and md_image_cvterm.image_id=?";
850 my $sth = $self->get_dbh()->prepare($query);
851 $sth->execute( $self->get_image_id() );
853 while (my ($cvterm_id) = $sth->fetchrow_array ) {
854 push @cvterms, $schema->resultset("Cv::Cvterm")->find(
855 { cvterm_id
=> $cvterm_id } );