Skip to content

Commit

Permalink
Merge pull request #48 from EATSTEAK:dev
Browse files Browse the repository at this point in the history
[release] v0.1.3
  • Loading branch information
EATSTEAK committed Oct 29, 2023
2 parents 910a53a + 3d1e72e commit b767c67
Show file tree
Hide file tree
Showing 13 changed files with 306 additions and 255 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rusaint"
version = "0.1.2"
version = "0.1.3"
description = "Easy-to-use SSU u-saint client"
keywords = ["ssu", "u-saint", "scraping", "parser"]
categories = ["web-programming"]
Expand Down
72 changes: 34 additions & 38 deletions src/application/course_grades/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::{collections::HashMap, sync::Arc};
use std::collections::HashMap;

use crate::{
define_elements,
model::SemesterType,
session::USaintSession,
webdynpro::{
application::{client::body::Body, Application},
application::{body::Body, Application},
element::{
action::Button,
complex::sap_table::{
Expand All @@ -29,13 +28,11 @@ use super::USaintApplication;

define_usaint_application!(
#[doc = "[학생 성적 조회](https://ecc.ssu.ac.kr/sap/bc/webdynpro/SAP/ZCMB3W0017)"]
pub struct CourseGrades
pub struct CourseGrades<"ZCMB3W0017">
);

#[allow(unused)]
impl<'a> CourseGrades {
const APP_NAME: &str = "ZCMB3W0017";

// Elements for Grade Summaries
define_elements!(
// Grade summaries by semester
Expand Down Expand Up @@ -75,23 +72,6 @@ impl<'a> CourseGrades {
GRADE_BY_CLASSES_TABLE: SapTable<'a> = "ZCMB3W0017.ID_0001:VIW_MAIN.TABLE_1";
);

/// 새로운 학기별 성적 조회 애플리케이션을 만듭니다.
/// ### 예시
/// ```no_run
/// # tokio_test::block_on(async {
/// #use std::sync::Arc;
/// # use rusaint::USaintSession;
///
/// let session = Arc::new(USaintSession::with_password("20212345", "password").await.unwrap());
/// let app = CourseGrades::new(session).await.unwrap();
/// # })
/// ```
pub async fn new(session: Arc<USaintSession>) -> Result<CourseGrades, WebDynproError> {
Ok(CourseGrades(
USaintApplication::with_session(Self::APP_NAME, session).await?,
))
}

async fn close_popups(&mut self) -> Result<(), WebDynproError> {
fn make_close_event(app: &CourseGrades) -> Option<Event> {
let body = app.body();
Expand Down Expand Up @@ -212,14 +192,18 @@ impl<'a> CourseGrades {
/// # use std::sync::Arc;
/// # use rusaint::USaintSession;
/// # use self::model::CourseType;
/// # use rusaint::application::USaintApplicationBuilder;
/// # let session = Arc::new(USaintSession::with_password("20212345", "password").await.unwrap());
/// let app = CourseGrades::new(session).await.unwrap();
/// let app = USaintApplicationBuilder::new().session(session).build_into::<CourseGrades>().await.unwrap();
/// let summary = app.recorded_summary(CourseType::Bachelor).unwrap();
/// println!("{:?}", summary);
/// // GradeSummary { ... }
/// # })
/// ```
pub async fn recorded_summary(&mut self, course_type: CourseType) -> Result<GradeSummary, WebDynproError> {
pub async fn recorded_summary(
&mut self,
course_type: CourseType,
) -> Result<GradeSummary, WebDynproError> {
self.close_popups().await?;
self.select_course(course_type).await?;
let body = self.body();
Expand All @@ -245,15 +229,19 @@ impl<'a> CourseGrades {
/// # tokio_test::block_on(async {
/// # use std::sync::Arc;
/// # use rusaint::USaintSession;
/// # use rusaint::application::USaintApplicationBuilder;
/// # use self::model::CourseType;
/// # let session = Arc::new(USaintSession::with_password("20212345", "password").await.unwrap());
/// let app = CourseGrades::new(session).await.unwrap();
/// let app = USaintApplicationBuilder::new().session(session).build_into::<CourseGrades>().await.unwrap();
/// let summary = app.certificated_summary(CourseType::Bachelor).unwrap();
/// println!("{:?}", summary);
/// // GradeSummary { ... }
/// # })
/// ```
pub async fn certificated_summary(&mut self, course_type: CourseType) -> Result<GradeSummary, WebDynproError> {
pub async fn certificated_summary(
&mut self,
course_type: CourseType,
) -> Result<GradeSummary, WebDynproError> {
self.close_popups().await?;
self.select_course(course_type).await?;
let body = self.body();
Expand All @@ -280,14 +268,18 @@ impl<'a> CourseGrades {
/// # use std::sync::Arc;
/// # use rusaint::USaintSession;
/// # use self::model::CourseType;
/// # use rusaint::application::USaintApplicationBuilder;
/// # let session = Arc::new(USaintSession::with_password("20212345", "password").await.unwrap());
/// let app = CourseGrades::new(session).await.unwrap();
/// let app = USaintApplicationBuilder::new().session(session).build_into::<CourseGrades>().await.unwrap();
/// let semesters = app.semesters(CourseType::Bachelor).unwrap();
/// println!("{:?}", semesters);
/// // [SemesterGrade { ... }, SemesterGrade { ... }]
/// # })
/// ```
pub async fn semesters(&mut self, course_type: CourseType) -> Result<Vec<SemesterGrade>, WebDynproError> {
pub async fn semesters(
&mut self,
course_type: CourseType,
) -> Result<Vec<SemesterGrade>, WebDynproError> {
fn parse_rank(value: String) -> Option<(u32, u32)> {
let mut spl = value.split("/");
let first: u32 = spl.next()?.parse().ok()?;
Expand Down Expand Up @@ -396,9 +388,10 @@ impl<'a> CourseGrades {
/// # tokio_test::block_on(async {
/// # use std::sync::Arc;
/// # use rusaint::USaintSession;
/// # use rusaint::application::USaintApplicationBuilder;
/// # use rusaint::model::{CourseType, SemesterType};
/// # let session = Arc::new(USaintSession::with_password("20212345", "password").await.unwrap());
/// let app = CourseGrades::new(session).await.unwrap();
/// let app = USaintApplicationBuilder::new().session(session).build_into::<CourseGrades>().await.unwrap();
/// let classes = app.classes(CourseType::Bachelor, "2022", SemesterType::Two, false).unwrap();
/// println!("{:?}", classes); // around 3s(depends on network environment)
/// // [ClassGrade { ... }, ClassGrade { ... }]
Expand All @@ -410,8 +403,9 @@ impl<'a> CourseGrades {
/// # use std::sync::Arc;
/// # use rusaint::USaintSession;
/// # use rusaint::model::{CourseType, SemesterType};
/// # use rusaint::application::USaintApplicationBuilder;
/// # let session = Arc::new(USaintSession::with_password("20212345", "password").await.unwrap());
/// let app = CourseGrades::new(session).await.unwrap();
/// let app = USaintApplicationBuilder::new().session(session).build_into::<CourseGrades>().await.unwrap();
/// let classes = app.classes("2022", SemesterType::Two, true).unwrap();
/// println!("{:?}", classes); // around 10s(depends on network environment)
/// // [ClassGrade { ... }, ClassGrade { ... }]
Expand Down Expand Up @@ -488,9 +482,10 @@ impl<'a> CourseGrades {
/// # tokio_test::block_on(async {
/// # use std::sync::Arc;
/// # use rusaint::USaintSession;
/// # use rusaint::application::USaintApplicationBuilder;
/// # use self::model::CourseType;
/// # let session = Arc::new(USaintSession::with_password("20212345", "password").await.unwrap());
/// let app = CourseGrades::new(session).await.unwrap();
/// let app = USaintApplicationBuilder::new().session(session).build_into::<CourseGrades>().await.unwrap();
/// let classes = app.classes(CourseType::Bachelor, "2022", SemesterType::Two, false).unwrap();
/// let class = classes.iter().first().unwrap();
/// let class_detail = app.class_detail("2022", SemesterType::Two, class.code());
Expand Down Expand Up @@ -560,12 +555,9 @@ mod test {
use std::sync::{Arc, OnceLock};

use crate::{
application::course_grades::CourseGrades,
application::{USaintApplicationBuilder, course_grades::CourseGrades},
session::USaintSession,
webdynpro::{
application::Application,
element::{layout::PopupWindow, Element},
},
webdynpro::{element::{layout::PopupWindow, Element}, application::Application},
};
use dotenv::dotenv;

Expand All @@ -590,7 +582,11 @@ mod test {
#[tokio::test]
async fn close_popups() {
let session = get_session().await.unwrap();
let mut app = CourseGrades::new(session).await.unwrap();
let mut app = USaintApplicationBuilder::new()
.session(session)
.build_into::<CourseGrades>()
.await
.unwrap();
app.close_popups().await.unwrap();
let body = app.body();
let popup_selector =
Expand Down
46 changes: 25 additions & 21 deletions src/application/course_schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ use crate::{
define_elements,
model::SemesterType,
webdynpro::{
application::Application,
element::{action::Button, complex::SapTable, layout::TabStrip, selection::ComboBox},
error::WebDynproError, application::Application,
error::WebDynproError,
},
};

use super::USaintApplication;

define_usaint_application!(pub struct CourseSchedule);
define_usaint_application!(pub struct CourseSchedule<"ZCMW2100">);

#[allow(unused)]
impl<'a> CourseSchedule {
const APP_NAME: &str = "ZCMW2100";

define_elements! {
PERIOD_YEAR: ComboBox<'a> = "ZCMW_PERIOD_RE.ID_A61C4ED604A2BFC2A8F6C6038DE6AF18:VIW_MAIN.PERYR";
PERIOD_ID: ComboBox<'a> = "ZCMW_PERIOD_RE.ID_A61C4ED604A2BFC2A8F6C6038DE6AF18:VIW_MAIN.PERID";
Expand All @@ -24,12 +23,6 @@ impl<'a> CourseSchedule {
MAIN_TABLE: SapTable<'a> = "SALV_WD_TABLE.ID_DE0D9128A4327646C94670E2A892C99C:VIEW_TABLE.SALV_WD_UIE_TABLE";
}

pub async fn new() -> Result<CourseSchedule, WebDynproError> {
Ok(CourseSchedule(
USaintApplication::new(Self::APP_NAME).await?,
))
}

fn semester_to_key(period: SemesterType) -> &'static str {
match period {
SemesterType::One => "090",
Expand Down Expand Up @@ -90,26 +83,31 @@ impl<'a> CourseSchedule {
}

pub fn read_edu_raw(&self) -> Result<SapTable, WebDynproError> {
let body = self.body();
let main_table = Self::MAIN_TABLE.from_body(body)?;
let main_table = Self::MAIN_TABLE.from_body(self.body())?;
Ok(main_table)
}
}

#[cfg(test)]
mod test {
use crate::{
application::course_schedule::CourseSchedule,
webdynpro::{element::{
complex::sap_table::cell::{SapTableCell, SapTableCellWrapper},
selection::list_box::{item::ListBoxItemWrapper, ListBoxWrapper},
ElementWrapper,
}, application::Application},
application::{course_schedule::CourseSchedule, USaintApplicationBuilder},
webdynpro::{
application::Application,
element::{
complex::sap_table::cell::{SapTableCell, SapTableCellWrapper},
selection::list_box::{item::ListBoxItemWrapper, ListBoxWrapper},
ElementWrapper,
},
},
};

#[tokio::test]
async fn examine_elements() {
let app = CourseSchedule::new().await.unwrap();
let app = USaintApplicationBuilder::new()
.build_into::<CourseSchedule>()
.await
.unwrap();
let ct_selector = scraper::Selector::parse("[ct]").unwrap();
for elem_ref in app.body().document().select(&ct_selector) {
let elem = ElementWrapper::dyn_elem(elem_ref);
Expand All @@ -121,7 +119,10 @@ mod test {

#[tokio::test]
async fn combobox_items() {
let app = CourseSchedule::new().await.unwrap();
let app = USaintApplicationBuilder::new()
.build_into::<CourseSchedule>()
.await
.unwrap();
let period_id_combobox = CourseSchedule::PERIOD_ID.from_body(app.body()).unwrap();
let listbox = period_id_combobox.item_list_box(app.body()).unwrap();
match listbox {
Expand All @@ -146,7 +147,10 @@ mod test {

#[tokio::test]
async fn table_test() {
let mut app = CourseSchedule::new().await.unwrap();
let mut app = USaintApplicationBuilder::new()
.build_into::<CourseSchedule>()
.await
.unwrap();
app.load_edu().await.unwrap();
let table = app.read_edu_raw().unwrap();
if let Some(table) = table.table() {
Expand Down

0 comments on commit b767c67

Please sign in to comment.