Added eval; site now shows clean dataset missing message instead of server error...
[sgn.git] / lib / SGN / Controller / Search / Feature.pm
bloba9c245a00222e581d9e09489f841002c97efd4cf
1 package SGN::Controller::Search::Feature;
2 use Moose;
3 use namespace::autoclean;
5 use SGN::View::Feature qw( location_string description_featureprop_types get_descriptions feature_types feature_organisms);
7 use URI::FromHash 'uri';
8 use YAML::Any;
9 use JSON;
11 BEGIN { extends 'Catalyst::Controller' }
12 with 'Catalyst::Component::ApplicationAttribute';
14 has 'default_page_size' => (
15 is => 'ro',
16 default => 20,
19 has 'maximum_export_size' => (
20 is => 'ro',
21 default => 10_000,
24 has 'schema' => (
25 is => 'rw',
26 isa => 'DBIx::Class::Schema',
27 lazy_build => 1,
29 sub _build_schema {
30 shift->_app->dbic_schema( 'Bio::Chado::Schema', 'sgn_chado' )
33 =head1 PUBLIC ACTIONS
35 =head2 search
37 Interactive search interface for features.
39 Public path: /search/features
41 =cut
43 sub oldsearch : Path('/feature/search') Args(0) {
44 $_[1]->res->redirect( '/search/features', 301 );
47 sub search :Path('/search/features') Args(0) {
48 my ( $self, $c ) = @_;
50 $c->stash(
51 template => '/search/features.mas',
52 maximum_export_size => $self->maximum_export_size,
53 feature_types => feature_types($self->schema),
54 organisms => feature_organisms($self->schema) ,
56 $c->forward('View::Mason');
59 sub csv_export_search : Path('/search/features/export_csv') {
60 my ( $self, $c ) = @_;
62 $c->forward('format_search_args');
63 $c->forward('make_feature_search_rs');
65 if( $c->stash->{search_resultset}->count > $self->maximum_export_size ) {
66 $c->throw_client_error( public_message => <<'' );
67 The search matched too many records, please make your search more specific.
71 $c->forward('assemble_result');
73 my @headings = @{ $c->stash->{search_result_fields} || [] };
74 my $results = $c->stash->{search_result};
75 my $body = join( ',', map '"'.ucfirst($_).'"', @headings )."\n";
76 for my $r ( @$results ) {
77 no warnings 'uninitialized';
78 $body .= join( ',',
79 map qq|"$r->{$_}"|,
80 @headings
81 )."\n";
84 $c->res->content_type( 'text/csv' );
85 $c->stash->{download_filename} = 'SGN_features.csv';
86 $c->forward('/download/set_download_headers');
87 $c->res->body( $body );
90 sub format_search_args : Private {
91 my ( $self, $c ) = @_;
93 my $params = $c->req->params;
94 $c->stash->{search_args} = {
95 map {
96 $_ => $params->{$_},
97 } qw(
99 organism
100 type
101 type_id
102 name
103 srcfeature_id
104 srcfeature_start
105 srcfeature_end
106 proptype_id
107 prop_value
108 description
110 sort
116 sub search_json :Path('/search/features/search_service') Args(0) {
117 my ( $self, $c ) = @_;
119 $c->forward('format_search_args');
120 $c->forward('make_feature_search_rs');
122 my $rs = $c->stash->{search_resultset};
124 my $params = $c->req->params;
125 my $total = $rs->count;
127 # set up paging if specified
128 if( defined $params->{'page'} ) {
129 $rs = $c->stash->{search_resultset} = $rs->search(
130 undef,
132 page => $params->{'page'} || 1,
133 rows => $params->{'limit'} || $self->default_page_size,
138 $c->forward('assemble_result');
140 $c->res->body( to_json( {
141 success => JSON::true,
142 totalCount => $total,
143 data => $c->stash->{search_result},
144 }));
147 sub assemble_result : Private {
148 my ( $self, $c ) = @_;
150 my $rs = $c->stash->{search_resultset};
152 # make sure we prefetch some stuff
153 $rs = $rs->search( undef,
155 prefetch => [
156 'type',
157 'organism',
158 ( $rs->is_paged ? () : ( { 'featureloc_features' => 'srcfeature' } ) ),
159 #'featureprops',
164 my $fields = $c->stash->{search_result_fields} = $c->req->params->{fields} || [qw[
165 feature_id
166 organism
167 type
168 name
169 seqlen
170 locations
171 description
174 my %field_handlers = (
175 organism => sub { shift->organism->species },
176 type => sub { shift->type->name },
177 name => sub { shift->name },
178 feature_id => sub { shift->feature_id },
179 seqlen => sub { shift->seqlen },
180 locations => sub {
181 join( ',', map {
182 my $fl = $_;
183 location_string( $fl );
184 } shift->featureloc_features
187 description => sub { join ' ', get_descriptions( shift, 'no_html' ) },
190 my @result;
191 while( my $feature = $rs->next ) {
192 push @result, {
193 map { $_ => $field_handlers{ $_ }->( $feature ) }
194 grep $field_handlers{$_}, # only try to make the fields we know how to make
195 @{ ref $fields ? $fields : [$fields] }
199 $c->stash->{search_result} = \@result;
202 sub feature_type_autocomplete : Path('/search/features/feature_types_service') {
203 my ( $self, $c ) = @_;
204 $c->stash->{typed_table} = 'feature';
205 $c->forward( 'type_autocomplete' );
208 sub featureprop_type_autocomplete : Path('/search/features/featureprop_types_service') {
209 my ( $self, $c ) = @_;
210 $c->stash->{typed_table} = 'featureprop';
211 $c->forward( 'type_autocomplete' );
214 sub type_autocomplete : Private {
215 my ( $self, $c ) = @_;
217 my $table = $c->stash->{typed_table} || 'feature';
218 my $types = $c->dbc->dbh->selectall_arrayref(<<"" );
219 SELECT cvterm_id, name
220 FROM cvterm ct
221 WHERE cvterm_id IN( SELECT DISTINCT type_id FROM $table )
222 ORDER BY name
224 $c->res->content_type('text/json');
225 $c->res->body( to_json( { success => JSON::true,
226 data => [
227 map +{ type_id => $_->[0], name => $_->[1] }, @{ $types || [] }
234 sub srcfeatures_autocomplete : Path('/search/features/srcfeatures_service') {
235 my ( $self, $c ) = @_;
237 my $srcfeatures = $c->dbc->dbh->selectall_arrayref(<<'' );
238 SELECT srcfeature_id, f.name, f.seqlen, count
239 FROM
240 ( SELECT srcfeature_id, count(*) as count
241 FROM featureloc
242 GROUP BY srcfeature_id
243 HAVING count(*) > 1
244 ) as srcfeatures
245 JOIN feature f ON srcfeature_id = f.feature_id
246 ORDER BY f.name ASC
249 $c->res->content_type('text/json');
250 $c->res->body( to_json( { success => JSON::true,
251 data => [
252 map +{ feature_id => $_->[0], name => $_->[1], seqlen => $_->[2], count => $_->[3] }, @{ $srcfeatures || [] }
259 # assembles a DBIC resultset for the search based on the submitted
260 # form values
261 sub make_feature_search_rs : Private {
262 my ( $self, $c ) = @_;
264 my $args = $c->stash->{search_args};
266 my $schema = $c->dbic_schema('Bio::Chado::Schema','sgn_chado');
267 #my $rs = $schema->resultset('Sequence::Feature');
268 #Get only features that have locations in featureloc
269 my $rs = $schema->resultset('Sequence::Feature')->search(
270 {'featureloc_features.locgroup' => 0},
271 {prefetch => ['featureloc_features']}
274 #debug
275 #$schema->storage->debug(1);
277 if( my $name = $args->{'name'} ) {
278 $rs = $rs->search({ 'me.name' => { ilike => '%'.$name.'%' }});
281 if( my $type = $args->{'type'} ) {
282 my $type_rs = $schema->resultset('Cv::Cvterm')
283 ->search({ 'lower(name)' => lc $type });
284 $rs = $rs->search({ 'me.type_id' => { -in => $type_rs->get_column('cvterm_id')->as_query }});
287 if( my $type_id = $args->{'type_id'} ) {
288 $rs = $rs->search({ 'me.type_id' => $type_id });
291 if( my $organism = $args->{'organism'} ) {
292 my $organism_rs = $schema->resultset('Organism::Organism')
293 ->search({ species => { -ilike => '%'.$organism.'%' }});
294 $rs = $rs->search({ 'me.organism_id' => { -in => $organism_rs->get_column('organism_id')->as_query } });
297 my $featureloc_prefetch = { prefetch => { 'featureloc_features' => 'srcfeature' }};
298 if( my $srcfeature_id = $args->{'srcfeature_id'} ) {
299 $rs = $rs->search({ 'featureloc_features.srcfeature_id' => $srcfeature_id }, $featureloc_prefetch );
302 if( my $start = $args->{'srcfeature_start'} ) {
303 $rs = $rs->search({ 'featureloc_features.fmax' => { '>=' => $start } }, $featureloc_prefetch );
306 if( my $end = $args->{'srcfeature_end'} ) {
307 $rs = $rs->search({ 'featureloc_features.fmin' => { '<=' => $end+1 } }, $featureloc_prefetch );
310 if( my $proptype_id = $args->{'proptype_id'} ) {
311 $rs = $rs->search({ 'featureprops.type_id' => $proptype_id },{ prefetch => 'featureprops' });
314 if( my $prop_value = $args->{'prop_value'} ) {
315 $rs = $rs->search({ 'featureprops.value' => { -ilike => '%'.$prop_value.'%' }},{ prefetch => 'featureprops' });
318 if( my $desc = $args->{'description'} ) {
319 $rs = $rs->search({
320 'featureprops.value' => { -ilike => '%'.$desc.'%' },
321 'featureprops.type_id' => { -in => [description_featureprop_types( $rs )->get_column('cvterm_id')->all] },
323 prefetch => 'featureprops'
328 # set the sort order
329 $rs = $c->stash->{search_resultset} = $rs->search(
330 undef,
332 order_by => {
333 '-'.(lc $args->{dir} || 'asc' )
336 'type' => 'type.name',
337 'organism' => 'organism.species',
338 'name' => 'me.name',
339 }->{lc $args->{'sort'}}
340 || 'me.feature_id'
346 $c->stash->{search_resultset} = $rs;