function _FlightPath::assign_courses_to_list

4.x _FlightPath.php _FlightPath::assign_courses_to_list(ObjList $list_requirements, Student $student, $bool_perform_assignment = true, Group $group = null, $bool_check_significant_courses = false)
5.x _FlightPath.php _FlightPath::assign_courses_to_list(ObjList $list_requirements, Student $student, $bool_perform_assignment = true, Group $group = null, $bool_check_significant_courses = false)

File

classes/_FlightPath.php, line 665

Class

_FlightPath

Code

function assign_courses_to_list(ObjList $list_requirements, Student $student, $bool_perform_assignment = true, Group $group = null, $bool_check_significant_courses = false) 
 {

  $count = 0;

  if ($group == null) 
   {
    $group = new Group();
    $group->group_id = 0;
  }
  else if ($group->min_grade != "") {
    $group->assign_min_grade($group->min_grade); // If this is a group with a min grade value, let's make sure we assign all the courses in it to this min grade.
  }


  $sort_policy = variable_get("initial_student_course_sort_policy", "alpha"); // will either be "alpha" or "grade"




  $bool_disallow_graduate_credits = (variable_get("disallow_graduate_credits", "yes") == "yes") ? TRUE : FALSE;
  $graduate_level_codes_array = csv_to_array(variable_get("graduate_level_codes", "GR"));

  // Get the course repeat policy.
  $course_repeat_policy = variable_get("course_repeat_policy", "most_recent_exclude_previous");
  // Set the $bool_mark_repeats_exclude variable based on the course_repeat_policy.
  $bool_mark_repeats_exclude = ($course_repeat_policy == "most_recent_exclude_previous" || $course_repeat_policy == "best_grade_exclude_others");

  $group_id = $group->group_id;
  // If the group_id == 0, we may be talking about the bare degree plan.

  $hours_required = $group->hours_required * 1;
  $hours_assigned = $group->hours_assigned;

  $meet_min_hours = 999999; // effectively infinite by default, to make logic easier later on.


  // If the group has min_hours, then we should allow the user to get at least the min hours before we stop trying to fill.
  if ($group->has_min_hours_allowed() && variable_get("group_full_at_min_hours", "yes") == "yes") {
    $meet_min_hours = $group->min_hours_allowed;
  }

  if ($hours_required <= 0 || $hours_required == "") 
   {
    $hours_required = 999999;
  }


  $list_requirements->sort_smallest_hours_first();
  // sort the requirement list by the best grades that the student has made?  Similar to the substitutions?
  if ($sort_policy == "grade") {
    $list_requirements->sort_best_grade_first($student);
  }
  else if ($sort_policy == "alpha") {
    $list_requirements->sort_alphabetical_order();
  }

  $list_requirements->sort_substitutions_first($student->list_substitutions, $group_id);


  $list_requirements->reset_counter();
  while ($list_requirements->has_more()) 
   {

    $course_requirement = $list_requirements->get_next();
    $req_by_degree_id = $course_requirement->req_by_degree_id; // what degree is requiring this course?

    // If we're dealing with a group, use it's required by degree id.
    if ($group->group_id != "" && $group->req_by_degree_id > 0) {
      $req_by_degree_id = $group->req_by_degree_id;
    }


    if ($bool_check_significant_courses == true) 
     {
      // Only look for the course_requirement if it is in the student's
      // array_significant_courses array.
      if (isset($student->array_significant_courses [$course_requirement->course_id]) && $student->array_significant_courses [$course_requirement->course_id] != true) 
       { // course was not in there, so skip!
        continue;
      }
    }


    if ($course_requirement->bool_specified_repeat == true) 
     {
      // Since this requirement has specified repeats, we want
      // to make all of the student's taken courses (for this course)
      // also have specified repeats.
      $student->list_courses_taken->set_specified_repeats($course_requirement, $course_requirement->specified_repeats);
    }



    // Does the student have any substitutions for this requirement?
    if ($substitution = $student->list_substitutions->find_requirement($course_requirement, true, $group_id, $req_by_degree_id)) 
     {
      // Since the substitution was made, I don't really care about
      // min grades or the like.  Let's just put it in.

      if ($bool_perform_assignment == TRUE) 
       {
        // Check to see if this course requirement has "infinite repeats".  If it does, then we want to
        // clone it and add it back into our list of requirements.
        if ($course_requirement->specified_repeats == Group::GROUP_COURSE_INFINITE_REPEATS) {
          $temp_ds = $course_requirement->to_data_string();
          $new_course = new Course();
          $new_course->load_course_from_data_string_for_requirement_clone($temp_ds);
          $new_course->min_grade = $course_requirement->min_grade;
          $list_requirements->add($new_course);
        }


        // Make sure this isn't a group addition and we are *currently*
        // NOT looking at the group it is being added to.  This is to
        // correct a bug.
        if ($substitution->bool_group_addition == true) 
         {
          if ($substitution->course_requirement->get_first_assigned_to_group_id() != $group_id) 
           {
            continue;
          }

        }

        // If the course_requirement's min_hours are greater than
        // the substitution's hours, then we have to split the
        // coureRequirement into 2 pieces, and add the second piece just
        // after this one in the list.
        $course_sub = $substitution->course_list_substitutions->get_first();
        if ($course_requirement->min_hours * 1 > $course_sub->get_hours_awarded($req_by_degree_id)) 
         {

          // Because float math can create some very strange results, we must
          // perform some rounding.  We will round to 6 decimal places, which should
          // provide us the accuracy w/o losing precision (since we can only represent a max
          // of 4 decimals in the database anyway.
          $remaining_hours = round($course_requirement->min_hours - $course_sub->get_hours_awarded($req_by_degree_id), 6);

          $new_course_string = $course_requirement->to_data_string();
          $new_course = new Course();
          $new_course->load_course_from_data_string($new_course_string);
          $new_course->min_hours = $new_course->max_hours = $remaining_hours;
          $new_course->set_bool_substitution_split($req_by_degree_id, TRUE);
          $new_course->set_bool_substitution_new_from_split($req_by_degree_id, TRUE);
          $new_course->requirement_type = $course_requirement->requirement_type;
          $new_course->min_grade = $course_requirement->min_grade;
          $new_course->req_by_degree_id = $req_by_degree_id;
          $new_course->assigned_to_degree_ids_array [$req_by_degree_id] = $req_by_degree_id;

          $course_requirement->set_bool_substitution_split($req_by_degree_id, TRUE);

          // I am commenting this out-- if we split up a sub multiple times, then we shouldn't
          // set the old course requirement to say it WASN'T from a split.  This was causing a bug
          // where the pie charts got weird if you did more than 1 split.  Was counting total
          // hours as more, in CourseList->count_hours().
          //$course_requirement->bool_substitution_new_from_split = false;




          // Only do this if we are NOT in a group!  This is to correct a bug where split additions to groups wound up
          // being displayed like available selections in the group.  It was like, weird man.  
          if ($group_id == 0) {
            // Now, add this into the list, right after the course_requirement.
            $current_i = $list_requirements->i;
            $list_requirements->insert_after_index($current_i, $new_course);
          }

        } // if min_hours > sub's awarded hours


        $course_requirement->course_list_fulfilled_by = $substitution->course_list_substitutions;

        $substitution->course_list_substitutions->assign_group_id($group_id);
        $substitution->course_list_substitutions->set_has_been_assigned(true);
        $substitution->course_list_substitutions->set_bool_substitution($req_by_degree_id, TRUE);
        $course_requirement->db_substitution_id_array [] = $substitution->db_substitution_id;
        $substitution->course_list_substitutions->set_course_substitution($course_requirement, $substitution->remarks, $req_by_degree_id);

        $substitution->bool_has_been_applied = TRUE;

        if ($group_id != "" && intval($group_id) > 0) {
          // We are going to add the course_sub's hours in as being assigned to this list, if this is for a group.
          // I am surprised this wasn't already here.  This fixes a bug with groups that allow min hours.  
          $hours_assigned += $course_sub->get_hours_awarded($req_by_degree_id);
        }

      }
      $count++;
      continue;
    } // if student has any substitutions for this requirement



    // Has the student taken this course requirement?
    if ($c = $student->list_courses_taken->find_best_match($course_requirement, $course_requirement->min_grade, $bool_mark_repeats_exclude, $req_by_degree_id, TRUE, TRUE, $group_id)) 
     {


      $h_get_hours = $c->get_hours();
      if ($c->bool_ghost_hour) {
        // If this is a ghost hour, then $h_get_hours would == 0 right now,
        // instead, use the the adjusted value (probably 1).
        $h_get_hours = $c->get_hours_awarded($req_by_degree_id);
      }

      // Can we assign any more hours to this group?  Are we
      // out of hours, and should stop?
      if ($hours_assigned >= $hours_required || $hours_assigned >= $meet_min_hours) 
       {
        continue;
      }

      $c_hours_awarded = $c->get_hours_awarded($req_by_degree_id);

      // Will the hours of this course put us over the hours_required limit?  NOTE:  We don't care if it puts us over the min_hours, in fact, we want that.
      if ($hours_assigned + $c_hours_awarded > $hours_required) 
       {
        continue;
      }

      // Do not apply substitution new_from_split courses to anything automatically.  (from any degree!)
      // They must be applied by substitutions.
      if ($c->get_bool_substitution_new_from_split() == TRUE) 
       {
        continue;
      }


      // Make sure the course meets min grade requirements.
      if (!$c->meets_min_grade_requirement_of($course_requirement)) 
       {
        continue;
      }


      // Has the course been unassigned from this group?
      if ($c->group_list_unassigned->find_match_with_degree_id($group, $req_by_degree_id)) 
       {
        continue;
      }


      // Make sure $c is not being used in a substitution (for this degree)
      if ($c->get_bool_substitution($req_by_degree_id) == TRUE) {
        continue;
      }

      // If this is a graduate level course, and we are not allowing grad credits, then skip!
      if ($c->level_code != "" && in_array($c->level_code, $graduate_level_codes_array)) {
        if ($bool_disallow_graduate_credits) {
          continue;
        }
      }



      // Check hooks to see if this course is allowed to be assigned to the GROUP in question.
      if ($group_id != "" && $group_id != 0) {
        $bool_can_proceed = TRUE;


        $result = invoke_hook("flightpath_can_assign_course_to_group", array($group, $c));
        foreach ($result as $m => $val) {
          // If *any* module said FALSE, then we must skip this course and not assign it to this degree.
          if ($val === FALSE) {
            $bool_can_proceed = $val;
          }
        }

        if (!$bool_can_proceed) {
          continue; // don't assign!
        }

      } // if group_id != ""


      // We want to see if this course has already been assigned to THIS degree...
      if (!in_array($req_by_degree_id, $c->assigned_to_degree_ids_array)) 
       { //Don't count courses which have already been placed in other groups.

        // Check hooks to see if this course is allowed to be assigned to the degree in question.
        $bool_can_proceed = TRUE;
        $result = invoke_hook("flightpath_can_assign_course_to_degree_id", array($req_by_degree_id, $c));
        foreach ($result as $m => $val) {
          // If *any* module said FALSE, then we must skip this course and not assign it to this degree.
          if ($val === FALSE) {
            $bool_can_proceed = $val;
          }
        }

        if (!$bool_can_proceed) {
          continue; // don't assign!
        }



        // Has another version of this course already been
        // assigned?  And if so, are repeats allowed for this
        // course?  And if so, then how many hours of the
        // repeat_hours have I used up?  If I cannot do any more
        // repeats, then quit.  Otherwise, let it continue...

        $course_list_repeats = $student->list_courses_taken->get_previous_assignments($c->course_id);


        if ($course_list_repeats->get_size() > 0) 
         {
          // So, a copy of this course has been assigned more than once...
          // Get the total number of hours taken up by this course.
          $cc = $course_list_repeats->count_hours();
          // have we exceeded the number of available repeat_hours
          // for this course?
          if ($course_requirement->repeat_hours <= 0) 
           {
            $course_requirement->load_descriptive_data();
          }

          if (($course_requirement->bool_ghost_hour != TRUE || $c->bool_ghost_hour != TRUE)
           && $cc + $h_get_hours > $course_requirement->repeat_hours * 1) 
           {
            // Do not allow the repeat, unless we are talking about courses worth zero hours.
            // meaning, they have a ghost hour.  In which case, allow it.
            continue;
          }



        }

        // Basically--- if the most recent attempt fails
        // a min grade check, then tag all attempts as "unuseable"
        // so that they can't be used in other groups.  --
        // unless they are able to be repeated.  BARF!

        // Inc hours_assigned, even if we aren't actually
        // performing an assignment.  This helps us accurately
        // calculate the count.

        $hours_assigned = $hours_assigned + $h_get_hours;



        if ($bool_perform_assignment == TRUE) 
         {

          // If this course has an "infinite" number of specified repeats, then
          // let's actually clone the course and insert it as a new requirement, to replace
          // the one we're about to use. 
          if ($course_requirement->specified_repeats == Group::GROUP_COURSE_INFINITE_REPEATS) {
            $temp_ds = $course_requirement->to_data_string();
            $new_course = new Course();
            $new_course->load_course_from_data_string_for_requirement_clone($temp_ds);
            $new_course->min_grade = $course_requirement->min_grade;

            $list_requirements->add($new_course);

          }

          // Which degree is this coming from?  
          //$req_by_degree_id = $course_requirement->req_by_degree_id;
          $c->assigned_to_degree_ids_array [$req_by_degree_id] = $req_by_degree_id;
          // Go ahead and state that the requirement was fulfilled.
          $course_requirement->course_list_fulfilled_by->add($c);
          $course_requirement->grade = $c->grade;
          $course_requirement->set_hours_awarded($req_by_degree_id, $c->get_hours_awarded($req_by_degree_id));
          $course_requirement->bool_ghost_hour = $c->bool_ghost_hour;

          // No longer using... using the assigned_to_degree_ids_array instead. // $c->bool_has_been_assigned = true;
          //$c->requirement_type = $course_requirement->requirement_type;
          if ($c->requirement_type == "") {
            // No requirement type given?  Perhaps we are part of a group.  If so, use that.
            //$c->requirement_type = $group->requirement_type;    
            //$course_requirement->requirement_type = $group->requirement_type;         
          }

          // Check what groups it has been assigned to already.
          //$c->assigned_to_group_id = $group_id;
          if ($group_id > 0) {
            $c->assigned_to_group_ids_array [$group_id . "_" . $req_by_degree_id] = $group_id;
          }

          $group->hours_assigned = $hours_assigned;

          // Should check for:
          // Can it be assigned, based on the number of allowed course repeats?
          if ($course_requirement->bool_specified_repeat == true) 
           {
            // $c is what they actually took.
            $c->bool_specified_repeat = true;
            $c->specified_repeats = $course_requirement->specified_repeats;
            $list_requirements->dec_specified_repeats($c);
          }
        }


        $count++;
      }
    }

  }

  return $count;
}