[Bio] / Sprout / SearchHelper.pm Repository:
ViewVC logotype

Diff of /Sprout/SearchHelper.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.1, Tue Sep 26 13:46:03 2006 UTC revision 1.9, Sat Oct 7 13:18:11 2006 UTC
# Line 16  Line 16 
16      use FIGRules;      use FIGRules;
17      use HTML;      use HTML;
18      use BasicLocation;      use BasicLocation;
19        use FeatureQuery;
20        use URI::Escape;
21        use PageBuilder;
22    
23  =head1 Search Helper Base Class  =head1 Search Helper Base Class
24    
# Line 72  Line 75 
75    
76  List of JavaScript statements to be executed after the form is closed.  List of JavaScript statements to be executed after the form is closed.
77    
78    =item genomeHash
79    
80    Cache of the genome group hash used to build genome selection controls.
81    
82    =item genomeParms
83    
84    List of the parameters that are used to select multiple genomes.
85    
86    =item filtered
87    
88    TRUE if this is a feature-filtered search, else FALSE. B<NOTE> that this
89    field is updated by the B<FeatureQuery> object.
90    
91  =back  =back
92    
93    =head2 Adding a new Search Tool
94    
95    To add a new search tool to the system, you must
96    
97    =over 4
98    
99    =item 1
100    
101    Choose a class name for your search tool.
102    
103    =item 2
104    
105    Create a new subclass of this object and implement each of the virtual methods. The
106    name of the subclass must be C<SH>I<className>.
107    
108    =item 3
109    
110    Create an include file among the web server pages that describes how to use
111    the search tool. The include file must be in the B<includes> directory, and
112    its name must be C<SearchHelp_>I<className>C<.inc>.
113    
114    =item 4
115    
116    In the C<SearchSkeleton.cgi> script, add a C<use> statement for your search tool
117    and then put the class name in the C<@advancedClasses> list.
118    
119    =back
120    
121    =head3 Building a Search Form
122    
123    All search forms are three-column tables. In general, you want one form
124    variable per table row. The first column should contain the label and
125    the second should contain the form control for specifying the variable
126    value. If the control is wide, you should use C<colspan="2"> to give it
127    extra room. B<Do not> specify a width in any of your table cells, as
128    width management is handled by this class.
129    
130    The general code for creating the form should be
131    
132        sub Form {
133            my ($self) = @_;
134            # Get the CGI object.
135            my $cgi = @self->Q();
136            # Start the form.
137            my $retVal = $self->FormStart("form title");
138            # Assemble the table rows.
139            my @rows = ();
140            ... push table row Html into @rows ...
141            push @rows, $self->SubmitRow();
142            ... push more Html into @rows ...
143            # Build the table from the rows.
144            $retVal .= $self->MakeTable(\@rows);
145            # Close the form.
146            $retVal .= $self->FormEnd();
147            # Return the form Html.
148            return $retVal;
149        }
150    
151    Several helper methods are provided for particular purposes.
152    
153    =over 4
154    
155    =item 1
156    
157    L</NmpdrGenomeMenu> generates a control for selecting one or more genomes. Use
158    L</GetGenomes> to retrieve all the genomes passed in for a specified parameter
159    name. Note that as an assist to people working with GET-style links, if no
160    genomes are specified and the incoming request style is GET, all genomes will
161    be returned.
162    
163    =item 2
164    
165    L</FeatureFilterRow> formats several rows of controls for filtering features.
166    When you start building the code for the L</Find> method, you can use a
167    B<FeatureQuery> object to automatically filter each genome's features using
168    the values from the filter controls.
169    
170    =item 3
171    
172    L</QueueFormScript> allows you to queue JavaScript statements for execution
173    after the form is fully generated. If you are using very complicated
174    form controls, the L</QueueFormScript> method allows you to perform
175    JavaScript initialization. The L</NmpdrGenomeMenu> control uses this
176    facility to display a list of the pre-selected genomes.
177    
178    =back
179    
180    Finally, when generating the code for your controls, be sure to use any incoming
181    query parameters as default values so that the search request is persistent.
182    
183    =head3 Finding Search Results
184    
185    The L</Find> method is used to create the search results. For a search that
186    wants to return features (which is most of them), the basic code structure
187    would work as follows. It is assumed that the L</FeatureFilterRows> method
188    has been used to create feature filtering parameters.
189    
190        sub Find {
191            my ($self) = @_;
192            # Get the CGI and Sprout objects.
193            my $cgi = $self->Q();
194            my $sprout = $self->DB();
195            # Declare the return variable. If it remains undefined, the caller will
196            # know that an error occurred.
197            my $retVal;
198            ... validate the parameters ...
199            if (... invalid parameters...) {
200                $self->SetMessage(...appropriate message...);
201            } elsif (FeatureQuery::Valid($self)) {
202                # Initialize the session file.
203                $self->OpenSession();
204                # Initialize the result counter.
205                $retVal = 0;
206                ... get a list of genomes ...
207                for my $genomeID (... each genome ...) {
208                    my $fq = FeatureQuery->new($self, $genomeID);
209                    while (my $feature = $fq->Fetch()) {
210                        ... examine the feature ...
211                        if (... we want to keep it ...) {
212                            $self->PutFeature($fq);
213                            $retVal++;
214                        }
215                    }
216                }
217                # Close the session file.
218                $self->CloseSession();
219            }
220            # Return the result count.
221            return $retVal;
222        }
223    
224    A Find method is of course much more complicated than generating a form, and there
225    are variations on the above them. For example, you could eschew feature filtering
226    entirely in favor of your own custom filtering, you could include extra columns
227    in the output, or you could search for something that's not a feature at all. The
228    above code is just a loose framework.
229    
230    If you wish to add your own extra columns to the output, use the B<AddExtraColumns>
231    method of the feature query object.
232    
233        $fq->AddExtraColumns(score => $sc);
234    
235    The L</Find> method must return C<undef> if the search parameters are invalid. If this
236    is the case, then a message describing the problem should be passed to the framework
237    by calling L</SetMessage>. If the parameters are valid, then the method must return
238    the number of items found.
239    
240  =cut  =cut
241    
242  # This counter is used to insure every form on the page has a unique name.  # This counter is used to insure every form on the page has a unique name.
# Line 113  Line 276 
276      # Compute the subclass name.      # Compute the subclass name.
277      $class =~ /SH(.+)$/;      $class =~ /SH(.+)$/;
278      my $subClass = $1;      my $subClass = $1;
     # Create the Sprout object.  
     my $sprout = SFXlate->new_sprout_only();  
279      # Insure everybody knows we're in Sprout mode.      # Insure everybody knows we're in Sprout mode.
280      $query->param(-name => 'SPROUT', -value => 1);      $query->param(-name => 'SPROUT', -value => 1);
281      # Generate the form name.      # Generate the form name.
# Line 122  Line 283 
283      $formCount++;      $formCount++;
284      # Create the shelp object. It contains the query object (with the session ID)      # Create the shelp object. It contains the query object (with the session ID)
285      # as well as an indicator as to whether or not the session is new, plus the      # as well as an indicator as to whether or not the session is new, plus the
286      # class name and the Sprout object.      # class name and a placeholder for the Sprout object.
287      my $retVal = {      my $retVal = {
288                    query => $query,                    query => $query,
289                    type => $type,                    type => $type,
290                    class => $subClass,                    class => $subClass,
291                    sprout => $sprout,                    sprout => undef,
292                    orgs => {},                    orgs => {},
293                    name => $formName,                    name => $formName,
294                    scriptQueue => [],                    scriptQueue => [],
295                      genomeList => undef,
296                      genomeParms => [],
297                      filtered => 0,
298                   };                   };
299      # Bless and return it.      # Bless and return it.
300      bless $retVal, $class;      bless $retVal, $class;
# Line 152  Line 316 
316      return $self->{query};      return $self->{query};
317  }  }
318    
319    
320    
321  =head3 DB  =head3 DB
322    
323  C<< my $sprout = $shelp->DB(); >>  C<< my $sprout = $shelp->DB(); >>
# Line 163  Line 329 
329  sub DB {  sub DB {
330      # Get the parameters.      # Get the parameters.
331      my ($self) = @_;      my ($self) = @_;
332        # Insure we have a database.
333        my $retVal = $self->{sprout};
334        if (! defined $retVal) {
335            $retVal = SFXlate->new_sprout_only();
336            $self->{sprout} = $retVal;
337        }
338      # Return the result.      # Return the result.
339      return $self->{sprout};      return $retVal;
340  }  }
341    
342  =head3 IsNew  =head3 IsNew
# Line 437  Line 609 
609    
610  =head3 PutFeature  =head3 PutFeature
611    
612  C<< $shelp->PutFeature($record, %extraCols); >>  C<< $shelp->PutFeature($fquery); >>
613    
614  Store a feature in the result cache. This is the workhorse method for most  Store a feature in the result cache. This is the workhorse method for most
615  searches, since the primary data item in the database is features.  searches, since the primary data item in the database is features.
616    
617  For each feature, there are certain columns that are standard: the feature name, the  For each feature, there are certain columns that are standard: the feature name, the
618  GBrowse and protein page links, the functional assignment, and so forth. If additional  GBrowse and protein page links, the functional assignment, and so forth. If additional
619  columns are required by a particular search subclass, they should be included in the  columns are required by a particular search subclass, they should be stored in
620  parameters, in key-value form. For example, the following call adds columns for  the feature query object using the B<AddExtraColumns> method. For example, the following
621  essentiality and virulence.  code adds columns for essentiality and virulence.
622    
623      $shelp->PutFeature($record, essential => $essentialFlag, virulence => $vfactor);      $fq->AddExtraColumns(essential => $essentialFlag, virulence => $vfactor);
624        $shelp->PutFeature($fq);
625    
626  For correct results, all values should be specified for all extra columns in all calls to  For correct results, all values should be specified for all extra columns in all calls to
627  B<PutFeature>. (In particular, the column header names are computed on the first  B<PutFeature>. (In particular, the column header names are computed on the first
# Line 458  Line 631 
631      if (! $essentialFlag) {      if (! $essentialFlag) {
632          $essentialFlag = undef;          $essentialFlag = undef;
633      }      }
634      $shelp->PutFeature($record, essential => $essentialFlag, virulence = $vfactor);      $fq->AddExtraColumns(essential => $essentialFlag, virulence => $vfactor);
635        $shelp->PutFeature($fq);
636    
637  =over 4  =over 4
638    
639  =item record  =item fquery
640    
641  DBObject record for the feature.  FeatureQuery object containing the current feature data.
   
 =item extraCols  
642    
643  =back  =back
644    
645  =cut  =cut
646    
647  sub PutFeature {  sub PutFeature {
648      # Get the parameters. Note that the extra columns are read in as a list      # Get the parameters.
649      # instead of a hash so that the column order is preserved.      my ($self, $fq) = @_;
650      my ($self, $record, @extraColList) = @_;      # Get the CGI query object.
651        my $cgi = $self->Q();
652        # Get the feature data.
653        my $record = $fq->Feature();
654        my $extraCols = $fq->ExtraCols();
655      # Check for a first-call situation.      # Check for a first-call situation.
656      if (! defined $self->{cols}) {      if (! defined $self->{cols}) {
657          # Here we need to set up the column information. Start with the defaults.          # Here we need to set up the column information. Start with the defaults.
658          $self->{cols} = $self->DefaultFeatureColumns();          $self->{cols} = $self->DefaultFeatureColumns();
659          # Append the extras. Note we proceed by twos because the columns are          # Add the externals if they were requested.
660          # specified in the form name => value.          if ($cgi->param('ShowAliases')) {
661          for (my $i = 0; $i <= $#extraColList; $i += 2) {              push @{$self->{cols}}, 'alias';
662              push @{$self->{cols}}, "X=$extraColList[$i]";          }
663            # Append the extras, sorted by column name.
664            for my $col (sort keys %{$extraCols}) {
665                push @{$self->{cols}}, "X=$col";
666          }          }
667          # Write out the column headers. This also prepares the cache file to receive          # Write out the column headers. This also prepares the cache file to receive
668          # output.          # output.
# Line 491  Line 670 
670      }      }
671      # Get the feature ID.      # Get the feature ID.
672      my ($fid) = $record->Value('Feature(id)');      my ($fid) = $record->Value('Feature(id)');
     # Now we process the columns themselves. First, convert the extra column list  
     # to a hash.  
     my %extraCols = @extraColList;  
673      # Loop through the column headers, producing the desired data.      # Loop through the column headers, producing the desired data.
674      my @output = ();      my @output = ();
675      for my $colName (@{$self->{cols}}) {      for my $colName (@{$self->{cols}}) {
676          push @output, $self->FeatureColumnValue($colName, $record, \%extraCols);          push @output, $self->FeatureColumnValue($colName, $record, $extraCols);
677      }      }
678      # Compute the sort key. The sort key floats NMPDR organism features to the      # Compute the sort key. The sort key usually floats NMPDR organism features to the
679      # top of the return list.      # top of the return list.
680      my $group = $self->FeatureGroup($fid);      my $key = $self->SortKey($record);
     my $key = ($group ? "A$group" : "ZZ");  
681      # Write the feature data.      # Write the feature data.
682      $self->WriteColumnData($key, @output);      $self->WriteColumnData($key, @output);
683  }  }
# Line 583  Line 758 
758      # Check for an open session file.      # Check for an open session file.
759      if (defined $self->{fileHandle}) {      if (defined $self->{fileHandle}) {
760          # We found one, so close it.          # We found one, so close it.
761            Trace("Closing session file.") if T(2);
762          close $self->{fileHandle};          close $self->{fileHandle};
763      }      }
764  }  }
# Line 600  Line 776 
776      my $retVal;      my $retVal;
777      # Get a digest encoder.      # Get a digest encoder.
778      my $md5 = Digest::MD5->new();      my $md5 = Digest::MD5->new();
779      # If we have a randomization file, use it to seed the digester.      # Add the PID, the IP, and the time stamp. Note that the time stamp is
780      if (open(R, "/dev/urandom")) {      # actually two numbers, and we get them both because we're in list
781          my $b;      # context.
782          read(R, $b, 1024);      $md5->add($$, $ENV{REMOTE_ADDR}, $ENV{REMOTE_PORT}, gettimeofday());
783          $md5->add($b);      # Hash up all this identifying data.
784      }      $retVal = $md5->hexdigest();
785      # Add the PID and the time stamp.      # Return the result.
     $md5->add($$, gettimeofday);  
     # Hash it up and clean the result so that it works as a file name.  
     $retVal = $md5->b64digest();  
     $retVal =~ s,/,\$,g;  
     $retVal =~ s,\+,@,g;  
     # Return it.  
786      return $retVal;      return $retVal;
787  }  }
788    
# Line 771  Line 941 
941      } else {      } else {
942          # Here we can get its genome data.          # Here we can get its genome data.
943          $retVal = $self->Organism($genomeID);          $retVal = $self->Organism($genomeID);
944          # Append the type and number.          # Append the FIG ID.
945          $retVal .= " [$type $num]";          $retVal .= " [$fid]";
946      }      }
947      # Return the result.      # Return the result.
948      return $retVal;      return $retVal;
# Line 888  Line 1058 
1058    
1059  =head3 NmpdrGenomeMenu  =head3 NmpdrGenomeMenu
1060    
1061  C<< my $htmlText = $shelp->NmpdrGenomeMenu($menuName, \%options, \@selected); >>  C<< my $htmlText = $shelp->NmpdrGenomeMenu($menuName, $multiple, \@selected, $rows); >>
1062    
1063  This method creates a hierarchical HTML menu for NMPDR genomes organized by category. The  This method creates a hierarchical HTML menu for NMPDR genomes organized by category. The
1064  category indicates the low-level NMPDR group. Organizing the genomes in this way makes it  category indicates the low-level NMPDR group. Organizing the genomes in this way makes it
# Line 900  Line 1070 
1070    
1071  Name to give to the menu.  Name to give to the menu.
1072    
1073  =item options  =item multiple
1074    
1075  Reference to a hash containing the options to be applied to the C<SELECT> tag form the menu.  TRUE if the user is allowed to select multiple genomes, else FALSE.
 Typical options would include C<multiple> to specify  
 that multiple selections are allowed and C<size> to set the number of rows to display  
 in the menu.  
1076    
1077  =item selected  =item selected
1078    
# Line 913  Line 1080 
1080  is not intended to allow multiple selections, the list should be a singleton. If the  is not intended to allow multiple selections, the list should be a singleton. If the
1081  list is empty, nothing will be pre-selected.  list is empty, nothing will be pre-selected.
1082    
1083    =item rows (optional)
1084    
1085    Number of rows to display. If omitted, the default is 1 for a single-select list
1086    and 10 for a multi-select list.
1087    
1088    =item crossMenu (optional)
1089    
1090    If specified, is presumed to be the name of another genome menu whose contents
1091    are to be mutually exclusive with the contents of this menu. As a result, instead
1092    of the standard onChange event, the onChange event will deselect any entries in
1093    the other menu.
1094    
1095  =item RETURN  =item RETURN
1096    
1097  Returns the HTML text to generate a C<SELECT> menu inside a form.  Returns the HTML text to generate a C<SELECT> menu inside a form.
# Line 923  Line 1102 
1102    
1103  sub NmpdrGenomeMenu {  sub NmpdrGenomeMenu {
1104      # Get the parameters.      # Get the parameters.
1105      my ($self, $menuName, $options, $selected) = @_;      my ($self, $menuName, $multiple, $selected, $rows, $cross) = @_;
1106      # Get the Sprout and CGI objects.      # Get the Sprout and CGI objects.
1107      my $sprout = $self->DB();      my $sprout = $self->DB();
1108      my $cgi = $self->Q();      my $cgi = $self->Q();
1109        # Compute the row count.
1110        if (! defined $rows) {
1111            $rows = ($multiple ? 10 : 1);
1112        }
1113        # Create the multiple tag.
1114        my $multipleTag = ($multiple ? " multiple" : "");
1115      # Get the form name.      # Get the form name.
1116      my $formName = $self->FormName();      my $formName = $self->FormName();
1117        # Check to see if we already have a genome list in memory.
1118        my $genomes = $self->{genomeList};
1119        my $groupHash;
1120        if (defined $genomes) {
1121            # We have a list ready to use.
1122            $groupHash = $genomes;
1123        } else {
1124      # Get a list of all the genomes in group order. In fact, we only need them ordered      # Get a list of all the genomes in group order. In fact, we only need them ordered
1125      # by name (genus,species,strain), but putting primary-group in front enables us to      # by name (genus,species,strain), but putting primary-group in front enables us to
1126      # take advantage of an existing index.      # take advantage of an existing index.
# Line 940  Line 1132 
1132      # Create a hash to organize the genomes by group. Each group will contain a list of      # Create a hash to organize the genomes by group. Each group will contain a list of
1133      # 2-tuples, the first element being the genome ID and the second being the genome      # 2-tuples, the first element being the genome ID and the second being the genome
1134      # name.      # name.
1135      my %groupHash = ();          my %gHash = ();
1136      for my $genome (@genomeList) {      for my $genome (@genomeList) {
1137          # Get the genome data.          # Get the genome data.
1138          my ($group, $genomeID, $genus, $species, $strain) = @{$genome};          my ($group, $genomeID, $genus, $species, $strain) = @{$genome};
# Line 950  Line 1142 
1142              $name .= " $strain";              $name .= " $strain";
1143          }          }
1144          # Push the genome into the group's list.          # Push the genome into the group's list.
1145          push @{$groupHash{$group}}, [$genomeID, $name];              push @{$gHash{$group}}, [$genomeID, $name];
1146            }
1147            # Save the genome list for future use.
1148            $self->{genomeList} = \%gHash;
1149            $groupHash = \%gHash;
1150      }      }
1151      # Now we are ready to unroll the menu out of the group hash. First, we sort the groups, putting      # Now we are ready to unroll the menu out of the group hash. First, we sort the groups, putting
1152      # the supporting-genome group last.      # the supporting-genome group last.
1153      my @groups = sort grep { $_ ne $FIG_Config::otherGroup } keys %groupHash;      my @groups = sort grep { $_ ne $FIG_Config::otherGroup } keys %{$groupHash};
1154      push @groups, $FIG_Config::otherGroup;      push @groups, $FIG_Config::otherGroup;
1155      # Next, create a hash that specifies the pre-selected entries.      # Next, create a hash that specifies the pre-selected entries. Note that we need to deal
1156      my %selectedHash = map { $_ => 1 } @{$selected};      # with the possibility of undefined values in the incoming list.
1157        my %selectedHash = ();
1158        if (defined $selected) {
1159            %selectedHash = map { $_ => 1 } grep { defined($_) } @{$selected};
1160        }
1161      # Now it gets complicated. We need a way to mark all the NMPDR genomes.      # Now it gets complicated. We need a way to mark all the NMPDR genomes.
1162      # Create the type counters.      # Create the type counters.
1163      my $groupCount = 1;      my $groupCount = 1;
# Line 967  Line 1167 
1167      my $showSelect = "showSelected($menuName, '$divID', 1000);";      my $showSelect = "showSelected($menuName, '$divID', 1000);";
1168      # If multiple selection is supported, create an onChange event.      # If multiple selection is supported, create an onChange event.
1169      my $onChange = "";      my $onChange = "";
1170      if ($options->{multiple}) {      if ($cross) {
1171            $onChange = " onChange=\"crossUnSelect($menuName, '$divID', $cross, '${formName}_${cross}_status', 1000)\"";
1172        } elsif ($multiple) {
1173          $onChange = " onChange=\"$showSelect\"";          $onChange = " onChange=\"$showSelect\"";
1174      }      }
1175      # Create the SELECT tag and stuff it into the output array.      # Create the SELECT tag and stuff it into the output array.
1176      my $select = "<" . join(" ", "SELECT name=\"$menuName\"$onChange", map { " $_=\"$options->{$_}\"" } keys %{$options}) . ">";      my $select = "<SELECT name=\"$menuName\"$onChange$multipleTag size=\"$rows\">";
1177      my @lines = ($select);      my @lines = ($select);
1178      # Loop through the groups.      # Loop through the groups.
1179      for my $group (@groups) {      for my $group (@groups) {
# Line 982  Line 1184 
1184          # label option may have functionality in future browsers. If that happens, we'll need          # label option may have functionality in future browsers. If that happens, we'll need
1185          # to modify the genome text so that the "selectSome" method can tell which are NMPDR          # to modify the genome text so that the "selectSome" method can tell which are NMPDR
1186          # organisms and which aren't. Sadly, the OPTGROUP tag is invisible in the DOM Javascript          # organisms and which aren't. Sadly, the OPTGROUP tag is invisible in the DOM Javascript
1187          # hierarchy.          # hierarchy, so we can't use it.
1188          my $label = ($group eq $FIG_Config::otherGroup ? "other" : "nmpdr");          my $label = ($group eq $FIG_Config::otherGroup ? "other" : "nmpdr");
1189          # Get the genomes in the group.          # Get the genomes in the group.
1190          for my $genome (@{$groupHash{$group}}) {          for my $genome (@{$groupHash->{$group}}) {
1191              my ($genomeID, $name) = @{$genome};              my ($genomeID, $name) = @{$genome};
1192              # See if it's selected.              # See if it's selected.
1193              my $select = ($selectedHash{$genomeID} ? " selected" : "");              my $select = ($selectedHash{$genomeID} ? " selected" : "");
# Line 999  Line 1201 
1201      # Close the SELECT tag.      # Close the SELECT tag.
1202      push @lines, "</SELECT>";      push @lines, "</SELECT>";
1203      # Check for multiple selection.      # Check for multiple selection.
1204      if ($options->{multiple}) {      if ($multiple) {
1205          # Since multi-select is on, we can set up some buttons to set and clear selections.          # Since multi-select is on, we set up some buttons to set and clear selections.
1206          push @lines, "<br />";          push @lines, "<br />";
1207          push @lines, "<INPUT type=\"button\" name=\"SelectAll\" class=\"bigButton\" value=\"Select All\" onClick=\"selectAll($menuName); $showSelect\" />";          push @lines, "<INPUT type=\"button\" name=\"SelectAll\" class=\"bigButton\" value=\"Select All\" onClick=\"selectAll($menuName); $showSelect\" />";
1208          push @lines, "<INPUT type=\"button\" name=\"ClearAll\" class=\"bigButton\"  value=\"Clear All\" onClick=\"clearAll($menuName); $showSelect\" />";          push @lines, "<INPUT type=\"button\" name=\"ClearAll\" class=\"bigButton\"  value=\"Clear All\" onClick=\"clearAll($menuName); $showSelect\" />";
1209          push @lines, "<INPUT type=\"button\" name=\"NMPDROnly\" class=\"bigButton\"  value=\"Select NMPDR\" onClick=\"selectSome($menuName, 'nmpdr'); $showSelect\" />";          push @lines, "<INPUT type=\"button\" name=\"NMPDROnly\" class=\"bigButton\"  value=\"Select NMPDR\" onClick=\"selectSome($menuName, 'nmpdr'); $showSelect\" />";
1210          push @lines, "<INPUT type=\"button\" name=\"OtherOnly\" class=\"bigButton\" value=\"Select Supporting\" onClick=\"selectSome($menuName, 'other'); $showSelect\" />";          push @lines, "<INPUT type=\"button\" name=\"OtherOnly\" class=\"bigButton\" value=\"Select Supporting\" onClick=\"selectSome($menuName, 'other'); $showSelect\" />";
1211            # Now add the search box. This allows the user to type text and have all genomes containing
1212            # the text selected automatically.
1213            my $searchThingName = "${menuName}_SearchThing";
1214            push @lines, "<br>Select genomes containing <INPUT type=\"text\" name=\"$searchThingName\" size=\"30\" />&nbsp;" .
1215                         "<INPUT type=\"button\" name=\"Select\" class=\"button\" value=\"Search\" onClick=\"selectViaSearch($menuName, $searchThingName); $showSelect\" />";
1216          # Add the status display, too.          # Add the status display, too.
1217          push @lines, "<DIV id=\"$divID\" class=\"selectStatus\"></DIV>";          push @lines, "<DIV id=\"$divID\" class=\"selectStatus\"></DIV>";
1218          # Queue to update the status display when the form loads. We need to modify the show statement          # Queue to update the status display when the form loads. We need to modify the show statement
# Line 1014  Line 1221 
1221          # in case we decide to twiddle the parameters.          # in case we decide to twiddle the parameters.
1222          $showSelect =~ s/showSelected\(/showSelected\(thisForm\./;          $showSelect =~ s/showSelected\(/showSelected\(thisForm\./;
1223          $self->QueueFormScript($showSelect);          $self->QueueFormScript($showSelect);
1224            # Finally, add this parameter to the list of genome parameters. This enables us to
1225            # easily find all the parameters used to select one or more genomes.
1226            push @{$self->{genomeParms}}, $menuName;
1227      }      }
1228      # Assemble all the lines into a string.      # Assemble all the lines into a string.
1229      my $retVal = join("\n", @lines, "");      my $retVal = join("\n", @lines, "");
# Line 1021  Line 1231 
1231      return $retVal;      return $retVal;
1232  }  }
1233    
1234    =head3 PropertyMenu
1235    
1236    C<< my $htmlText = $shelp->PropertyMenu($menuName, $selected, $force); >>
1237    
1238    Generate a property name dropdown menu.
1239    
1240    =over 4
1241    
1242    =item menuName
1243    
1244    Name to give to the menu.
1245    
1246    =item selected
1247    
1248    Value of the property name to pre-select.
1249    
1250    =item force (optional)
1251    
1252    If TRUE, then the user will be forced to choose a property name. If FALSE,
1253    then an additional menu choice will be provided to select nothing.
1254    
1255    =item RETURN
1256    
1257    Returns a dropdown menu box that allows the user to select a property name. An additional
1258    selection entry will be provided for selecting no property name
1259    
1260    =back
1261    
1262    =cut
1263    
1264    sub PropertyMenu {
1265        # Get the parameters.
1266        my ($self, $menuName, $selected, $force) = @_;
1267        # Get the CGI and Sprout objects.
1268        my $sprout = $self->DB();
1269        my $cgi = $self->Q();
1270        # Create the property name list.
1271        my @propNames = ();
1272        if (! $force) {
1273            push @propNames, "";
1274        }
1275        # Get all the property names, putting them after the null choice if one exists.
1276        push @propNames, $sprout->GetChoices('Property', 'property-name');
1277        # Create a menu from them.
1278        my $retVal = $cgi->popup_menu(-name=> $menuName, -values => \@propNames,
1279                                      -default => $selected);
1280        # Return the result.
1281        return $retVal;
1282    }
1283    
1284  =head3 MakeTable  =head3 MakeTable
1285    
1286  C<< my $htmlText = $shelp->MakeTable(\@rows); >>  C<< my $htmlText = $shelp->MakeTable(\@rows); >>
# Line 1083  Line 1343 
1343      # Get the parameters.      # Get the parameters.
1344      my ($self) = @_;      my ($self) = @_;
1345      my $cgi = $self->Q();      my $cgi = $self->Q();
1346      # Declare the return variable.      # Get the current page size.
1347        my $pageSize = $cgi->param('PageSize');
1348        # Get the incoming external-link flag.
1349        my $aliases = ($cgi->param('ShowAliases') ? 1 : 0);
1350        # Create the row.
1351      my $retVal = $cgi->Tr($cgi->td("Results/Page"),      my $retVal = $cgi->Tr($cgi->td("Results/Page"),
1352                            $cgi->td($cgi->popup_menu(-name => 'PageSize',                            $cgi->td($cgi->popup_menu(-name => 'PageSize',
1353                                                      -values => [10, 25, 45, 100, 1000],                                                      -values => [10, 25, 50, 100, 1000],
1354                                                      -default => $cgi->param('PageSize'))),                                                      -default => $pageSize) . " " .
1355                                       $cgi->checkbox(-name => 'ShowURL',
1356                                                      -value => 1,
1357                                                      -label => 'Show URL')),
1358                            $cgi->td($cgi->submit(-class => 'goButton',                            $cgi->td($cgi->submit(-class => 'goButton',
1359                                                  -name => 'Search',                                                  -name => 'Search',
1360                                                  -value => 'Go')));                                                  -value => 'Go')));
1361      # Return the result.      # Return the result.
1362      return $retVal;      return $retVal;
1363  }  }
1364    
1365    =head3 FeatureFilterRows
1366    
1367    C<< my $htmlText = $shelp->FeatureFilterRows(); >>
1368    
1369    This method creates table rows that can be used to filter features. There are
1370    two rows returned, and the values can be used to select features by genome
1371    using the B<FeatureQuery> object.
1372    
1373    =cut
1374    
1375    sub FeatureFilterRows {
1376        # Get the parameters.
1377        my ($self) = @_;
1378        # Return the result.
1379        return FeatureQuery::FilterRows($self);
1380    }
1381    
1382  =head3 GBrowseFeatureURL  =head3 GBrowseFeatureURL
1383    
1384  C<< my $url = SearchHelper::GBrowseFeatureURL($sprout, $feat); >>  C<< my $url = SearchHelper::GBrowseFeatureURL($sprout, $feat); >>
# Line 1133  Line 1418 
1418          # Get the feature location string.          # Get the feature location string.
1419          my $loc = $sprout->FeatureLocation($feat);          my $loc = $sprout->FeatureLocation($feat);
1420          # Compute the contig, start, and stop points.          # Compute the contig, start, and stop points.
1421          my($start, $stop, $contig) = BasicLocation::Parse($loc);          my($contig, $start, $stop) = BasicLocation::Parse($loc);
1422            Trace("Start and stop are ($start,$stop) on contig $contig.") if T(3);
1423          # Now we need to do some goofiness to insure that the location is not too          # Now we need to do some goofiness to insure that the location is not too
1424          # big and that we get some surrounding stuff.          # big and that we get some surrounding stuff.
1425          my $mid = int(($start + $stop) / 2);          my $mid = int(($start + $stop) / 2);
# Line 1163  Line 1449 
1449          }          }
1450          my $seg_id = $contig;          my $seg_id = $contig;
1451          $seg_id =~ s/:/--/g;          $seg_id =~ s/:/--/g;
1452            Trace("Show limits are ($show_start,$show_stop) in genome $genomeID with ref $seg_id.") if T(3);
1453          # Assemble all the pieces.          # Assemble all the pieces.
1454          $retVal = "gbrowse.cgi/GB_$genomeID?ref=$seg_id&start=$show_start&stop=$show_stop";          $retVal = "gbrowse.cgi/GB_$genomeID?ref=$seg_id&start=$show_start&stop=$show_stop";
1455      }      }
# Line 1170  Line 1457 
1457      return $retVal;      return $retVal;
1458  }  }
1459    
1460    =head3 GetGenomes
1461    
1462    C<< my @genomeList = $shelp->GetGenomes($parmName); >>
1463    
1464    Return the list of genomes specified by the specified CGI query parameter.
1465    If the request method is POST, then the list of genome IDs is returned
1466    without preamble. If the request method is GET and the parameter is not
1467    specified, then it is treated as a request for all genomes. This makes it
1468    easier for web pages to link to a search that wants to specify all genomes.
1469    
1470    =over 4
1471    
1472    =item parmName
1473    
1474    Name of the parameter containing the list of genomes. This will be the
1475    first parameter passed to the L</NmpdrGenomeMenu> call that created the
1476    genome selection control on the form.
1477    
1478    =item RETURN
1479    
1480    Returns a list of the genomes to process.
1481    
1482    =back
1483    
1484    =cut
1485    
1486    sub GetGenomes {
1487        # Get the parameters.
1488        my ($self, $parmName) = @_;
1489        # Get the CGI query object.
1490        my $cgi = $self->Q();
1491        # Get the list of genome IDs in the request header.
1492        my @retVal = $cgi->param($parmName);
1493        Trace("Genome list for $parmName is (" . join(", ", @retVal) . ") with method " . $cgi->request_method() . ".") if T(3);
1494        # Check for the special GET case.
1495        if ($cgi->request_method() eq "GET" && ! @retVal) {
1496            # Here the caller wants all the genomes.
1497            my $sprout = $self->DB();
1498            @retVal = $sprout->Genomes();
1499        }
1500        # Return the result.
1501        return @retVal;
1502    }
1503    
1504    =head3 GetHelpText
1505    
1506    C<< my $htmlText = $shelp->GetHelpText(); >>
1507    
1508    Get the help text for this search. The help text is stored in files on the template
1509    server. The help text for a specific search is taken from a file named
1510    C<SearchHelp_>I<class>C<.inc> in the template directory C<$FIG_Config::template_url>.
1511    There are also three standard help files: C<SearchHelp1_Filtering.inc> describes the
1512    feature filtering performed by the B<FeatureQuery> object, C<SearchHelp1_GenomeControl.inc>
1513    describes how to use a multiple-selection genome control, and C<SearchHelp1_Standard.inc>
1514    describes the standard controls for a search, such as page size, URL display, and
1515    external alias display.
1516    
1517    =cut
1518    
1519    sub GetHelpText {
1520        # Get the parameters.
1521        my ($self) = @_;
1522        # Create a list to hold the pieces of the help.
1523        my @helps = ();
1524        # Get the template directory URL.
1525        my $urlBase = $FIG_Config::template_url;
1526        # Start with the specific help.
1527        my $class = $self->{class};
1528        push @helps, PageBuilder::GetPage("$urlBase/SearchHelp_$class.inc");
1529        # Add the genome control help if needed.
1530        if (scalar @{$self->{genomeParms}}) {
1531            push @helps, PageBuilder::GetPage("$urlBase/SearchHelp1_GenomeControl.inc");
1532        }
1533        # Next the filter help.
1534        if ($self->{filtered}) {
1535            push @helps, PageBuilder::GetPage("$urlBase/SearchHelp1_Filtering.inc");
1536        }
1537        # Finally, the standard help.
1538        push @helps, PageBuilder::GetPage("$urlBase/SearchHelp1_Standard.inc");
1539        # Assemble the pieces.
1540        my $retVal = join("\n<p>&nbsp;</p>\n", @helps);
1541        # Return the result.
1542        return $retVal;
1543    }
1544    
1545    =head3 ComputeSearchURL
1546    
1547    C<< my $url = $shelp->ComputeSearchURL(); >>
1548    
1549    Compute the GET-style URL for the current search. In order for this to work, there
1550    must be a copy of the search form on the current page. This will always be the
1551    case if the search is coming from C<SearchSkeleton.cgi>.
1552    
1553    A little expense is involved in order to make the URL as smart as possible. The
1554    main complication is that if the user specified all genomes, we'll want to
1555    remove the parameter entirely from a get-style URL.
1556    
1557    =cut
1558    
1559    sub ComputeSearchURL {
1560        # Get the parameters.
1561        my ($self) = @_;
1562        # Get the database and CGI query object.
1563        my $cgi = $self->Q();
1564        my $sprout = $self->DB();
1565        # Start with the full URL.
1566        my $retVal = $cgi->url(-full => 1);
1567        # Get all the query parameters in a hash.
1568        my %parms = $cgi->Vars();
1569        # Now we need to do some fixing. Each multi-valued parameter is encoded as a string with null
1570        # characters separating the individual values. We have to convert those to lists. In addition,
1571        # the multiple-selection genome parameters and the feature type parameter must be checked to
1572        # determine whether or not they can be removed from the URL. First, we get a list of the
1573        # genome parameters and a list of all genomes. Note that we only need the list if a
1574        # multiple-selection genome parameter has been found on the form.
1575        my %genomeParms = map { $_ => 1 } @{$self->{genomeParms}};
1576        my @genomeList;
1577        if (keys %genomeParms) {
1578            @genomeList = $sprout->Genomes();
1579        }
1580        # Create a list to hold the URL parameters we find.
1581        my @urlList = ();
1582        # Now loop through the parameters in the hash, putting them into the output URL.
1583        for my $parmKey (keys %parms) {
1584            # Get a list of the parameter values. If there's only one, we'll end up with
1585            # a singleton list, but that's okay.
1586            my @values = split (/\0/, $parms{$parmKey});
1587            # Check for special cases.
1588            if ($parmKey eq 'featureTypes') {
1589                # Here we need to see if the user wants all the feature types. If he
1590                # does, we erase all the values so that the parameter is not output.
1591                my %valueCheck = map { $_ => 1 } @values;
1592                my @list = FeatureQuery::AllFeatureTypes();
1593                my $okFlag = 1;
1594                for (my $i = 0; $okFlag && $i <= $#list; $i++) {
1595                    if (! $valueCheck{$list[$i]}) {
1596                        $okFlag = 0;
1597                    }
1598                }
1599                if ($okFlag) {
1600                    @values = ();
1601                }
1602            } elsif (grep { $_ eq $parmKey } qw(SessionID ResultCount Page PageSize Trace TF ShowURL)) {
1603                # These are bookkeeping parameters we don't need to start a search.
1604                @values = ();
1605            } elsif ($parmKey =~ /_SearchThing$/) {
1606                # Here the value coming in is from a genome control's search thing. It does
1607                # not affect the results of the search, so we clear it.
1608                @values = ();
1609            } elsif ($genomeParms{$parmKey}) {
1610                # Here we need to see if the user wants all the genomes. If he does,
1611                # we erase all the values just like with features.
1612                my $allFlag = $sprout->IsAllGenomes(\@values, \@genomeList);
1613                if ($allFlag) {
1614                    @values = ();
1615                }
1616            }
1617            # If we still have values, create the URL parameters.
1618            if (@values) {
1619                push @urlList, map { "$parmKey=" . uri_escape($_) } @values;
1620            }
1621        }
1622        # Add the parameters to the URL.
1623        $retVal .= "?" . join(";", @urlList);
1624        # Return the result.
1625        return $retVal;
1626    }
1627    
1628    =head3 GetRunTimeValue
1629    
1630    C<< my $htmlText = $shelp->GetRunTimeValue($text); >>
1631    
1632    Compute a run-time column value.
1633    
1634    =over 4
1635    
1636    =item text
1637    
1638    The run-time column text. It consists of 2 percent signs, a column type, an equal
1639    sign, and the data for the current row.
1640    
1641    =item RETURN
1642    
1643    Returns the fully-formatted HTML text to go into the current column of the current row.
1644    
1645    =back
1646    
1647    =cut
1648    
1649    sub GetRunTimeValue {
1650        # Get the parameters.
1651        my ($self, $text) = @_;
1652        # Declare the return variable.
1653        my $retVal;
1654        # Parse the incoming text.
1655        if ($text =~ /^%%([^=]+)=(.*)$/) {
1656            $retVal = $self->RunTimeColumns($1, $2);
1657        } else {
1658            Confess("Invalid run-time column string \"$text\" encountered in session file.");
1659        }
1660        # Return the result.
1661        return $retVal;
1662    }
1663    
1664    =head3 FeatureTypeMap
1665    
1666    C<< my %features = SearchHelper::FeatureTypeMap(); >>
1667    
1668    Return a map of feature types to descriptions. The feature type data is stored
1669    in the B<FIG_Config> file. Currently, it only contains a space-delimited list of
1670    feature types. The map returned by this method is a hash mapping the type codes to
1671    descriptive names.
1672    
1673    The reason we have to convert the list from a string is that the B<NMPDRSetup.pl>
1674    script is only able to insert strings into the generated B<FIG_Config> file.
1675    
1676    =cut
1677    
1678    sub FeatureTypeMap {
1679        my @list = split /\s+/, $FIG_Config::feature_types;
1680        my %retVal = map { $_ => $_ } @list;
1681        return %retVal;
1682    }
1683    
1684    =head3 AdvancedClassList
1685    
1686    C<< my @classes = SearchHelper::AdvancedClassList(); >>
1687    
1688    Return a list of advanced class names. This list is used to generate the directory
1689    of available searches on the search page.
1690    
1691    The reason we have to convert the list from a string is that the B<NMPDRSetup.pl>
1692    script is only able to insert strings into the generated B<FIG_Config> file.
1693    
1694    =cut
1695    
1696    sub AdvancedClassList {
1697        return split /\s+/, $FIG_Config::advanced_classes;
1698    }
1699    
1700  =head2 Feature Column Methods  =head2 Feature Column Methods
1701    
1702  The methods in this column manage feature column data. If you want to provide the  The methods in this column manage feature column data. If you want to provide the
# Line 1202  Line 1729 
1729      # Get the parameters.      # Get the parameters.
1730      my ($self) = @_;      my ($self) = @_;
1731      # Return the result.      # Return the result.
1732      return ['orgName', 'function', 'gblink', 'protlink'];      return ['orgName', 'function', 'gblink', 'protlink',
1733                FeatureQuery::AdditionalColumns($self)];
1734  }  }
1735    
1736  =head3 FeatureColumnTitle  =head3 FeatureColumnTitle
# Line 1311  Line 1839 
1839          $retVal = HTML::set_prot_links($fid);          $retVal = HTML::set_prot_links($fid);
1840      } elsif ($colName eq 'alias') {      } elsif ($colName eq 'alias') {
1841          # In this case, the user wants a list of external aliases for the feature.          # In this case, the user wants a list of external aliases for the feature.
1842          # The complicated part is we have to hyperlink them. First, get the          # These are very expensive, so we compute them when the row is displayed.
1843          # aliases.          $retVal = "%%aliases=$fid";
         my @aliases = $sprout->FeatureAliases($fid);  
         # Only proceed if we found some.  
         if (@aliases) {  
             # Join the aliases into a comma-delimited list.  
             my $aliasList = join(", ", @aliases);  
             # Ask the HTML processor to hyperlink them.  
             $retVal = HTML::set_prot_links($aliasList);  
         }  
1844      } elsif ($colName eq 'function') {      } elsif ($colName eq 'function') {
1845          # The functional assignment is just a matter of getting some text.          # The functional assignment is just a matter of getting some text.
1846          ($retVal) = $record->Value('Feature(assignment)');          ($retVal) = $record->Value('Feature(assignment)');
# Line 1350  Line 1870 
1870      return $retVal;      return $retVal;
1871  }  }
1872    
1873    =head3 RunTimeColumns
1874    
1875    C<< my $htmlText = $shelp->RunTimeColumns($type, $text); >>
1876    
1877    Return the HTML text for a run-time column. Run-time columns are evaluated when the
1878    list is displayed, rather than when it is generated.
1879    
1880    =over 4
1881    
1882    =item type
1883    
1884    Type of column.
1885    
1886    =item text
1887    
1888    Data relevant to this row of the column.
1889    
1890    =item RETURN
1891    
1892    Returns the fully-formatted HTML text to go in the specified column.
1893    
1894    =back
1895    
1896    =cut
1897    
1898    sub RunTimeColumns {
1899        # Get the parameters.
1900        my ($self, $type, $text) = @_;
1901        # Declare the return variable.
1902        my $retVal = "";
1903        # Get the Sprout and CGI objects.
1904        my $sprout = $self->DB();
1905        my $cgi = $self->Q();
1906        # Separate the text into a type and data.
1907        if ($type eq 'aliases') {
1908            # Here the caller wants external alias links for a feature. The text
1909            # is the feature ID.
1910            my $fid = $text;
1911            # The complicated part is we have to hyperlink them. First, get the
1912            # aliases.
1913            Trace("Generating aliases for feature $fid.") if T(4);
1914            my @aliases = $sprout->FeatureAliases($fid);
1915            # Only proceed if we found some.
1916            if (@aliases) {
1917                # Join the aliases into a comma-delimited list.
1918                my $aliasList = join(", ", @aliases);
1919                # Ask the HTML processor to hyperlink them.
1920                $retVal = HTML::set_prot_links($cgi, $aliasList);
1921            }
1922        }
1923        # Return the result.
1924        return $retVal;
1925    }
1926    
1927    =head2 Virtual Methods
1928    
1929    =head3 Form
1930    
1931    C<< my $html = $shelp->Form(); >>
1932    
1933    Generate the HTML for a form to request a new search.
1934    
1935    =head3 Find
1936    
1937    C<< my $resultCount = $shelp->Find(); >>
1938    
1939    Conduct a search based on the current CGI query parameters. The search results will
1940    be written to the session cache file and the number of results will be
1941    returned. If the search parameters are invalid, a result count of C<undef> will be
1942    returned and a result message will be stored in this object describing the problem.
1943    
1944    =head3 Description
1945    
1946    C<< my $htmlText = $shelp->Description(); >>
1947    
1948    Return a description of this search. The description is used for the table of contents
1949    on the main search tools page. It may contain HTML, but it should be character-level,
1950    not block-level, since the description is going to appear in a list.
1951    
1952    =head3 SortKey
1953    
1954    C<< my $key = $shelp->SortKey($record); >>
1955    
1956    Return the sort key for the specified record. The default is to sort by feature name,
1957    floating NMPDR organisms to the top. This sort may be overridden by the search class
1958    to provide fancier functionality. This method is called by B<PutFeature>, so it
1959    is only used for feature searches. A non-feature search would presumably have its
1960    own sort logic.
1961    
1962    =over 4
1963    
1964    =item record
1965    
1966    The C<DBObject> from which the current row of data is derived.
1967    
1968    =item RETURN
1969    
1970    Returns a key field that can be used to sort this row in among the results.
1971    
1972    =back
1973    
1974    =cut
1975    
1976    sub SortKey {
1977        # Get the parameters.
1978        my ($self, $record) = @_;
1979        # Get the feature ID from the record.
1980        my ($fid) = $record->Value('Feature(id)');
1981        # Get the group from the feature ID.
1982        my $group = $self->FeatureGroup($fid);
1983        # Ask the feature query object to form the sort key.
1984        my $retVal = FeatureQuery::SortKey($self, $group, $record);
1985        # Return the result.
1986        return $retVal;
1987    }
1988    
1989    
1990  1;  1;

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.9

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3