1 package OpenILS::WWW::EGCatLoader;
2 use strict; use warnings;
3 use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_GONE HTTP_INTERNAL_SERVER_ERROR REDIRECT HTTP_BAD_REQUEST HTTP_NOT_FOUND);
4 use OpenSRF::Utils::Logger qw/$logger/;
5 use OpenILS::Utils::CStoreEditor qw/:funcs/;
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Application::AppUtils;
10 my $U = 'OpenILS::Application::AppUtils';
16 $ctx->{page} = 'course';
17 $ctx->{readonly} = $self->cgi->param('readonly');
19 my $course_id = $ctx->{page_args}->[0];
21 return Apache2::Const::HTTP_BAD_REQUEST
22 unless $course_id and $course_id =~ /^\d+$/;
24 $ctx->{course} = $U->simplereq(
26 'open-ils.circ.courses.retrieve',
30 $ctx->{instructors} = $U->simplereq(
32 'open-ils.circ.course_users.retrieve',
36 $ctx->{course_materials} = $U->simplereq(
38 'open-ils.circ.course_materials.retrieve.fleshed',
39 {course => $course_id}
41 return Apache2::Const::OK;
44 sub load_course_browse {
48 my $e = $self->editor;
50 my $browse_results = [];
52 # Are we searching? Cool, let's generate some links
53 if ($cgi->param('bterm')) {
54 my $bterm = $cgi->param('bterm');
55 my $qtype = $cgi->param('qtype');
56 # Search term is optional. If it's empty, start at the
57 # beginning. Otherwise, center results on a match.
58 # Regardless, we're listing everything, so retrieve all.
61 if ($qtype eq 'instructor') {
62 $instructors = $e->json_query({
64 "select" => {"acmcu" => [
69 # TODO: We need to support the chosen library as well...
70 "where" => {'+acmcu' => 'is_public'}
72 $results = $e->json_query({
74 "select" => {"au" => [
76 'pref_first_given_name',
78 'pref_second_given_name',
83 "order_by" => {'au' => ['pref_family_name', 'family_name']},
84 "where" => {'-and' => [{
90 "where" => {'-and' => [
91 {'+acmcu' => 'is_public'},
92 {"course" => { "in" =>{
97 "where" => {'-not' => [{'+acmc' => 'is_archived'}]}
104 $results = $e->json_query({
106 "select" => {"acmc" => [
113 "order_by" => {"acmc" => [$qtype]},
114 # TODO: We need to support the chosen library as well...
115 "where" => {'-not' => {'+acmc' => 'is_archived'}}
119 for my $result(@$results) {
120 my $value_exists = 0;
124 'results_count' => 0,
128 if ($qtype eq 'instructor') {
129 # Put together the name
131 if ($result->{'pref_family_name'}) {
132 $name_str = $result->{'pref_family_name'} . ", ";
133 } elsif ($result->{'family_name'}) {
134 $name_str = $result->{'family_name'} . ", ";
137 if ($result->{'pref_first_given_name'}) {
138 $name_str .= $result->{'pref_first_given_name'};
139 } elsif ($result->{'first_given_name'}) {
140 $name_str .= $result->{'first_given_name'};
143 if ($result->{'pref_second_given_name'}) {
144 $name_str .= " " . $result->{'pref_second_given_name'};
145 } elsif ($result->{'second_given_name'}) {
146 $name_str .= " " . $result->{'second_given_name'};
149 $result->{$rqtype} = $name_str;
151 # Get an accurate count of matching courses
152 for my $instructor(@$instructors) {
153 if ($instructor->{'usr'} eq $result->{'id'}) {
154 $entry->{'results_count'} += 1;
159 $entry->{'results_count'} += 1;
162 for my $existing_entry(@$browse_results) {
163 if ($existing_entry->{'value'} eq $result->{$rqtype} && $value_exists eq 0) {
165 $existing_entry->{'results_count'} += 1;
170 if ($value_exists eq 0) {
171 # For Name/Course Number browse queries...
172 if ($bterm_match eq 0) {
173 if ($result->{$qtype} =~ m/^$bterm./ || $result->{$qtype} eq $bterm) {
175 $entry->{'match'} = 1;
178 $entry->{'value'} = $result->{$rqtype};
179 push @$browse_results, $entry;
182 # Feels a bit hacky, but we need the index of the matching entry
185 for my $i (0..$#$browse_results) {
186 if ($browse_results->[$i]->{'match'}) {
193 for my $i(0..$#$browse_results) {
194 $browse_results->[$i]->{'browse_index'} = $i;
196 $ctx->{match_idx} = $match_idx;
197 $ctx->{browse_results} = $browse_results;
200 return Apache2::Const::OK;
206 my $internal = $args{internal};
207 my $cgi = $self->cgi;
208 my $ctx = $self->ctx;
209 my $e = $self->editor;
212 $ctx->{page} = 'cresult' unless $internal;
214 $ctx->{courses} = [];
215 $ctx->{hit_count} = 0;
216 $ctx->{search_ou} = $self->_get_search_lib();
217 my $page = $cgi->param('page') || 0;
218 my $offset = $page * $limit;
220 $ctx->{page_size} = $limit;
221 $ctx->{search_page} = $page;
222 $ctx->{pagable_limit} = 50;
224 # fetch this page plus the first hit from the next page
226 $limit = $offset + $limit + 1;
230 my ($user_query, $query, @queries, $modifiers) = _prepare_course_search($cgi, $ctx);
232 return Apache2::Const::OK unless $query;
234 $ctx->{user_query} = $user_query;
235 $ctx->{processed_search_query} = $query;
236 my $search_args = {};
237 my $course_numbers = ();
243 # Handle is_archived checkbox and Org Selector
244 my $search_orgs = $U->get_org_descendants($ctx->{search_ou});
245 push @$and_terms, {'owning_lib' => $search_orgs};
246 push @$and_terms, {'-not' => {'+acmc' => 'is_archived'}} unless $query =~ qr\#include_archived\;
248 # Now let's push the actual queries
249 for my $query_obj (@queries) {
250 my $type = $query_obj->{'qtype'};
251 my $query = $query_obj->{'value'};
252 my $bool = $query_obj->{'bool'};
253 my $contains = $query_obj->{'contains'};
254 my $operator = ($contains eq 'nocontains') ? '!~*' : '~*';
256 if ($type eq 'instructor') {
257 my $in = ($contains eq 'nocontains') ? "not in" : "in";
258 $search_query = {'id' => {$in => {
260 'select' => {'acmcu' => ['course']},
261 'where' => {'usr' => {'in' => {
263 'select' => {'au' => ['id']},
266 {'pref_first_given_name' => {'~*' => $query}},
267 {'first_given_name' => {'~*' => $query}},
268 {'pref_second_given_name' => {'~*' => $query}},
269 {'second_given_name' => {'~*' => $query}},
270 {'pref_family_name' => {'~*' => $query}},
271 {'family_name' => {'~*' => $query}}
277 $search_query = ($contains eq 'nocontains') ?
278 {'+acmc' => { $type => {$operator => $query}}} :
279 {$type => {$operator => $query}};
283 push @$or_terms, $search_query;
286 if ($bool eq 'and') {
287 push @$and_terms, $search_query;
291 if ($or_terms and @$or_terms > 0) {
292 if ($and_terms and @$and_terms > 0) {
293 push @$or_terms, $and_terms;
295 $where_clause = {'-or' => $or_terms};
297 $where_clause = {'-and' => $and_terms};
300 my $hits = $e->json_query({
302 "select" => {"acmc" => ['id']},
303 "where" => $where_clause
306 my $results = $e->json_query({
308 "select" => {"acmc" => [
318 "order_by" => {"acmc" => ['id']},
319 "where" => $where_clause
321 for my $result (@$results) {
322 push @{$ctx->{courses}}, {
324 course_number => $result->{course_number},
325 section_number => $result->{section_number},
326 owning_lib => $result->{owning_lib},
327 name => $result->{name},
328 is_archived => $result->{is_archived},
333 #$ctx->{courses} = $@courses;#[{id=>10, name=>"test", course_number=>"LIT"}];
334 $ctx->{hit_count} = @$hits || 0;
335 #$ctx->{hit_count} = 0;
336 return Apache2::Const::OK;
339 sub _prepare_course_search {
340 my ($cgi, $ctx) = @_;
342 my ($user_query, @queries) = _prepare_query($cgi);
346 my $query = $user_query;
347 $query .= ' ' . $ctx->{global_search_filter} if $ctx->{global_search_filter};
349 foreach ($cgi->param('modifier')) {
350 $query = ('#' . $_ . ' ' . $query) unless $query =~ qr/\#\Q$_/;
354 foreach (grep /^fi:/, $cgi->param) {
356 my $term = join(",", $cgi->param($_));
357 $query .= " $1($term)" if length $term;
360 return () unless $query;
362 return ($user_query, $query, @queries);
368 return $cgi->param('query') unless $cgi->param('qtype');
371 my @part_names = qw/qtype contains query bool modifier/;
372 $parts{$_} = [ $cgi->param($_) ] for (@part_names);
376 for (my $i = 0; $i < scalar @{$parts{'qtype'}}; $i++) {
377 my ($qtype, $contains, $query, $bool, $modifier) = map { $parts{$_}->[$i] } @part_names;
378 next unless $query =~ /\S/;
380 $contains = "" unless defined $contains;
383 contains => $contains,
389 $bool = ($bool and $bool eq 'or') ? '||' : '&&';
391 $query = "$qtype:$query";
393 $full_query = $full_query ? "($full_query $bool $query)" : $query;
396 return ($full_query, @queries);