use std::collections::HashMap;
use serde::{
Deserialize, Serialize,
de::{IntoDeserializer, value::MapDeserializer},
};
use crate::application::utils::de_with::deserialize_optional_string;
use crate::{
application::course_schedule::utils::{
request, request_lv1, request_lv2, request_lv3, request_text,
},
client::USaintClient,
};
use wdpe::element::parser::ElementParser;
use wdpe::{
define_elements,
element::{
action::Button, complex::sap_table::FromSapTable, definition::ElementDefinition,
layout::tab_strip::item::TabStripItem, selection::ComboBox,
},
error::{ElementError, WebDynproError},
};
#[allow(unused)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
#[derive(Debug, Serialize, Deserialize)]
pub enum LectureCategory {
Major {
collage: String,
department: String,
major: Option<String>,
},
RequiredElective {
lecture_name: String,
},
OptionalElective {
category: String,
},
Chapel {
lecture_name: String,
},
Education,
Graduated {
collage: String,
department: String,
},
ConnectedMajor {
major: String,
},
UnitedMajor {
major: String,
},
FindByProfessor {
keyword: String,
},
FindByLecture {
keyword: String,
},
RecognizedOtherMajor {
collage: String,
department: String,
major: Option<String>,
},
Cyber,
}
impl LectureCategory {
pub fn major(collage: &str, department: &str, major: Option<&str>) -> Self {
Self::Major {
collage: collage.to_string(),
department: department.to_string(),
major: major.map(|str| str.to_string()),
}
}
pub fn required_elective(lecture_name: &str) -> Self {
Self::RequiredElective {
lecture_name: lecture_name.to_string(),
}
}
pub fn optional_elective(category: &str) -> Self {
Self::OptionalElective {
category: category.to_string(),
}
}
pub fn chapel(lecture_name: &str) -> Self {
Self::Chapel {
lecture_name: lecture_name.to_string(),
}
}
pub fn education() -> Self {
Self::Education
}
pub fn graduated(collage: &str, department: &str) -> Self {
Self::Graduated {
collage: collage.to_string(),
department: department.to_string(),
}
}
pub fn connected_major(major: &str) -> Self {
Self::ConnectedMajor {
major: major.to_string(),
}
}
pub fn united_major(major: &str) -> Self {
Self::UnitedMajor {
major: major.to_string(),
}
}
pub fn find_by_professor(keyword: &str) -> Self {
Self::FindByProfessor {
keyword: keyword.to_string(),
}
}
pub fn find_by_lecture(keyword: &str) -> Self {
Self::FindByLecture {
keyword: keyword.to_string(),
}
}
pub fn recognized_other_major(collage: &str, department: &str, major: Option<&str>) -> Self {
Self::RecognizedOtherMajor {
collage: collage.to_string(),
department: department.to_string(),
major: major.map(|str| str.to_string()),
}
}
pub fn cyber() -> Self {
Self::Cyber
}
pub(super) async fn request_query(
&self,
client: &mut USaintClient,
) -> Result<(), WebDynproError> {
match self {
LectureCategory::Major {
collage,
department,
major,
} => {
define_elements! {
TAB_OTHERS: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_OTHERS";
OTHERS_DDK_LV3: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_OTHERS.DDK_LV3";
OTHERS_DDK_LV4: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_OTHERS.DDK_LV4";
OTHERS_DDK_LV5: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_OTHERS.DDK_LV5";
SEARCH_OTHERS: Button<'_> = "ZCMW2100.ID_0001:VIW_TAB_OTHERS.BUTTON";
}
if let Some(major) = major {
request_lv3(
client,
TAB_OTHERS,
0,
OTHERS_DDK_LV3,
OTHERS_DDK_LV4,
OTHERS_DDK_LV5,
SEARCH_OTHERS,
collage,
department,
major,
)
.await?;
} else {
request_lv2(
client,
TAB_OTHERS,
0,
OTHERS_DDK_LV3,
OTHERS_DDK_LV4,
SEARCH_OTHERS,
collage,
department,
)
.await?;
}
}
LectureCategory::RequiredElective { lecture_name } => {
define_elements! {
TAB_GENERAL_REQ: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_GENERAL_REQ";
GENERAL_REQ_TYPE: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_GENERAL_REQ.SM_OBJID";
SEARCH_GENERAL_REQ: Button<'_> = "ZCMW2100.ID_0001:VIW_TAB_GENERAL_REQ.BUTTON_SEARCH";
}
request_lv1(
client,
TAB_GENERAL_REQ,
1,
GENERAL_REQ_TYPE,
SEARCH_GENERAL_REQ,
lecture_name,
)
.await?;
}
LectureCategory::OptionalElective { category } => {
define_elements! {
TAB_GENERAL_OPT: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_GENERAL_OPT";
GENERAL_OPT_DISCIPLINES: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_GENERAL_OPT.DISCIPLINES";
SEARCH_GENERAL_OPT: Button<'_> = "ZCMW2100.ID_0001:VIW_TAB_GENERAL_OPT.BUTTON_SEARCH";
}
request_lv1(
client,
TAB_GENERAL_OPT,
2,
GENERAL_OPT_DISCIPLINES,
SEARCH_GENERAL_OPT,
category,
)
.await?;
}
LectureCategory::Chapel { lecture_name } => {
define_elements! {
TAB_CHAPEL: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_CHAPEL_REQ";
CHAPEL_TYPE: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_CHAPEL_REQ.SM_OBJID";
SEARCH_CHAPEL: Button<'_> = "ZCMW2100.ID_0001:VIW_TAB_CHAPEL_REQ.BUTTON_SEARCH";
}
request_lv1(
client,
TAB_CHAPEL,
3,
CHAPEL_TYPE,
SEARCH_CHAPEL,
lecture_name,
)
.await?;
}
LectureCategory::Education => {
define_elements! {
TAB_EDU: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_EDU";
SEARCH_EDU: Button<'_> = "ZCMW2100.ID_0001:VIW_MAIN.BUTTON_EDU";
}
request(client, TAB_EDU, 4, SEARCH_EDU).await?;
}
LectureCategory::Graduated {
collage,
department,
} => {
define_elements! {
TAB_GRADUATE: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_GRADUATE";
GRADUATE_DDK_LV3: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_GRADUATE.DDK_LV3";
GRADUATE_DDK_LV4: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_GRADUATE.DDK_LV4";
SEARCH_GRADUATE: Button<'_> = "ZCMW2100.ID_0001:VIW_TAB_GRADUATE.BUTTON";
}
request_lv2(
client,
TAB_GRADUATE,
7,
GRADUATE_DDK_LV3,
GRADUATE_DDK_LV4,
SEARCH_GRADUATE,
collage,
department,
)
.await?;
}
LectureCategory::ConnectedMajor { major } => {
define_elements! {
TAB_YOMA: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_YOMA";
COMBO_YOMA: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_YOMA.CONNECT_MAJO";
SEARCH_YOMA: Button<'_> = "ZCMW2100.ID_0001:VIW_TAB_YOMA.BUTTON_SEARCH";
}
request_lv1(client, TAB_YOMA, 8, COMBO_YOMA, SEARCH_YOMA, major).await?;
}
LectureCategory::UnitedMajor { major } => {
define_elements! {
TAB_UNMA: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_UNMA";
COMBO_UNMA: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_UNMA.CG_OBJID";
SEARCH_UNMA: Button<'_> = "ZCMW2100.ID_0001:VIW_TAB_UNMA.BUTTON_SEARCH";
}
request_lv1(client, TAB_UNMA, 9, COMBO_UNMA, SEARCH_UNMA, major).await?;
}
LectureCategory::FindByProfessor { keyword } => {
define_elements! {
TAB_PROFESSOR: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_PROFESSOR";
COMBO_PROFESSOR: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_PROFESSOR.PROFESSOR"; SEARCH_PROFESSOR: Button<'_> = "ZCMW2100.ID_0001:VIW_TAB_PROFESSOR.BUTTON_SEARCH";
}
request_text(
client,
TAB_PROFESSOR,
10,
COMBO_PROFESSOR,
SEARCH_PROFESSOR,
keyword,
)
.await?;
}
LectureCategory::FindByLecture { keyword } => {
define_elements! {
TAB_SEARCH: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_SEARCH";
COMBO_SEARCH: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_SEARCH.SEARCH_TEXT";
SEARCH_SEARCH: Button<'_> = "ZCMW2100.ID_0001:VIW_TAB_SEARCH.BUTTON_SEARCH";
}
request_text(client, TAB_SEARCH, 11, COMBO_SEARCH, SEARCH_SEARCH, keyword).await?;
}
LectureCategory::RecognizedOtherMajor {
collage,
department,
major,
} => {
define_elements! {
TAB_OTHER_GC: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_OTHER_GC";
OTHER_GC_DDK_LV3: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_OTHER_GC.DDK_LV3";
OTHER_GC_DDK_LV4: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_OTHER_GC.DDK_LV4";
OTHER_GC_DDK_LV5: ComboBox<'_> = "ZCMW2100.ID_0001:VIW_TAB_OTHER_GC.DDK_LV5";
SEARCH_OTHER_GC: Button<'_> = "ZCMW2100.ID_0001:VIW_TAB_OTHER_GC.BTN_OTHER_GC";
}
if let Some(major) = major {
request_lv3(
client,
TAB_OTHER_GC,
12,
OTHER_GC_DDK_LV3,
OTHER_GC_DDK_LV4,
OTHER_GC_DDK_LV5,
SEARCH_OTHER_GC,
collage,
department,
major,
)
.await?;
} else {
request_lv2(
client,
TAB_OTHER_GC,
12,
OTHER_GC_DDK_LV3,
OTHER_GC_DDK_LV4,
SEARCH_OTHER_GC,
collage,
department,
)
.await?;
}
}
LectureCategory::Cyber => {
define_elements! {
TAB_CYBER: TabStripItem<'_> = "ZCMW2100.ID_0001:VIW_MAIN.TAB_CYBER";
SEARCH_CYBER: Button<'_> = "ZCMW2100.ID_0001:VIW_MAIN.BTN_CYBER";
}
request(client, TAB_CYBER, 14, SEARCH_CYBER).await?;
}
}
Ok(())
}
}
#[allow(unused)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
pub struct Lecture {
#[serde(
rename(deserialize = "계획"),
default,
deserialize_with = "deserialize_optional_string"
)]
pub syllabus: Option<String>,
#[serde(rename(deserialize = "이수구분(주전공)"))]
pub category: String,
#[serde(
rename(deserialize = "이수구분(다전공)"),
default,
deserialize_with = "deserialize_optional_string"
)]
pub sub_category: Option<String>,
#[serde(
rename(deserialize = "공학인증"),
default,
deserialize_with = "deserialize_optional_string"
)]
pub abeek_info: Option<String>,
#[serde(
rename(deserialize = "교과영역"),
default,
deserialize_with = "deserialize_optional_string"
)]
pub field: Option<String>,
#[serde(rename(deserialize = "과목번호"))]
pub code: String,
#[serde(rename(deserialize = "과목명"))]
pub name: String,
#[serde(
rename(deserialize = "분반"),
default,
deserialize_with = "deserialize_optional_string"
)]
pub division: Option<String>,
#[serde(rename(deserialize = "교수명"))]
pub professor: String,
#[serde(rename(deserialize = "개설학과"))]
pub department: String,
#[serde(rename(deserialize = "시간/학점(설계)"))]
pub time_points: String,
#[serde(rename(deserialize = "수강인원"))]
pub personeel: String,
#[serde(rename(deserialize = "여석"))]
pub remaining_seats: String,
#[serde(rename(deserialize = "강의시간(강의실)"))]
pub schedule_room: String,
#[serde(rename(deserialize = "수강대상"))]
pub target: String,
}
impl<'body> FromSapTable<'body> for Lecture {
fn from_table(
header: Option<&'body wdpe::element::complex::sap_table::SapTableHeader>,
row: &'body wdpe::element::complex::sap_table::SapTableRow,
parser: &'body ElementParser,
) -> Result<Self, WebDynproError> {
let map_string = row.try_row_into::<HashMap<String, String>>(header, parser)?;
let map_de: MapDeserializer<_, serde::de::value::Error> = map_string.into_deserializer();
Ok(
Lecture::deserialize(map_de).map_err(|e| ElementError::InvalidContent {
element: row.table_def().id().to_string(),
content: e.to_string(),
})?,
)
}
}
#[cfg(feature = "uniffi")]
#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
pub struct LectureCategoryBuilder {}
#[cfg(feature = "uniffi")]
#[cfg_attr(feature = "uniffi", uniffi::export)]
impl LectureCategoryBuilder {
#[uniffi::constructor]
pub fn new() -> Self {
Self {}
}
pub fn major(
&self,
collage: &str,
department: &str,
major: &Option<String>,
) -> LectureCategory {
LectureCategory::major(collage, department, major.as_ref().map(String::as_str))
}
pub fn required_elective(&self, lecture_name: &str) -> LectureCategory {
LectureCategory::required_elective(lecture_name)
}
pub fn optional_elective(&self, category: &str) -> LectureCategory {
LectureCategory::optional_elective(category)
}
pub fn chapel(&self, lecture_name: &str) -> LectureCategory {
LectureCategory::chapel(lecture_name)
}
pub fn education(&self) -> LectureCategory {
LectureCategory::education()
}
pub fn graduated(&self, collage: &str, department: &str) -> LectureCategory {
LectureCategory::graduated(collage, department)
}
pub fn connected_major(&self, major: &str) -> LectureCategory {
LectureCategory::connected_major(major)
}
pub fn united_major(&self, major: &str) -> LectureCategory {
LectureCategory::united_major(major)
}
pub fn find_by_professor(&self, keyword: &str) -> LectureCategory {
LectureCategory::find_by_professor(keyword)
}
pub fn find_by_lecture(&self, keyword: &str) -> LectureCategory {
LectureCategory::find_by_lecture(keyword)
}
pub fn recognized_other_major(
&self,
collage: &str,
department: &str,
major: &Option<String>,
) -> LectureCategory {
LectureCategory::recognized_other_major(
collage,
department,
major.as_ref().map(String::as_str),
)
}
pub fn cyber(&self) -> LectureCategory {
LectureCategory::cyber()
}
}
#[cfg(feature = "uniffi")]
impl Default for LectureCategoryBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
pub struct DetailedLecture {
pub lecture: Lecture,
pub detail: Option<LectureDetail>,
pub syllabus: Option<LectureSyllabus>,
}
mod detail;
mod syllabus;
pub use detail::{AlternativeLecture, LectureChangeHistory, LectureDetail, PrerequisiteLecture};
pub use syllabus::{LectureSyllabus, SyllabusCompetency, SyllabusGradingItem, SyllabusWeeklyPlan};