Compare commits

...

3 Commits

7 changed files with 161 additions and 42 deletions

View File

@ -5,8 +5,11 @@ import org.apache.commons.csv.CSVRecord;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.*; import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static nl.andrewlalis.human_task_distributor.HumanTaskDistributor.CSV_FORMAT; import static nl.andrewlalis.human_task_distributor.HumanTaskDistributor.CSV_FORMAT;
@ -74,4 +77,20 @@ public class FileParser {
} }
return previousDistributions; return previousDistributions;
} }
public Set<Task> parseTaskList(String path, String regex) {
try {
String contents = String.join(" ", Files.readAllLines(Paths.get(path), StandardCharsets.UTF_8));
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(contents);
Set<Task> tasks = new HashSet<>();
while (matcher.find()) {
tasks.add(new Task(matcher.group()));
}
return tasks;
} catch (Exception e) {
e.printStackTrace();
return new HashSet<>();
}
}
} }

View File

@ -0,0 +1,38 @@
package nl.andrewlalis.human_task_distributor;
import org.apache.commons.csv.CSVPrinter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import static nl.andrewlalis.human_task_distributor.HumanTaskDistributor.CSV_FORMAT;
public class FileWriter {
public void write(Map<Human, Set<Task>> taskDistributions, String filePath) throws IOException {
CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(Paths.get(filePath), StandardCharsets.UTF_8), CSV_FORMAT);
for (Map.Entry<Human, Set<Task>> entry : taskDistributions.entrySet()) {
Human human = entry.getKey();
Set<Task> assignedTasks = entry.getValue();
List<Task> sortedTasks = assignedTasks.stream().sorted(Comparator.comparing(Task::getName)).collect(Collectors.toList());
for (Task task : sortedTasks) {
printer.printRecord(human.getName(), task.getName());
}
}
printer.close(true);
}
public void write(Set<Task> tasks, String filePath) throws IOException {
CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(Paths.get(filePath), StandardCharsets.UTF_8), CSV_FORMAT);
List<Task> orderedTasks = new ArrayList<>(tasks);
orderedTasks.sort(Task::compareTo);
for (Task t : orderedTasks) {
printer.printRecord(t.getName());
}
printer.close(true);
}
}

View File

@ -1,14 +1,9 @@
package nl.andrewlalis.human_task_distributor; package nl.andrewlalis.human_task_distributor;
import nl.andrewlalis.human_task_distributor.commands.DistributeTasks;
import nl.andrewlalis.human_task_distributor.commands.PrepareTasksList;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
public class HumanTaskDistributor { public class HumanTaskDistributor {
public static final CSVFormat CSV_FORMAT = CSVFormat.RFC4180; public static final CSVFormat CSV_FORMAT = CSVFormat.RFC4180;
@ -18,41 +13,14 @@ public class HumanTaskDistributor {
CommandLineParser cmdParser = new DefaultParser(); CommandLineParser cmdParser = new DefaultParser();
try { try {
CommandLine cmd = cmdParser.parse(options, args); CommandLine cmd = cmdParser.parse(options, args);
FileParser fileParser = new FileParser(); if (cmd.hasOption("ptl")) {
Map<Human, Float> nameWeightMap = fileParser.parseHumanList(cmd.getOptionValue("hl")); new PrepareTasksList().execute(cmd);
Set<Task> tasks = fileParser.parseTaskList(cmd.getOptionValue("tl")); } else if (cmd.hasOption("hl") && cmd.hasOption("tl")) {
String[] previousDistributionPaths = cmd.getOptionValues("prev"); new DistributeTasks().execute(cmd);
if (previousDistributionPaths == null) previousDistributionPaths = new String[0];
List<Map<Human, Set<Task>>> previousDistributions = fileParser.parsePreviousTaskDistributions(previousDistributionPaths);
long start = System.currentTimeMillis();
Map<Human, Set<Task>> taskDistributions = new Distributor().generateDistribution(nameWeightMap, tasks, previousDistributions);
long durationMillis = System.currentTimeMillis() - start;
System.out.printf(
"Completed distribution of %d tasks to %d people in %d ms.%n",
tasks.size(),
taskDistributions.keySet().size(),
durationMillis
);
// Write to a file.
String filePath = cmd.hasOption("o") ? cmd.getOptionValue("o") : "distribution.csv";
CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(Paths.get(filePath), StandardCharsets.UTF_8), CSV_FORMAT);
for (Map.Entry<Human, Set<Task>> entry : taskDistributions.entrySet()) {
Human human = entry.getKey();
Set<Task> assignedTasks = entry.getValue();
List<Task> sortedTasks = assignedTasks.stream().sorted(Comparator.comparing(Task::getName)).collect(Collectors.toList());
for (Task task : sortedTasks) {
printer.printRecord(human.getName(), task.getName());
}
} }
printer.close(true); throw new IllegalArgumentException("Invalid command.");
System.out.println("Wrote task distribution data to " + filePath);
} catch (Exception e) { } catch (Exception e) {
System.err.println("Error: " + e.getMessage()); System.err.println("Error: " + e.getMessage());
e.printStackTrace();
HelpFormatter hf = new HelpFormatter(); HelpFormatter hf = new HelpFormatter();
hf.printHelp("HumanTaskDistributor", options); hf.printHelp("HumanTaskDistributor", options);
System.exit(1); System.exit(1);
@ -65,16 +33,25 @@ public class HumanTaskDistributor {
.longOpt("humans-list") .longOpt("humans-list")
.hasArg(true) .hasArg(true)
.desc("Path to a CSV file containing list of humans to distribute tasks to. First column should be the name of the person, and second column can be empty, or contain a floating-point weight.") .desc("Path to a CSV file containing list of humans to distribute tasks to. First column should be the name of the person, and second column can be empty, or contain a floating-point weight.")
.required(true) .required(false)
.numberOfArgs(1) .numberOfArgs(1)
.type(String.class) .type(String.class)
.build() .build()
); );
options.addOption(Option.builder("ptl")
.longOpt("prepare-tasks-list")
.desc("Prepares a tasks-list CSV from a TXT file with one task for each item that matches the given Regex.")
.required(false)
.numberOfArgs(2)
.valueSeparator(',')
.type(String.class)
.build()
);
options.addOption(Option.builder("tl") options.addOption(Option.builder("tl")
.longOpt("tasks-list") .longOpt("tasks-list")
.hasArg(true) .hasArg(true)
.desc("Path to a CSV file containing list of tasks that can be distributed to humans. First column should be unique task name.") .desc("Path to a CSV file containing list of tasks that can be distributed to humans. First column should be unique task name.")
.required(true) .required(false)
.numberOfArgs(1) .numberOfArgs(1)
.type(String.class) .type(String.class)
.build() .build()

View File

@ -5,7 +5,7 @@ import lombok.Getter;
import java.util.Objects; import java.util.Objects;
@Getter @Getter
public class Task { public class Task implements Comparable<Task> {
private final String name; private final String name;
public Task(String name) { public Task(String name) {
@ -24,4 +24,9 @@ public class Task {
public int hashCode() { public int hashCode() {
return Objects.hash(getName()); return Objects.hash(getName());
} }
@Override
public int compareTo(Task o) {
return this.getName().compareToIgnoreCase(o.getName());
}
} }

View File

@ -0,0 +1,8 @@
package nl.andrewlalis.human_task_distributor.commands;
import org.apache.commons.cli.CommandLine;
public interface Command {
void execute(CommandLine cmd);
}

View File

@ -0,0 +1,41 @@
package nl.andrewlalis.human_task_distributor.commands;
import nl.andrewlalis.human_task_distributor.*;
import org.apache.commons.cli.CommandLine;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DistributeTasks implements Command {
@Override
public void execute(CommandLine cmd) {
final FileParser fileParser = new FileParser();
final FileWriter fileWriter = new FileWriter();
Map<Human, Float> nameWeightMap = fileParser.parseHumanList(cmd.getOptionValue("hl"));
Set<Task> tasks = fileParser.parseTaskList(cmd.getOptionValue("tl"));
String[] previousDistributionPaths = cmd.getOptionValues("prev");
if (previousDistributionPaths == null) previousDistributionPaths = new String[0];
List<Map<Human, Set<Task>>> previousDistributions = fileParser.parsePreviousTaskDistributions(previousDistributionPaths);
long start = System.currentTimeMillis();
Map<Human, Set<Task>> taskDistributions = new Distributor().generateDistribution(nameWeightMap, tasks, previousDistributions);
long durationMillis = System.currentTimeMillis() - start;
System.out.printf(
"Created distribution of %d tasks to %d people in %d ms.%n",
tasks.size(),
taskDistributions.keySet().size(),
durationMillis
);
// Write to a file.
final String filePath = cmd.hasOption("o") ? cmd.getOptionValue("o") : "distribution.csv";
try {
fileWriter.write(taskDistributions, filePath);
System.out.println("Wrote task distribution data to " + filePath);
} catch (IOException e) {
System.err.println("Couldn't write to file: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,31 @@
package nl.andrewlalis.human_task_distributor.commands;
import nl.andrewlalis.human_task_distributor.FileParser;
import nl.andrewlalis.human_task_distributor.FileWriter;
import nl.andrewlalis.human_task_distributor.Task;
import org.apache.commons.cli.CommandLine;
import java.io.IOException;
import java.util.Set;
public class PrepareTasksList implements Command {
@Override
public void execute(CommandLine cmd) {
String[] values = cmd.getOptionValues("ptl");
if (values.length != 2) {
throw new IllegalArgumentException("Expected exactly 2 parameters for ptl arg.");
}
String filePath = values[0].trim();
String regex = values[1].trim();
Set<Task> tasks = new FileParser().parseTaskList(filePath, regex);
System.out.println("Read " + tasks.size() + " tasks from file.");
String outFilePath = filePath.replaceFirst("\\..*", ".csv");
try {
new FileWriter().write(tasks, outFilePath);
System.out.println("Wrote tasks to " + outFilePath);
} catch (IOException e) {
e.printStackTrace();
System.err.println("Couldn't write output file.");
}
}
}