Skip to content
This repository was archived by the owner on Jun 3, 2025. It is now read-only.

Commit b9d8fbb

Browse files
author
Aleksi Salmela
committed
Implement initial admin command.
1 parent 8cffbcc commit b9d8fbb

File tree

13 files changed

+1789
-33
lines changed

13 files changed

+1789
-33
lines changed

src/main/java/fi/helsinki/cs/tmc/cli/command/ListExercisesCommand.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import fi.helsinki.cs.tmc.cli.io.EnvironmentUtil;
1111
import fi.helsinki.cs.tmc.cli.io.ExternalsUtil;
1212
import fi.helsinki.cs.tmc.cli.io.Io;
13-
import fi.helsinki.cs.tmc.cli.io.WorkDir;
1413

1514
import fi.helsinki.cs.tmc.core.domain.Course;
1615
import fi.helsinki.cs.tmc.core.domain.Exercise;
Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
package fi.helsinki.cs.tmc.cli.command.admin;
2+
3+
import fi.helsinki.cs.tmc.cli.core.AbstractCommand;
4+
import fi.helsinki.cs.tmc.cli.core.CliContext;
5+
import fi.helsinki.cs.tmc.cli.core.Command;
6+
import fi.helsinki.cs.tmc.cli.io.Io;
7+
8+
import fi.helsinki.cs.tmc.langs.LanguagePlugin;
9+
import fi.helsinki.cs.tmc.langs.abstraction.ValidationResult;
10+
import fi.helsinki.cs.tmc.langs.domain.ExerciseDesc;
11+
import fi.helsinki.cs.tmc.langs.domain.ExercisePackagingConfiguration;
12+
import fi.helsinki.cs.tmc.langs.domain.Filer;
13+
import fi.helsinki.cs.tmc.langs.domain.FilterFileTreeVisitor;
14+
import fi.helsinki.cs.tmc.langs.domain.GeneralDirectorySkipper;
15+
import fi.helsinki.cs.tmc.langs.domain.NoLanguagePluginFoundException;
16+
import fi.helsinki.cs.tmc.langs.domain.RunResult;
17+
import fi.helsinki.cs.tmc.langs.util.ProjectType;
18+
import fi.helsinki.cs.tmc.langs.util.TaskExecutor;
19+
20+
import com.google.common.base.Optional;
21+
import com.google.gson.Gson;
22+
import org.apache.commons.cli.CommandLine;
23+
import org.apache.commons.cli.Options;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
27+
import java.io.FileWriter;
28+
import java.io.IOException;
29+
import java.nio.file.FileVisitResult;
30+
import java.nio.file.Path;
31+
import java.nio.file.Paths;
32+
import java.util.HashMap;
33+
import java.util.HashSet;
34+
import java.util.Locale;
35+
import java.util.Map;
36+
import java.util.Set;
37+
38+
/*TODO split this command to multiple admin commands */
39+
@Command(name = "admin", desc = "The admin command")
40+
public class AdminCommand extends AbstractCommand {
41+
42+
private static final Logger logger = LoggerFactory.getLogger(AdminCommand.class);
43+
private Io io;
44+
45+
private TaskExecutor executor;
46+
private Path exercisePath;
47+
private Path outputPath;
48+
private Locale locale;
49+
50+
@Override
51+
public void getOptions(Options options) {
52+
options.addOption("e", "exercise", true, "Path to exercise");
53+
options.addOption("o", "output", true, "Output path for the sub-command");
54+
options.addOption("l", "locale", true, "Locale of the exercise?");
55+
}
56+
57+
@Override
58+
public void run(CliContext context, CommandLine args) {
59+
this.io = context.getIo();
60+
String[] stringArguments = args.getArgs();
61+
62+
if (stringArguments.length == 0) {
63+
io.errorln("You have to give some sub-command.");
64+
printUsage(context);
65+
return;
66+
}
67+
68+
if (!context.loadBackendWithoutLogin()) {
69+
return;
70+
}
71+
72+
this.executor = context.getTmcLangs();
73+
String subCommand = stringArguments[0];
74+
if (args.hasOption("exercise")) {
75+
this.exercisePath = Paths.get(args.getOptionValue("exercise"));
76+
}
77+
if (args.hasOption("output")) {
78+
this.outputPath = Paths.get(args.getOptionValue("output"));
79+
}
80+
if (args.hasOption("locale")) {
81+
this.locale = new Locale(args.getOptionValue("locale"));
82+
}
83+
runSubCommand(subCommand);
84+
}
85+
86+
private void runSubCommand(String subCommand) {
87+
switch (subCommand) {
88+
case "help":
89+
io.errorln("Not implemented!");
90+
break;
91+
case "checkstyle":
92+
runCheckCodeStyle();
93+
break;
94+
case "scan-exercise":
95+
runScanExercise();
96+
break;
97+
case "find-exercises":
98+
runFindExercises();
99+
break;
100+
case "run-tests":
101+
runTests();
102+
break;
103+
case "prepare-stubs":
104+
runPrepareStubs();
105+
break;
106+
case "prepare-solutions":
107+
runPrepareSolutions();
108+
break;
109+
case "get-exercise-packaging-configuration":
110+
runGetExercisePackagingConfiguration();
111+
break;
112+
case "clean":
113+
runClean();
114+
break;
115+
default:
116+
break;
117+
}
118+
}
119+
120+
private void runCheckCodeStyle() {
121+
if (this.exercisePath == null || this.outputPath == null || this.locale == null) {
122+
io.errorln("Checkstyle command expects exercise path, output path and locale.");
123+
return;
124+
}
125+
ValidationResult validationResult;
126+
try {
127+
validationResult =
128+
executor.runCheckCodeStyle(this.exercisePath, this.locale);
129+
} catch (NoLanguagePluginFoundException e) {
130+
logger.error(
131+
"No suitable language plugin for project at {}", this.exercisePath, e);
132+
io.errorln(
133+
"ERROR: Could not find suitable language plugin for the given exercise "
134+
+ "path.");
135+
return;
136+
}
137+
138+
try {
139+
writeObjectIntoJsonFormat(validationResult, this.outputPath);
140+
io.println("Codestyle report can be found at " + this.outputPath);
141+
} catch (IOException e) {
142+
logger.error("Could not write result into {}", this.outputPath, e);
143+
io.errorln("ERROR: Could not write the results to the given file.");
144+
}
145+
}
146+
147+
private void runScanExercise() {
148+
if (this.exercisePath == null || this.outputPath == null) {
149+
io.errorln("ScanExercise command expects exercise path and output path.");
150+
return;
151+
}
152+
String exerciseName = this.exercisePath.toFile().getName();
153+
Optional<ExerciseDesc> exerciseDesc;
154+
try {
155+
exerciseDesc = executor.scanExercise(this.exercisePath, exerciseName);
156+
157+
if (exerciseDesc == null || !exerciseDesc.isPresent()) {
158+
logger.error("Absent exercise description after running scanExercise");
159+
io.errorln("ERROR: Could not scan the exercises.");
160+
return;
161+
}
162+
} catch (NoLanguagePluginFoundException e) {
163+
logger.error(
164+
"No suitable language plugin for project at {}", this.exercisePath, e);
165+
io.errorln(
166+
"ERROR: Could not find suitable language plugin for the given "
167+
+ "exercise path.");
168+
return;
169+
}
170+
171+
try {
172+
writeObjectIntoJsonFormat(exerciseDesc.get(), this.outputPath);
173+
io.println(
174+
"Exercises scanned successfully, results can be found in "
175+
+ this.outputPath);
176+
} catch (IOException e) {
177+
logger.error("Could not write output to {}", this.outputPath, e);
178+
io.errorln("ERROR: Could not write the results to the given file.");
179+
}
180+
}
181+
182+
private void runFindExercises() {
183+
if (this.exercisePath == null || this.outputPath == null) {
184+
io.errorln("FindExercises command expects exercise path and output path.");
185+
return;
186+
}
187+
Path clonePath = this.exercisePath;
188+
final Set<String> exercises = new HashSet<>();
189+
Filer exerciseMatchingFiler =
190+
new Filer() {
191+
192+
@Override
193+
public FileVisitResult decideOnDirectory(Path directory) {
194+
if (executor.isExerciseRootDirectory(directory)) {
195+
exercises.add(directory.toString());
196+
return FileVisitResult.SKIP_SUBTREE;
197+
}
198+
return FileVisitResult.CONTINUE;
199+
}
200+
201+
@Override
202+
public void visitFile(Path source, Path relativePath) {}
203+
};
204+
new FilterFileTreeVisitor()
205+
.addSkipper(new GeneralDirectorySkipper())
206+
.setClonePath(clonePath)
207+
.setStartPath(clonePath)
208+
.setFiler(exerciseMatchingFiler)
209+
.traverse();
210+
211+
try {
212+
writeObjectIntoJsonFormat(exercises, this.outputPath);
213+
io.println("Results can be found in " + this.outputPath);
214+
} catch (IOException e) {
215+
logger.error("Could not write output to {}", this.outputPath, e);
216+
io.errorln("ERROR: Could not write the results to the given file.");
217+
}
218+
}
219+
220+
private void runTests() {
221+
if (this.exercisePath == null || this.outputPath == null) {
222+
io.errorln("RunTests command expects exercise path and output path.");
223+
return;
224+
}
225+
RunResult runResult;
226+
try {
227+
runResult = executor.runTests(this.exercisePath);
228+
} catch (NoLanguagePluginFoundException e) {
229+
logger.error(
230+
"No suitable language plugin for project at {}", this.exercisePath, e);
231+
io.errorln(
232+
"ERROR: Could not find suitable language plugin for the given "
233+
+ "exercise path.");
234+
return;
235+
}
236+
237+
try {
238+
writeObjectIntoJsonFormat(runResult, this.outputPath);
239+
io.println("Test results can be found in " + this.outputPath);
240+
} catch (IOException e) {
241+
logger.error("Could not write output to {}", this.outputPath, e);
242+
io.errorln("ERROR: Could not write the results to the given file.");
243+
}
244+
}
245+
246+
private void runPrepareStubs() {
247+
if (this.exercisePath == null || this.outputPath == null || this.locale == null) {
248+
io.errorln("RunTests command expects exercise path, output path and locale.");
249+
return;
250+
}
251+
try {
252+
executor.prepareStubs(
253+
findExerciseDirectoriesAndGetLanguagePlugins(),
254+
this.exercisePath,
255+
this.outputPath);
256+
} catch (NoLanguagePluginFoundException e) {
257+
logger.error(
258+
"No suitable language plugin for project at {}", this.exercisePath, e);
259+
io.errorln(
260+
"ERROR: Could not find suitable language plugin for the given "
261+
+ "exercise path.");
262+
}
263+
}
264+
265+
private void runClean() {
266+
try {
267+
executor.clean(this.exercisePath);
268+
} catch (NoLanguagePluginFoundException e) {
269+
logger.error(
270+
"No suitable language plugin for project at {}", this.exercisePath, e);
271+
io.errorln(
272+
"ERROR: Could not find suitable language plugin for the given "
273+
+ "exercise path.");
274+
}
275+
}
276+
277+
private void runPrepareSolutions() {
278+
try {
279+
executor.prepareSolutions(
280+
findExerciseDirectoriesAndGetLanguagePlugins(),
281+
this.exercisePath,
282+
this.outputPath);
283+
} catch (NoLanguagePluginFoundException e) {
284+
logger.error(
285+
"No suitable language plugin for project at {}", this.exercisePath, e);
286+
io.errorln(
287+
"ERROR: Could not find suitable language plugin for the given "
288+
+ "exercise path.");
289+
}
290+
}
291+
292+
private void runGetExercisePackagingConfiguration() {
293+
ExercisePackagingConfiguration configuration;
294+
try {
295+
configuration = executor.getExercisePackagingConfiguration(this.exercisePath);
296+
} catch (NoLanguagePluginFoundException e) {
297+
logger.error(
298+
"No suitable language plugin for project at {}", this.exercisePath, e);
299+
io.errorln(
300+
"ERROR: Could not find suitable language plugin for the given "
301+
+ "exercise path.");
302+
return;
303+
}
304+
305+
try {
306+
writeObjectIntoJsonFormat(configuration, this.outputPath);
307+
io.println("Results can be found in " + this.outputPath);
308+
} catch (IOException e) {
309+
logger.error("Could not write output to {}", this.outputPath, e);
310+
io.errorln("ERROR: Could not write the results to the given file.");
311+
}
312+
}
313+
314+
private Map<Path, LanguagePlugin> findExerciseDirectoriesAndGetLanguagePlugins() {
315+
final Map<Path, LanguagePlugin> map = new HashMap<>();
316+
Filer exerciseMatchingFiler =
317+
new Filer() {
318+
319+
@Override
320+
public FileVisitResult decideOnDirectory(Path directory) {
321+
if (executor.isExerciseRootDirectory(directory)) {
322+
try {
323+
map.put(
324+
directory,
325+
ProjectType.getProjectType(directory).getLanguagePlugin());
326+
} catch (NoLanguagePluginFoundException ex) {
327+
throw new IllegalStateException(ex);
328+
}
329+
return FileVisitResult.SKIP_SUBTREE;
330+
}
331+
return FileVisitResult.CONTINUE;
332+
}
333+
334+
@Override
335+
public void visitFile(Path source, Path relativePath) {}
336+
};
337+
new FilterFileTreeVisitor()
338+
.addSkipper(new GeneralDirectorySkipper())
339+
.setClonePath(this.exercisePath)
340+
.setStartPath(this.exercisePath)
341+
.setFiler(exerciseMatchingFiler)
342+
.traverse();
343+
return map;
344+
}
345+
346+
private static void writeObjectIntoJsonFormat(Object obj, Path outputFile) throws IOException {
347+
try (FileWriter writer = new FileWriter(outputFile.toAbsolutePath().toFile())) {
348+
writer.write(new Gson().toJson(obj));
349+
}
350+
}
351+
}

src/main/java/fi/helsinki/cs/tmc/cli/command/admin/TestAdminCommand.java

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)