File Coverage

blib/lib/WebService/UWO/Directory/Student.pm
Criterion Covered Total %
statement 30 67 44.7
branch 1 34 2.9
condition 2 13 15.3
subroutine 7 9 77.7
pod 2 2 100.0
total 42 125 33.6


line stmt bran cond sub pod time code
1             # WebService::UWO::Directory::Student
2             # Retrieve student information from the Western Student Directory
3             #
4             # $Id: Student.pm 10608 2009-12-23 16:06:17Z FREQUENCY@cpan.org $
5            
6             package WebService::UWO::Directory::Student;
7            
8 4     4   95498 use strict;
  4         12  
  4         146  
9 4     4   25 use warnings;
  4         9  
  4         103  
10 4     4   23 use Carp ();
  4         6  
  4         91  
11            
12 4     4   4449 use LWP::UserAgent;
  4         268893  
  4         147  
13 4     4   15163 use HTML::Entities ();
  4         527212  
  4         5276  
14            
15             =head1 NAME
16            
17             WebService::UWO::Directory::Student - Perl module for searching the UWO
18             student directory
19            
20             =head1 VERSION
21            
22             Version 1.004 ($Id: Student.pm 10608 2009-12-23 16:06:17Z FREQUENCY@cpan.org $)
23            
24             =cut
25            
26             our $VERSION = '1.004';
27             $VERSION = eval $VERSION;
28            
29             =head1 DESCRIPTION
30            
31             This module provides a Perl interface to the public directory search system
32             which lists current students at the University of Western Ontario. For more
33             information, see the web interface at L.
34            
35             =head1 SYNOPSIS
36            
37             Example code:
38            
39             use WebService::UWO::Directory::Student;
40            
41             # Create Perl interface to API
42             my $dir = WebService::UWO::Directory::Student->new;
43            
44             # Look up a student by name
45             my $results = $dir->lookup({
46             first => 'John',
47             last => 'S'
48             });
49            
50             # Go through results
51             foreach my $stu (@{$results}) {
52             print 'email: ' . $stu->email . "\n";
53             }
54            
55             # Reverse a lookup (use e-mail to find record)
56             my $reverse = $dir->lookup({
57             email => 'jsmith@uwo.ca'
58             });
59            
60             if (defined $reverse) {
61             print "Found: $reverse\n";
62             }
63            
64             =head1 COMPATIBILITY
65            
66             This module was tested under Perl 5.10.0, using Debian Linux. However, because
67             it's Pure Perl and doesn't do anything too obscure, it should be compatible
68             with any version of Perl that supports its prerequisite modules.
69            
70             If you encounter any problems on a different version or architecture, please
71             contact the maintainer.
72            
73             =head1 METHODS
74            
75             =head2 new
76            
77             WebService::UWO::Directory::Student->new( \%params )
78            
79             Creates a C search object, which uses a given web page
80             and server. Being that this module is developed to target UWO's in-house
81             system, the defaults should suffice.
82            
83             The parameters available are:
84            
85             my $dir = UWO::Directory::Student->new({
86             url => 'http://uwo.ca/cgi-bin/dsgw/whois2html2',
87             server => 'localhost',
88             });
89            
90             Which instantiates a C instance using C as the
91             frontend and C as the "black-box" backend.
92            
93             =cut
94            
95             sub new {
96 1     1 1 2858 my ($class, $params) = @_;
97            
98 1   50     18 my $self = {
      50        
99             url => $params->{url} || 'http://uwo.ca/cgi-bin/dsgw/whois2html2',
100             server => $params->{server} || 'localhost',
101             };
102            
103 1         6 return bless($self, $class);
104             }
105            
106             =head2 lookup
107            
108             $dir->lookup( \%params )
109            
110             Uses a C search object to locate a given
111             person based on either their name (C and/or C) or their e-mail
112             address (C).
113            
114             The module uses the following procedure to locate users:
115            
116             =over
117            
118             =item 1
119            
120             If an e-mail address is provided:
121            
122             =over
123            
124             =item 1
125            
126             The address is deconstructed into a first initial and the portion of the last
127             name. (According to the regular expression C<^(\w)([^\d]+)([\d]*)$>)
128            
129             =item 2
130            
131             The partial name is looked up in the directory.
132            
133             =item 3
134            
135             The resulting records are tested against the e-mail address. If the e-mail
136             address matches a given record, an anonymous hash containing user information
137             is returned. The lookup returns a false value (0) upon failure.
138            
139             =back
140            
141             =item 2
142            
143             If first and/or last names are provided:
144            
145             =over
146            
147             =item 1
148            
149             The name is searched using the normal interface (using the query
150             C) and the results are returned as an array reference.
151             If there are no results, the method returns a false value (0).
152            
153             =back
154            
155             =back
156            
157             Example code:
158            
159             # Look up "John S" in the student directory
160             my $results = $dir->lookup({
161             first => 'John',
162             last => 'S'
163             });
164            
165             # Look up jsmith@uwo.ca
166             my $reverse = $dir->lookup({
167             email => 'jsmith@uwo.ca'
168             });
169            
170             This method is not guaranteed to return results. Keep in mind that if no
171             results are found, the return code will be 0, so make sure to check return
172             codes before attempting to dereference the expected array/hash.
173            
174             =head3 Record Format
175            
176             Each returned record will be a hash with the following fields:
177            
178             =over
179            
180             =item *
181            
182             last_name,
183            
184             =item *
185            
186             given_name (which may contain middle names)
187            
188             =item *
189            
190             email (the registered @uwo.ca e-mail address)
191            
192             =item *
193            
194             faculty
195            
196             =back
197            
198             You may explore this using C.
199            
200             =cut
201            
202             sub lookup {
203 0     0 1 0 my ($self, $params) = @_;
204            
205 0 0       0 Carp::croak('You must call this method as an object')
206             unless ref $self;
207            
208 0 0       0 Carp::croak('Parameter not a hash reference')
209             unless ref($params) eq 'HASH';
210            
211 0 0 0     0 Carp::croak('No search parameters provided')
      0        
212             unless(
213             exists($params->{first}) ||
214             exists($params->{last}) ||
215             exists($params->{email})
216             );
217            
218 0 0       0 $params->{first} = '' unless defined($params->{first});
219 0 0       0 $params->{last} = '' unless defined($params->{last});
220            
221             # Don't do anything in void context
222 0 0       0 unless (defined wantarray) {
223 0         0 Carp::carp('Output from function discarded');
224 0         0 return;
225             }
226            
227 0 0       0 if (exists $params->{email}) {
228 0         0 my $query;
229 0 0       0 if ($params->{email} =~ /^(\w+)(\@uwo\.ca)?$/s) {
230 0         0 $query = $1;
231            
232             # no domain provided, assume @uwo.ca for matching
233 0 0       0 if (!defined($2)) {
234             # This is intentionally not interpolated
235             ## no critic(RequireInterpolationOfMetachars)
236 0         0 $params->{email} .= '@uwo.ca';
237             }
238             }
239             else {
240 0         0 Carp::croak('Only UWO usernames and addresses can be searched');
241             }
242            
243             # Discover query by deconstructing the username
244             # jdoe32
245             # First name: j
246             # Last name: doe
247             # E-mail: jdoe32@uwo.ca
248 0 0       0 if ($query =~ /^(\w)([^\d]+)([\d]*)$/s) {
249 0         0 my $result = $self->lookup({
250             first => $1,
251             last => $2,
252             });
253 0         0 foreach my $stu (@{$result}) {
  0         0  
254 0 0       0 return $stu if ($stu->{email} eq $params->{email});
255             }
256             }
257             else {
258 0         0 Carp::croak('Given username does not match UWO username pattern');
259             }
260             }
261             else {
262 0         0 my $query;
263            
264             # If both first and last are given
265 0 0 0     0 if (length $params->{first} && length $params->{last}) {
    0          
266 0         0 $query = $params->{last} . ',' . $params->{first};
267             }
268             # First name only
269             elsif (length $params->{first}) {
270 0         0 $query = $params->{first} . '.';
271             }
272             # Last name only
273             else {
274 0         0 $query = $params->{last} . ',';
275             }
276            
277 0         0 return _parse($self->_query($query));
278             }
279 0         0 return 0;
280             }
281            
282             =head1 UNSUPPORTED API
283            
284             C provides access to some internal
285             methods used to retrieve and process raw data from the directory server. Its
286             behaviour is subject to change and may be finalized later as the need arises.
287            
288             =head2 _query
289            
290             $dir->_query( $query, $ua )
291            
292             This method performs an HTTP lookup using C and returns a
293             SCALAR reference to the returned page content. A C object may
294             optionally be passed, which is particularly useful if a proxy is required to
295             access the Internet.
296            
297             Please note that if a C is passed, the User-Agent string will
298             not be modified. In normal operation, this module reports its user agent as
299             C<'WebService::UWO::Directory::Student/' . $VERSION>.
300            
301             =cut
302            
303             sub _query {
304 0     0   0 my ($self, $query, $ua) = @_;
305            
306 0 0       0 Carp::croak('You must call this method as an object') unless ref($self);
307            
308 0 0       0 if (!defined $ua) {
309 0         0 $ua = LWP::UserAgent->new;
310 0         0 $ua->agent(__PACKAGE__ . '/' . $VERSION);
311             }
312            
313 0         0 my $r = $ua->post($self->{'url'},
314             {
315             server => $self->{'server'},
316             query => $query,
317             });
318            
319 0 0       0 Carp::croak('Error reading response: ' . $r->status_line)
320             unless $r->is_success;
321            
322 0         0 return \$r->content;
323             }
324            
325             =head2 _parse
326            
327             WebService::UWO::Directory::Student::_parse( $response )
328            
329             This method processes the HTML content retrieved by _query method and returns
330             an ARRAY reference containing HASH references to the result set. This is most
331             likely only useful for testing purposes.
332            
333             =cut
334            
335             sub _parse {
336 1     1   33 my ($data) = @_;
337            
338 1 50       5 Carp::croak('Expecting a scalar reference') unless ref($data) eq 'SCALAR';
339            
340 1         3 HTML::Entities::decode_entities(${$data});
  1         1222  
341            
342             # Record format from the directory server:
343             # Full Name: Last,First Middle
344             # E-mail: e-mail@uwo.ca
345             # Registered In: Faculty Name
346            
347             # 4 fields captured
348            
349             # We don't want the \n swallowed in .+
350             ## no critic(RequireDotMatchAnything)
351 1         46 my @matches = (
352 1         14 ${$data} =~ m{
353             [ ]{4}Full\ Name:\ ([^,]+),(.+)\n
354             [ ]{7}E-mail:.*\>(.+)\\n
355             Registered\ In:\ (.+)\n
356             }xg
357             );
358            
359 1         4 my $res;
360             # Requires an irregular count - in steps of 4
361             ## no critic (ProhibitCStyleForLoops)
362            
363             # Copy the fields four at a time based on the above regular expression
364 1         13 for (my $i = 0; $i < scalar(@matches); $i += 4) {
365 2         18 my $stu = {
366             last_name => $matches[$i],
367             given_name => $matches[$i+1],
368             email => $matches[$i+2],
369             faculty => $matches[$i+3],
370             };
371 2         7 push(@{$res}, $stu);
  2         14  
372             }
373            
374 1         9 return $res;
375             }
376            
377             =head1 AUTHOR
378            
379             Jonathan Yu Ejawnsy@cpan.orgE
380            
381             =head1 SUPPORT
382            
383             You can find documentation for this module with the perldoc command.
384            
385             perldoc WebService::UWO::Directory::Student
386            
387             You can also look for information at:
388            
389             =over
390            
391             =item * AnnoCPAN: Annotated CPAN documentation
392            
393             L
394            
395             =item * CPAN Ratings
396            
397             L
398            
399             =item * Search CPAN
400            
401             L
402            
403             =item * CPAN Request Tracker
404            
405             L
406            
407             =item * CPAN Testing Service (Kwalitee Tests)
408            
409             L
410            
411             =item * CPAN Testers Platform Compatibility Matrix
412            
413             L
414            
415             =back
416            
417             =head1 REPOSITORY
418            
419             You can access the most recent development version of this module at:
420            
421             L
422            
423             If you are a CPAN developer and would like to make modifications to the code
424             base, please contact Adam Kennedy Eadamk@cpan.orgE, the repository
425             administrator. I only ask that you contact me first to discuss the changes you
426             wish to make to the distribution.
427            
428             =head1 FEEDBACK
429            
430             Please send relevant comments, rotten tomatoes and suggestions directly to the
431             maintainer noted above.
432            
433             If you have a bug report or feature request, please file them on the CPAN
434             Request Tracker at L. If you are able to submit your bug
435             report in the form of failing unit tests, you are B encouraged to do
436             so.
437            
438             =head1 SEE ALSO
439            
440             L, the site this module uses
441             to query the database
442            
443             =head1 CAVEATS
444            
445             =head2 KNOWN BUGS
446            
447             There are no known bugs as of this release.
448            
449             =head2 LIMITATIONS
450            
451             =over
452            
453             =item *
454            
455             This module is only able to access partial student records since students must
456             give consent for their contact information to be published on the web. For
457             more, see L.
458            
459             =item *
460            
461             Some students change their name (for example, a marriage), while retainining
462             the same email address. This means their email addresses cannot be effectively
463             reverse-searched.
464            
465             =item *
466            
467             This module has not been very thoroughly tested for memory consumption. It
468             does a lot of copying that should be optimized, however, it is probably not
469             necessary for most uses.
470            
471             =back
472            
473             =head1 LICENSE
474            
475             In a perfect world, I could just say that this package and all of the code
476             it contains is Public Domain. It's a bit more complicated than that; you'll
477             have to read the included F file to get the full details.
478            
479             =head1 DISCLAIMER OF WARRANTY
480            
481             The software is provided "AS IS", without warranty of any kind, express or
482             implied, including but not limited to the warranties of merchantability,
483             fitness for a particular purpose and noninfringement. In no event shall the
484             authors or copyright holders be liable for any claim, damages or other
485             liability, whether in an action of contract, tort or otherwise, arising from,
486             out of or in connection with the software or the use or other dealings in
487             the software.
488            
489             =cut
490            
491             1;