feat : update name grouping

This commit is contained in:
2025-12-03 11:07:32 +08:00
parent 067e5b0462
commit eb6f2b18f7
5 changed files with 65 additions and 37 deletions

View File

@@ -3,10 +3,11 @@ use chrono::Datelike;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MonthlyRecord {
pub month: u32,
pub month_name: String, // e.g., "9月"
pub teacher: Option<String>,
pub month_name: String, // e.g., "9月(老师名)"
pub count: i32,
pub price: f64,
pub total: f64,
@@ -37,22 +38,32 @@ pub fn process_data(classes: Vec<ClassData>) -> Vec<ClassReport> {
let mut student_reports = Vec::new();
for record in class_data.records {
let mut month_counts: HashMap<u32, i32> = HashMap::new();
// Group by (Month, Teacher)
let mut month_teacher_counts: HashMap<(u32, Option<String>), i32> = HashMap::new();
for (date, _) in record.attendance {
for (date, teacher, _) in record.attendance {
let month = date.month();
*month_counts.entry(month).or_insert(0) += 1;
*month_teacher_counts.entry((month, teacher)).or_insert(0) += 1;
}
let mut monthly_records = Vec::new();
let mut months: Vec<u32> = month_counts.keys().cloned().collect();
months.sort();
let mut keys: Vec<(u32, Option<String>)> = month_teacher_counts.keys().cloned().collect();
// Sort by month then teacher
keys.sort();
for (month, teacher) in keys {
let count = month_teacher_counts[&(month, teacher.clone())];
let month_name = if let Some(t) = &teacher {
format!("{}月({}", month, t)
} else {
format!("{}", month)
};
for month in months {
let count = month_counts[&month];
monthly_records.push(MonthlyRecord {
month,
month_name: format!("{}月", month),
teacher,
month_name,
count,
price,
total: count as f64 * price,

View File

@@ -16,8 +16,8 @@ pub struct Student {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AttendanceRecord {
pub student: Student,
// Date and attendance count (1.0 for present)
pub attendance: Vec<(NaiveDate, f64)>,
// Date, Teacher Name, and attendance count (1.0 for present)
pub attendance: Vec<(NaiveDate, Option<String>, f64)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -67,6 +67,30 @@ pub fn read_attendance_file<P: AsRef<Path>>(path: P) -> Result<Vec<ClassData>> {
continue;
}
// Find Teacher Row
// Scan all rows to find "授课教师确认签名"
let mut teacher_names = vec![None; dates.len()];
for row in range.rows() {
if let Some(first_cell) = row.first() {
if let Data::String(s) = first_cell {
if s.contains("授课教师确认签名") {
// Found teacher row
for (i, &col_idx) in date_col_indices.iter().enumerate() {
if col_idx < row.len() {
if let Data::String(name) = &row[col_idx] {
let trimmed = name.trim();
if !trimmed.is_empty() {
teacher_names[i] = Some(trimmed.to_string());
}
}
}
}
break;
}
}
}
}
// Iterate over data rows (starting from row 4, index 3)
// Row 1: Title, Row 2: Headers, Row 3: Weekdays, Row 4: Data
for row in range.rows().skip(3) {
@@ -78,7 +102,7 @@ pub fn read_attendance_file<P: AsRef<Path>>(path: P) -> Result<Vec<ClassData>> {
_ => continue, // Skip if no name
};
if name.is_empty() || name == "姓名" {
if name.is_empty() || name == "姓名" || name == "到班人数" || name.contains("授课教师") {
continue;
}
@@ -110,7 +134,7 @@ pub fn read_attendance_file<P: AsRef<Path>>(path: P) -> Result<Vec<ClassData>> {
if is_present {
if i < dates.len() {
attendance_entries.push((dates[i], 1.0));
attendance_entries.push((dates[i], teacher_names[i].clone(), 1.0));
}
}
}

View File

@@ -68,38 +68,37 @@ pub fn write_report<P: AsRef<Path>>(path: P, reports: Vec<ClassReport>) -> Resul
worksheet.write_string_with_format(current_row, i as u16, *header, &main_header_format)?;
}
// Collect all months
let mut all_months = std::collections::BTreeSet::new();
// Collect all unique (month, teacher) pairs
let mut all_month_teachers = std::collections::BTreeSet::new();
for student in &report.students {
for record in &student.monthly_records {
all_months.insert(record.month);
all_month_teachers.insert((record.month, record.teacher.clone()));
}
}
// Map month to start column index
// Map (month, teacher) to start column index
let mut month_col_map = std::collections::HashMap::new();
let mut col_idx = 5; // Start after "姓名"
for month in &all_months {
for (month, teacher) in &all_month_teachers {
// Write Month Header (merged 3 cells)
let month_str = format!("{}", month);
let month_str = if let Some(t) = teacher {
format!("{}月({}", month, t)
} else {
format!("{}", month)
};
worksheet.merge_range(current_row, col_idx, current_row, col_idx + 2, &month_str, &main_header_format)?;
month_col_map.insert(*month, col_idx);
month_col_map.insert((*month, teacher.clone()), col_idx);
col_idx += 3;
}
current_row += 1;
// Write Sub-headers (次数, 单价, 费用合计)
// Columns A-E are empty in this row in the sample?
// Sample Row 149: [None, None, None, None, None, '次数', ...]
// So we leave A-E empty or apply a default format?
// Let's apply data format to keep borders consistent if needed, or just leave blank.
// Sample shows blank.
let mut sub_col_idx = 5;
for _ in &all_months {
for _ in &all_month_teachers {
worksheet.write_string_with_format(current_row, sub_col_idx, "次数", &sub_header_format)?;
worksheet.write_string_with_format(current_row, sub_col_idx + 1, "单价", &sub_header_format)?;
worksheet.write_string_with_format(current_row, sub_col_idx + 2, "费用合计", &sub_header_format)?;
@@ -133,7 +132,7 @@ pub fn write_report<P: AsRef<Path>>(path: P, reports: Vec<ClassReport>) -> Resul
worksheet.write_string_with_format(current_row, 4, &student.student.name, &data_format)?;
for record in &student.monthly_records {
if let Some(&start_col) = month_col_map.get(&record.month) {
if let Some(&start_col) = month_col_map.get(&(record.month, record.teacher.clone())) {
worksheet.write_number_with_format(current_row, start_col, record.count as f64, &data_format)?;
worksheet.write_number_with_format(current_row, start_col + 1, record.price, &data_format)?;
@@ -148,14 +147,8 @@ pub fn write_report<P: AsRef<Path>>(path: P, reports: Vec<ClassReport>) -> Resul
}
// Add Total Row
// Sample Row 162: [None, ..., =SUM(...)]
// We need to sum the columns.
// Let's add a "Total" row.
// Columns F, H, I, K, etc. (Count and Total Cost)
// We need to iterate through all month columns again
for month in &all_months {
if let Some(&start_col) = month_col_map.get(month) {
for (month, teacher) in &all_month_teachers {
if let Some(&start_col) = month_col_map.get(&(*month, teacher.clone())) {
// Sum Count (start_col)
let start_cell = rust_xlsxwriter::utility::row_col_to_cell(current_row - report.students.len() as u32, start_col);
let end_cell = rust_xlsxwriter::utility::row_col_to_cell(current_row - 1, start_col);