rusaint 0.16.3

Easy-to-use SSU u-saint client
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
use std::collections::HashMap;

use serde::{
    Deserialize, Serialize,
    de::{IntoDeserializer, value::MapDeserializer},
};

use crate::application::utils::de_with::{deserialize_semester_type, deserialize_u32_string};
use crate::{RusaintError, error::ApplicationError, model::SemesterType};
use wdpe::command::WebDynproCommandExecutor;
use wdpe::element::parser::ElementParser;
use wdpe::{
    command::element::complex::SapTableBodyCommand,
    define_elements,
    element::{
        ElementDefWrapper,
        complex::{
            SapTable,
            sap_table::{
                FromSapTable,
                cell::{SapTableCell, SapTableCellWrapper},
            },
        },
        definition::ElementDefinition,
    },
    error::{ElementError, WebDynproError},
};

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// 학기별 채플 정보
pub struct ChapelInformation {
    year: u32,
    semester: SemesterType,
    general_information: GeneralChapelInformation,
    attendances: Vec<ChapelAttendance>,
    absence_requests: Vec<ChapelAbsenceRequest>,
}

impl ChapelInformation {
    pub(crate) fn new(
        year: u32,
        semester: SemesterType,
        general_information: GeneralChapelInformation,
        attendances: Vec<ChapelAttendance>,
        absence_requests: Vec<ChapelAbsenceRequest>,
    ) -> Self {
        Self {
            year,
            semester,
            general_information,
            attendances,
            absence_requests,
        }
    }

    /// 해당 채플 정보의 학년도를 반환합니다.
    pub fn year(&self) -> u32 {
        self.year
    }

    /// 해당 채플 정보의 학기를 반환합니다.
    pub fn semester(&self) -> SemesterType {
        self.semester
    }

    /// 기본 채플 정보(좌석번호, 결석현황, 성적결과)를 반환합니다.
    pub fn general_information(&self) -> &GeneralChapelInformation {
        &self.general_information
    }

    /// 채플 출결 정보를 반환합니다.
    pub fn attendances(&self) -> &[ChapelAttendance] {
        &self.attendances
    }

    /// 채플 결석신청 정보를 반환합니다.
    pub fn absence_requests(&self) -> &[ChapelAbsenceRequest] {
        &self.absence_requests
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// 채플 기본 정보(좌석번호, 결석현황, 성적결과)
pub struct GeneralChapelInformation {
    #[serde(
        rename(deserialize = "분반"),
        deserialize_with = "deserialize_u32_string"
    )]
    division: u32,
    #[serde(rename(deserialize = "시간표"))]
    chapel_time: String,
    #[serde(rename(deserialize = "강의실"))]
    chapel_room: String,
    #[serde(
        rename(deserialize = "층수"),
        deserialize_with = "deserialize_u32_string"
    )]
    floor_level: u32,
    #[serde(rename(deserialize = "좌석번호"))]
    seat_number: String,
    #[serde(
        rename(deserialize = "결석일수"),
        deserialize_with = "deserialize_u32_string"
    )]
    absence_time: u32,
    #[serde(rename(deserialize = "성적"))]
    result: String,
    #[serde(rename(deserialize = "비고"))]
    note: String,
}

impl<'a> GeneralChapelInformation {
    define_elements! {
        TABLE: SapTable<'a> = "ZCMW3681.ID_0001:V_MAIN.TABLE";
    }

    pub(crate) fn with_parser(parser: &'a ElementParser) -> Result<Vec<Self>, RusaintError> {
        let table = parser.read(SapTableBodyCommand::new(Self::TABLE))?;
        let Some(first_row) = table.iter().next() else {
            return Err(ApplicationError::NoChapelInformation.into());
        };
        if let Some(Ok(SapTableCellWrapper::Normal(cell))) = first_row.iter_value(parser).next()
            && let Some(ElementDefWrapper::TextView(tv_def)) = cell.content()
            && let Ok(tv) = parser.element_from_def(&tv_def)
            && tv.text().contains("없습니다.")
        {
            return Err(ApplicationError::NoChapelInformation.into());
        }
        Ok(table.try_table_into::<Self>(parser)?)
    }

    /// 분반 번호를 반환합니다.
    pub fn division(&self) -> u32 {
        self.division
    }

    /// 채플 시간을 반환합니다.
    pub fn chapel_time(&self) -> &str {
        &self.chapel_time
    }

    /// 채플 강의실을 반환합니다.
    pub fn chapel_room(&self) -> &str {
        &self.chapel_room
    }

    /// 층수를 반환합니다.
    pub fn floor_level(&self) -> u32 {
        self.floor_level
    }

    /// 좌석번호를 반환합니다.
    pub fn seat_number(&self) -> &str {
        &self.seat_number
    }

    /// 결석일수를 반환합니다.
    pub fn absence_time(&self) -> u32 {
        self.absence_time
    }

    /// 성적을 반환합니다.
    pub fn result(&self) -> &str {
        &self.result
    }

    /// 비고 내용을 반환합니다.
    pub fn note(&self) -> &str {
        &self.note
    }
}

impl<'body> FromSapTable<'body> for GeneralChapelInformation {
    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(
            Self::deserialize(map_de).map_err(|e| ElementError::InvalidContent {
                element: row.table_def().id().to_string(),
                content: e.to_string(),
            })?,
        )
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// 채플 수업별 출석정보
pub struct ChapelAttendance {
    #[serde(
        rename(deserialize = "분반"),
        deserialize_with = "deserialize_u32_string"
    )]
    division: u32,
    #[serde(rename(deserialize = "수업일자"))]
    class_date: String,
    #[serde(rename(deserialize = "강의구분"))]
    category: String,
    #[serde(rename(deserialize = "강사"))]
    instructor: String,
    #[serde(rename(deserialize = "소속"))]
    instructor_department: String,
    #[serde(rename(deserialize = "제목"))]
    title: String,
    #[serde(rename(deserialize = "출결상태"))]
    attendance: String,
    #[serde(rename(deserialize = "평가"))]
    result: String,
    #[serde(rename(deserialize = "비고"))]
    note: String,
}

impl<'a> ChapelAttendance {
    define_elements! {
        TABLE_A: SapTable<'a> = "ZCMW3681.ID_0001:V_MAIN.TABLE_A";
    }

    pub(crate) fn with_parser(parser: &'a ElementParser) -> Result<Vec<Self>, WebDynproError> {
        let table = parser.read(SapTableBodyCommand::new(Self::TABLE_A))?;
        let Some(first_row) = table.iter().next() else {
            return Ok(Vec::with_capacity(0));
        };
        if let Some(Ok(SapTableCellWrapper::Normal(cell))) = first_row.iter_value(parser).next()
            && let Some(ElementDefWrapper::TextView(tv_def)) = cell.content()
            && let Ok(tv) = parser.element_from_def(&tv_def)
            && tv.text().contains("채플 출결 상세내용")
        {
            return Ok(Vec::with_capacity(0));
        }
        table.try_table_into::<Self>(parser)
    }

    /// 채플 분반 번호를 반환합니다.
    pub fn division(&self) -> u32 {
        self.division
    }

    /// 수업일자를 반환합니다.
    pub fn class_date(&self) -> &str {
        &self.class_date
    }

    /// 강의구분을 반환합니다.
    pub fn category(&self) -> &str {
        &self.category
    }

    /// 강사명을 반환합니다.
    pub fn instructor(&self) -> &str {
        &self.instructor
    }

    /// 강사의 소속을 반환합니다.
    pub fn instructor_department(&self) -> &str {
        &self.instructor_department
    }

    /// 강의 제목을 반환합니다.
    pub fn title(&self) -> &str {
        &self.title
    }

    /// 출결상태를 반환합니다.
    pub fn attendance(&self) -> &str {
        &self.attendance
    }

    /// 평가 내용을 반환합니다.
    pub fn result(&self) -> &str {
        &self.result
    }

    /// 비고를 반환합니다.
    pub fn note(&self) -> &str {
        &self.note
    }
}

impl<'body> FromSapTable<'body> for ChapelAttendance {
    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(
            Self::deserialize(map_de).map_err(|e| ElementError::InvalidContent {
                element: row.table_def().id().to_string(),
                content: e.to_string(),
            })?,
        )
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// 채플 결석신청 정보
pub struct ChapelAbsenceRequest {
    #[serde(
        rename(deserialize = "학년도"),
        deserialize_with = "deserialize_u32_string"
    )]
    year: u32,
    #[serde(
        rename(deserialize = "학기"),
        deserialize_with = "deserialize_semester_type"
    )]
    semester: SemesterType,
    #[serde(rename(deserialize = "결석구분상세"))]
    absence_detail: String,
    #[serde(rename(deserialize = "결석시작일자"))]
    absence_start: String,
    #[serde(rename(deserialize = "결석종료일자"))]
    absence_end: String,
    #[serde(rename(deserialize = "결석사유(국문)"))]
    absence_reason_kr: String,
    #[serde(rename(deserialize = "결석사유(영문)"))]
    absence_reason_en: String,
    #[serde(rename(deserialize = "신청일자"))]
    application_date: String,
    #[serde(rename(deserialize = "승인일자"))]
    approval_date: String,
    #[serde(rename(deserialize = "거부사유"))]
    denial_reason: String,
    #[serde(rename(deserialize = "상태"))]
    status: String,
}

impl<'a> ChapelAbsenceRequest {
    define_elements! {
        TABLE02_CP_CP: SapTable<'a> = "ZCMW3681.ID_0001:V_MAIN.TABLE02_CP_CP";
    }
    pub(crate) fn with_parser(parser: &'a ElementParser) -> Result<Vec<Self>, RusaintError> {
        let table = parser.read(SapTableBodyCommand::new(Self::TABLE02_CP_CP))?;
        let Some(first_row) = table.iter().next() else {
            return Ok(Vec::with_capacity(0));
        };
        if let Some(Ok(SapTableCellWrapper::Normal(cell))) = first_row.iter_value(parser).next()
            && let Some(ElementDefWrapper::TextView(tv_def)) = cell.content()
            && let Ok(tv) = parser.element_from_def(&tv_def)
            && tv.text().contains("없습니다.")
        {
            return Ok(Vec::with_capacity(0));
        }

        Ok(table.try_table_into::<Self>(parser)?)
    }

    /// 신청 학년도를 반환합니다.
    pub fn year(&self) -> u32 {
        self.year
    }

    /// 신청 학기를 반환합니다.
    pub fn semester(&self) -> SemesterType {
        self.semester
    }

    /// 결석구분상세를 반환합니다.
    pub fn absence_detail(&self) -> &str {
        &self.absence_detail
    }

    /// 결석시작일자를 반환합니다.
    pub fn absence_start(&self) -> &str {
        &self.absence_start
    }

    /// 결석종료일자를 반환합니다.
    pub fn absence_end(&self) -> &str {
        &self.absence_end
    }

    /// 국문 결석사유를 반환합니다.
    pub fn absence_reason_kr(&self) -> &str {
        &self.absence_reason_kr
    }

    /// 영문 결석사유를 반환합니다.
    pub fn absence_reason_en(&self) -> &str {
        &self.absence_reason_en
    }

    /// 신청일자를 반환합니다.
    pub fn application_date(&self) -> &str {
        &self.application_date
    }

    /// 승인일자를 반환합니다.
    pub fn approval_date(&self) -> &str {
        &self.approval_date
    }

    /// 거부사유를 반환합니다.
    pub fn denial_reason(&self) -> &str {
        &self.denial_reason
    }

    /// 요청 상태를 반환합니다.
    pub fn status(&self) -> &str {
        &self.status
    }
}

impl<'body> FromSapTable<'body> for ChapelAbsenceRequest {
    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(
            Self::deserialize(map_de).map_err(|e| ElementError::InvalidContent {
                element: row.table_def().id().to_string(),
                content: e.to_string(),
            })?,
        )
    }
}