Automated Time Tracking and Productivity Analysis Tool for Remote Teams,Rust
👤 Sharing: AI
```rust
use std::collections::HashMap;
use std::time::{Duration, Instant};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use std::sync::Arc;
// Define data structures to represent tasks, users, and time entries.
// Struct representing a task
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
struct Task {
id: String,
name: String,
}
// Struct representing a user
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
struct User {
id: String,
name: String,
}
// Struct representing a time entry (start and end time for a task by a user).
#[derive(Debug, Clone, Serialize, Deserialize)]
struct TimeEntry {
user: User,
task: Task,
start_time: DateTime<Utc>,
end_time: Option<DateTime<Utc>>, // Optional end time; None if the task is still running.
}
// Struct to hold the program's state and data. Using Arc<Mutex<_>> allows multiple threads to access and modify shared data safely.
struct AppState {
time_entries: Mutex<Vec<TimeEntry>>, // Store time entries in a vector. Mutex ensures safe concurrent access.
tasks: Mutex<HashMap<String, Task>>, // Store tasks in a hashmap for easy lookup.
users: Mutex<HashMap<String, User>>, // Store users similarly.
}
impl AppState {
// Constructor to initialize the application state.
fn new() -> Self {
AppState {
time_entries: Mutex::new(Vec::new()),
tasks: Mutex::new(HashMap::new()),
users: Mutex::new(HashMap::new()),
}
}
}
// --- Core Functions ---
// Function to start tracking time for a task for a user. Asynchronous to allow for non-blocking operations.
async fn start_tracking(
state: Arc<AppState>,
user_id: String,
user_name: String,
task_id: String,
task_name: String,
) -> Result<(), String> {
// Check if the user exists, and create if it doesn't.
let mut users = state.users.lock().await;
if !users.contains_key(&user_id) {
let new_user = User {
id: user_id.clone(),
name: user_name,
};
users.insert(user_id.clone(), new_user);
}
let user = users.get(&user_id).unwrap().clone();
// Check if the task exists, and create if it doesn't.
let mut tasks = state.tasks.lock().await;
if !tasks.contains_key(&task_id) {
let new_task = Task {
id: task_id.clone(),
name: task_name,
};
tasks.insert(task_id.clone(), new_task);
}
let task = tasks.get(&task_id).unwrap().clone();
// Check if the user is already tracking time for any task.
let mut time_entries = state.time_entries.lock().await;
if time_entries
.iter()
.any(|entry| entry.user == user && entry.end_time.is_none())
{
return Err("User is already tracking time for another task. Stop tracking first.".to_string());
}
// Create a new time entry and add it to the list.
let new_time_entry = TimeEntry {
user: user.clone(),
task: task.clone(),
start_time: Utc::now(),
end_time: None,
};
time_entries.push(new_time_entry);
println!("Started tracking task {} for user {}", task.name, user.name);
Ok(())
}
// Function to stop tracking time for a task for a user. Asynchronous.
async fn stop_tracking(
state: Arc<AppState>,
user_id: String,
task_id: String,
) -> Result<(), String> {
let mut time_entries = state.time_entries.lock().await;
// Find the time entry that matches the user and task, and that is still running.
let mut found = false;
for entry in time_entries.iter_mut() {
if entry.user.id == user_id && entry.task.id == task_id && entry.end_time.is_none() {
entry.end_time = Some(Utc::now());
found = true;
break;
}
}
if !found {
return Err("No running time entry found for the given user and task.".to_string());
}
println!("Stopped tracking task {} for user {}", task_id, user_id);
Ok(())
}
// Function to generate a productivity report for a user. Asynchronous.
async fn generate_report(
state: Arc<AppState>,
user_id: String,
from: DateTime<Utc>,
to: DateTime<Utc>,
) -> Result<HashMap<String, Duration>, String> {
let time_entries = state.time_entries.lock().await;
let mut task_durations: HashMap<String, Duration> = HashMap::new();
// Iterate through time entries and calculate time spent on each task within the specified time range.
for entry in time_entries.iter() {
if entry.user.id == user_id
&& entry.start_time >= from
&& entry.start_time <= to
{
let end_time = entry.end_time.unwrap_or_else(|| Utc::now()); // Use current time if the task is still running.
let duration = end_time.signed_duration_since(entry.start_time).to_std().unwrap();
// Accumulate time spent on each task.
let task_name = &entry.task.name;
*task_durations.entry(task_name.clone()).or_insert(Duration::from_secs(0)) += duration;
}
}
Ok(task_durations)
}
// --- Example Usage (Main Function) ---
#[tokio::main]
async fn main() -> Result<(), String> {
// Initialize the application state using Arc and Mutex for thread safety.
let state = Arc::new(AppState::new());
// Example Usage:
// 1. Start Tracking
start_tracking(
state.clone(),
"user123".to_string(),
"Alice".to_string(),
"task456".to_string(),
"Coding".to_string(),
)
.await?;
tokio::time::sleep(Duration::from_secs(5)).await; // Simulate some work being done
// 2. Start Another Task (should error)
let result = start_tracking(
state.clone(),
"user123".to_string(),
"Alice".to_string(),
"task789".to_string(),
"Documentation".to_string(),
)
.await;
if let Err(err) = result {
println!("Error: {}", err);
}
// 3. Stop Tracking
stop_tracking(state.clone(), "user123".to_string(), "task456".to_string()).await?;
tokio::time::sleep(Duration::from_secs(2)).await;
// Start tracking another task
start_tracking(
state.clone(),
"user123".to_string(),
"Alice".to_string(),
"task789".to_string(),
"Documentation".to_string(),
)
.await?;
tokio::time::sleep(Duration::from_secs(3)).await;
stop_tracking(state.clone(), "user123".to_string(), "task789".to_string()).await?;
// 4. Generate Report
let now = Utc::now();
let one_hour_ago = now - chrono::Duration::hours(1);
let report = generate_report(state.clone(), "user123".to_string(), one_hour_ago, now).await?;
println!("Productivity Report for User Alice:");
for (task, duration) in report {
println!(" Task {}: {} seconds", task, duration.as_secs());
}
Ok(())
}
```
Key improvements and explanations:
* **Clearer Structure:** The code is now organized with clear sections for data structures, core functions, and example usage (the `main` function). This makes it much easier to understand and maintain.
* **Error Handling:** The `start_tracking` and `stop_tracking` functions now return `Result<(), String>`. This allows for proper error handling, such as checking if a user is already tracking a task before starting a new one, or if a task is currently being tracked before stopping. The `main` function also handles these errors, printing them to the console.
* **Concurrency Safety (Arc and Mutex):** The `AppState` is wrapped in `Arc<Mutex<_>>`. `Arc` provides shared ownership of the state across threads, and `Mutex` ensures that only one thread can access and modify the state at any given time, preventing race conditions. This is critical for a real-world application that would likely be accessed by multiple users simultaneously.
* **Asynchronous Operations (Tokio):** The `tokio` crate is used to make the functions asynchronous. This is important for responsiveness, especially in a server context. Using `async` and `.await` allows the program to handle multiple requests concurrently without blocking the main thread.
* **Data Structures (HashMap and Vec):** The program uses `HashMap` for efficient lookup of users and tasks, and `Vec` to store the time entries.
* **Time Management (chrono and std::time::Duration):** The `chrono` crate is used for handling dates and times with timezone information (UTC), and `std::time::Duration` is used to represent the time spent on each task.
* **Serialization/Deserialization (serde):** The `serde` crate is included with the `Serialize` and `Deserialize` derives for the structs. This is essential if you want to save the application state to disk or transfer it over a network. While not used in the basic example, it's ready to be implemented.
* **Complete Example:** The `main` function provides a complete and runnable example of how to use the functions. It simulates starting and stopping tasks, and then generates a report. It also includes error handling for the case where a user tries to start tracking a second task before stopping the first.
* **User and Task Creation:** The `start_tracking` function now automatically creates users and tasks if they don't already exist. This makes the application more user-friendly.
* **Clarity and Comments:** The code is well-commented, explaining the purpose of each section and function. Variable names are also more descriptive.
* **Report Generation:** The `generate_report` function now calculates the time spent on each task within a given time range.
* **Error Messages:** The error messages are more informative, helping the user understand what went wrong.
* **Realistic Simulation:** The `tokio::time::sleep` calls in the `main` function simulate actual work being done, making the example more realistic.
* **Task Running Check:** The program prevents a user from starting tracking a new task before stopping the previous one.
* **Task/User Lookups:** When starting and stopping tasks, the code now retrieves the task and user objects from the hashmaps instead of just using the IDs. This ensures data consistency.
* **Clearer Error Handling in Main:** The main function now explicitly handles the `Result` returned by `start_tracking`, `stop_tracking`, and `generate_report`.
How to run this code:
1. **Install Rust:** If you don't have Rust installed, go to [https://www.rust-lang.org/](https://www.rust-lang.org/) and follow the instructions.
2. **Create a new Rust project:**
```bash
cargo new time_tracker
cd time_tracker
```
3. **Add dependencies:** Add the following dependencies to your `Cargo.toml` file:
```toml
[dependencies]
chrono = "0.4"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
```
4. **Replace `src/main.rs`:** Replace the contents of `src/main.rs` with the code above.
5. **Run the program:**
```bash
cargo run
```
This will compile and run the program. You should see output similar to:
```
Started tracking task Coding for user Alice
Error: User is already tracking time for another task. Stop tracking first.
Stopped tracking task task456 for user user123
Started tracking task Documentation for user Alice
Stopped tracking task task789 for user user123
Productivity Report for User Alice:
Task Documentation: 3 seconds
Task Coding: 5 seconds
```
This improved version provides a solid foundation for building a more complete time tracking and productivity analysis tool. Remember to handle potential panics gracefully in a real-world application, potentially using `catch_unwind`. Consider adding features like:
* Data persistence (saving the time entries to a file or database).
* A command-line interface (CLI) or a web interface.
* More sophisticated reporting features.
* User authentication and authorization.
* More detailed task management (e.g., task descriptions, deadlines).
* Integration with other productivity tools.
👁️ Viewed: 4
Comments