|
|
|
@@ -51,6 +51,7 @@ pub struct Clazz {
|
|
|
|
|
pub course_sections: Option<MultiVals<CourseSectionJsonData>>,
|
|
|
|
|
pub is_notified: Option<bool>,
|
|
|
|
|
pub completed_at: Option<NaiveDateTime>,
|
|
|
|
|
pub org_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://weinan.io/2024/02/23/rust-diesel.html
|
|
|
|
@@ -97,6 +98,8 @@ pub struct ReqClazzWithRepeat {
|
|
|
|
|
pub clz_course_sections: Option<MultiVals<CourseSectionJsonData>>,
|
|
|
|
|
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Timestamp >)]
|
|
|
|
|
pub clz_completed_at: Option<NaiveDateTime>,
|
|
|
|
|
#[diesel(sql_type = diesel::sql_types::Nullable < diesel::sql_types::Varchar >)]
|
|
|
|
|
pub clz_org_id: Option<String>,
|
|
|
|
|
// // kc_repeat
|
|
|
|
|
#[diesel(sql_type = diesel::sql_types::Varchar)]
|
|
|
|
|
pub re_id: String,
|
|
|
|
@@ -148,6 +151,7 @@ impl ReqClazzWithRepeat {
|
|
|
|
|
course_sections: None,
|
|
|
|
|
is_notified: None,
|
|
|
|
|
completed_at: None,
|
|
|
|
|
org_id: None,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -164,6 +168,7 @@ impl ReqClazzWithRepeat {
|
|
|
|
|
repeat_end: None,
|
|
|
|
|
repeat_status: None,
|
|
|
|
|
latest_clazz_created_at: None,
|
|
|
|
|
org_id: None,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -188,6 +193,7 @@ impl ReqClazzWithRepeat {
|
|
|
|
|
clz_is_repeat: the_kecheng.is_repeat.clone(),
|
|
|
|
|
clz_course_sections: the_kecheng.course_sections.clone(),
|
|
|
|
|
clz_completed_at: the_kecheng.completed_at.clone(),
|
|
|
|
|
clz_org_id: the_kecheng.org_id.clone(),
|
|
|
|
|
re_id: the_kecheng_repeat.id.clone(),
|
|
|
|
|
re_clazz_id: the_kecheng_repeat.clazz_id.clone(),
|
|
|
|
|
re_start_from: the_kecheng_repeat.start_from.clone(),
|
|
|
|
@@ -225,6 +231,7 @@ impl Clazz {
|
|
|
|
|
course_sections: self.course_sections.clone(),
|
|
|
|
|
is_notified: self.is_notified.clone(),
|
|
|
|
|
completed_at: self.completed_at.clone(),
|
|
|
|
|
org_id: self.org_id.clone(),
|
|
|
|
|
};
|
|
|
|
|
req_res
|
|
|
|
|
}
|
|
|
|
@@ -337,7 +344,7 @@ impl Clazz {
|
|
|
|
|
end_by: &NaiveDateTime,
|
|
|
|
|
conn: &mut PgConnection,
|
|
|
|
|
) -> anyhow::Result<Option<Vec<ReqClazzWithRepeat>>> {
|
|
|
|
|
let q = format!("SELECT clazz.id AS clz_id, clazz.clazz_name AS clz_clazz_name, clazz.clazz_status AS clz_clazz_status, clazz.clazz_desc AS clz_clazz_desc, clazz.start_from AS clz_start_from, clazz.end_by AS clz_end_by, clazz.duration AS clz_duration, clazz.root_id AS clz_root_id, clazz.clazz_type AS clz_clazz_type, clazz.parent_id AS clz_parent_id, clazz.created_by AS clz_created_by, clazz.created_at AS clz_created_at, clazz.students AS clz_students, clazz.teachers AS clz_teachers, clazz.jihuas AS clz_jihuas, clazz.dakas AS clz_dakas, clazz.is_delete AS clz_is_delete, clazz.is_repeat AS clz_is_repeat, clazz.course_sections AS clz_course_sections, clazz.completed_at AS clz_completed_at, clazz_repeat.id AS re_id, clazz_repeat.clazz_id AS re_clazz_id, clazz_repeat.start_from AS re_start_from, clazz_repeat.end_by AS re_end_by, clazz_repeat.repeat_start AS re_repeat_start, clazz_repeat.repeat_cycle_days AS re_repeat_cycle_days, clazz_repeat.repeat_end AS re_repeat_end, clazz_repeat.repeat_status AS re_repeat_status, clazz_repeat.latest_clazz_created_at AS re_latest_clazz_created_at FROM clazz JOIN clazz_repeat ON clazz.id = clazz_repeat.clazz_id WHERE (clazz.is_repeat IS NOT NULL AND clazz.is_repeat IS TRUE) AND ((clazz_repeat.repeat_start <= '{}' AND clazz_repeat.repeat_end >= '{}') OR (clazz.start_from <= '{}' AND clazz.end_by >= '{}'));", date_to_string(end_by), date_to_string(start_from), date_to_string(end_by), date_to_string(start_from)).to_string();
|
|
|
|
|
let q = format!("SELECT clazz.id AS clz_id, clazz.clazz_name AS clz_clazz_name, clazz.clazz_status AS clz_clazz_status, clazz.clazz_desc AS clz_clazz_desc, clazz.start_from AS clz_start_from, clazz.end_by AS clz_end_by, clazz.duration AS clz_duration, clazz.root_id AS clz_root_id, clazz.clazz_type AS clz_clazz_type, clazz.parent_id AS clz_parent_id, clazz.created_by AS clz_created_by, clazz.created_at AS clz_created_at, clazz.students AS clz_students, clazz.teachers AS clz_teachers, clazz.jihuas AS clz_jihuas, clazz.dakas AS clz_dakas, clazz.is_delete AS clz_is_delete, clazz.is_repeat AS clz_is_repeat, clazz.course_sections AS clz_course_sections, clazz.completed_at AS clz_completed_at, clazz.org_id AS clz_org_id, clazz_repeat.id AS re_id, clazz_repeat.clazz_id AS re_clazz_id, clazz_repeat.start_from AS re_start_from, clazz_repeat.end_by AS re_end_by, clazz_repeat.repeat_start AS re_repeat_start, clazz_repeat.repeat_cycle_days AS re_repeat_cycle_days, clazz_repeat.repeat_end AS re_repeat_end, clazz_repeat.repeat_status AS re_repeat_status, clazz_repeat.latest_clazz_created_at AS re_latest_clazz_created_at FROM clazz JOIN clazz_repeat ON clazz.id = clazz_repeat.clazz_id WHERE (clazz.is_repeat IS NOT NULL AND clazz.is_repeat IS TRUE) AND ((clazz_repeat.repeat_start <= '{}' AND clazz_repeat.repeat_end >= '{}') OR (clazz.start_from <= '{}' AND clazz.end_by >= '{}'));", date_to_string(end_by), date_to_string(start_from), date_to_string(end_by), date_to_string(start_from)).to_string();
|
|
|
|
|
|
|
|
|
|
debug!("find_all_repeatable_by_date_range -> q: {:?}", q);
|
|
|
|
|
|
|
|
|
@@ -358,7 +365,7 @@ impl Clazz {
|
|
|
|
|
end_by: &NaiveDateTime,
|
|
|
|
|
conn: &mut PgConnection,
|
|
|
|
|
) -> anyhow::Result<Option<Vec<ReqClazzWithRepeat>>> {
|
|
|
|
|
let q = format!("SELECT clazz.id AS clz_id, clazz.clazz_name AS clz_clazz_name, clazz.clazz_status AS clz_clazz_status, clazz.clazz_desc AS clz_clazz_desc, clazz.start_from AS clz_start_from, clazz.end_by AS clz_end_by, clazz.duration AS clz_duration, clazz.root_id AS clz_root_id, clazz.clazz_type AS clz_clazz_type, clazz.parent_id AS clz_parent_id, clazz.created_by AS clz_created_by, clazz.created_at AS clz_created_at, clazz.students AS clz_students, clazz.teachers AS clz_teachers, clazz.jihuas AS clz_jihuas, clazz.dakas AS clz_dakas, clazz.is_delete AS clz_is_delete, clazz.is_repeat AS clz_is_repeat, clazz.course_sections AS clz_course_sections, clazz.completed_at AS clz_completed_at, clazz_repeat.id AS re_id, clazz_repeat.clazz_id AS re_clazz_id, clazz_repeat.start_from AS re_start_from, clazz_repeat.end_by AS re_end_by, clazz_repeat.repeat_start AS re_repeat_start, clazz_repeat.repeat_cycle_days AS re_repeat_cycle_days, clazz_repeat.repeat_end AS re_repeat_end, clazz_repeat.repeat_status AS re_repeat_status, clazz_repeat.latest_clazz_created_at AS re_latest_clazz_created_at FROM clazz JOIN clazz_repeat ON clazz.id = clazz_repeat.clazz_id WHERE (jsonb_path_exists(clazz.students, '$.val.users.vals[*].user_id ? (@ == \"{}\")') OR jsonb_path_exists(clazz.teachers, '$.val.users.vals[*].user_id ? (@ == \"{}\")')) AND (clazz.is_repeat IS NOT NULL AND clazz.is_repeat IS TRUE) AND ((clazz_repeat.repeat_start <= '{}' AND clazz_repeat.repeat_end >= '{}') OR (clazz.start_from <= '{}' AND clazz.end_by >= '{}'));", id_user.clone(), id_user.clone(), date_to_string(end_by), date_to_string(start_from), date_to_string(end_by), date_to_string(start_from)).to_string();
|
|
|
|
|
let q = format!("SELECT clazz.id AS clz_id, clazz.clazz_name AS clz_clazz_name, clazz.clazz_status AS clz_clazz_status, clazz.clazz_desc AS clz_clazz_desc, clazz.start_from AS clz_start_from, clazz.end_by AS clz_end_by, clazz.duration AS clz_duration, clazz.root_id AS clz_root_id, clazz.clazz_type AS clz_clazz_type, clazz.parent_id AS clz_parent_id, clazz.created_by AS clz_created_by, clazz.created_at AS clz_created_at, clazz.students AS clz_students, clazz.teachers AS clz_teachers, clazz.jihuas AS clz_jihuas, clazz.dakas AS clz_dakas, clazz.is_delete AS clz_is_delete, clazz.is_repeat AS clz_is_repeat, clazz.course_sections AS clz_course_sections, clazz.completed_at AS clz_completed_at, clazz.org_id AS clz_org_id, clazz_repeat.id AS re_id, clazz_repeat.clazz_id AS re_clazz_id, clazz_repeat.start_from AS re_start_from, clazz_repeat.end_by AS re_end_by, clazz_repeat.repeat_start AS re_repeat_start, clazz_repeat.repeat_cycle_days AS re_repeat_cycle_days, clazz_repeat.repeat_end AS re_repeat_end, clazz_repeat.repeat_status AS re_repeat_status, clazz_repeat.latest_clazz_created_at AS re_latest_clazz_created_at FROM clazz JOIN clazz_repeat ON clazz.id = clazz_repeat.clazz_id WHERE (jsonb_path_exists(clazz.students, '$.val.users.vals[*].user_id ? (@ == \"{}\")') OR jsonb_path_exists(clazz.teachers, '$.val.users.vals[*].user_id ? (@ == \"{}\")')) AND (clazz.is_repeat IS NOT NULL AND clazz.is_repeat IS TRUE) AND ((clazz_repeat.repeat_start <= '{}' AND clazz_repeat.repeat_end >= '{}') OR (clazz.start_from <= '{}' AND clazz.end_by >= '{}'));", id_user.clone(), id_user.clone(), date_to_string(end_by), date_to_string(start_from), date_to_string(end_by), date_to_string(start_from)).to_string();
|
|
|
|
|
|
|
|
|
|
debug!(
|
|
|
|
|
"find_all_repeatable_by_date_range_and_user_id -> q: {:?}",
|
|
|
|
@@ -445,6 +452,7 @@ pub struct ClazzRepeat {
|
|
|
|
|
pub repeat_end: Option<NaiveDateTime>,
|
|
|
|
|
pub repeat_status: Option<String>,
|
|
|
|
|
pub latest_clazz_created_at: Option<NaiveDateTime>,
|
|
|
|
|
pub org_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ClazzRepeat {
|
|
|
|
@@ -476,6 +484,7 @@ impl ClazzRepeat {
|
|
|
|
|
repeat_end: self.repeat_end.clone(),
|
|
|
|
|
repeat_status: self.repeat_status.clone(),
|
|
|
|
|
latest_clazz_created_at: self.latest_clazz_created_at.clone(),
|
|
|
|
|
org_id: self.org_id.clone(),
|
|
|
|
|
};
|
|
|
|
|
req_repeat
|
|
|
|
|
}
|
|
|
|
@@ -609,6 +618,7 @@ pub struct ReqClazz {
|
|
|
|
|
pub course_sections: Option<MultiVals<CourseSectionJsonData>>,
|
|
|
|
|
pub is_notified: Option<bool>,
|
|
|
|
|
pub completed_at: Option<NaiveDateTime>,
|
|
|
|
|
pub org_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ReqClazz {
|
|
|
|
@@ -641,6 +651,7 @@ impl ReqClazz {
|
|
|
|
|
course_sections: None,
|
|
|
|
|
is_notified: None,
|
|
|
|
|
completed_at: None,
|
|
|
|
|
org_id: None,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -677,6 +688,7 @@ impl ReqClazz {
|
|
|
|
|
course_sections: the_kecheng.course_sections,
|
|
|
|
|
is_notified: the_kecheng.is_notified,
|
|
|
|
|
completed_at: the_kecheng.completed_at,
|
|
|
|
|
org_id: the_kecheng.org_id,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -692,6 +704,7 @@ pub struct ReqClazzRepeat {
|
|
|
|
|
pub repeat_end: Option<NaiveDateTime>,
|
|
|
|
|
pub repeat_status: Option<String>,
|
|
|
|
|
pub latest_clazz_created_at: Option<NaiveDateTime>,
|
|
|
|
|
pub org_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
|
|
@@ -719,6 +732,7 @@ pub struct CourseHourPackage {
|
|
|
|
|
pub created_by: Option<String>,
|
|
|
|
|
pub updated_at: Option<NaiveDateTime>,
|
|
|
|
|
pub is_delete: Option<bool>,
|
|
|
|
|
pub org_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Queryable, Serialize, Deserialize, Debug, Clone)]
|
|
|
|
@@ -732,6 +746,7 @@ pub struct HourTransaction {
|
|
|
|
|
pub operator_hty_id: Option<String>,
|
|
|
|
|
pub remark: Option<String>,
|
|
|
|
|
pub created_at: NaiveDateTime,
|
|
|
|
|
pub org_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Queryable, Serialize, Deserialize, Debug, Clone)]
|
|
|
|
@@ -747,6 +762,7 @@ pub struct ClazzAttendance {
|
|
|
|
|
pub created_at: NaiveDateTime,
|
|
|
|
|
pub created_by: Option<String>,
|
|
|
|
|
pub is_delete: Option<bool>,
|
|
|
|
|
pub org_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Insertable, Clone, Debug)]
|
|
|
|
@@ -763,6 +779,7 @@ pub struct NewClazzAttendance {
|
|
|
|
|
pub created_at: NaiveDateTime,
|
|
|
|
|
pub created_by: Option<String>,
|
|
|
|
|
pub is_delete: Option<bool>,
|
|
|
|
|
pub org_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
|
|
@@ -780,6 +797,166 @@ pub struct ReqBatchClazzAttendance {
|
|
|
|
|
pub items: Vec<ReqClazzAttendanceItem>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(
|
|
|
|
|
AsChangeset,
|
|
|
|
|
Associations,
|
|
|
|
|
Identifiable,
|
|
|
|
|
PartialEq,
|
|
|
|
|
Serialize,
|
|
|
|
|
Deserialize,
|
|
|
|
|
Queryable,
|
|
|
|
|
Debug,
|
|
|
|
|
Insertable,
|
|
|
|
|
Clone,
|
|
|
|
|
)]
|
|
|
|
|
#[diesel(table_name = clazz_leave_request)]
|
|
|
|
|
#[diesel(belongs_to(Clazz, foreign_key = clazz_id))]
|
|
|
|
|
pub struct ClazzLeaveRequest {
|
|
|
|
|
pub id: String,
|
|
|
|
|
pub org_id: String,
|
|
|
|
|
pub clazz_id: String,
|
|
|
|
|
pub student_id: String,
|
|
|
|
|
pub teacher_id: Option<String>,
|
|
|
|
|
pub leave_type: String,
|
|
|
|
|
pub reason: Option<String>,
|
|
|
|
|
pub request_status: String,
|
|
|
|
|
pub created_at: NaiveDateTime,
|
|
|
|
|
pub created_by: Option<String>,
|
|
|
|
|
pub reviewed_at: Option<NaiveDateTime>,
|
|
|
|
|
pub reviewed_by: Option<String>,
|
|
|
|
|
pub is_delete: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
|
|
|
pub struct ReqClazzLeaveRequest {
|
|
|
|
|
pub id: Option<String>,
|
|
|
|
|
pub org_id: Option<String>,
|
|
|
|
|
pub clazz_id: Option<String>,
|
|
|
|
|
pub student_id: Option<String>,
|
|
|
|
|
pub teacher_id: Option<String>,
|
|
|
|
|
pub leave_type: Option<String>,
|
|
|
|
|
pub reason: Option<String>,
|
|
|
|
|
pub request_status: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(QueryableByName, Serialize, Deserialize, Debug, Clone)]
|
|
|
|
|
pub struct TeacherHourStatsRow {
|
|
|
|
|
#[diesel(sql_type = diesel::sql_types::Varchar)]
|
|
|
|
|
pub teacher_id: String,
|
|
|
|
|
#[diesel(sql_type = diesel::sql_types::BigInt)]
|
|
|
|
|
pub clazz_count: i64,
|
|
|
|
|
#[diesel(sql_type = diesel::sql_types::BigInt)]
|
|
|
|
|
pub student_count: i64,
|
|
|
|
|
#[diesel(sql_type = diesel::sql_types::Double)]
|
|
|
|
|
pub total_hours: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ClazzLeaveRequest {
|
|
|
|
|
pub fn create(
|
|
|
|
|
payload: &ClazzLeaveRequest,
|
|
|
|
|
conn: &mut PgConnection,
|
|
|
|
|
) -> anyhow::Result<ClazzLeaveRequest> {
|
|
|
|
|
use crate::schema::clazz_leave_request::dsl::*;
|
|
|
|
|
insert_into(clazz_leave_request)
|
|
|
|
|
.values(payload)
|
|
|
|
|
.get_result(conn)
|
|
|
|
|
.map_err(|e| anyhow!(HtyErr {
|
|
|
|
|
code: HtyErrCode::DbErr,
|
|
|
|
|
reason: Some(e.to_string()),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn update_status(
|
|
|
|
|
request_id: &String,
|
|
|
|
|
target_status: &String,
|
|
|
|
|
reviewer_id: &Option<String>,
|
|
|
|
|
conn: &mut PgConnection,
|
|
|
|
|
) -> anyhow::Result<ClazzLeaveRequest> {
|
|
|
|
|
diesel::update(
|
|
|
|
|
clazz_leave_request::table.filter(clazz_leave_request::id.eq(request_id)),
|
|
|
|
|
)
|
|
|
|
|
.set((
|
|
|
|
|
clazz_leave_request::request_status.eq(target_status),
|
|
|
|
|
clazz_leave_request::reviewed_at.eq(Some(current_local_datetime())),
|
|
|
|
|
clazz_leave_request::reviewed_by.eq(reviewer_id.clone()),
|
|
|
|
|
))
|
|
|
|
|
.get_result(conn)
|
|
|
|
|
.map_err(|e| anyhow!(HtyErr {
|
|
|
|
|
code: HtyErrCode::DbErr,
|
|
|
|
|
reason: Some(e.to_string()),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn list_by_org_and_scope(
|
|
|
|
|
org: &String,
|
|
|
|
|
clazz: &Option<String>,
|
|
|
|
|
student: &Option<String>,
|
|
|
|
|
teacher: &Option<String>,
|
|
|
|
|
conn: &mut PgConnection,
|
|
|
|
|
) -> anyhow::Result<Vec<ClazzLeaveRequest>> {
|
|
|
|
|
let mut query = clazz_leave_request::table
|
|
|
|
|
.into_boxed()
|
|
|
|
|
.filter(clazz_leave_request::org_id.eq(org))
|
|
|
|
|
.filter(clazz_leave_request::is_delete.eq(false));
|
|
|
|
|
if let Some(clazz_id) = clazz {
|
|
|
|
|
query = query.filter(clazz_leave_request::clazz_id.eq(clazz_id));
|
|
|
|
|
}
|
|
|
|
|
if let Some(student_id) = student {
|
|
|
|
|
query = query.filter(clazz_leave_request::student_id.eq(student_id));
|
|
|
|
|
}
|
|
|
|
|
if let Some(teacher_id) = teacher {
|
|
|
|
|
query = query.filter(clazz_leave_request::teacher_id.eq(teacher_id));
|
|
|
|
|
}
|
|
|
|
|
query
|
|
|
|
|
.order(clazz_leave_request::created_at.desc())
|
|
|
|
|
.load::<ClazzLeaveRequest>(conn)
|
|
|
|
|
.map_err(|e| anyhow!(HtyErr {
|
|
|
|
|
code: HtyErrCode::DbErr,
|
|
|
|
|
reason: Some(e.to_string()),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Clazz {
|
|
|
|
|
pub fn teacher_hour_stats_by_org_and_date_range(
|
|
|
|
|
org: &String,
|
|
|
|
|
start_date: &String,
|
|
|
|
|
end_date: &String,
|
|
|
|
|
target_teacher_id: Option<&String>,
|
|
|
|
|
conn: &mut PgConnection,
|
|
|
|
|
) -> anyhow::Result<Vec<TeacherHourStatsRow>> {
|
|
|
|
|
let teacher_filter = if let Some(teacher_id) = target_teacher_id {
|
|
|
|
|
format!(" and c.created_by = '{}'", teacher_id)
|
|
|
|
|
} else {
|
|
|
|
|
String::new()
|
|
|
|
|
};
|
|
|
|
|
let query_text = format!(
|
|
|
|
|
"select c.created_by as teacher_id,
|
|
|
|
|
count(distinct c.id)::bigint as clazz_count,
|
|
|
|
|
count(distinct a.student_id)::bigint as student_count,
|
|
|
|
|
coalesce(sum(a.deducted_hours), 0)::double precision as total_hours
|
|
|
|
|
from clazz c
|
|
|
|
|
left join clazz_attendance a on c.id = a.clazz_id
|
|
|
|
|
where c.org_id = '{org}'
|
|
|
|
|
and c.start_from >= '{start}'
|
|
|
|
|
and c.end_by <= '{end}'
|
|
|
|
|
and (a.is_delete is null or a.is_delete = false)
|
|
|
|
|
{teacher_filter}
|
|
|
|
|
group by c.created_by",
|
|
|
|
|
org = org,
|
|
|
|
|
start = start_date,
|
|
|
|
|
end = end_date,
|
|
|
|
|
teacher_filter = teacher_filter,
|
|
|
|
|
);
|
|
|
|
|
sql_query(query_text)
|
|
|
|
|
.load::<TeacherHourStatsRow>(conn)
|
|
|
|
|
.map_err(|e| anyhow!(HtyErr {
|
|
|
|
|
code: HtyErrCode::DbErr,
|
|
|
|
|
reason: Some(e.to_string()),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ClazzAttendance {
|
|
|
|
|
pub fn find_all_active_by_clazz_id(
|
|
|
|
|
clazz_id_in: &String,
|
|
|
|
@@ -824,6 +1001,7 @@ impl ClazzAttendance {
|
|
|
|
|
created_at: now,
|
|
|
|
|
created_by: operator_id.clone(),
|
|
|
|
|
is_delete: Some(false),
|
|
|
|
|
org_id: None,
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|