started implementing listTimes – way to complicated

This commit is contained in:
2025-07-15 08:23:08 +02:00
parent 65b5dded01
commit a75c58932d
17 changed files with 330 additions and 25 deletions

View File

@@ -0,0 +1,18 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.time;
public class Constants {
private Constants(){}
public static final String CONFIG_DATABASE = "umbrella.modules.time.database";
public static final String TABLE_TASK_TIMES = "task_times";
public static final String TABLE_TIMES = "times";
public static final String TASK_ID = "task_id";
public static final String TASKS = "tasks";
public static final String TIME_ID = "time_id";
public static final String TIMES = "times";
public static final String CHILDREN = "children";
}

View File

@@ -0,0 +1,50 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.time;
import static de.srsoftware.tools.jdbc.Condition.in;
import static de.srsoftware.tools.jdbc.Query.select;
import static de.srsoftware.umbrella.core.Constants.ID;
import static de.srsoftware.umbrella.time.Constants.*;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Time;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
public class SqliteDb implements TimeDb {
private final Connection db;
public SqliteDb(Connection connection) {
db = connection;
}
@Override
public Collection<Time> listTimes(Collection<Long> taskIds) throws UmbrellaException {
try {
var rs = select("*").from(TABLE_TASK_TIMES).where(TASK_ID,in(taskIds.toArray())).exec(db);
var mapFromTimesToTasks = new HashMap<Long,HashSet<Long>>();
while (rs.next()){
var timeId = rs.getLong(TIME_ID);
var taskId = rs.getLong(TASK_ID);
mapFromTimesToTasks.computeIfAbsent(timeId, k -> new HashSet<>()).add(taskId);
}
rs.close();
rs = select("*").from(TABLE_TIMES).where(ID,in(mapFromTimesToTasks.keySet().toArray())).exec(db);
var times = new HashSet<Time>();
while (rs.next()) {
var time = Time.of(rs);
time.taskIds().addAll(mapFromTimesToTasks.get(time.id()));
times.add(time);
}
rs.close();
return times;
} catch (SQLException e) {
throw new UmbrellaException("Failed to load times for task list");
}
}
}

View File

@@ -0,0 +1,11 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.time;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Time;
import java.util.Collection;
import java.util.List;
public interface TimeDb {
Collection<Time> listTimes(Collection<Long> taskIds) throws UmbrellaException;
}

View File

@@ -0,0 +1,92 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.time;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.Paths.LIST;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
import static de.srsoftware.umbrella.time.Constants.*;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.configuration.Configuration;
import de.srsoftware.tools.Path;
import de.srsoftware.tools.SessionToken;
import de.srsoftware.umbrella.core.BaseHandler;
import de.srsoftware.umbrella.core.api.*;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.*;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
public class TimeModule extends BaseHandler implements TimeService {
private final UserService users;
private final TimeDb timeDb;
private final TaskService tasks;
private final CompanyService companies;
private final ProjectService projects;
public TimeModule(Configuration config, TaskService taskService) throws UmbrellaException {
companies = taskService.companyService();
projects = taskService.projectService();
tasks = taskService;
users = tasks.userService();
var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE));
timeDb = new SqliteDb(connect(dbFile));
}
@Override
public boolean doPost(Path path, HttpExchange ex) throws IOException {
addCors(ex);
try {
Optional<Token> token = SessionToken.from(ex).map(Token::of);
var user = users.loadUser(token);
if (user.isEmpty()) return unauthorized(ex);
var head = path.pop();
return switch (head) {
case LIST -> listTimes(ex,user.get());
default -> super.doPost(path,ex);
};
} catch (UmbrellaException e){
return send(ex,e);
}
}
private boolean listTimes(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException {
var json = json(ex);
if (!(json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number cid)) throw missingFieldException(COMPANY_ID);
long companyId = cid.longValue();
if (!companies.membership(companyId,user.id())) throw UmbrellaException.forbidden("You are not a member of compayn {0}",companyId);
var projectMap = projects.listProjects(companyId,false).stream().collect(Collectors.toMap(Project::id,p -> p));
var taskMap = tasks.listCompanyTasks(companyId).stream().collect(Collectors.toMap(Task::id,Task::toMap));
var timesList = timeDb.listTimes(taskMap.keySet());
var tasksWithTime = new HashMap<Long,Map<String,Object>>();
var tree = new HashMap<Long,Map<String,Object>>();
for (var time : timesList) {
if (time.state().code() >= 60) continue;
var timeMap = time.toMap();
for (var taskId : time.taskIds()) {
var task = tasksWithTime.computeIfAbsent(taskId, k -> taskMap.get(taskId));
@SuppressWarnings("unchecked")
HashMap<Long,Map<String, Object>> taskTimes = (HashMap<Long,Map<String, Object>>) task.computeIfAbsent(TIMES, k -> new HashMap<Long,Map<String, Object>>());
taskTimes.put(time.id(),timeMap);
while (task.get(PARENT_TASK_ID) instanceof Long parentTaskId){
var parentTask = taskMap.get(parentTaskId);
@SuppressWarnings("unchecked")
HashMap<Long,Map<String, Object>> children = (HashMap<Long,Map<String, Object>>) parentTask.computeIfAbsent(CHILDREN, k -> new HashMap<Long,Map<String, Object>>());
children.put(taskId,task);
task = parentTask;
taskId = parentTaskId;
}
if (task.get(PROJECT_ID) instanceof Long projectId){
var project = tree.computeIfAbsent(projectId,k -> new HashMap<>(projectMap.get(projectId).toMap()));
@SuppressWarnings("unchecked")
HashMap<Long,Map<String,Object>> projectTasks = (HashMap<Long,Map<String, Object>>) project.computeIfAbsent(TASKS, k -> new HashMap<Long,Map<String,Object>>());
projectTasks.put(taskId,task);
}
}
}
return sendContent(ex,tree);
}
}