29_timesheet-management #64
2 changed files with 17 additions and 19 deletions
|
|
@ -5,6 +5,9 @@ use chrono::Datelike;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::config::Settings;
|
use crate::config::Settings;
|
||||||
|
|
||||||
|
const SEPARATOR_WIDTH: usize = 71;
|
||||||
|
const COLUMN_SEPARATOR_WIDTH: usize = 65;
|
||||||
use crate::error::StreamdError;
|
use crate::error::StreamdError;
|
||||||
use crate::extract::parse_markdown_file;
|
use crate::extract::parse_markdown_file;
|
||||||
use crate::localize::localize_stream_file;
|
use crate::localize::localize_stream_file;
|
||||||
|
|
@ -66,13 +69,10 @@ fn weekday_abbrev(date: chrono::NaiveDate) -> &'static str {
|
||||||
|
|
||||||
/// Print the timesheet report header.
|
/// Print the timesheet report header.
|
||||||
fn print_header() {
|
fn print_header() {
|
||||||
println!(
|
let double_line = "\u{2550}".repeat(SEPARATOR_WIDTH);
|
||||||
"\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}"
|
println!("{}", double_line);
|
||||||
);
|
|
||||||
println!(" TIMESHEET REPORT");
|
println!(" TIMESHEET REPORT");
|
||||||
println!(
|
println!("{}", double_line);
|
||||||
"\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}"
|
|
||||||
);
|
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,10 +91,9 @@ fn print_month(month: &MonthReport) {
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
// Column headers
|
// Column headers
|
||||||
|
let light_line = "\u{2500}".repeat(COLUMN_SEPARATOR_WIDTH);
|
||||||
println!(" Date Day Expected Actual Diff Type");
|
println!(" Date Day Expected Actual Diff Type");
|
||||||
println!(
|
println!(" {}", light_line);
|
||||||
" \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Day rows
|
// Day rows
|
||||||
for day in &month.days {
|
for day in &month.days {
|
||||||
|
|
@ -132,9 +131,7 @@ fn print_month(month: &MonthReport) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monthly totals
|
// Monthly totals
|
||||||
println!(
|
println!(" {}", light_line);
|
||||||
" \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}"
|
|
||||||
);
|
|
||||||
println!(
|
println!(
|
||||||
" Monthly: {:>7} {:>7} {:>6}",
|
" Monthly: {:>7} {:>7} {:>6}",
|
||||||
format_hours(month.total_expected()),
|
format_hours(month.total_expected()),
|
||||||
|
|
@ -146,16 +143,13 @@ fn print_month(month: &MonthReport) {
|
||||||
|
|
||||||
/// Print the cumulative balance.
|
/// Print the cumulative balance.
|
||||||
fn print_cumulative_balance(balance: f64) {
|
fn print_cumulative_balance(balance: f64) {
|
||||||
println!(
|
let light_line = "\u{2500}".repeat(SEPARATOR_WIDTH);
|
||||||
"\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}"
|
println!("{}", light_line);
|
||||||
);
|
|
||||||
println!(
|
println!(
|
||||||
" CUMULATIVE BALANCE: {}",
|
" CUMULATIVE BALANCE: {}",
|
||||||
format_diff(balance)
|
format_diff(balance)
|
||||||
);
|
);
|
||||||
println!(
|
println!("{}", light_line);
|
||||||
"\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print warnings section.
|
/// Print warnings section.
|
||||||
|
|
|
||||||
|
|
@ -136,8 +136,12 @@ fn aggregate_timecard_day(points: &[TimesheetPoint]) -> Result<Option<Timesheet>
|
||||||
fn aggregate_timecards(points: &[TimesheetPoint]) -> Result<Vec<Timesheet>, StreamdError> {
|
fn aggregate_timecards(points: &[TimesheetPoint]) -> Result<Vec<Timesheet>, StreamdError> {
|
||||||
let mut timesheets = Vec::new();
|
let mut timesheets = Vec::new();
|
||||||
|
|
||||||
|
// Sort points by moment to ensure proper grouping
|
||||||
|
let mut sorted_points = points.to_vec();
|
||||||
|
sorted_points.sort_by_key(|p| p.moment);
|
||||||
|
|
||||||
// Group by date
|
// Group by date
|
||||||
for (_date, group) in &points.iter().chunk_by(|p| p.moment.date_naive()) {
|
for (_date, group) in &sorted_points.iter().chunk_by(|p| p.moment.date_naive()) {
|
||||||
let day_points: Vec<_> = group.cloned().collect();
|
let day_points: Vec<_> = group.cloned().collect();
|
||||||
if let Some(timesheet) = aggregate_timecard_day(&day_points)? {
|
if let Some(timesheet) = aggregate_timecard_day(&day_points)? {
|
||||||
timesheets.push(timesheet);
|
timesheets.push(timesheet);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue