Skip to content

Commit

Permalink
Merge pull request #37 from ewang2002/chore/add-examples
Browse files Browse the repository at this point in the history
chore: add examples
  • Loading branch information
ewang2002 committed Aug 8, 2023
2 parents b1d5ddb + 3480fb3 commit 6d4e68f
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 247 deletions.
257 changes: 10 additions & 247 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
An asynchronous API wrapper, written in Rust, for the [University of California San Diego](https://ucsd.edu/)'s [WebReg](https://act.ucsd.edu/webreg2/start) course enrollment system.

## Usage
To use this crate, please see the **Install** section of webweg's [crates.io](https://crates.io/crates/webweg) page.
To use this crate, run the following command:
```
cargo add webweg
```
See the [crates.io](https://crates.io/crates/webweg) page for more information.

## Wrapper Features
A lot of the things that you can do on WebReg can be done with this
Expand All @@ -30,6 +34,8 @@ You're also able to do things like:
- Create, remove, or rename your schedules.
- Send a confirmation email to yourself.

To see some examples, check out the `examples` folder.


## Authentication

Expand Down Expand Up @@ -63,242 +69,6 @@ if you want to perform continuous requests.

</details>


## Walkthrough

<details>
<summary>Click Here</summary>
<br>

To use the wrapper, you need to create a new instance of it. For example:
```rs
use reqwest::Client;
use webweg::wrapper::WebRegWrapper;

let term = "SP22";
// For authentication cookies, see previous section.
let cookie = "your authentication cookies here";
let w = WebRegWrapper::new(Client::new(), cookie.to_string(), term);
```
For your convenience, `reqwest` is automatically exported so you can use that
from `webweg`.

Once created, you're able to use the various wrapper functions. Some useful
examples are shown below (note that `w` refers to the declaration above).

Most wrapper functions will return a `Result<T, Error>`, where `T` is the
result type. So, if a request is successful, you'll get `T` back. If the
request is unsuccessful, you will get back an `Error`, which contains
information about why the request failed.

### Check Login Status
You can check to see if you are logged in (i.e., if the wrapper can actually
perform any useful requests).
```rs
if !w.is_valid().await {
eprintln!("You aren't logged in!");
return;
}
```

### Get Schedule
You can get your current schedule, which lists your Enrolled, Planned, and
Waitlisted courses. You are able to fetch either the default schedule (`None`)
or a specific schedule (e.g., `My Schedule 2`)

Example: Suppose you wanted to see what courses are currently in your *default*
schedule. We can use the following code:

```rs
let my_schedule = w.get_schedule(None).await;
match my_schedule {
Ok(s) => s.into_iter().for_each(|p| println!("{}", p.to_string())),
Err(e) => eprintln!("{}", e),
};
```

**Remark:** If you wanted to see what courses you have planned in some other
schedule, you can replace `None` with `Some("your schedule name here")`.



### Get Course Information
You are able to search up course information for a particular course. If no
issues occur, then this function will return a vector where each element
contains the instructor name, number of seats, and all meetings.

Example: Suppose we wanted to look up all CSE 101 sections. We can use the following code:

```rs
let courses_101 = w.get_course_info("CSE", "101").await;
match courses_101 {
Ok(s) => s.into_iter().for_each(|p| println!("{}", p.to_string())),
Err(e) => eprintln!("{}", e),
};
```

### Search Courses
You can also search up courses that meet a particular criteria. This is
very similar in nature to the Advanced Search option.


Example 1: Suppose we wanted to search for specific sections. In our
example below, we'll search for one section of CSE 100, one section
of Math 184, and one section of POLI 28 (for Winter 2022). The following
code will do just that:

```rs
use webweg::wrapper::SearchType;

let search_res = w
.search_courses_detailed(SearchType::ByMultipleSections(&[
"079913", "078616", "075219",
]))
.await;
match search_res {
Ok(s) => s.into_iter().for_each(|p| println!("{}", p.to_string())),
Err(e) => eprintln!("{}", e),
};
```

Example 2: Suppose we wanted to search for any lower- or upper-division
CSE course. We can use the following code:

```rs
use webweg::wrapper::{CourseLevelFilter, SearchRequestBuilder};

let search_res = w
.search_courses_detailed(SearchType::Advanced(
&SearchRequestBuilder::new()
.add_department("CSE")
.filter_courses_by(CourseLevelFilter::UpperDivision)
.filter_courses_by(CourseLevelFilter::LowerDivision),
))
.await;
match search_res {
Ok(s) => s.into_iter().for_each(|p| println!("{}", p.to_string())),
Err(e) => eprintln!("{}", e),
};
```


### Planning & Un-planning a Section
You can use the wrapper to plan a section, adding it to your schedule.

Example 1: Suppose you wanted to plan a section of CSE 100 to your default
schedule. You can use the following code:

```rs
let res = w.add_to_plan(PlanAdd {
subject_code: "CSE",
course_code: "100",
section_id: "079911",
section_code: "A01",
// Using S/U grading.
grading_option: Some(GradeOption::S),
// Put in default schedule
schedule_name: None,
unit_count: 4
}, true).await;
match res {
Ok(o) => println!("{}", if o { "Successful" } else { "Unsuccessful" }),
Err(e) => eprintln!("{}", e),
};
```

Example 2: Suppose you want to remove the section of CSE 100 from your default
schedule. You can use the following code:

```rs
let res = w.remove_from_plan("079911", None).await;
match res {
Ok(o) => println!("{}", if o { "Successful" } else { "Unsuccessful" }),
Err(e) => eprintln!("{}", e),
};
```

**Remark:** If you wanted to add (or remove) this section to (from) a different
schedule, you can do so by replacing `None` with `Some("your schedule name here")`.

### Enrolling & Waitlisting Sections
You can also use the wrapper to programmatically enroll or waitlist particular
sections.

Example: Suppose we wanted to enroll or waitlist a section of Math 184 with
section number `078616`, and then drop it afterwards. This is how we could
do this.

```rs
use std::time::Duration;
use webweg::types::EnrollmentStatus;
use webweg::wrapper::EnrollWaitAdd;

let section_res = w
.search_courses_detailed(SearchType::BySection("078616"))
.await
.unwrap_or_else(|_| vec![]);

if section_res.is_empty() {
eprintln!("No section found.");
return;
}

let add_res = w
.add_section(
section_res[0].has_seats(),
EnrollWaitAdd {
section_id: "078616",
// Use default grade option
grading_option: None,
// Use default unit count
unit_count: None,
},
// Validate our request with WebReg
true,
)
.await;

match add_res {
Ok(o) => println!("{}", if o { "Successful" } else { "Unsuccessful" }),
Err(e) => {
eprintln!("{}", e);
return;
}
};

// Wait a bit, although this is unnecessary.
std::thread::sleep(Duration::from_secs(2));

// Get your current schedule
let course_to_drop = w
.get_schedule(None)
.await
.unwrap_or_else(|_| vec![])
.into_iter()
.find(|x| x.section_id == 78616);

// Check if we're enrolled in this course
let is_enrolled = if let Some(r) = course_to_drop {
match r.enrolled_status {
EnrollmentStatus::Enrolled => true,
_ => false,
}
} else {
eprintln!("Course not enrolled or waitlisted.");
return;
};

// Drop the class.
let rem_res = w.drop_section(is_enrolled, "078616").await;

match rem_res {
Ok(o) => println!("{}", if o { "Successful" } else { "Unsuccessful" }),
Err(e) => eprintln!("{}", e),
};
```

</details>

## Definition Files
This crate comes with two definition files:
- `raw_types`
Expand All @@ -310,16 +80,9 @@ the only time you will need to use `raw_types` is if you're using
the `search_courses` method.

## Tests
Very basic tests can be found in the `tests` folder. You will need
to provide your cookies in the `cookie.txt` file; place this file in
the project root directory (i.e., the directory with the `src` and
`tests` directories).

Due to WebReg constantly changing, making long-term tests is not
feasible. Tests for a specific term are not guaranteed to be maintained.

That being said, there are tests for all utility functions (things that
can be tested in the long-term).
Many tests here are focused on the _parsing_ aspect of the wrapper, and
not making the request itself. It is assumed that making the request
should be relatively error-free.

## Versioning
This crate uses a versioning scheme that is roughly based on [Semantic Versioning](https://semver.org/). For a version
Expand Down
34 changes: 34 additions & 0 deletions examples/enroll_in_class.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use webweg::wrapper::input_types::{AddType, EnrollWaitAdd, GradeOption};
use webweg::wrapper::wrapper_builder::WebRegWrapperBuilder;

#[tokio::main(flavor = "current_thread")]
async fn main() {
let wrapper = WebRegWrapperBuilder::new()
.with_cookies("my cookies here")
.with_default_term("FA23")
.try_build_wrapper()
.unwrap();

// Essentially registers the FA23 term with our session
_ = wrapper.associate_term("FA23");

let course_to_enroll = EnrollWaitAdd::builder()
.with_section_id("123456")
.with_grading_option(GradeOption::P)
.try_build()
.unwrap();

let enroll_result = wrapper
.default_request()
.add_section(AddType::DecideForMe, course_to_enroll, true)
.await;

match enroll_result {
Ok(res) => {
println!("Enrolled in class? {res}");
}
Err(e) => {
println!("Got an error when trying to enroll: {e}")
}
}
}
37 changes: 37 additions & 0 deletions examples/raw_requests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use webweg::wrapper::wrapper_builder::WebRegWrapperBuilder;

#[tokio::main(flavor = "current_thread")]
async fn main() {
let wrapper = WebRegWrapperBuilder::new()
.with_cookies("my cookies here")
.with_default_term("FA23")
.try_build_wrapper()
.unwrap();

// Registers all active terms so we can switch between active quarters.
_ = wrapper.associate_term("FA23").await;

// `default_request` will by default give you a "parsed" response. A parsed response
// is one that takes the original WebReg response and "cleans" it and then returns
// the cleaned version.
let my_schedule = wrapper.default_request().get_schedule(None).await;
println!("{my_schedule:?}");

// The above few lines are equivalent to...
let my_schedule = wrapper
.make_request()
.build_term_parser()
.get_schedule(None)
.await;

println!("{my_schedule:?}");

// But let's say you want a raw response (not a cleaned one from WebReg)...
let my_raw_schedule = wrapper
.make_request()
.build_term_raw()
.get_schedule(None)
.await;

println!("{my_raw_schedule:?}");
}
39 changes: 39 additions & 0 deletions examples/search_classes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use webweg::wrapper::input_types::{CourseLevelFilter, SearchRequestBuilder, SearchType};
use webweg::wrapper::wrapper_builder::WebRegWrapperBuilder;

#[tokio::main(flavor = "current_thread")]
async fn main() {
let wrapper = WebRegWrapperBuilder::new()
.with_cookies("my cookies here")
.with_default_term("FA23")
.try_build_wrapper()
.unwrap();

// Essentially registers the FA23 term with our session
_ = wrapper.associate_term("FA23");

let search = SearchRequestBuilder::new()
.filter_courses_by(CourseLevelFilter::Graduate)
.filter_courses_by(CourseLevelFilter::LowerDivision)
.set_start_time(5, 30)
.set_end_time(12 + 6, 30)
.add_department("CSE")
// This can be lowercase or uppercase
.add_department("cogs")
.add_department("MATH")
.only_allow_open();

let search_results = wrapper
.default_request()
.search_courses(SearchType::Advanced(search))
.await;

if let Ok(results) = search_results {
for course in results {
println!(
"{} {}: {}",
course.subj_code, course.course_code, course.course_title
);
}
}
}
Loading

0 comments on commit 6d4e68f

Please sign in to comment.