PrevUpHomeNext

CourseLoad

CourseLoad Nested Classes
CourseLoad Methods

The CourseLoad class is one of the main access points to the API functionality.

A CourseLoad represents a course load (recall that this a choice of study session and a choice of courses to take in that study session) as well as associated information such as selected activity configurations for the course load and generated timetables for these activity configurations. CourseLoad contains methods that allow the user to interact with the course load, such as by selecting courses, selecting activity configurations, generating timetables, and sorting the timetables by various criteria. As such, CourseLoad is one of the most important classes in the API.

A CourseLoad is associated with a user session, and is created by calling NewCourseLoad() or OpenCourseLoad() on the UserSession object representing the user session with which it is to be associated. The CourseLoad is freed when its destructor is caled. Note that CourseLoad is moveable but not copyable; user code can move it around as desired.

Here is an outline of the CourseLoad class:

class CourseLoad
{
public:
    // Nested classes
    struct IErrorHandler
    {
        enum class response { ignore, abort };
        virtual response course_not_found(const string& activity_key) = 0;
        virtual response session_not_found(const Offering& offering) = 0;
    };

    // API functions
    bool IsUpToDate() const;
    string GetFileName() const;
    void Save(const string& filename);
    course_load_state GetState() const;
    vector<StudySession> GetStudySessions() const;
    string GetCurrentStudySession() const;
    void SelectStudySession(size_t study_session_index);
    void ClearStudySession();
    CourseList GetCoursesForSelectedStudySession() const;
    vector<Offerings> GetSelectedCourses() const;
    void SelectCourses(const vector<string>& activity_keys);
    void ClearSelectedCourses();
    void SpecifyTerm(const string& activity_code, session_type term);
    bool GenerateConfigurations();
    void UnspecifyTerms();
    void SetConfigurations(const vector<ActivityConfiguration>& configs);
    size_t GetConfigCount() const;
    void SelectConfigurations(const vector<size_t>& configs);
    size_t GetSelectedConfigCount() const;
    size_t GetActivityCount() const;
    ActivityConfiguration GetConfig(size_t config_index) const;
    ActivityConfiguration GetSelectedConfig(size_t config_index) const;
    void ClearSelectedConfigurations();
    void GenerateTimetables(ProgressIndicator progress_indicator, function<void(GenerationStatistics)> generation_stats_callback);
    vector<bool> GenerateTimetables(ProgressIndicator progress_indicator, function<void(GenerationStatistics)> generation_stats_callback, const vector<size_t>& timetable_limits);
    size_t GetTimetableCount(size_t config_index) const;
    const Timetable& GetTimetable(size_t config_index, size_t timetable_index) const;
    void ClearTimetables();
    vector<pair<string, string>> GetConflicts(size_t config_index) const;
    timetable_sort_criteria GetCurrentSortCriteria(size_t config_index) const;
    void SortTimetables(size_t config_index, const timetable_sort_criteria& sort_criteria, ProgressIndicator progress_indicator);
    size_t GetMemoryUsagePerTimetable(size_t config_index) const;
};

The interface IErrorHandler allows clients to specify how various errors should be handled when opening a course load file. An error can either be ignored (see below for the specific semantics for each error), in which case opening of the course load resumes, or aborted, in which case an exception is thrown.

IErrorHandler has one method for each type of potentially-recoverable error that can occur during the opening of a course load file. Every time a potentially-recoverable error occurs, the appropriate method is called, with details about the specific error (for example, the activity key of a course that could not be found) passed as arguments to the call, and the method's return value will determine how the error is handled.

The IErrorHandler instance on which the methods are called is passed in as an optional argument to UserSession::OpenCourseLoad(). The default value for this argument is an error handler which aborts for all errors. To specify a different behaviour, clients should derive from IErrorHandler and pass in an object of their derived type to UserSession::OpenCourseLoad().

Note that, since clients get details about the errors that occurred, they can implement a variety of end-user behaviours - for example, warning the user that a course in the course load was not found and showing the course code of the offending course.

struct IErrorHandler
{
    enum class response { ignore, abort };
    virtual response course_not_found(const string& activity_key) = 0;
    virtual response session_not_found(const Offering& offering) = 0;
};

enum class response { ignore, abort };

An enumeration used to specify the desired response to an error when opening a course load file. The error can either be ignored (see below for the specific semantics for each error), or the error can cause the opening of the course load to be aborted.

virtual response course_not_found(const string& activity_key) = 0;

Called when a course specified in the course load file could not be found in the timetable data. This can happen if all offerings the course have been cancelled since the time the course load file was saved. activity_key is the activity key of the course that could not be found. If the return value is response::ignored, the course is removed from the course load.

virtual response session_not_found(const Offering& offering) = 0;

Called when a session (offering) of a course specified in the course load file could not be found in the timetable data. This can happen if the offering has been cancelled since the time the course load file was saved. If the return value is response::ignored, the configuration that the offering participated in is removed from the course load.

bool CourseLoad::IsUpToDate() const;

Checks whether the course load is up to date; that is, whether any modifications have been made to it since being created or being last saved using Save().

string CourseLoad::GetFileName() const;

Get the filename that the course load was last saved as (using Save()), or from which the course load was loaded (if created using OpenCourseLoad()).

The returned filename is empty if the course load was not loaded from a file and has not been saved.

void CourseLoad::Save(const string& path);

Save the course load to a file with the given path, in a plain text format. If path is not absolute, it is interpreted as being relative to the client program's current working directory. The exact format is not exposed by the API; the file is meant to be opened later by OpenCourseLoad().

Note: only information about which study session, courses, and configurations were selected is saved, not the actual timetables. The actual timetables can be re-generated after the saved course load is opened with OpenCourseLoad().

Requires: GetState() == course_load_state::timetables_generated

course_load_state CourseLoad::GetState() const;

Return the current state of the course load.

See also: course_load_state

vector<StudySession> CourseLoad::GetStudySessions() const;

Returns a list of all study sessions. There will be one for each line in sessions.txt.

string CourseLoad::GetCurrentStudySession() const;

Return the name of the study session that is currently selected.

Requires: GetState() >= course_load_state::session_selected

void CourseLoad::SelectStudySession(size_t study_session_index);

Select the study session identified by the specified index (into the list returned by GetStudySessions()).

After calling this function, the course load state will be course_load_state::session_selected.

Requires: GetState() == course_load_state::empty

void CourseLoad::ClearStudySession();

De-select the currently selected study session.

After calling this function, the course load state will be course_load_state::empty.

Requires: GetState() == course_load_state::session_selected

CourseList CourseLoad::GetCoursesForSelectedStudySession() const;

Get all the courses available for the currently selected study session, represented as a CourseList.

Requires: GetState() >= course_load_state::session_selected

vector<Offerings> CourseLoad::GetSelectedCourses() const;

Get a list of the currently selected courses.

Requires: GetState() >= course_load_state::courses_selected

void CourseLoad::SelectCourses(const vector<string>& activity_keys);

Select the courses identified by the specified keys. The key should come from the key field of the Offerings structure for the course.

The course load state after this operation depends on whether or not there are any half-courses that are offered in both terms among the selected courses. If there are none, so that only one activity configuration is possible for this course load, that one activity configuration is automatically selected, and the course load state will be course_load_state::configs_selected. In this case, the next API function to call is GenerateTimetables(). Otherwise, the course load state will be course_load_state::courses_selected and the next API function to call is GenerateConfigurations() (optionally preceded by some calls to SpecifyTerm()).

To determine which of these courses of actions to take, call GetState() after this operation and see what it returns.

Requires: GetState() == course_load_state::session_selected

void CourseLoad::ClearSelectedCourses();

De-select all selected courses.

After this operation, the course load state willbe course_load_state::session_selected

Requires: GetState() == course_load_state::courses_selected

void CourseLoad::SpecifyTerm(const string& activity_code, session_type term);

Restrict the possible activity configurations that will be generated when calling GenerateConfigurations() to the ones where the course identified by the specified activity code runs in the specified term. The course in question must be a half-course that runs in both terms.

This operation does not change the course_load_state, but will affect which configurations are generated when calling GenerateConfigurations() later.

Observe that by calling this function for every selected course that is offered in both terms, the set of possible activity configurations can be narrowed down to one; in this case, the subsequent call to GenerateConfigurations() will automatically select that one configuration and advance the course load state to course_load_state::configs_selected. If generating timetables for a single activity configuration is desired, then prefer specifying that activity configuration this way rather than using SelectConfigurations().

Requires: GetState() == course_load_state::courses_selected

See also: Activity Configuration Fine Points

bool CourseLoad::GenerateConfigurations();

Generate the set of possible activity configurations for the course load. The generated activity configurations will be available for examination via GetConfigCount() and GetConfig(), and selection via SelectConfigurations().

When generating configurations, terms specified in any previous calls to SpecifyTerm() are taken into account; that is, configurations where a course runs in a different term than the term specified for that course by SpecifyTerm() will not be generated.

Not all possible configurations are generated, only ones that are balanced between the two semesters (where possible, subject to the constraints imposed by half-courses that run only in one term, and half-courses which run in both terms but for which we are only considering one of the terms due to a previous call to SpecifyTerm()). If unbalanced configurations are desired, they can be specified using calls to SpecifyTerm(). (If multiple unbalanced configurations are desired, SetConfigurations() can be used to specify them instead).

The course load state after this operation depends on the number of configurations generated. If only one or two configurations were generated, the generated configuration(s) are auto-selected and the course load state is advanced to course_load_state::configs_selected. In this case, GenerateConfigurations() returns true, and the next API function to call is GenerateTimetables(). If more than two configurations were generated, the course load state remains course_load_state::courses_selected, GenerateConfigurations() returns false, and the next API function that should be called is SelectConfigurations().

Note: The rationale for auto-selecting the generated configuration if only one configuration is generated is that that would be the only choice to select anyways. The rationale for auto-selecting the generated configurations if two configurations are generated is that the API assumes that those configuration choices that can be expressed using SpecifyTerm(), have been expressed using SpecifyTerm() prior to calling GenerateConfigurations(). SelectConfigurations() is meant only to make configuration choices that cannot be expressed using SpecifyTerm(). For example, if four half-courses CSC148H1, CSC165H1, CHM138H1, and CHM139H1 are taken, and you want to take any of them in either term , but subject to the constraint that CHM138H1 and CHM139H1 cannot be taken in the same term, this constraint cannot be expressed by any calls to SpecifyTerm() - instead you must call GenerateConfigurations() without making any calls to SpecifyTerm(), and then use SelectConfigurations() to weed out the configurations where CHM138H and CHM139H1 run in the same term. In the case where only two configurations are generated, it must be the case that the two configurations differ only by the placement of a half-course into one term or the other. If you had wanted a specific term for this half course, you should have specified so using SpecifyTerm() - since you didn't, the API assumes that you want to take the course in either semester, and goes ahead and auto-selects both configurations. In cases where more than two configurations are generated, no such intent can be deduced, since it could be a situation like the one with CHM138H1/CHM139H1.

Requires: GetState() == course_load_state::courses_selected

See also: Activity Configuration Fine Points

void CourseLoad::UnspecifyTerms();

Undo the effect of any calls made to SpecifyTerm() for this course load. A subsequent call to GenerateConfigurations() will use both terms for all half-courses that are offered in both terms (unless SpecifyTerm() is called again before calling GenerateConfigurations()).

Requires: GetState() == course_load_state::courses_selected

void CourseLoad::SetConfigurations(const vector<ActivityConfiguration>& configs);

Set the set of available activity configurations to the ones in configs. This call undoes the effects of any previous calls to SpecifyTerm() or GenerateConfigurations().

This can be used to achieve selecting multiple unbalanced activity configurations, something which is impossible using SpecifyTerm() and GenerateConfigurations() (see Activity Configuration Fine Points for details).

Requires: GetState() == course_load_state::courses_selected

size_t CourseLoad::GetConfigCount() const;

Return the number of possible activity configurations for this course load.

Requires: GetState() >= course_load_state::courses_selected and GenerateConfigurations() or SetConfigurations() has been called (or SelectCourses() auto-generated and auto-selected configurations).

void CourseLoad::SelectConfigurations(const vector<size_t>& configs);

Select the activity configurations identified by the specified indices for this course load.

configs is a list of indices into the list of possible activity configurations, which is accessible via the functions GetConfigCount() and GetConfig().

Timetables will be generated for the selected activity configurations when calling GenerateTimetables().

After this operation, the course load state will be course_load_state::configs_selected

Requires: GetState() == course_load_state::courses_selected and GenerateConfigurations() or SetConfigurations() has been called.

size_t CourseLoad::GetSelectedConfigCount() const;

Get the number of selected activity configurations for this course load.

Requires: GetState() >= course_load_state::configs_selected

size_t CourseLoad::GetActivityCount() const;

Get the number of selected courses for this course load.

Requires: GetState() >= course_load_state::courses_selected

ActivityConfiguration CourseLoad::GetConfig(size_t i) const;

Get the ith activity configuration for this course load. i can range from 0 to GetConfigCount() - 1.

Requires: GetState() >= course_load_state::courses_selected and GenerateConfigurations() or SetConfigurations() has been called (or SelectCourses() auto-generated and auto-selected configurations).

ActivityConfiguration CourseLoad::GetSelectedConfig(size_t i) const;

Get the ith selected activity configuration for this course load. i can range from 0 to GetSelectedConfigCount - 1. Note that a configuration's index in the list of all configurations (as accessible via GetConfigCount() and GetConfig(), and that same configuration's index in the list of selected configurations (as accessible via GetSelectedConfigCount() and GetSelectedConfig(), are not necessarily the same.

Requires: GetState() >= course_load_state::configs_selected

void CourseLoad::ClearSelectedConfigurations();

De-select all selected activity configurations for this course load.

After this operation, the course load state will be course_load_state::courses_selected. The list of possible configurations, as accessible via GetConfigCount() and GetConfig(), is not cleared, and remains accessible (it can be re-generated with different SpecifyTerm() constraints if desired, by calling UnspecifyTerms(), making the calls to SpecifyTerm(), and calling GenerateConfigurations() again.

Requires: GetState() == course_load_state::configs_selected

void CourseLoad::GenerateTimetables(ProgressIndicator progress_indicator, function<void(GenerationStatistics)> generation_stats_callback);

vector<bool> CourseLoad::GenerateTimetables(ProgressIndicator progress_indicator, function<void(GenerationStatistics)> generation_stats_callback, const vector<size_t>& timetable_limits);

Generate all conflict-free timetables for all selected activity configurations for this course load. The generated timetables will be accessible via GetTimetableCount() and GetTimetable().

Note that while for most course loads, the number of conflict-free timetables is relatively low (anywhere from one or two to a few hundred or a few thousand), there are some course loads for which the number of conflict-free timetables can be in the millions (typically these will be first-year life science course loads, where almost all of the courses have lecture, tutorial, and practical sections, with as many as 10 choices for each). The timetable generation algorithm is extremely efficient - capable of generating hundreds of thousands of timetables per second - but for these course loads, the generation process may take a user-noticeably long time. For this reason, two callbacks are provided that allow the generation algorithm to keep the caller updated about the status of the generation process.

The progress_indicator callback is used to indicate the percentage completion of the generation process. See ProgressIndicator for details. Please note that due to the nature of the generation algorithm, the percentage completion figure is just an estimate and may not increase at a constant rate.

The generation_stats_callback callback is used to indicate various other statistics about the generation process, such as the number of timetables generated so far, the elapsed time, and the average generation speed. Any function or function object that is callable with a GenerationStatistics argument can be used for this callback. Note that this callback will be called relatively infrequently - about once every 20,000 generated timetables. Its main purpose is to allow clients to inform their users of the progress of the generation process in cases where the process takes a user-noticeably long time.

If either callback throws an exception, the API will allow the thrown exception to propagate out of the GenerateTimetables() call and the course load state will remain at course_load_state::configs_selected, with any timetables generated being discarded.

Otherwise, after the call completes the course load state will be course_load_state::timetables_generated.

The three-argument version allows specifying, for each selected activity configuration, a limit on the number of timetables that will be generated for that configuration. If a limit is specified, the number of timetables generated for the configuration will be no greater than the limit. If there are more valid timetables than the limit, it is not specified which of the valid timetables are generated. A limit must be specified for every selected configuration, but the special value 0 can be used to indicate that no limit should be observed for a given configuration. This version returns a vector<bool> whose size equals the number of selected configurations. For each configuration, the boolean is true if all possible valid timetables were generated without exceeding the limit, and false otherwise.

Requires: GetState() == course_load_state::configs_selected

size_t CourseLoad::GetTimetableCount(size_t config_index) const;

Get the number of timetables generated for the selected activity configuration identified by config_index, for this course load.

config_index should be the same as what you'd pass to GetSelectedConfig().

Requires: GetState() == course_load_state::timetables_generated

const __Timetable_page_rep__& CourseLoad::GetTimetable(size_t config_index, size_t timetable_index) const;

Get the generated timetable identified by timetable_index for the selected activity configuration identified by config_index, for this course load.

config_index should be the same as what you'd pass to GetSelectedConfig().

timetable_index can range from 0 to GetTimetableCount(config_index) - 1.

The return value of this function is a reference to an object that lives inside the API implementation. Clients may make a copy of this object if desired, but this is unnecessary (and inefficient).

Requires: GetState() == course_load_state::timetables_generated

void CourseLoad::ClearTimetables();

Clear all generated timetables for this course load. Timetables are cleared for all selected activity configurations.

After this operation, the course load state will be course_load_state::configs_selected.

Requires: GetState() == course_load_state::timetables_generated

vector<pair<string, string>> CourseLoad::GetConflicts(size_t config_index) const;

Get a list of pairs of strings, with each pair describing a pair of conflicting meeting sections for the selected activity configuration identified by config_index for this course load.

Each string describes a meeting section by its activity/session code, section code, and list of timeslots.

This is useful in cases where there are no conflict-free timetables for a given activity configuration (GetTimetableCount() == 0), and you want to help the user understand why.

Requires: GetState() == course_load_state::timetables_generated

timetable_sort_criteria CourseLoad::GetCurrentSortCriteria(size_t config_index) const;

Get the list of sort criteria used in the most recent call to SortTimetables() for the specified selected configuration in this course load. If SortTimetables() has never been called for this configuration in this session, an empty list is returned.

Requires: GetState() == course_load_state::timetables_generated

void CourseLoad::SortTimetables(size_t config_index, const timetable_sort_criteria& sort_criteria, ProgressIndicator progress_indicator);

Sort the generated timetables for the selected activity configuration identified by config_index for this course load, using the specified sort criteria.

sort_criteria should contain a subset of the sort criteria returned by GetTimetableSortCriteria()

The order of sort_criteria is important: the timetables will be sorted first by the first criteria; then, each set of timetables that are equivalent according to the first criteria are sorted by the second criteria; and so on.

After sorting, GetTimetable(config_index, 0) will be the best timetable according to the sort criteria, GetTimetable(config_index, 1) will be the next best, and so on.

If there are many timetables (millions), the sorting process may take a user-noticeably long time, so a progress indicator is provided to allow the function to report its progress from time to time. See ProgressIndicator for details.

Requires: GetState() == course_load_state::timetables_generated

size_t CourseLoad::GetMemoryUsagePerTimetable(size_t config_index) const;

Get the number of bytes of memory used by the internal representation of each timetable for the selected activity configuration identified by config_index for the this course.

This is provided to help the client choose appropriate timetable limits when calling the version of GenerateTimetables() that takes a timetable limit for each selected configuration, in cases where the purpose of setting the limit is to constrain the total memory consumed by the library after generating timetables.

Requires: GetState() >= course_load_state::configs_selected


PrevUpHomeNext