--- loncom/enrollment/localenroll.pm 2014/06/26 15:53:29 1.50 +++ loncom/enrollment/localenroll.pm 2021/06/15 20:52:27 1.61 @@ -1,6 +1,6 @@ # functions to glue school database system into Lon-CAPA for # automated enrollment -# $Id: localenroll.pm,v 1.50 2014/06/26 15:53:29 raeburn Exp $ +# $Id: localenroll.pm,v 1.61 2021/06/15 20:52:27 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -39,8 +39,6 @@ described at http://www.lon-capa.org. =head1 NOTABLE SUBROUTINES -=over - =cut package localenroll; @@ -48,6 +46,8 @@ package localenroll; use strict; =pod + +=over =item run() set this to return 1 if you want the auto enrollment to run @@ -144,7 +144,7 @@ sub run() { Manager, or via the 'Upload a class list','Enroll a single student' or 'Modify student data' utilities in the Enrollment Manager, by checking the 'make these dates the default for future enrollment' checkbox. If no default - dates have been set, then the tudent role will be active immediately, and will + dates have been set, then the student role will be active immediately, and will remain active until the role is explicitly expired using ENRL -> Drop students. If dates are to included in the XML file, they should be in the format YYYY:MM:DD:HH:MM:SS (: separators required). @@ -207,7 +207,7 @@ sub fetch_enrollment { ("001","601","602") would be returned If the array returned contains at least one element, then - the interface offerred to the course coordinator, lists + the interface offered to the course coordinator, lists official sections and provides a checkbox to use to select enrollment in the LON-CAPA course from each official section. @@ -258,9 +258,11 @@ sub get_sections { username:domain (c) the LON-CAPA domain that contains the course - new_course also takes a fourth (optional) argument - + new_course also takes optional fourth and fifth arguments - (d) the course co-owners, as a comma-separated list of username:domain for - any co-owners. + any co-owners. + (e) database handle (might be set when new_course() is called by check_section + routine within localenroll.pm). =cut @@ -340,6 +342,32 @@ sub validate_instcode { =pod +=item validate_crosslist_access() + +This is called for an official course to check whether a course +with the institutional code can have access to enrollment data +from a cross-listed institutional section code, given a co-owner. + +validate_crosslist_access() takes four arguments - +(a) the course's LON-CAPA domain +(b) the institional course code assigned to the course +(c) the institutional course section code for the crosslisting +(d) the co-owner to check for affiliation with the crosslisting + (username:domain). + +A combination of (a), (b), (c) and (d) with access to enrollment +data, as per institutional policies, is confirmed by returning 'valid'. + +=cut + +sub validate_crosslist_access { + my ($dom,$instcode,$inst_xlist,$coowner) = @_; + my $outcome = ''; + return $outcome; +} + +=pod + =item validate_crsreq() This is used to check whether a course request should be processed @@ -347,9 +375,10 @@ automatically, or held in a queue pendin the institution. Course requests will trigger this check if the process type has been set -to 'validate' for the course type (official, unofficial or community) and -the requestor's affiliation. Whether "validate" is an available option -in the Domain Configuration menu is controlled by auto_courserequest_checks(). +to 'validate' for the course type (official, unofficial, textbook, +placement or community) and the requestor's affiliation. Whether +"validate" is an available option in the Domain Configuration menu +is controlled by auto_courserequest_checks(). One scenario is where the request is for an official course, in which case a check could be made that the requestor is listed as instructor of record for the course in the institution's course schedule/database. @@ -358,10 +387,10 @@ Other scenarios are possible, and the ro to whatever rules a domain wishes to implement to run validations against given the data passed in to the routine. -validate_crsreq takes six arguments - +validate_crsreq takes seven arguments - (a) the LON-CAPA domain that will contain the course. (b) the username:domain for the course owner. - (c) the course type (official, unofficial or community) + (c) the course type (official, unofficial,textbook, placement or community) (d) a comma-separated list of institutional affiliations of the course owner. (e) the institutional code (in the MSU case this is a concatenation of @@ -369,21 +398,27 @@ validate_crsreq takes six arguments - (f) a comma-separated list of institutional sections included in the course request (only applicable to official courses). (g) an optional reference to a hash of custom form data. - The custom form data will come from crsreq_updates(). + The custom form data will come from crsreq_updates(), with one + additional item: $custominfo->{'_LC_clonefrom'}, provided internally + (the courseID of the LON-CAPA course being cloned). A valid courserequest is confirmed by returning 'process'. The following can be returned: process, rejected, pending, approval or error (with error condition - no :), followed by a : and then an optional message. (a) process - the requestor is the recorded instructor - create the course + (b) rejected - the requestor should never be requesting this course, reject the request permanently + (c) pending - the requestor is not the recorded instructor, but could become so after administrative action at the institution. Put the request in a queue and, if an official course, check localenroll:validate_instcode() periodically until the status changes to "valid". + (d) approval - the request will be held pending review by a Domain Coordinator. + (e) error (followed by the error condition). =cut @@ -402,8 +437,9 @@ This is used to determine whether the "v possible choices for course request processing in the Domain Configuration menu for Course Requests. Ultimately it is called by domainprefs.pm (via: lonnet -> lond -> localenroll.pm) The domain configuration menu includes -a table where columns are course type (official, unofficial or community) -and rows are institutional affiliations (e.g., Faculty, Staff, Student etc.). +a table where columns are course type (official, unofficial, textbook, +placement or community) and rows are institutional affiliations +(e.g., Faculty, Staff, Student etc.). crsreq_checks() takes three arguments: $dom, $reqtypes, $validations. $dom - the domain for which validation options are needed. @@ -444,6 +480,42 @@ sub crsreq_checks { return 'ok'; } +=pod + +=item crsreq_updates() + +This is used to customize the LON-CAPA course request process. +There are two hash references: $incoming, and $outgoing; $incoming can +contain additional information collected from the requester, whereas $outgoing +can contain custom items to send back to lonrequestcourse.pm, which creates the +HTML displayed to the user during a course request. + +Different key-value pairs may be returned to lonrequestcourse.pm in the $outgoing +hashref depending on the current action. The available actions are: +review, prevalidate, process, created and queued. + +One scenario would be to return HTML markup in: $outgoing->{'reviewweb'}, +i.e., where the action is 'review', to prompt the user to provide additional +information as part of the course request, at the request review stage, +(i.e,, the page which contains the button used to submit a completed course request). + +The HTML could contain form elements (e.g., radio buttons etc.). The value(s) +selected by the requester in those form elements will be available in the incoming +hashref, for a subsequent action, if the corresponding keys have been included +in $outgoing->{'formitems'}, i.e., $outgoing will be hash of a hash. If a +particular form item will the single valued, the value set for the key in the +inner hash in $outgoing should be 1, otherwise, if it will be multi-valued, +the value should be multiple. + +The $outgoing hashref can contain a 'formitems' key for both the prevalidate +and process actions, as calls to localenroll::crsreq_update() can originate +in lonrequestcourse::process_request() for both of those actions. + +The retrieved form values are passed to localenroll::validate_crsreq() as the +optional seventh arg (a hashref) -- $custominfo. + +=cut + sub crsreq_updates { my ($cdom,$cnum,$crstype,$action,$ownername,$ownerdomain,$fullname,$title, $code,$accessstart,$accessend,$incoming,$outgoing) = @_; @@ -498,6 +570,189 @@ sub crsreq_updates { =pod +=item export_grades() + +This routine can be customized to push grade information to some other gradebook, +LCMS, or administrative system external to LON-CAPA. + +export_grades() takes five arguments - +(a) the LON-CAPA course ID +(b) the LON-CAPA course domain +(c) a hash reference containing the following: + scope => scope of the grades (e.g., course, map or resource). + instcode => institutional course code (if an official course) + crstype => course type -- Course, Community or Placement + context => calling context, e.g., "completion" when a student completes a placement test. +(d) a perl data structure (hash of a hash) containing the grade data. + in the outer hash, the keys are student's username:domain + in the inner hash, keys are: + id => student/employee ID + lastname => student's last name + firstname => student's first name + email => student's "permannent" e-mail address + section => student's LON-CAPA course section + total => total points earned + bytitle => reference to a hash (keys are question titles, values are points + bysymb => reference to a hash (keys are symbs, i.e., unique resource identifiers). +(e) reference to a hash which will contain information to return. + keys will be the student's username:domain. Value of 1 to show grades pushed + successfully. + +=cut + +sub export_grades { + my ($cnum,$cdom,$hashref,$dataref,$outgoing) = @_; + my %info; + if (ref($hashref) eq 'HASH') { + %info = %{$hashref}; + } + if ((ref($dataref) eq 'HASH') && (ref($outgoing) eq 'HASH')) { + foreach my $key (keys(%{$dataref})) { + $outgoing->{$key} = 1; + } + return 'ok'; + } else { + return 'error'; + } +} + +=pod + +=item check_instclasses() + + This is used to supply information about which instituional course sections + and cross-listings are available to supply enrollment data, given the current + list of owner and co-owners. The data are used to populate the column titled: + "Auto-enrollment of registered students" when showing full detailed for a course + in the course catalog. + + This subroutine takes four arguments - + + (a) $owners - comma-separated list of username:domain for course owner + and co-owners. + (b) $dom - domain of course. + (c) $classes - reference to hash of institutional course sections and + crosslistings for which access to enrollment data is being checked. + (d) $validated - reference to hash which will be populated with all + keys from incoming $classes hashref, for which one or more of the + owner/co-owners has rights to access enrollment data. For each + key included in $validated hashref, corresponding value will be set to 1. + + The subroutine returns 'ok' if there is no processing error. + +=cut + + +sub check_instclasses { + my ($owners,$dom,$classes,$validated) = @_; + if ((ref($classes) eq 'HASH') && (ref($validated) eq 'HASH')) { + foreach my $class (keys(%{$classes})){ + if (&check_section($class,$owners,$dom) eq 'ok') { + $validated->{$class} = 1; + } + } + } + return 'ok'; +} + +=pod + +=item instsec_reformat() + + Inputs: $dom, $action, $instsecref + + $dom is the course's domain + $action is either: clutter or declutter + $instsecref is a reference to a hash, in which each key is + course num:course code, and each value is either an array of + institutional sections, or (in the case of crosslisted courses) + an array of institutional course sections. + + Returns: ok + + Side effects: will modify the items in the array as determined by + code implemented for the domain. Modification will differ depending + on whether the action is clutter or declutter. + + The idea is that "clutter" will modify the name of the section such + that a concatenation of institutional code then (modified) section + will result in a string that other customized routines in localenroll.pm + can separate without ambiguity into instituional code then (real) + institutional section using a regular expression. + + Conversely, "declutter" will modify the name of an already modified + item such that display of the concatenated string (e.g., for a + crosslisting in the course catalog) does not include the "added" + characters used to eliminate ambiguity. + + Examples (MSU): + + Starting in Fall 2021 at MSU, institution section numbers are no + longer guaranteed to be three digit numbers (including leading zeroes). + + So, for example the course code: fs21phy183b might have sections: + 001, 002, LEC1, LEC2, and be crosslisted with fs21phy233b (with + sections: 730, LEC3, LEC4). + + The sections: LEC1, and LEC2 should be changed to _LEC1, and _LEC2 + before creating the inner keys in the %affiliates hash of a hash, + passed to fetch_enrollment() in Enrollment.pm. They will however + be stored in the course's environment as LEC1 and LEC2. + + For the crosslistings, LEC3 and LEC4 should be changed to + _LEC3 and _LEC4 before storing in the course's environment.db file. + + In both cases when it comes time to extract the various components + of an institutional section code (i.e., the concatenated string) in + fetch_enrollment(), for example, the regexp used at MSU would be: + + if ($class =~ m/^([suf]s)(\d{2})(\w{2,4})(\d{3,4}[A-Za-z]?)(\d{3}|_[A-Za-z0-9]{1,5})$/) { + my ($sem,$yr,$subj,$crse,$sec) = ($1,$2,$3,$4,$5); + + The three digit sections would match the \d{3} and the other sections + (LEC1, LEC2 etc.) would match the _[A-Za-z0-9]{1,5}. + + The customization in &instsec_reformat() would be: + + if ($action eq 'clutter') { + unless ($item =~ /^\d{3}$/) { + $item = '_'.$item; + } + } elsif ($action eq 'declutter') { + if ($item =~ /^([suf]s\d{2}\w{2,4}\d{3,4}[A-Za-z]?)(\d{3}|_[A-Za-z0-9]{1,5})$/) { + my ($instcode,$instsec) = ($1,$2); + $instsec =~ s/^_//; + $item = $instcode.$instsec; + } elsif ($item =~ /^_[A-Za-z0-9]{1,5}$/) { + $item =~ s/^_//; + } + } + +=cut + +sub instsec_reformat { + my ($dom,$action,$instsecref) = @; + if ((ref($instsecref) eq 'HASH') && + (($action eq 'clutter') || ($action eq 'declutter'))) { + foreach my $key (keys(%{$instsecref})) { + if (ref($instsecref->{$key}) eq 'ARRAY') { + foreach my $sec (@{$instsecref->{$key}}) { + if ($action eq 'clutter') { + # modify the section, as needed. + next; + } elsif ($action eq 'declutter') { + # modify the section, as needed. + next; + } + } + } + } + } + return 'ok'; +} + +=pod + =item create_password() This is called when the authentication method set for the automated @@ -863,8 +1118,8 @@ sub allusers_info { institutional types to check. (g) $srchby - optional if $uname or $id defined, otherwise required. Allowed values include: 1. lastfirst, 2. last, 3. uname - corresponding to searches by 1. lastname,firstname; - 2. lastname; 3. username + 4. email, corresponding to searches by 1. lastname,firstname; + 2. lastname; 3. username; 4. e-mail address (h) $srchterm - optional if $uname or $id defined, otherwise required String to search for. (i) $srchtype - optional. Allowed values: contains, begins (defaults @@ -893,6 +1148,47 @@ sub get_userinfo { return $outcome; } +=pod + +=item get_multusersinfo + + (a) $dom - domain + (b) $type - username or id + (c) $unamenames - reference to hash containing usernames of users + (d) $instusers - reference to hash which will contain info for user + as key = value; keys will be one or all of: + lastname,firstname,middlename,generation,id,inststatus - + institutional status (e.g., faculty,staff,student) + Values are all scalars except inststatus, + which is an array. + (e) $instids - reference to hash which will contain ID numbers - + keys will be unique IDs (student or faculty/staff ID) + values will be either: scalar (username) or an array + if a single ID matches multiple usernames. + + returns 1 parameter - 'ok' if no processing error, or other value + if an error occurred. + + side effects - populates the $instusers and $instids refs to hashes. + with information for specified username, or specified + id, if fifth argument provided, from all available, or + specified (e.g., faculty only) institutional datafeeds, + if sixth argument provided. + + WARNING: You need to set $outcome to 'ok' once you have customized + this routine to communicate with an instititional + directory data source, otherwise retrieval of institutional + user information will always be reported as being unavailable + in domain $dom. + +=cut + +sub get_multusersinfo { + my ($dom,$type,$usernames,$instusers,$instids) = @_; + my $outcome = 'unavailable'; + return $outcome; +} + =pod =item inst_usertypes()