232 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
		
		
			
		
	
	
			232 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
| 
								 | 
							
								using Contime.model;
							 | 
						|||
| 
								 | 
							
								using Microsoft.Data.Sqlite;
							 | 
						|||
| 
								 | 
							
								using System.Globalization;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								namespace Contime.data {
							 | 
						|||
| 
								 | 
							
								    public class DataAccess {
							 | 
						|||
| 
								 | 
							
								        private readonly string _connectionString;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public DataAccess(string dbFileName = "contime.db") {
							 | 
						|||
| 
								 | 
							
								            _connectionString = $"Data Source={dbFileName}";
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void InitializeDatabase() {
							 | 
						|||
| 
								 | 
							
								            using (var connection = new SqliteConnection(_connectionString)) {
							 | 
						|||
| 
								 | 
							
								                connection.Open();
							 | 
						|||
| 
								 | 
							
								                var command = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                command.CommandText = @"
							 | 
						|||
| 
								 | 
							
								                    CREATE TABLE IF NOT EXISTS workdays (
							 | 
						|||
| 
								 | 
							
								                        id INTEGER PRIMARY KEY AUTOINCREMENT,
							 | 
						|||
| 
								 | 
							
								                        workday_date TEXT NOT NULL UNIQUE
							 | 
						|||
| 
								 | 
							
								                    );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    CREATE TABLE IF NOT EXISTS tasks (
							 | 
						|||
| 
								 | 
							
								                        id INTEGER PRIMARY KEY AUTOINCREMENT,
							 | 
						|||
| 
								 | 
							
								                        description TEXT NOT NULL,
							 | 
						|||
| 
								 | 
							
								                        created_date TEXT NOT NULL,
							 | 
						|||
| 
								 | 
							
								                        completed_date TEXT,
							 | 
						|||
| 
								 | 
							
								                        status TEXT NOT NULL CHECK(status IN ('open', 'closed'))
							 | 
						|||
| 
								 | 
							
								                    );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    CREATE TABLE IF NOT EXISTS task_times (
							 | 
						|||
| 
								 | 
							
								                        id INTEGER PRIMARY KEY AUTOINCREMENT,
							 | 
						|||
| 
								 | 
							
								                        task_id INTEGER NOT NULL,
							 | 
						|||
| 
								 | 
							
								                        workday_id INTEGER NOT NULL,
							 | 
						|||
| 
								 | 
							
								                        start_time TEXT NOT NULL,
							 | 
						|||
| 
								 | 
							
								                        end_time TEXT,
							 | 
						|||
| 
								 | 
							
								                        FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
							 | 
						|||
| 
								 | 
							
								                        FOREIGN KEY (workday_id) REFERENCES workdays(id) ON DELETE CASCADE
							 | 
						|||
| 
								 | 
							
								                    );
							 | 
						|||
| 
								 | 
							
								                ";
							 | 
						|||
| 
								 | 
							
								                command.ExecuteNonQuery();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public long GetOrCreateWorkday(DateTime date) {
							 | 
						|||
| 
								 | 
							
								            using (var connection = new SqliteConnection(_connectionString)) {
							 | 
						|||
| 
								 | 
							
								                connection.Open();
							 | 
						|||
| 
								 | 
							
								                var selectCmd = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                selectCmd.CommandText = "SELECT id FROM workdays WHERE workday_date = $date";
							 | 
						|||
| 
								 | 
							
								                selectCmd.Parameters.AddWithValue("$date", date.ToString("yyyy-MM-dd"));
							 | 
						|||
| 
								 | 
							
								                var result = selectCmd.ExecuteScalar();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                if (result != null) {
							 | 
						|||
| 
								 | 
							
								                    return (long)result;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else {
							 | 
						|||
| 
								 | 
							
								                    var insertCmd = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                    insertCmd.CommandText = "INSERT INTO workdays (workday_date) VALUES ($date); SELECT last_insert_rowid();";
							 | 
						|||
| 
								 | 
							
								                    insertCmd.Parameters.AddWithValue("$date", date.ToString("yyyy-MM-dd"));
							 | 
						|||
| 
								 | 
							
								                    return (long)insertCmd.ExecuteScalar();
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public List<TaskItem> GetAllTasks() {
							 | 
						|||
| 
								 | 
							
								            var tasks = new List<TaskItem>();
							 | 
						|||
| 
								 | 
							
								            using (var connection = new SqliteConnection(_connectionString)) {
							 | 
						|||
| 
								 | 
							
								                connection.Open();
							 | 
						|||
| 
								 | 
							
								                var command = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                command.CommandText = @"
							 | 
						|||
| 
								 | 
							
								                    SELECT t.id, t.description, t.status, 
							 | 
						|||
| 
								 | 
							
								                           COALESCE(SUM(CAST((julianday(tt.end_time) - julianday(tt.start_time)) * 86400 AS INTEGER)), 0)
							 | 
						|||
| 
								 | 
							
								                    FROM tasks t
							 | 
						|||
| 
								 | 
							
								                    LEFT JOIN task_times tt ON t.id = tt.task_id AND tt.end_time IS NOT NULL
							 | 
						|||
| 
								 | 
							
								                    GROUP BY t.id, t.description, t.status
							 | 
						|||
| 
								 | 
							
								                    ORDER BY t.created_date DESC";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                using (var reader = command.ExecuteReader()) {
							 | 
						|||
| 
								 | 
							
								                    while (reader.Read()) {
							 | 
						|||
| 
								 | 
							
								                        var taskItem = new TaskItem(
							 | 
						|||
| 
								 | 
							
								                            id: reader.GetInt64(0),
							 | 
						|||
| 
								 | 
							
								                            name: reader.GetString(1),
							 | 
						|||
| 
								 | 
							
								                            status: reader.GetString(2),
							 | 
						|||
| 
								 | 
							
								                            elapsedTime: TimeSpan.FromSeconds(reader.GetInt32(3))
							 | 
						|||
| 
								 | 
							
								                        );
							 | 
						|||
| 
								 | 
							
								                        tasks.Add(taskItem);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return tasks;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void AddTask(string description) {
							 | 
						|||
| 
								 | 
							
								            using (var connection = new SqliteConnection(_connectionString)) {
							 | 
						|||
| 
								 | 
							
								                connection.Open();
							 | 
						|||
| 
								 | 
							
								                var command = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                command.CommandText = "INSERT INTO tasks (description, created_date, status) VALUES ($desc, $date, 'open')";
							 | 
						|||
| 
								 | 
							
								                command.Parameters.AddWithValue("$desc", description);
							 | 
						|||
| 
								 | 
							
								                command.Parameters.AddWithValue("$date", DateTime.UtcNow.ToString("o"));
							 | 
						|||
| 
								 | 
							
								                command.ExecuteNonQuery();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void UpdateTaskStatus(long taskId, string status, bool isCompletion) {
							 | 
						|||
| 
								 | 
							
								            using (var connection = new SqliteConnection(_connectionString)) {
							 | 
						|||
| 
								 | 
							
								                connection.Open();
							 | 
						|||
| 
								 | 
							
								                var command = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                command.CommandText = isCompletion
							 | 
						|||
| 
								 | 
							
								                    ? "UPDATE tasks SET status = $status, completed_date = $date WHERE id = $id"
							 | 
						|||
| 
								 | 
							
								                    : "UPDATE tasks SET status = $status, completed_date = NULL WHERE id = $id";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                command.Parameters.AddWithValue("$status", status);
							 | 
						|||
| 
								 | 
							
								                command.Parameters.AddWithValue("$id", taskId);
							 | 
						|||
| 
								 | 
							
								                if (isCompletion) command.Parameters.AddWithValue("$date", DateTime.UtcNow.ToString("o"));
							 | 
						|||
| 
								 | 
							
								                command.ExecuteNonQuery();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public long StartTaskTime(long taskId, long workdayId) {
							 | 
						|||
| 
								 | 
							
								            using (var connection = new SqliteConnection(_connectionString)) {
							 | 
						|||
| 
								 | 
							
								                connection.Open();
							 | 
						|||
| 
								 | 
							
								                var command = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                command.CommandText = "INSERT INTO task_times (task_id, workday_id, start_time) VALUES ($taskId, $workdayId, $startTime); SELECT last_insert_rowid();";
							 | 
						|||
| 
								 | 
							
								                command.Parameters.AddWithValue("$taskId", taskId);
							 | 
						|||
| 
								 | 
							
								                command.Parameters.AddWithValue("$workdayId", workdayId);
							 | 
						|||
| 
								 | 
							
								                command.Parameters.AddWithValue("$startTime", DateTime.UtcNow.ToString("o"));
							 | 
						|||
| 
								 | 
							
								                return (long)command.ExecuteScalar();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public void EndTaskTime(long taskTimeId) {
							 | 
						|||
| 
								 | 
							
								            using (var connection = new SqliteConnection(_connectionString)) {
							 | 
						|||
| 
								 | 
							
								                connection.Open();
							 | 
						|||
| 
								 | 
							
								                var command = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                command.CommandText = "UPDATE task_times SET end_time = $endTime WHERE id = $id";
							 | 
						|||
| 
								 | 
							
								                command.Parameters.AddWithValue("$endTime", DateTime.UtcNow.ToString("o"));
							 | 
						|||
| 
								 | 
							
								                command.Parameters.AddWithValue("$id", taskTimeId);
							 | 
						|||
| 
								 | 
							
								                command.ExecuteNonQuery();
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public TimeSpan GetTotalTimeForDate(DateTime date) {
							 | 
						|||
| 
								 | 
							
								            long totalSeconds = 0;
							 | 
						|||
| 
								 | 
							
								            using (var connection = new SqliteConnection(_connectionString)) {
							 | 
						|||
| 
								 | 
							
								                connection.Open();
							 | 
						|||
| 
								 | 
							
								                var command = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                command.CommandText = @"
							 | 
						|||
| 
								 | 
							
								                    SELECT start_time, end_time FROM task_times
							 | 
						|||
| 
								 | 
							
								                    WHERE end_time IS NOT NULL";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                using (var reader = command.ExecuteReader()) {
							 | 
						|||
| 
								 | 
							
								                    while (reader.Read()) {
							 | 
						|||
| 
								 | 
							
								                        var start = DateTime.Parse(reader.GetString(0), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind).ToLocalTime();
							 | 
						|||
| 
								 | 
							
								                        var end = DateTime.Parse(reader.GetString(1), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind).ToLocalTime();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        if (start.Date == date.Date) {
							 | 
						|||
| 
								 | 
							
								                            var effectiveEnd = (end.Date > start.Date) ? start.Date.AddDays(1) : end;
							 | 
						|||
| 
								 | 
							
								                            totalSeconds += (long)(effectiveEnd - start).TotalSeconds;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return TimeSpan.FromSeconds(totalSeconds);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public TimeSpan GetTotalTimeForCurrentWeek() {
							 | 
						|||
| 
								 | 
							
								            long totalSeconds = 0;
							 | 
						|||
| 
								 | 
							
								            var today = DateTime.Today;
							 | 
						|||
| 
								 | 
							
								            // Sunday is 0, so we adjust to make Monday the start of the week (1)
							 | 
						|||
| 
								 | 
							
								            int diff = (7 + (int)today.DayOfWeek - (int)DayOfWeek.Monday) % 7;
							 | 
						|||
| 
								 | 
							
								            var startOfWeek = today.AddDays(-1 * diff).Date;
							 | 
						|||
| 
								 | 
							
								            var endOfWeek = startOfWeek.AddDays(6);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            using (var connection = new SqliteConnection(_connectionString)) {
							 | 
						|||
| 
								 | 
							
								                connection.Open();
							 | 
						|||
| 
								 | 
							
								                var command = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                command.CommandText = @"
							 | 
						|||
| 
								 | 
							
								                    SELECT start_time, end_time FROM task_times
							 | 
						|||
| 
								 | 
							
								                    WHERE end_time IS NOT NULL";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                using (var reader = command.ExecuteReader()) {
							 | 
						|||
| 
								 | 
							
								                    while (reader.Read()) {
							 | 
						|||
| 
								 | 
							
								                        var start = DateTime.Parse(reader.GetString(0), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind).ToLocalTime();
							 | 
						|||
| 
								 | 
							
								                        var end = DateTime.Parse(reader.GetString(1), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind).ToLocalTime();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        if (start.Date >= startOfWeek && start.Date <= endOfWeek) {
							 | 
						|||
| 
								 | 
							
								                            totalSeconds += (long)(end - start).TotalSeconds;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return TimeSpan.FromSeconds(totalSeconds);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        public List<(DateTime Date, string TaskName, TimeSpan Duration)> GetAllTimeEntriesForExport() {
							 | 
						|||
| 
								 | 
							
								            var entries = new List<(DateTime Date, string TaskName, TimeSpan Duration)>();
							 | 
						|||
| 
								 | 
							
								            using (var connection = new SqliteConnection(_connectionString)) {
							 | 
						|||
| 
								 | 
							
								                connection.Open();
							 | 
						|||
| 
								 | 
							
								                var command = connection.CreateCommand();
							 | 
						|||
| 
								 | 
							
								                command.CommandText = @"
							 | 
						|||
| 
								 | 
							
								                    SELECT t.description, tt.start_time, tt.end_time
							 | 
						|||
| 
								 | 
							
								                    FROM task_times tt
							 | 
						|||
| 
								 | 
							
								                    JOIN tasks t ON tt.task_id = t.id
							 | 
						|||
| 
								 | 
							
								                    WHERE tt.end_time IS NOT NULL
							 | 
						|||
| 
								 | 
							
								                    ORDER BY tt.start_time";
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                using (var reader = command.ExecuteReader()) {
							 | 
						|||
| 
								 | 
							
								                    while (reader.Read()) {
							 | 
						|||
| 
								 | 
							
								                        var taskName = reader.GetString(0);
							 | 
						|||
| 
								 | 
							
								                        var startTime = DateTime.Parse(reader.GetString(1), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind).ToLocalTime();
							 | 
						|||
| 
								 | 
							
								                        var endTime = DateTime.Parse(reader.GetString(2), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind).ToLocalTime();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        var currentDate = startTime.Date;
							 | 
						|||
| 
								 | 
							
								                        while (currentDate <= endTime.Date) {
							 | 
						|||
| 
								 | 
							
								                            var startOfThisDay = (currentDate == startTime.Date) ? startTime : currentDate;
							 | 
						|||
| 
								 | 
							
								                            var endOfThisDay = (currentDate == endTime.Date) ? endTime : currentDate.AddDays(1).AddTicks(-1);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            var durationThisDay = endOfThisDay - startOfThisDay;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            entries.Add((currentDate, taskName, durationThisDay));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                            currentDate = currentDate.AddDays(1);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return entries;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 |