]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Course.pm
LP1849212: Use a set list of roles for course users
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / WWW / EGCatLoader / Course.pm
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;
8 use Net::HTTP::NB;
9 use IO::Select;
10 my $U = 'OpenILS::Application::AppUtils';
11
12 sub load_course {
13     my $self = shift;
14     my $ctx = $self->ctx;
15
16     $ctx->{page} = 'course';
17     $ctx->{readonly} = $self->cgi->param('readonly');
18
19     my $course_id = $ctx->{page_args}->[0];
20
21     return Apache2::Const::HTTP_BAD_REQUEST
22         unless $course_id and $course_id =~ /^\d+$/;
23
24     $ctx->{course} = $U->simplereq(
25         'open-ils.courses',
26         'open-ils.courses.courses.retrieve',
27         [$course_id]
28     )->[0];
29     
30     $ctx->{instructors} = $U->simplereq(
31         'open-ils.courses',
32         'open-ils.courses.course_users.retrieve',
33         $course_id
34     );
35
36     $ctx->{course_materials} = $U->simplereq(
37         'open-ils.courses',
38         'open-ils.courses.course_materials.retrieve.fleshed.atomic',
39         {course => $course_id}
40     );
41     return Apache2::Const::OK;
42 }
43
44 sub load_course_browse {
45     my $self = shift;
46     my $cgi = $self->cgi;
47     my $ctx = $self->ctx;
48     my $e = $self->editor;
49
50     my $browse_results = [];
51
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.
59         my $results;
60         my $instructors;
61         if ($qtype eq 'instructor') {
62             $instructors = $e->json_query({
63                 "from" => "acmcu",
64                 "select" => {"acmcu" => [
65                     'id',
66                     'usr',
67                 ]},
68                 # TODO: We need to support the chosen library as well...
69                 "where" => {'usr_role' => {'in' => {'select' => {'acmr' => ['id']}, 'where' => {'+acmr' => 'is_public'}}}}
70             });
71             $results = $e->json_query({
72                 "from" => "au",
73                 "select" => {"au" => [
74                     'id',
75                     'pref_first_given_name',
76                     'first_given_name',
77                     'pref_second_given_name',
78                     'second_given_name',
79                     'pref_family_name',
80                     'family_name'
81                 ]},
82                 "order_by" => {'au' => ['pref_family_name', 'family_name']},
83                 "where" => {'-and' => [{
84                     "id" => { "in" => {
85                         "from" => "acmcu",
86                         "select" => {
87                             "acmcu" => ['usr']
88                         },
89                         "where" => {'-and' => [
90                             {'usr_role' => { 'in' => {
91                                 'from' => 'acmr',
92                                 "select" => {
93                                     "acmr" => ['id']
94                                 },
95                                 "where" => {'+acmr' => 'is_public'}}}},
96                             {"course" => { "in" =>{
97                                 "from" => "acmc",
98                                 "select" => {
99                                     "acmc" => ['id']
100                                 },
101                                 "where" => {'-not' => [{'+acmc' => 'is_archived'}]}
102                             }}}
103                         ]}
104                     }}
105                 }]}
106             });
107         } else {
108             $results = $e->json_query({
109                 "from" => "acmc",
110                 "select" => {"acmc" => [
111                     'id',
112                     'name',
113                     'course_number',
114                     'is_archived',
115                     'owning_lib'
116                 ]},
117                 "order_by" => {"acmc" => [$qtype]},
118                 # TODO: We need to support the chosen library as well...
119                 "where" => {'-not' => {'+acmc' => 'is_archived'}}
120             });
121         }
122         my $bterm_match = 0;
123         for my $result(@$results) {
124             my $value_exists = 0;
125             my $rqtype = $qtype;
126             my $entry = {
127                 'value' => '',
128                 'results_count' => 0,
129                 'match' => 0
130             };
131
132             if ($qtype eq 'instructor') {
133                 # Put together the name
134                 my $name_str = '';
135                 if ($result->{'pref_family_name'}) {
136                     $name_str = $result->{'pref_family_name'} . ", ";
137                 } elsif ($result->{'family_name'}) {
138                     $name_str = $result->{'family_name'} . ", ";
139                 }
140
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'};
145                 }
146
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'};
151                 }
152
153                 $result->{$rqtype} = $name_str;
154
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;
159                         last;
160                     }
161                 }
162             } else {
163                 $entry->{'results_count'} += 1;
164             }
165
166             for my $existing_entry(@$browse_results) {
167                 if ($existing_entry->{'value'} eq $result->{$rqtype} && $value_exists eq 0) {
168                     $value_exists = 1;
169                     $existing_entry->{'results_count'} += 1;
170                     last;
171                 }
172             }
173
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) {
178                         $bterm_match = 1;
179                         $entry->{'match'} = 1;
180                     }
181                 }
182                 $entry->{'value'} = $result->{$rqtype};
183                 push @$browse_results, $entry;
184             }
185         }
186         # Feels a bit hacky, but we need the index of the matching entry
187         my $match_idx = 0;
188         if ($bterm_match) {
189             for my $i (0..$#$browse_results) {
190                 if ($browse_results->[$i]->{'match'}) {
191                     $match_idx = $i;
192                     last;
193                 }
194             }
195         }
196
197         for my $i(0..$#$browse_results) {
198             $browse_results->[$i]->{'browse_index'} = $i;
199         }
200         $ctx->{match_idx} = $match_idx;
201         $ctx->{browse_results} = $browse_results;
202     }
203
204     return Apache2::Const::OK;
205 }
206
207 sub load_cresults {
208     my $self = shift;
209     my %args = @_;
210     my $internal = $args{internal};
211     my $cgi = $self->cgi;
212     my $ctx = $self->ctx;
213     my $e = $self->editor;
214     my $limit = 10;
215
216     $ctx->{page} = 'cresult' unless $internal;
217     $ctx->{ids} = [];
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;
223     my $results;
224     $ctx->{page_size} = $limit;
225     $ctx->{search_page} = $page;
226     $ctx->{pagable_limit} = 50;
227
228     # fetch this page plus the first hit from the next page
229     if ($internal) {
230         $limit = $offset + $limit + 1;
231         $offset = 0;
232     }
233
234     my ($user_query, $query, @queries, $modifiers) = _prepare_course_search($cgi, $ctx);
235
236     return Apache2::Const::OK unless $query;
237
238     $ctx->{user_query} = $user_query;
239     $ctx->{processed_search_query} = $query;
240     my $search_args = {};
241     my $course_numbers = ();
242     
243     my $where_clause;
244     my $and_terms = [];
245     my $or_terms = [];
246
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\;
251
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'};
256         my $bool = $query_obj->{'bool'};
257         my $contains = $query_obj->{'contains'};
258         my $operator = ($contains eq 'nocontains') ? '!~*' : '~*';
259         my $search_query;
260         if ($type eq 'instructor') {
261             my $in = ($contains eq 'nocontains') ? "not in" : "in";
262             $search_query = {'id' => {$in => {
263                 'from' => 'acmcu',
264                 'select' => {'acmcu' => ['course']},
265                 'where' => {'usr' => {'in' => {
266                     'from' => 'au',
267                     'select' => {'au' => ['id']},
268                     'where' => {
269                         '-or' => [
270                             {'pref_first_given_name' => {'~*' => $query}},
271                             {'first_given_name' => {'~*' => $query}},
272                             {'pref_second_given_name' => {'~*' => $query}},
273                             {'second_given_name' => {'~*' => $query}},
274                             {'pref_family_name' => {'~*' => $query}},
275                             {'family_name' => {'~*' => $query}}
276                         ]
277                     }
278                 }}}
279             }}};
280         } else {
281             $search_query = ($contains eq 'nocontains') ?
282               {'+acmc' => { $type => {$operator => $query}}} :
283               {$type => {$operator => $query}};
284         }
285
286         if ($bool eq 'or') {
287             push @$or_terms, $search_query;
288         }
289
290         if ($bool eq 'and') {
291             push @$and_terms, $search_query;
292         }
293     }
294
295     if ($or_terms and @$or_terms > 0) {
296         if ($and_terms and @$and_terms > 0) {
297             push @$or_terms, $and_terms;
298         }
299         $where_clause = {'-or' => $or_terms};
300     } else {
301         $where_clause = {'-and' => $and_terms};
302     }
303
304     my $hits = $e->json_query({
305         "from" => "acmc",
306         "select" => {"acmc" => ['id']},
307         "where" => $where_clause
308     });
309
310     my $results = $e->json_query({
311         "from" => "acmc",
312         "select" => {"acmc" => [
313             'id',
314             'name',
315             'course_number',
316             'section_number',
317             'is_archived',
318             'owning_lib'
319         ]},
320         "limit" => $limit,
321         "offset" => $offset,
322         "order_by" => {"acmc" => ['id']},
323         "where" => $where_clause
324     });
325     for my $result (@$results) {
326         push @{$ctx->{courses}}, {
327             id => $result->{id},
328             course_number => $result->{course_number},
329             section_number => $result->{section_number},
330             owning_lib => $result->{owning_lib},
331             name => $result->{name},
332             is_archived => $result->{is_archived},
333             instructors => []
334         }
335     }
336
337     #$ctx->{courses} = $@courses;#[{id=>10, name=>"test", course_number=>"LIT"}];
338     $ctx->{hit_count} = @$hits || 0;
339     #$ctx->{hit_count} = 0;
340     return Apache2::Const::OK;
341 }
342
343 sub _prepare_course_search {
344     my ($cgi, $ctx) = @_;
345
346     my ($user_query, @queries) = _prepare_query($cgi);
347     my $modifiers;
348     $user_query //= '';
349
350     my $query = $user_query;
351     $query .= ' ' . $ctx->{global_search_filter} if $ctx->{global_search_filter};
352
353     foreach ($cgi->param('modifier')) {
354         $query = ('#' . $_ . ' ' . $query) unless $query =~ qr/\#\Q$_/;
355
356     }
357     # filters
358     foreach (grep /^fi:/, $cgi->param) {
359         /:(-?\w+)$/ or next;
360         my $term = join(",", $cgi->param($_));
361         $query .= " $1($term)" if length $term;
362     }
363
364     return () unless $query;
365
366     return ($user_query, $query, @queries);
367 }
368
369 sub _prepare_query {
370     my $cgi = shift;
371
372     return $cgi->param('query') unless $cgi->param('qtype');
373
374     my %parts;
375     my @part_names = qw/qtype contains query bool modifier/;
376     $parts{$_} = [ $cgi->param($_) ] for (@part_names);
377
378     my $full_query = '';
379     my @queries;
380     for (my $i = 0; $i < scalar @{$parts{'qtype'}}; $i++) {
381         my ($qtype, $contains, $query, $bool, $modifier) = map { $parts{$_}->[$i] } @part_names;
382         next unless $query =~ /\S/;
383
384         $contains = "" unless defined $contains;
385
386         push @queries, {
387             contains => $contains,
388             bool => $bool,
389             qtype => $qtype,
390             value => $query
391         };
392
393         $bool = ($bool and $bool eq 'or') ? '||' : '&&';
394
395         $query = "$qtype:$query";
396
397         $full_query = $full_query ? "($full_query $bool $query)" : $query;
398     }
399
400     return ($full_query, @queries);
401 }