#!/usr/bin/perl -w # search.page # creates the search page # set libraries use strict; use DBI; use CGI qw(:standard *div *param *table *main *ul *li checkbox*); # intiail variables my $n = "\015\012"; my $base_url = 'http://philately.dyerhouse.com'; my $dbh = connect_to_database(); # capture values from user my $search_term = param('search_term') || ''; my @sc = param('collections_selected'); my @sa = param('albums_selected'); my $year_spec = param('year_spec') || ''; my $cat_spec = param('cat_spec') || '0'; my $type_spec = param('type_spec') || ''; my %collection_hash = ( it=>' Italy', un=>' United Nations', us=>' United States' ); my %album_hash = ( 'pre-states'=>' Pre-Italy', 'kingdom'=>' Kingdom of Italy', 'colonies'=>' Italian Colonies', 'transition'=>' Italy in Transition', 'republic'=>' Italian Republic', 'new-york'=>' U.N. New York', 'early-years'=>' U.S. Early Years', 'bureau'=>' U.S. Bureau Stamps', 'post-world-wars'=>' U.S. Post World Wars' ); my %type_hash = ( aa=>'All Types' ); my $types = get_types($dbh); foreach (@$types) { my ($key,$value) = @$_; $type_hash{$key} = $value; } my @type_keys = keys %type_hash; @type_keys = sort @type_keys; # dealing with no collections or albums selected my $collection_count = @sc; my $album_count = @sa; if($collection_count == 0) { @sc = ( keys %collection_hash ); } if($album_count == 0) { @sa = ( keys %album_hash ); } my $heading = 'Search Page'; my $description = 'Experimental page for search stamps database.'; my $main_text = 'This is an experimental page for searching my stamps database.'; my @header_extras = (meta({-http_equiv=>'Content-Type', -content=>'text/html; charset=utf-8'}), Link({-rel=>'icon', -type=>'text/css', -href=>'/favicon.ico', -media=>'image/x-icon'}), Link({-rel=>'stylesheet', -type=>'text/css', -href=>'/_static/pygments.css', -media=>'screen'}), Link({-rel=>'stylesheet', -type=>'text/css', -href=>'/_static/theme.css', -media=>'screen'}), Link({-rel=>'stylesheet', -type=>'text/css', -href=>'/_static/main.css', -media=>'screen'}), Link({-rel=>'stylesheet', -type=>'text/css', -href=>'/_static/dark.css', -media=>'screen'}), Link({-rel=>'stylesheet', -type=>'text/css', -href=>'/_static/tables.css', -media=>'screen'}), Link({-rel=>'stylesheet', -type=>'text/css', -href=>'/_static/mobile.css', -media=>'screen'}), Link({-rel=>'stylesheet', -type=>'text/css', -href=>'/_static/printing.css', -media=>'print'}), Link({-rel=>'stylesheet', -type=>'text/css', -href=>'http://use.fontawesome.com/releases/v5.0.7/css/all.css', -media=>'screen'}),); # web page print header(-charset=>'UTF-8'), $n, start_html(-title=>"$heading", -lang=>'en-US', -head=>\@header_extras, -meta=>{'title'=>$heading, 'copyright'=>qq|Copyright 2022 - 2024 Russell J.T. Dyer. All rights reserved|, 'description'=>$description, 'viewport'=>qq|width=device-width,initial-scale=1.0|}), $n,$n, start_div({id=>'page', class=>'page'}), $n,$n, start_div({id=>'left_margin'}), $n, "", $n, end_div(), $n,$n, # end left_margin div # main panel start_div({class=>'main'}), $n,$n, "", $n,$n, start_div({class=>'wy-nav-content'}), $n, start_div({class=>'rst-content'}), $n, # main search form start_div({class=>'search_block'}), $n, start_form(-name =>'main_form', -method=>'POST', -enctype=>&CGI::URL_ENCODED, -action=>'search.page'), $n, b("Search Term"), br(), $n, textfield(-name=>'search_term', -class=>'search_term', -value=>$search_term, -placeholder=>'Enter Search Terms ', -size=>20, -maxlength=>20), $n, submit(-name=>'submit_form', -class=>'search_button', -value=>'Search'), br(), $n, b("Collections:"), br(), $n, checkbox_group({-name=>'collections_selected', -values=>['it','un','us'], -default=>['it','un','us'], -linebreak=>'false', -labels=>\%collection_hash}), br(), $n, b("Albums:"), br(), $n, checkbox_group({-name=>'albums_selected', -values=>['pre-states','kingdom','colonies','transition','republic','new-york','early-years','bureau','post-world-wars'], -default=>['pre-states','kingdom','colonies','transition','republic','new-york','early-years','bureau','post-world-wars'], -linebreak=>'false', -labels=>\%album_hash}), br(), $n, b("Specifics:"), br(), $n, span('Year: '), textfield({-name=>'year_spec', -class=>'search_specific', -value=>$year_spec, -default=>'0', -placeholder=>'Year Issued', -size=>4, -maxlength=>4}), $n, span('Catalog No: '), textfield({-name=>'cat_spec', -class=>'search_specific', -value=>$cat_spec, -default=>'0', -placeholder=>'Sassone', -size=>2, -maxlength=>4}), br(), $n, span('  Type: '), popup_menu({-name=>'type_spec', -class=>'search_specific', -values=>\@type_keys, -labels=>\%type_hash, -default=>'aa'}), $n, end_form(), $n, end_div(), $n,$n, h1({class=>'search_heading'}, $heading), $n, p("This page and its form will allow you to search my stamp collections. You can enter multiple search terms, separated by spaces, but they will be treated as a phrase and only complete matches will be shown."), $n, p("To search on each term, separate the words by commas. For example, to match all instances of parcel and post, you would enter parcel, post. To match only the pairing of those two words, enter it without commas (i.e., parcel post). You can do a combination of single words and phrases (e.g., parcel post, air mail). Incidentally, you can search on any of the fields related an entry in the database for a stamp (e.g., year, color, etc.)."), $n, br({clear=>'all'}), $n,$n; my (@page_ids, @stamp_ids); if($cat_spec) { my $stamp_found = catalog_search($dbh,\@sc,\@sa,$cat_spec); my $st_count = @$stamp_found || 0; if($st_count > 0) { foreach (@$stamp_found) { my ($stamp_id) = @$_; push(@stamp_ids, $stamp_id); } } } elsif($year_spec) { my $stamps_found = stamps_search($dbh,\@sc,\@sa,$year_spec,$type_spec,$cat_spec,''); my $st_count = @$stamps_found || 0; if($st_count > 0) { foreach (@$stamps_found) { my ($stamp_id) = @$_; push(@stamp_ids, $stamp_id); } } } elsif($search_term) { my @terms = split(',', $search_term); my $term_count = @terms; foreach (@terms) { my $pages_found = pages_search($dbh,\@sc,\@sa,$_); my $pg_count = @$pages_found || 0; if($pg_count > 0) { foreach (@$pages_found) { my ($page_id) = @$_; if($year_spec || ($type_spec && $type_spec ne 'aa')) { my $pages_found = page_check($dbh,$page_id,$year_spec,$type_spec); if(!$pages_found) { next } } push(@page_ids, $page_id); } } my $stamps_found = stamps_search($dbh,\@sc,\@sa,$year_spec,$type_spec,$cat_spec,$_); my $st_count = @$stamps_found || 0; if($st_count > 0) { foreach (@$stamps_found) { my ($stamp_id) = @$_; push(@stamp_ids, $stamp_id); } } } } if($cat_spec or $year_spec or $search_term) { my $pg_count = @page_ids || 0; my $st_count = @stamp_ids || 0; if($pg_count == 0 && $st_count == 0 ) { print "Nothing found in the database matching the search term entered.", $n, br({clear=>'all'}), br(), br(), hr(), br(), br(), $n,$n; } elsif($pg_count > 0 || $st_count > 0 ) { print h2({class=>'search_heading'}, "Search Results"), $n, p("Below are the results of your search. They're in two groups: The first lists album pages that contain the search term in the title, description, or other text on the web page — not necessarily in the text related to the stamps on the album page. The second section lists stamps in my database for which the description or some other bit of data contains the search term."), $n,$n; } # display page details if($pg_count > 0) { @page_ids = do { my %seen; grep { !$seen{$_}++ } @page_ids }; print h3({class=>'stamp-details rubric'}, 'Album Pages'), $n, hr(), $n,$n, start_table({-class=>'page-stamp-list', -width=>'90%', -border=>'0', -cellpadding=>'4', -cellspacing=>'4'}), $n,$n, Tr(td({-width=>'10%'}, 'collection'), td({-width=>'30%'}, 'album & sub-album'), td({-width=>'25%'}, 'page title'), td({-width=>'35%'}, 'page description')), $n; foreach(@page_ids) { my ($page_details,$page_stamp_cnt) = page_data($dbh,$_); my ($page_id,$dir,$collect_name,$album_id,$file,$album_heading,$child_dir,$pg_title,$pg_label,$meta_desc) = @{$page_details->[0]}; my $highlight = ''; if($search_term) { $highlight = $search_term } elsif($cat_spec) { $highlight = $$cat_spec } if($child_dir) { $child_dir = $child_dir . '/' } my $path_file = "$base_url/$dir/$album_id/$child_dir$file.page?highlight=$highlight"; print Tr(td(a({href=>$path_file}, $collect_name)), td(a({href=>$path_file}, $album_heading . br() .' (' . $album_id . ', ' . $pg_label . ')')), td(a({href=>$path_file}, $pg_title . br() . $page_stamp_cnt . ' stamps')), td(a({href=>$path_file}, $meta_desc))), $n; } print Tr(td({colspan=>'4'}, ' ')), $n, end_table(), $n,$n; } if($pg_count > 0 || $st_count > 0 ) { print p("To see a particular page or stamp, click on it. That will take you to the album web page you selected or, for a stamp, the album page containing the stamp. Your search terms will be highlighted."), br(), $n,$n; } # display stamp details if($st_count > 0) { @stamp_ids = do { my %seen; grep { !$seen{$_}++ } @stamp_ids }; print h3({class=>'stamp-details rubric'}, 'Stamps'), $n, hr(), $n,$n, start_table({-class=>'page-stamp-list', -width=>'90%', -border=>'0', -cellpadding=>'0', -cellspacing=>'4'}), $n,$n, Tr(td({-width=>'10%'}, 'collection'), td({-width=>'20%'}, 'album & sub-album'), td({-width=>'8%'}, 'catalog'), td({-width=>'20%'}, 'subject of stamp'), td({-width=>'8%'}, 'face amt.'), td({-width=>'10%'}, 'type'), td({-width=>'10%'}, 'issue date'), td({-width=>'14%'}, 'colors')), $n; foreach (@stamp_ids) { my $stamp_details = stamp_data($dbh,$_); my ($stamp_id,$page_id,$file,$dir,$collect_name,$album_heading,$child_dir,$album_id,$pg_title,$pg_label,$title,$sub_title,$issue_date,$amt,$color,$type,$cat) = @{$stamp_details->[0]}; if($sub_title) { $title = $title . br() . $sub_title } if($child_dir) { $child_dir = $child_dir . '/' } my $path_file = "$base_url/$dir/$album_id/$child_dir$file.page?highlight=$search_term"; print Tr(td(a({href=>$path_file}, $collect_name)), td(a({href=>$path_file}, $album_heading . br() . ' (' . $album_id . ', ' . $pg_label . ')')), td(a({href=>$path_file}, $cat)), td(a({href=>$path_file}, $title . br() . $pg_title)), td(a({href=>$path_file}, $amt)), td(a({href=>$path_file}, $type)), td(a({href=>$path_file}, $issue_date)), td(a({href=>$path_file}, $color))), $n; } print Tr(td({colspan=>'8'}, ' ')), $n, end_table(), $n,$n; } } else { print "No search term entered.", br({clear=>'all'}), $n; my @breaks = (1..40); foreach(@breaks){ print br() } } print "', $n,$n, end_div(), end_div(), $n, # end wy-nav-content & rst-content divs br({class=>'clear'}), br(), br(), ' ', $n, end_div(), $n, # end main div br({class=>'clear'}), ' ', $n, end_div(), $n,$n, # end page div end_html(); exit; sub connect_to_database { # connect to database my $db = 'philately'; my $hst = 'localhost'; my $usr = 'philatelist'; my $pwd = 'Iceblink-Colonist-1102-Neuroma'; my $dbh = DBI->connect(qq|DBI:mysql:$db;host=$hst;user=$usr;password=$pwd|,undef,undef, {RaiseError=>1}) || die 'Could not connect to MariaDB: ' . DBI->errstr; return $dbh; } sub catalog_search { # get stamp based on catalog number my ($dbh,$sc,$sa,$cat_spec) = @_; # determine which collections and albums to search my $collections = join ', ', map "'$_'", @$sc; my $albums = join ', ', map "'$_'", @$sa; my $sql_stmnt = qq|SELECT DISTINCT stamp_id FROM stamps JOIN pages USING(page_id) JOIN albums USING(album_id) LEFT JOIN series USING(series_id) WHERE pg_status IN('pub','stk') AND collection IN($collections) AND album_id IN($albums) AND cat_nbr = ?|; my $sth = $dbh->prepare($sql_stmnt); $sth->execute($cat_spec); my $stamp_found = $sth->fetchall_arrayref(); $sth->finish(); return $stamp_found; } sub pages_search { # get lists of stamps my ($dbh,$sc,$sa,$search_term) = @_; # determine which collections and albums to search my $collections = join ', ', map "'$_'", @$sc; my $albums = join ', ', map "'$_'", @$sa; my $sql_stmnt = qq|SELECT page_id FROM (SELECT page_id, SUM(rating) AS rate FROM (SELECT DISTINCT page_id, 1 AS rating FROM pages JOIN albums USING(album_id) JOIN search_fields USING(page_id) WHERE pg_status IN('pub','stk') AND collection IN($collections) AND album_id IN($albums) AND (keywords REGEXP '\\\\b$search_term\\\\b') UNION SELECT DISTINCT page_id, 2 AS rating FROM pages JOIN albums USING(album_id) JOIN search_fields USING(page_id) WHERE pg_status IN('pub','stk') AND collection IN($collections) AND album_id IN($albums) AND (text_copy REGEXP '\\\\b$search_term\\\\b') UNION SELECT DISTINCT page_id, 0.5 AS rating FROM pages JOIN albums USING(album_id) JOIN search_fields USING(page_id) WHERE pg_status IN('pub','stk') AND collection IN($collections) AND album_id IN($albums) AND (page_title REGEXP '\\\\b$search_term\\\\b') UNION SELECT DISTINCT page_id, 0.5 AS rating FROM pages JOIN albums USING(album_id) JOIN search_fields USING(page_id) WHERE pg_status IN('pub','stk') AND collection IN($collections) AND album_id IN($albums) AND (meta_desc REGEXP '\\\\b$search_term\\\\b')) AS derived1 GROUP BY page_id) AS derived2 WHERE rate > 0.75 ORDER BY rate DESC|; my $sth = $dbh->prepare($sql_stmnt); $sth->execute(); my $pages_found = $sth->fetchall_arrayref(); $sth->finish(); return $pages_found; } sub page_check { # get lists of stamps my ($dbh,$page_id,$year_spec,$type_spec) = @_; my $sql_stmnt = qq|SELECT count(*) FROM pages JOIN stamps USING(page_id) WHERE issue_year = ? AND page_id = ? GROUP BY page_id|; my $sth = $dbh->prepare($sql_stmnt); $sth->execute($year_spec,$page_id); my ($pages_found_yr) = $sth->fetchrow() || 0; $sql_stmnt = qq|SELECT count(*) FROM pages JOIN stamps USING(page_id) WHERE type_id = ? AND page_id = ? GROUP BY page_id|; $sth = $dbh->prepare($sql_stmnt); $sth->execute($type_spec,$page_id); my ($pages_found_type) = $sth->fetchrow() || 0; my $pages_found = $pages_found_yr + $pages_found_type; $sth->finish(); return $pages_found; } sub get_types { # get lists of stamps my ($dbh,$page_id,$year_spec,$type_spec) = @_; my $sql_stmnt = qq|SELECT type_id, type_label FROM types WHERE type_label != ''|; my $sth = $dbh->prepare($sql_stmnt); $sth->execute(); my $types = $sth->fetchall_arrayref(); $sth->finish(); return $types; } sub page_data { # get lists of stamps my ($dbh,$page_id) = @_; my $sql_stmnt = qq|SELECT DISTINCT page_id, collection_dir, collection_name, pages.album_id, file_prefix, album_heading, child_id, page_title, page_label, meta_desc FROM pages JOIN albums USING(album_id) JOIN child_albums ON(parent_id = album_id) JOIN search_fields USING(page_id) JOIN collections USING(collection) WHERE page_id = ?|; my $sth = $dbh->prepare($sql_stmnt); $sth->execute($page_id); my $page_details = $sth->fetchall_arrayref(); $sql_stmnt = qq|SELECT count(*) FROM stamps WHERE page_id = ? GROUP BY page_id|; $sth = $dbh->prepare($sql_stmnt); $sth->execute($page_id); my ($page_stamp_cnt) = $sth->fetchrow() || 0; $sth->finish(); return ($page_details,$page_stamp_cnt); } sub stamps_search { # get lists of stamps my ($dbh,$sc,$sa,$year_spec,$type_spec,$cat_spec,$search_term) = @_; # determine which collections and albums to search my $collections = join ', ', map "'$_'", @$sc; my $albums = join ', ', map "'$_'", @$sa; my $spec = ''; if($year_spec) { $spec .= " AND issue_year = '$year_spec'"; } if($type_spec ne 'aa') { $spec .= " AND type_id = '$type_spec'"; } my $sql_stmnt = qq|SELECT stamp_id FROM (SELECT stamp_id, SUM(rating) AS rate FROM (SELECT DISTINCT stamp_id, 2 AS rating FROM stamps JOIN pages USING(page_id) JOIN albums USING(album_id) LEFT JOIN series USING(series_id) WHERE pg_status IN('pub','stk') AND collection IN($collections) AND album_id IN($albums) AND cat_nbr = '$cat_spec' UNION SELECT DISTINCT stamp_id, 1 AS rating FROM stamps JOIN pages USING(page_id) JOIN albums USING(album_id) LEFT JOIN series USING(series_id) WHERE pg_status IN('pub','stk') AND collection IN($collections) AND album_id IN($albums) $spec AND (title REGEXP '\\\\b$search_term\\\\b') UNION SELECT DISTINCT stamp_id, 0.5 AS rating FROM stamps JOIN pages USING(page_id) JOIN albums USING(album_id) LEFT JOIN series USING(series_id) WHERE pg_status IN('pub','stk') AND collection IN($collections) AND album_id IN($albums) $spec AND (sub_title REGEXP '\\\\b$search_term\\\\b') UNION SELECT DISTINCT stamp_id, 0.5 AS rating FROM stamps JOIN pages USING(page_id) JOIN albums USING(album_id) LEFT JOIN series USING(series_id) WHERE pg_status IN('pub','stk') AND collection IN($collections) AND album_id IN($albums) AND (issue_year = '$year_spec') UNION SELECT DISTINCT stamp_id, 0.25 AS rating FROM stamps JOIN pages USING(page_id) JOIN albums USING(album_id) LEFT JOIN series USING(series_id) WHERE pg_status IN('pub','stk') AND collection IN($collections) AND album_id IN($albums) $spec AND (series.heading REGEXP '\\\\b$search_term\\\\b')) AS derived1 GROUP BY stamp_id) AS derived2 WHERE rate > 0 ORDER BY rate DESC|; my $sth = $dbh->prepare($sql_stmnt); $sth->execute(); my $stamps_found = $sth->fetchall_arrayref(); $sth->finish(); return $stamps_found; } sub stamp_data { # get lists of stamps my ($dbh,$stamp_id) = @_; my $sql_stmnt = qq|SELECT stamp_id, stamps.page_id, file_prefix, collection_dir, collection_name, album_heading, child_id, pages.album_id, page_title, page_label, title, sub_title, CONCAT(issue_month, SPACE(1), issue_year) AS issue_date, face_amt, colors, type, CONCAT(cat, '
', cat_nbr) FROM stamps JOIN pages USING(page_id) JOIN albums USING(album_id) JOIN child_albums ON(parent_id = album_id) JOIN collections USING(collection) JOIN types USING(type_id) WHERE stamp_id = ?|; my $sth = $dbh->prepare($sql_stmnt); $sth->execute($stamp_id); my $stamp_details = $sth->fetchall_arrayref(); $sth->finish(); return $stamp_details; }