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.courses.courses.retrieve',
30 $ctx->{instructors} = $U->simplereq(
32 'open-ils.courses.course_users.retrieve',
36 $ctx->{course_materials} = $U->simplereq(
38 'open-ils.courses.course_materials.retrieve.fleshed.atomic',
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" => [
68 # TODO: We need to support the chosen library as well...
69 "where" => {'usr_role' => {'in' => {'from' => 'acmr', 'select' => {'acmr' => ['id']}, 'where' => {'+acmr' => 'is_public'}}}}
71 $results = $e->json_query({
73 "select" => {"au" => [
75 'pref_first_given_name',
77 'pref_second_given_name',
82 "order_by" => {'au' => ['pref_family_name', 'family_name']},
83 "where" => {'-and' => [{
89 "where" => {'-and' => [
90 {'usr_role' => { 'in' => {
95 "where" => {'+acmr' => 'is_public'}}}},
96 {"course" => { "in" =>{
101 "where" => {'-not' => [{'+acmc' => 'is_archived'}]}
108 $results = $e->json_query({
110 "select" => {"acmc" => [
117 "order_by" => {"acmc" => [$qtype]},
118 # TODO: We need to support the chosen library as well...
119 "where" => {'-not' => {'+acmc' => 'is_archived'}}
123 for my $result(@$results) {
124 my $value_exists = 0;
128 'results_count' => 0,
132 if ($qtype eq 'instructor') {
133 # Put together the name
135 if ($result->{'pref_family_name'}) {
136 $name_str = $result->{'pref_family_name'} . ", ";
137 } elsif ($result->{'family_name'}) {
138 $name_str = $result->{'family_name'} . ", ";
141 if ($result->{'pref_first_given_name'}) {
142 $name_str .= $result->{'pref_first_given_name'};
143 } elsif ($result->{'first_given_name'}) {
144 $name_str .= $result->{'first_given_name'};
147 if ($result->{'pref_second_given_name'}) {
148 $name_str .= " " . $result->{'pref_second_given_name'};
149 } elsif ($result->{'second_given_name'}) {
150 $name_str .= " " . $result->{'second_given_name'};
153 $result->{$rqtype} = $name_str;
155 # Get an accurate count of matching courses
156 for my $instructor(@$instructors) {
157 if ($instructor->{'usr'} eq $result->{'id'}) {
158 $entry->{'results_count'} += 1;
163 $entry->{'results_count'} += 1;
166 for my $existing_entry(@$browse_results) {
167 if ($existing_entry->{'value'} eq $result->{$rqtype} && $value_exists eq 0) {
169 $existing_entry->{'results_count'} += 1;
174 if ($value_exists eq 0) {
175 # For Name/Course Number browse queries...
176 if ($bterm_match eq 0) {
177 if ($result->{$qtype} =~ m/^$bterm./ || $result->{$qtype} eq $bterm) {
179 $entry->{'match'} = 1;
182 $entry->{'value'} = $result->{$rqtype};
183 push @$browse_results, $entry;
186 # Feels a bit hacky, but we need the index of the matching entry
189 for my $i (0..$#$browse_results) {
190 if ($browse_results->[$i]->{'match'}) {
197 for my $i(0..$#$browse_results) {
198 $browse_results->[$i]->{'browse_index'} = $i;
200 $ctx->{match_idx} = $match_idx;
201 $ctx->{browse_results} = $browse_results;
204 return Apache2::Const::OK;
210 my $internal = $args{internal};
211 my $cgi = $self->cgi;
212 my $ctx = $self->ctx;
213 my $e = $self->editor;
216 $ctx->{page} = 'cresult' unless $internal;
218 $ctx->{courses} = [];
219 $ctx->{hit_count} = 0;
220 $ctx->{search_ou} = $self->_get_search_lib();
221 my $page = $cgi->param('page') || 0;
222 my $offset = $page * $limit;
224 $ctx->{page_size} = $limit;
225 $ctx->{search_page} = $page;
226 $ctx->{pagable_limit} = 50;
228 # fetch this page plus the first hit from the next page
230 $limit = $offset + $limit + 1;
234 my ($user_query, $query, @queries, $modifiers) = _prepare_course_search($cgi, $ctx);
236 #return Apache2::Const::OK unless $query;
238 $ctx->{user_query} = $user_query;
239 $ctx->{processed_search_query} = $query;
240 my $search_args = {};
241 my $course_numbers = ();
247 # Handle is_archived checkbox and Org Selector
248 my $search_orgs = $U->get_org_descendants($ctx->{search_ou});
249 push @$and_terms, {'owning_lib' => $search_orgs};
250 push @$and_terms, {'-not' => {'+acmc' => 'is_archived'}} unless $query =~ qr\#include_archived\;
252 # Now let's push the actual queries
253 for my $query_obj (@queries) {
254 my $type = $query_obj->{'qtype'};
255 my $query = $query_obj->{'value'};
257 my $bool = $query_obj->{'bool'};
258 my $contains = $query_obj->{'contains'};
259 my $operator = ($contains eq 'nocontains') ? '!~*' : '~*';
261 if ($type eq 'instructor') {
262 my $in = ($contains eq 'nocontains') ? "not in" : "in";
263 $search_query = {'id' => {$in => {
265 'select' => {'acmcu' => ['course']},
266 'where' => {'usr' => {'in' => {
268 'select' => {'au' => ['id']},
270 'name_kw_tsvector' => {
271 '@@' => {'value' => [ 'plainto_tsquery', $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 $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);