4 CXGN::Image - a class for accessing the md_metadata.image table.
9 This class provides database access and store functions
10 and functions to associate tags with the image.
12 Image uploads are handled by the SGN::Image subclass.
14 The implementation how images are stored has been changed. Whereas the
15 images were stored in the image root dir keyed to the image_id, it is
16 now keyed to the md5sum of the original image, with the md5sum stemmed
17 into two byte directories. The constructor now takes a hash instead of
22 Lukas Mueller (lam87@cornell.edu)
23 Naama Menda (nm249@cornell.edu)
29 =head1 MEMBER FUNCTIONS
31 The following functions are provided in this class:
40 use Carp qw
/ cluck carp confess /;
43 use File
::Path
'make_path';
45 use File
::Basename qw
| basename dirname
|;
46 use File
::Temp
'tempdir';
47 use File
::Copy qw
| copy move
|;
52 use base qw
| CXGN
::DB
::ModifiableI
|;
55 # some pseudo constant definitions
57 our $LARGE_IMAGE_SIZE = 800;
58 our $MEDIUM_IMAGE_SIZE = 400;
59 our $SMALL_IMAGE_SIZE = 200;
60 our $THUMBNAIL_IMAGE_SIZE = 100;
65 Usage: my $image = CXGN::Image->new(dbh=>$dbh, image_id=>23423
66 image_dir => $image_dir)
69 Args: a hash of a database handle, optional identifier, and the
70 path to the root image_dir, with keys dbh, image_id and image_dir.
71 Side Effects: if an identifier is specified, the image object
72 will be populated from the database, otherwise
73 an empty object is returned.
74 Either way, a database connection is established.
83 my $self = $class->SUPER::new
($args{dbh
}, $args{image_id
});
85 unless( exists $args{dbh
} && exists $args{image_dir
} ) {
86 die "Required arguments: dbh, image_dir";
88 $self->set_image_dir($args{image_dir
});
89 $self->set_dbh($args{dbh
});
91 if( exists $args{image_id
} ) {
92 $self->set_image_id($args{image_id
});
93 $self->_fetch_image() if $args{image_id
};
100 my $query = "SELECT image_id,
109 FROM metadata.md_image
111 and obsolete != 't' ";
113 my $sth = $self->get_dbh()->prepare($query);
114 $sth->execute($self->get_image_id()) ;
116 my ( $image_id, $name, $description, $original_filename, $file_ext, $sp_person_id, $modified_date, $create_date, $md5sum) =
117 $sth->fetchrow_array();
120 $self->set_name($name);
121 $self->set_description($description);
122 $self->set_original_filename($original_filename);
123 $self->set_file_ext($file_ext);
124 $self->set_sp_person_id($sp_person_id);
125 $self->set_create_date($create_date);
126 $self->set_modification_date($modified_date);
127 $self->set_image_id($image_id);
128 $self->set_md5sum($md5sum);# we do this that if is an image that has been deleted,
129 # the object will get the NULL from the database and not
130 # the image_id that was fed into the object.
132 #print STDERR "Loaded image $image_id, $md5sum, $name, $original_filename, $file_ext\n";
138 Usage: $image->store()
139 Desc: will store the data in the image object to the database
140 if the image has an associated image_id, an update will
141 occur. if the image does not have an associated image_id,
142 an insert into the database will occur.
143 Ret: the image_id of the updated or inserted object.
145 Side Effects: database update or insert. Note that the image itself is
146 stored on the file system and that it is not affected by
147 this operation, unless the filename property is changed.
154 if ($self->get_image_id()) {
158 my $query = "UPDATE metadata.md_image SET
164 modified_date = now(),
166 WHERE md_image.image_id=?";
168 my $sth = $self->get_dbh()->prepare($query);
172 $self->get_description(),
173 $self->get_original_filename(),
174 $self->get_file_ext(),
175 $self->get_sp_person_id(),
177 $self->get_image_id(),
180 return $self->get_image_id();
186 my $query = "INSERT INTO metadata.md_image
195 VALUES (?, ?, ?, ?, ?, ?, now(), ?)";
196 my $sth = $self->get_dbh()->prepare($query);
199 $self->get_description(),
200 $self->get_original_filename(),
201 $self->get_file_ext(),
202 $self->get_sp_person_id(),
203 $self->get_obsolete(),
206 my $image_id= $self->get_currval("metadata.md_image_image_id_seq");
207 $self->set_image_id($image_id);
208 return $self->get_image_id();
214 Usage: $self->delete()
215 Desc: set the image status to obsolete='t'
216 Ret: true on success, false on failure
218 Side Effects: set to obsolete='t' in individual_image, and locus_image
225 if ($self->get_image_id()) {
226 my $query = "UPDATE metadata.md_image set obsolete='t' WHERE image_id=?";
227 my $sth = $self->get_dbh()->prepare($query);
228 $sth->execute($self->get_image_id());
229 $self->set_obsolete(1);
230 #delete image-individual associations
231 my $query2 = "UPDATE phenome.individual_image set obsolete = 't' WHERE image_id = ?";
232 my $sth2 = $self->get_dbh()->prepare($query2);
233 $sth2->execute($self->get_image_id() );
234 #deanx - nov.14 2007 remove from locus_image too
235 my $query3 = "UPDATE phenome.locus_image set obsolete = 't', modified_date = now() WHERE image_id = ?";
236 my $sth3 = $self->get_dbh()->prepare($query3);
237 $sth3->execute($self->get_image_id() );
241 warn("Image.pm: Trying to delete an image from the db that has not yet been stored.");
247 =head2 get_image_id, set_image_id
249 Usage: accessor for the image_id property.
253 Side Effects: if the image_id is not set, the store() function
254 will insert a new row and set the image_id to
255 the inserted row. Otherwise, store() performs
263 return $self->{image_id
};
269 my $id = $self->{image_id
} = shift;
271 !defined $id || $id =~ /^\d+$/
272 or confess
"invalid image_id '$id'";
277 =head2 get_name, set_name
280 Desc: gets/sets the name of the image
283 Side Effects: will be stored in db
290 return $self->{name
};
299 =head2 get_description, set_description
302 Desc: accessor for the description property
310 sub get_description
{
312 return $self->{description
};
316 sub set_description
{
318 $self->{description
}=shift;
322 =head2 get_original_filename, set_original_filename
325 Desc: accessor for the original_filename property
333 sub get_original_filename
{
335 return $self->{original_filename
};
338 sub set_original_filename
{
340 $self->{original_filename
}=shift;
343 =head2 get_file_ext, set_file_ext
346 Desc: accessor for the file_ext property
356 return $self->{file_ext
};
362 $self->{file_ext
}=shift;
365 =head2 accessors get_image_dir(), set_image_dir()
367 Usage: returns the image dir
378 return $self->{image_dir
};
383 $self->{image_dir
} = shift;
386 =head2 accessors get_locus_page_display_order(), set_locus_page_display_order()
391 Side Effects: Will modify the db asap (no store() required)
392 image object needs to have an image_id
393 setter will return error on failure
398 sub get_locus_page_display_order
{
400 my $locus_id = shift;
402 my @results = $self->get_locus_page_display_order_info($locus_id);
405 return $results[0]->[3];
411 sub get_locus_page_display_order_info
{
413 my $locus_id = shift;
415 my $q = "SELECT image_id, locus_image_id, locus_id, display_order FROM phenome.locus_image WHERE image_id = ? and locus_id=?";
416 my $h = $self->get_dbh()->prepare($q);
417 $h->execute($self->get_image_id(), $locus_id);
420 while (my ($image_id, $locus_id, $locus_image_id, $display_order) = $h->fetchrow_array()) {
421 push @results, [ $image_id, $locus_id, $locus_image_id, $display_order ];
425 print STDERR
"Multiple associations of image found ".Dumper
(\
@results)."\n";
431 sub set_locus_page_display_order
{
433 my $locus_id = shift;
434 my $display_order = shift;
436 if (!$self->get_image_id()) {
437 print STDERR
"Please store object first before making connections.\n";
440 # check if there is a display_order property for the image
442 my @results = $self->get_locus_page_display_order_info($locus_id);
446 print STDERR
"Multiple image locus association were found. Modifying only the first one.\n";
447 $locus_image_id = $results[0]->[2];
450 elsif (@results == 1) {
451 $locus_image_id = $results[0]->[2];
455 if ($locus_image_id) {
456 my $q = "UPDATE phenome.locus_image SET display_order=? WHERE locus_image_id=?";
457 my $h = $self->get_dbh()->prepare($q);
458 $h->execute($display_order, $locus_image_id);
462 my $q = "INSERT INTO phenome.locus_image (image_id, sp_person_id, locus_id, display_order) VALUES (?, ?, ?, ?)";
463 my $h = $self->get_dbh()->prepare($q);
464 $h->execute($self->get_image_id(), $self->get_sp_person_id(), $locus_id, $display_order);
469 return "ERROR: $@\n";
473 =head2 accessors get_stock_page_display_order(), set_stock_page_display_order()
478 Side Effects: Will modify the db asap (no store() required)
479 image object needs to have an image_id
480 setter will return error on failure
485 sub get_stock_page_display_order
{
487 my $stock_id = shift;
489 my @results = $self->get_stock_page_display_order_info($stock_id);
491 print STDERR Dumper
(\
@results);
493 return $results[0]->[3];
499 sub get_stock_page_display_order_info
{
501 my $stock_id = shift;
503 my $q = "SELECT stock_image_id, image_id, stock_id, display_order FROM phenome.stock_image WHERE image_id = ? and stock_id=?";
504 my $h = $self->get_dbh()->prepare($q);
505 $h->execute($self->get_image_id(), $stock_id);
508 while (my ($stock_image_id, $image_id, $stock_id, $display_order) = $h->fetchrow_array()) {
509 push @results, [ $stock_image_id, $image_id, $stock_id, $display_order ];
513 print STDERR
"Multiple associations of image found ".Dumper
(\
@results)."\n";
515 print STDERR Dumper
(\
@results);
520 sub set_stock_page_display_order
{
522 my $stock_id = shift;
523 my $display_order = shift;
525 if (!$self->get_image_id()) {
526 print STDERR
"Please store object first before making connections.\n";
529 # check if there is a display_order property for the image
531 my @results = $self->get_stock_page_display_order_info($stock_id);
535 print STDERR
"Multiple image locus association were found. Modifying only the first one.\n";
536 $stock_image_id = $results[0]->[0];
539 elsif (@results == 1) {
540 $stock_image_id = $results[0]->[0];
544 if ($stock_image_id) {
545 print STDERR
"Updating stock_image... (row $stock_image_id)\n";
546 my $q = "UPDATE phenome.stock_image SET display_order=? WHERE stock_image_id=?";
547 my $h = $self->get_dbh()->prepare($q);
548 $h->execute($display_order, $stock_image_id);
552 print STDERR
"Inserting into stock_image...\n";
553 my $q = "INSERT INTO phenome.stock_image (image_id, stock_id, display_order) VALUES (?, ?, ?)";
554 my $h = $self->get_dbh()->prepare($q);
555 $h->execute($self->get_image_id(), $stock_id, $display_order);
560 return "ERROR: $@\n";
565 sub get_display_order_info
{
568 my $q = "SELECT stock_image_id, image_id, stock_id, display_order, uniquename FROM phenome.stock_image join stock using(stock_id) WHERE image_id = ?";
569 my $h = $self->get_dbh()->prepare($q);
570 $h->execute($self->get_image_id());
573 while (my ($stock_image_id, $image_id, $stock_id, $display_order, $name) = $h->fetchrow_array()) {
574 push @info, { image_id
=> $image_id, type
=> "stock", id
=> $stock_id, display_order
=> $display_order, name
=> $name };
577 my $q = "SELECT locus_image_id, image_id, locus_id, display_order, locus_name FROM phenome.locus_image join locus using(locus_id) WHERE image_id = ?";
578 my $h = $self->get_dbh()->prepare($q);
579 $h->execute($self->get_image_id());
580 while (my ($stock_image_id, $image_id, $locus_id, $display_order, $name) = $h->fetchrow_array()) {
581 push @info, { image_id
=> $image_id, type
=> "locus", id
=> $locus_id, display_order
=> $display_order, name
=> $name };
583 print STDERR Dumper
(\
@info);
589 Usage: $return_code = $image -> process_image($filename);
590 Desc: processes the image that has been uploaded with the upload command.
591 Ret: the image id of the image in the database as a positive number,
592 error conditions as negative numbers.
593 Args: the filename of the file (complete path)
594 Side Effects: generates a new subdirectory in the image_dir for the image files,
595 copies the image file to a temp dir directory where it is processed
596 (resized thumnbnails and other views for the image). After that
597 is done, the image object is stored in the database, and the
598 image files are moved to the final location in the filesystem.
607 my $file_name = shift;
611 if ( my $id = $self->get_image_id() ) {
612 warn "process_image: The image object ($id) should already have an associated image. The old image will be overwritten with the new image provided!\n";
615 make_path
( $self->get_image_dir );
616 my ($processing_dir) =
617 File
::Temp
::tempdir
( "process_XXXXXX",
618 DIR
=> $self->get_image_dir() );
619 system("chmod 775 $processing_dir");
620 $self->set_processing_dir($processing_dir);
624 $processing_dir = $self->get_processing_dir();
626 # copy unmodified image to be fullsize image
628 #my ($basename, $directories, $file_ext) = File::Basename::fileparse($file_name, qr/\.(?!\.)(.*)$/); #filename may contain one additional dot
629 my $full_basename = basename
($file_name);
630 my $directories = dirname
($file_name);
632 my $basename; # without file_ext;
633 if ($full_basename =~ m/(.*)(\.(?!\.).*)$/) { # extension is what follows last .
639 #print STDERR "BASENAME: $basename, DIRECTORIES: $directories FILE_EXT $file_ext\n";
640 my $original_filename = $basename;
641 my $original_file_ext = $file_ext;
643 my $dest_name = $self->get_processing_dir() . "/" . $basename.$file_ext;
645 #print STDERR "Destination: ".$dest_name."\n";
646 File
::Copy
::copy
( $file_name, $dest_name )
647 || die "Can't copy file $file_name to $dest_name";
648 my $chmod = "chmod 664 '$dest_name'";
650 ### Multi Page Document Support
651 # deanx - nov. 16 2007
652 # PDF, PS, EPS documents are now supported by ImageMagick/Ghostscript
653 # A primary impact is these types can multipage. 'mogrify' produces
654 # one image per page, labelled filename-0.jpg, filename-1.jpg ...
655 # This code detects multipage documents and copies the first page for
656 # thumbnail processing
658 my @image_pages = `/usr/bin/identify "$dest_name"`;
660 if ( $#image_pages > 0 ) { # multipage, pdf, ps or eps
663 # note mogrify used since 'convert' will not correctly
664 # reformat (convert makes blank images) Convert and mogrify
665 # both dislike the format of our filenames intensely if
666 # ghostscript is envoked ... change filename to something
667 # beign like temp.<ext>
671 # note; mogrify will create files named basename-0.jpg, basename-1.jpg
672 my $mogrified_first_image = $processing_dir . "/temp-0.jpg";
673 my $tempname = $processing_dir . "/temp" . $file_ext;
674 $newname = $basename . ".jpg";
675 my $new_dest = $processing_dir . "/" . $newname;
677 # use temp name for mogrify/ghostscript
678 File
::Copy
::copy
( $dest_name, $tempname )
679 || die "Can't copy file $basename to $tempname";
681 if ( `mogrify -format jpg '$tempname'` ) {
682 die "Sorry, can't convert image $basename";
685 File
::Copy
::copy
( $mogrified_first_image, $new_dest )
686 || die "Can't copy file $mogrified_first_image to $newname";
689 $basename = $newname;
692 else { # appears to be a regular simple image
696 if ( ! `mogrify -format jpg '$dest_name'` ) {
697 # has no jpg extension
698 if ($file_ext !~ /jpg|jpeg/i) {
699 $newname = $original_filename . ".JPG"; # convert it to extention .JPG
701 # has no extension at all
703 $newname = $original_filename . ".JPG"; # add an extension .JPG
706 $newname = $original_filename.".JPG"; # add standard JPG file extension.
709 system( "convert", "$processing_dir/$basename$file_ext", "$processing_dir/$newname" );
710 $?
and die "Sorry, can't convert image $basename$file_ext to $newname";
712 $original_filename = $newname;
713 $basename = $newname;
718 $self->copy_image_resize(
719 "$processing_dir/$basename",
720 $self->get_processing_dir() . "/large.jpg",
721 $self->get_image_size("large")
724 # create midsize images
725 $self->copy_image_resize(
726 "$processing_dir/$basename",
727 $self->get_processing_dir() . "/medium.jpg",
728 $self->get_image_size("medium")
732 $self->copy_image_resize(
733 "$processing_dir/$basename",
734 $self->get_processing_dir() . "/small.jpg",
735 $self->get_image_size("small")
739 $self->copy_image_resize(
740 "$processing_dir/$basename",
741 $self->get_processing_dir() . "/thumbnail.jpg",
742 $self->get_image_size("thumbnail")
745 # enter preliminary image data into database
747 if ( $original_filename =~ /(.*)(\.\S{1,4})$/ ) {
748 $original_filename = $1;
752 $self->set_original_filename($original_filename);
753 $self->set_file_ext($file_ext); # this is the original file
755 # start transaction, store the image object, and associate it to
756 # the given type and type_id.
758 # move the image into the md5sum subdirectory
760 my $original_file_path = $self->get_processing_dir()."/".$self->get_original_filename().$self->get_file_ext();
762 my $md5sum = $self->calculate_md5sum($original_file_path);
763 print STDERR
"MD5SUM NOW: $md5sum\n";
765 # check if the image already exists in the database.
766 # For an image to exist, the md5checksum must exist in the metadata.md_image
767 # table, and the file must exist on disk.
769 # check if this md5sum already exists in the database
771 my $image_id = CXGN
::Image
->is_duplicate($self->get_dbh(), $original_file_path);
776 print STDERR
"An image with an identical md5sum ($md5sum) has already been uploaded with the image id $image_id\n";
777 $message = "Duplicate image $image_id\n";
778 $self->set_image_id($image_id);
784 $self->set_md5sum($md5sum);
786 $self->finalize_location($processing_dir);
788 $image_id = $self->store();
791 return ($image_id, $message);
801 Desc: creates the directory structure for image from
802 image_dir onwards (a split md5sum)
812 my $image_sub_path = $self->image_subpath();
814 my $path = File
::Spec
->catdir( $self->get_image_dir(), $image_sub_path );
815 if (my $dirs = make_path
($path) ) {
816 #print STDERR "Created $dirs Dirs (should be 4)\n";
821 =head2 finalize_location
826 Args: the source location as a path to a dir
832 sub finalize_location
{
834 my $processing_dir = shift;
836 my $image_dir = File
::Spec
->catdir( $self->get_image_dir, $self->image_subpath );
837 foreach my $f (glob($processing_dir."/*")) {
839 File
::Copy
::move
( $f, $image_dir )
840 || die "Couldn't move temp dir to image dir ($f, $image_dir)";
841 #print STDERR "Moved image file $f to final location $image_dir...\n";
845 rmdir $processing_dir;
853 my $source_dir = shift;
855 my $image_dir = $self->get_image_dir() ."/".$self->image_subpath();
856 foreach my $f (glob($source_dir."/*")) {
858 print STDERR
"$f does not exist... moving on...\n";
861 File
::Copy
::copy
( "$f", "$image_dir/" )
862 || die "Couldn't move temp dir to image dir ($f, $image_dir). $!";
863 #print STDERR "Moved image file $f to final location $image_dir...\n";
872 Usage: $image->image_subpath
873 Desc: returns the image subpath, which is a md5sum on an image file,
874 divided into 16 directory levels at 2 bytes length each.
875 Ret: path part in which to store the various sizes of this image
876 under the image root dir, something like 'ab/cd/ef/01/ab1fab1fab1fab1fab1fab1f'
885 my $md5sum = $self->get_md5sum;
887 # if the image has no md5sum, either from the database or for
888 # some other reason, warn copiously about it but don't die
889 cluck
'cannot calculate image_subpath, no md5sum set for image_id '.$self->get_image_id;
893 return join '/', $md5sum =~ /^(..)(..)(..)(..)(.+)$/;
896 =head2 calculate_md5sum
907 sub calculate_md5sum
{
911 open (my $F, "<", $file) || confess
"Can't open $file ";
913 my $md5 = Digest
::MD5
->new();
917 my $md5sum = $md5->hexdigest();
923 sub copy_image_resize
{
925 my ( $original_image, $new_image, $width ) = @_;
927 #print STDERR "Resizing: Destination: $new_image\n";
928 File
::Copy
::copy
( $original_image, $new_image );
929 my $chmod = "chmod 664 '$new_image'";
931 # now resize the new file, and ensure it is a jpeg
932 my $resize = `mogrify -format jpg -geometry $width '$new_image'`;
936 =head2 get_image_size_hash
947 sub get_image_size_hash
{
950 large
=> $LARGE_IMAGE_SIZE,
951 medium
=> $MEDIUM_IMAGE_SIZE,
952 small
=> $SMALL_IMAGE_SIZE,
953 thumbnail
=> $THUMBNAIL_IMAGE_SIZE,
957 =head2 get_image_size
962 Args: "large" | "medium" | "small" | "thumbnail"
972 my %hash = $self->get_image_size_hash();
973 if ( exists( $hash{$size} ) ) {
979 return $MEDIUM_IMAGE_SIZE;
997 my $type = shift || ''; # full or partial
1001 ?
$self->image_subpath
1002 : File
::Spec
->catdir( $self->get_image_dir, $self->image_subpath );
1004 if ($size eq "thumbnail") {
1005 return File
::Spec
->catfile($image_dir, 'thumbnail.jpg');
1007 if ($size eq "small") {
1008 return File
::Spec
->catfile($image_dir, 'small.jpg');
1010 if ($size eq "large") {
1011 return File
::Spec
->catfile($image_dir, 'large.jpg');
1013 if ($size eq "original") {
1014 return File
::Spec
->catfile($image_dir, $self->get_original_filename().$self->get_file_ext());
1016 if ($size eq "original_converted") {
1017 return File
::Spec
->catfile($image_dir, $self->get_original_filename().".JPG");
1019 return File
::Spec
->catfile($image_dir, 'medium.jpg');
1023 =head2 get_processing_dir
1034 sub get_processing_dir
{
1036 return $self->{processing_dir
};
1040 sub set_processing_dir
{
1042 $self->{processing_dir
} = shift;
1046 =head2 function get_copyright, set_copyright
1048 Synopsis: $copyright = $image->get_copyright();
1049 $image->set_copyright("Copyright (c) 2001 by Picasso");
1050 Arguments: getter: the copyright information string
1051 Returns: setter: the copyright information string
1052 Side effects: will be stored in the database in the copyright column.
1059 return $self->{copyright
};
1064 $self->{copyright
} = shift;
1067 =head2 accessors get_md5sum, set_md5sum
1079 return $self->{md5sum
};
1084 $self->{md5sum
} = shift;
1090 Usage: Iconify_file ($filename)
1091 Desc: This is used only for PDF, PS and EPS files during Upload processing to produce a thumbnail image
1092 for these filetypes for the CONFIRM screen. Results end up on disk but are not used other than to t
1093 produce the thumbnail
1095 Args: Full Filename of PDF file
1102 my $file_name = shift;
1104 my $basename = File
::Basename
::basename
($file_name);
1106 my $self = SGN
::Context
->new()
1107 ; # merely used to retrieve correct temp dir on this host
1109 $self->get_conf("basepath") . "/"
1110 . $self->get_conf("tempfiles_subdir")
1113 my @image_pages = `/usr/bin/identify $file_name`;
1115 my $mogrified_image;
1117 if ( $basename =~ /(.*)\.(.{1,4})$/ )
1118 { #note; mogrify will create files name
1119 # basename-0.jpg, basename-1.jpg
1120 if ( $#image_pages > 0 ) { # multipage, pdf, ps or eps
1121 $mogrified_image = $temp_dir . "/temp-0.jpg";
1124 $mogrified_image = $temp_dir . "/temp.jpg";
1126 my $tempname = $temp_dir . "/temp." . $2; # retrieve file extension
1127 $newname = $basename . ".jpg"; #
1128 my $new_dest = $temp_dir . "/" . $newname;
1130 # use temp name for mogrify/ghostscript
1131 File
::Copy
::copy
( $file_name, $tempname )
1132 || die "Can't copy file $basename to $tempname";
1134 if ( (`mogrify -format jpg '$tempname'`) ) {
1135 die "Sorry, can't convert image $basename";
1138 File
::Copy
::copy
( $mogrified_image, $new_dest )
1139 || die "Can't copy file $mogrified_image to $newname";
1148 Usage: $image->hard_delete()
1149 Desc: "hard" deletes the image.
1150 NEVER USE THIS FUNCTION!
1153 Side Effects: completely removes all the traces of this image.
1154 Example: to be used in testing scripts only. Deletion should be
1155 implemented using the 'obsolete' flag.
1161 my $test_mode = shift;
1163 if ( $self->get_original_filename && $self->pointer_count() < 2) {
1164 foreach my $size ('original', 'thumbnail', 'small', 'medium', 'large') {
1165 my $filename = $self->get_filename($size);
1168 print STDERR
"Test Mode: Would delete $filename.\n";
1171 print STDERR
"Deleting $filename...\n";
1177 $self->get_dbh->do('delete from phenome.stock_image where image_id= ?', undef, $self->get_image_id());
1178 $self->get_dbh->do('delete from metadata.md_tag_image where image_id= ?', undef, $self->get_image_id());
1179 $self->get_dbh->do('delete from phenome.locus_image where image_id= ?', undef, $self->get_image_id());
1180 $self->get_dbh->do('delete from md_image where image_id = ?', undef, $self->get_image_id );
1183 =head2 pointer_count
1185 Usage: print $image->pointer_count." db rows reference this image"
1186 Desc: get a count of how many rows in the db refer to the same image file
1189 Side Effects: queries the db
1196 return $self->get_dbh->selectrow_array( <<'', undef, $self->get_md5sum );
1197 SELECT count
( distinct
( image_id
) ) from md_image WHERE md5sum
=?
1204 Usage: $self->add_tag($tag)
1205 Desc: adds a tag to the image
1207 Args: a tag object (CXGN::Tag).
1208 Side Effects: the tag is immediately store in the database.
1209 there is no need to call store() on the image object.
1218 my $query = "INSERT INTO metadata.md_tag_image (tag_id, image_id) values (?, ?)";
1220 my $sth = $self->get_dbh()->prepare($query);
1221 $sth->execute($tag->get_tag_id(), $self->get_image_id());
1222 my $id= $self->get_currval("metadata.md_tag_image_tag_image_id_seq");
1228 Usage: my @tags = $image->get_tags();
1229 Desc: gets all the tags associated with this image object
1232 Side Effects: the tags are being fetched from the database. The image
1233 object does not 'buffer' tag associations (see also add_tag()).
1241 my $query = "SELECT tag_id FROM metadata.md_tag_image WHERE image_id=?";
1242 my $sth = $self->get_dbh()->prepare($query);
1243 $sth->execute($self->get_image_id());
1245 while (my ($tag_id) = $sth->fetchrow_array()) {
1246 push @tags, CXGN
::Tag
->new($self->get_dbh(), $tag_id);
1253 Usage: $self->remove_tag($tag)
1254 Desc: Delete a tag_image association
1257 Side Effects: the association to the tag object will be removed
1258 directly accessing the database backstore. There is no
1259 need to call store() after remove_tag(). The tag itself
1268 my $query = "DELETE FROM metadata.md_tag_image WHERE tag_id=? and image_id=?";
1269 my $sth = $self->get_dbh()->prepare($query);
1270 $sth->execute($tag->get_tag_id(), $self->get_image_id());
1274 =head2 exists_tag_image_named
1276 Usage: CXGN::Image::exists_tag_image_named($dbh, $tag_id, $image_id)
1277 Desc: Check if a tag is already associated with an image
1278 Ret: a database id or undef
1279 Args: dbh, tag_id, image_id
1285 sub exists_tag_image_named
{
1289 my $query = "SELECT tag_image_id
1290 FROM metadata.md_tag_image
1291 WHERE tag_id= ? AND image_id= ?";
1292 my $sth = $dbh->prepare($query);
1293 $sth->execute($tag_id, $image_id);
1294 if (my ($id)=$sth->fetchrow_array()) {
1302 =head2 is_duplicate($dbh, $image_file_path)
1304 checks if the image in the file $image_file_path is already in the database, checked by matching md5sums.
1306 class function. Call as CXGN::Image->is_duplicate($dbh, $image_file_path);
1312 my $image_file_path = shift;
1314 my $md5sum = $class->calculate_md5sum($image_file_path);
1316 my $image_id = $class->find_image_with_md5sum($dbh, $md5sum);
1317 print STDERR
"Retrieved image_id $image_id\n";
1320 # maybe the provided image needs to be converted the same way as the final
1321 # image for the md5sums to match the database
1322 print STDERR
"raw image did not match, trying conversion...\n";
1323 copy
($image_file_path, $image_file_path.".mogrified");
1324 system( "convert", $image_file_path.".mogrified", $image_file_path.".mogrified.JPG" );
1325 `mogrify -format jpg '$image_file_path.mogrified.JPG'`;
1326 $md5sum = $class->calculate_md5sum($image_file_path.".mogrified.JPG");
1327 $image_id = $class->find_image_with_md5sum($dbh, $md5sum);
1328 print STDERR
"Retrieved image_id $image_id (second try)\n";
1333 print STDERR
"An image with an identical md5sum ($md5sum) has already been uploaded with the image id <a href=\"/ajax/image/$image_id/view\">$image_id</a>\n";
1336 print STDERR
"The image $md5sum is not yet in the database!\n";
1341 sub find_image_with_md5sum
{
1346 print STDERR
"checking existence of image with md5sum $md5sum\n";
1347 my $q = "SELECT image_id from metadata.md_image where md5sum=?";
1348 my $h = $dbh->prepare($q);
1349 $h->execute($md5sum);
1350 my ($image_id) = $h->fetchrow_array();
1354 =head2 create_schema
1370 $self->get_dbh()->do(
1371 "CREATE table metadata.md_image (
1372 image_id serial primary key,
1375 original_filename varchar(100),
1376 file_ext varchar(20),
1377 sp_person_id bigint REFERENCES sgn_people.sp_person,
1378 modified_date timestamp with time zone,
1379 create_date timestamp with time zone,
1381 obsolete boolean default false
1384 $self->get_dbh()->do("GRANT SELECT, UPDATE, INSERT, DELETE ON metadata.md_image TO web_usr");
1385 $self->get_dbh()->do("GRANT select, update ON metadata.md_image_image_id_seq TO web_usr");
1387 $self->get_dbh()->do(
1388 "CREATE table phenome.individual_image (
1389 individual_image_id serial primary key,
1390 image_id bigint references metadata.md_image,
1391 individual_id bigint references phenome.individual,
1392 obsolete boolean DEFAULT 'false',
1393 sp_person_id integer REFERENCES sgn_people.sp_person(sp_person_id),
1394 create_date timestamp with time zone DEFAULT now(),
1395 modified_date timestamp with time zone
1398 $self->get_dbh()->do("GRANT SELECT, UPDATE, INSERT ON phenome.individual_image TO web_usr");
1399 $self->get_dbh()->do("GRANT select, update ON phenome.individual_image_individual_image_id_seq TO web_usr");
1401 $self->get_dbh()->do(
1402 "CREATE table phenome.locus_image (
1403 locus_image_id serial primary key,
1404 image_id bigint references metadata.md_image,
1405 locus_id bigint references phenome.locus,
1406 obsolete boolean DEFAULT 'false',
1407 sp_person_id integer REFERENCES sgn_people.sp_person(sp_person_id),
1408 create_date timestamp with time zone DEFAULT now(),
1409 modified_date timestamp with time zone
1412 $self->get_dbh()->do("GRANT SELECT, UPDATE, INSERT ON phenome.locus_image TO web_usr");
1413 $self->get_dbh()->do("GRANT select, update ON phenome.locus_image_locus_image_id_seq TO web_usr");
1416 $self->get_dbh()->do(
1417 "CREATE table insitu.experiment_image (
1418 experiment_image_id serial primary key,
1419 image_id bigint references metadata.md_image,
1420 experiment_id bigint references insitu.experiment,
1421 obsolete boolean DEFAULT 'false',
1422 sp_person_id integer REFERENCES sgn_people.sp_person(sp_person_id),
1423 create_date timestamp with time zone DEFAULT now(),
1424 modified_date timestamp with time zone
1427 $self->get_dbh()->do ("GRANT SELECT, UPDATE, INSERT ON insitu.experiment_image TO web_usr");
1428 $self->get_dbh()->do ("GRANT select, update ON insitu.experiment_image_experiment_image_id_seq TO web_usr");
1430 $self->get_dbh()->do ("CREATE table sgn.fish_result_image (
1431 fish_result_image_id serial primary key,
1432 image_id bigint references metadata.md_image,
1433 fish_result_id bigint references sgn.fish_result
1435 $self->get_dbh()->do ("GRANT SELECT ON sgn.fish_result_image TO web_usr");
1436 $self->get_dbh()->do ("GRANT select ON sgn.fish_result_image_fish_result_image_id_seq TO web_usr");
1437 # we don't grant access to webusr for image_fish_result because as of now users cannot submit
1438 # these image directly themselves.
1440 print STDERR
"Schemas created.\n";