Skip to content

Commit 56d7260

Browse files
authored
Merge pull request #379 from mp-access/release/v0.10.2
Release/v0.11.0
2 parents 7be7d32 + 8086c29 commit 56d7260

20 files changed

Lines changed: 396 additions & 137 deletions

File tree

.circleci/config.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ jobs:
8686
curl -Ls -o codacy-coverage-reporter-assembly.jar $(curl -Ls https://api.github.com/repos/codacy/codacy-coverage-reporter/releases/latest | jq -r '.assets | map({content_type, browser_download_url} | select(.content_type | contains("java-archive"))) | .[0].browser_download_url')
8787
java -jar codacy-coverage-reporter-assembly.jar report -l Java -r build/reports/jacoco/test/jacocoTestReport.xml
8888
89+
- run:
90+
name: Create ACCESS local tester
91+
command: gradle customFatJar
92+
- store_artifacts:
93+
path: ./build/libs
94+
8995
push_to_dockerhub:
9096
docker:
9197
- image: circleci/openjdk:11-jdk
@@ -123,7 +129,7 @@ jobs:
123129
- run:
124130
name: Deploy app to Digital Ocean Server via Docker
125131
command: |
126-
ssh -o StrictHostKeyChecking=no $DIGITAL_OCEAN_USER@$DIGITAL_OCEAN_HOST "cd /root/access/infrastructure && sh ./scripts/restart-containers-do.sh"
132+
ssh -o StrictHostKeyChecking=no $DIGITAL_OCEAN_USER@$DIGITAL_OCEAN_HOST "cd /root/Infrastructure && sh ./scripts/restart-containers-do.sh"
127133
128134
workflows:
129135
version: 2

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ gradle-app.setting
168168

169169

170170
course_repositories/
171-
runner/
171+
/runner/
172+
/tmp/
172173
courses_db
173174

174175
#load_testing/

build.gradle

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,21 @@ gatling {
100100
}
101101
}
102102

103-
task customFatJar(type: Jar) {
103+
bootJar {
104+
archiveFileName = "${archiveBaseName.get()}-${archiveVersion.get()}.${archiveExtension.get()}"
105+
}
106+
107+
import org.springframework.boot.gradle.tasks.bundling.BootJar
108+
109+
task customFatJar(type: BootJar) {
104110
manifest {
105-
attributes 'Main-Class': 'ch.uzh.ifi.access.localtester.LocalTester'
111+
attributes 'Main-Class': 'org.springframework.boot.loader.JarLauncher'
112+
attributes 'Start-Class': 'ch.uzh.ifi.access.localtester.LocalTester'
113+
attributes 'Spring-Boot-Classes': 'BOOT-INF/classes/'
114+
attributes 'Spring-Boot-Lib': 'BOOT-INF/lib/'
106115
}
107-
from {
108-
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
109-
}
110-
archivesBaseName = "ACCESSTester"
116+
117+
archiveFileName = "ACCESSTester.jar"
111118
enabled = true
119+
with bootJar
112120
}

src/main/java/ch/uzh/ifi/access/coderunner/CodeRunner.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ private RunResult createAndRunContainer(ContainerConfig containerConfig, String
134134
String stdErr = readStdErr(containerId);
135135

136136
if (isOomKilled) {
137-
console = String.format("Out of Memory. Submission run terminated.");
138-
stdErr = String.format("Out of Memory. Submission run terminated.");
137+
console = "Out of Memory. Submission run terminated.";
138+
stdErr = "Out of Memory. Submission run terminated.";
139139
}
140140

141141
if (didTimeout) {

src/main/java/ch/uzh/ifi/access/course/model/Exercise.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
@ToString(callSuper = true)
1515
@EqualsAndHashCode(callSuper = true)
1616
@AllArgsConstructor
17-
public class Exercise extends ExerciseConfig implements Indexed<Exercise>, HasBreadCrumbs {
17+
public class Exercise extends ExerciseConfig implements Indexed<Exercise>, HasBreadCrumbs, HasSetupScript {
1818

1919
private final String id;
2020
private int index;
@@ -51,8 +51,8 @@ public Exercise(String name) {
5151
}
5252

5353
@Builder
54-
private Exercise(ExerciseType type, String language, Boolean isGraded, int maxScore, int maxSubmits, List<String> options, List<String> solutions, List<String> hints, String id, int index, String gitHash, Assignment assignment, String question, List<VirtualFile> private_files, List<VirtualFile> solution_files, List<VirtualFile> resource_files, List<VirtualFile> public_files, CodeExecutionLimits executionLimits, String title, String longTitle) {
55-
super(title, longTitle, type, language, isGraded, maxScore, maxSubmits, options, solutions, hints, executionLimits);
54+
private Exercise(ExerciseType type, String language, Boolean isGraded, int maxScore, int maxSubmits, String gradingSetup, List<String> options, List<String> solutions, List<String> hints, String id, int index, String gitHash, Assignment assignment, String question, List<VirtualFile> private_files, List<VirtualFile> solution_files, List<VirtualFile> resource_files, List<VirtualFile> public_files, CodeExecutionLimits executionLimits, String title, String longTitle) {
55+
super(title, longTitle, type, language, isGraded, maxScore, maxSubmits, gradingSetup, options, solutions, hints, executionLimits);
5656
this.id = id;
5757
this.index = index;
5858
this.gitHash = gitHash;
@@ -77,6 +77,7 @@ public void set(ExerciseConfig other) {
7777
this.isGraded = other.getIsGraded();
7878
this.maxScore = other.getMaxScore();
7979
this.maxSubmits = other.getMaxSubmits();
80+
this.gradingSetup = other.getGradingSetup();
8081
this.options = other.getOptions();
8182
this.solutions = other.getSolutions();
8283
this.hints = other.getHints();
@@ -103,9 +104,8 @@ public void update(Exercise other) {
103104
*
104105
* @param id file id
105106
*/
106-
// TODO: should private files ever be visible to a student? (for example after the due date)?
107107
public Optional<VirtualFile> getAnyFileById(String id) {
108-
Stream<VirtualFile> files = Stream.concat(Stream.concat(public_files.stream(), resource_files.stream()), solution_files.stream());
108+
Stream<VirtualFile> files = Stream.concat(Stream.concat(public_files.stream(), resource_files.stream()), Stream.concat(solution_files.stream(), private_files.stream()));
109109
return files.filter(file -> file.getId().equals(id)).findFirst();
110110
}
111111

src/main/java/ch/uzh/ifi/access/course/model/ExerciseConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ public class ExerciseConfig implements Serializable {
1717
protected Boolean isGraded;
1818
protected int maxScore;
1919
protected int maxSubmits;
20+
protected String gradingSetup;
2021

2122
protected List<String> options;
2223
protected List<String> solutions;
2324
protected List<String> hints;
2425

2526
protected CodeExecutionLimits executionLimits;
2627

28+
2729
public ExerciseConfig() {
2830
this.isGraded = true;
2931
this.maxSubmits = 1;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package ch.uzh.ifi.access.course.model;
2+
3+
import org.springframework.util.StringUtils;
4+
5+
public interface HasSetupScript {
6+
7+
String getGradingSetup();
8+
9+
default boolean hasGradingSetupScript() {
10+
return getGradingSetup() != null && !StringUtils.isEmpty(getGradingSetup());
11+
}
12+
}

src/main/java/ch/uzh/ifi/access/course/util/RepoCacher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public List<Course> retrieveCourseData(String[] urls) {
6666
return courses;
6767
}
6868

69-
public static List<Course> retrieveCourseData(List<File> repos) {
69+
public static List<Course> retrieveLocalCourseData(List<File> repos) {
7070
initializeMapper();
7171

7272
List<Course> courses = new ArrayList<>();

src/main/java/ch/uzh/ifi/access/localtester/LocalTester.java

Lines changed: 94 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,45 +7,30 @@
77
import ch.uzh.ifi.access.course.util.RepoCacher;
88

99
import java.io.File;
10-
import java.util.ArrayList;
1110
import java.util.List;
1211

1312
public class LocalTester {
14-
public static void main(String[] args){
15-
if(args.length <= 0){
16-
System.out.println("ERROR: To few arguments. You gave " + args.length + ". Please provide path to local repository!");
13+
public static void main(String[] args) {
14+
if (args.length <= 0) {
15+
System.out.println("ERROR: Too few arguments. You gave " + args.length + ". Please provide path to local repository!");
1716
return;
1817
}
1918

20-
List<File> repos = new ArrayList<>();
21-
for(String s : args){
22-
File folder = new File(s);
23-
if(folder.exists() && folder.isDirectory()){
24-
repos.add(folder);
25-
}else{
26-
System.out.println("ERROR: The folder \"" + folder.getName() + "\" was not found!");
27-
return;
28-
}
29-
}
30-
List<Course> courses = RepoCacher.retrieveCourseData(repos);
19+
boolean showDetails = args.length >= 2 && args[1].equals("-d");
3120

32-
System.out.println("----------------- START PARSING TEST ------------------");
3321

34-
for(Course c : courses){
35-
testCourse(c);
36-
for(Assignment a : c.getAssignments()){
37-
testAssignment(a);
38-
for(Exercise e : a.getExercises()){
39-
testExercise(e);
40-
}
41-
}
22+
File dir = new File(args[0]);
23+
if (!dir.exists() || !dir.isDirectory()) {
24+
System.out.println("ERROR: The folder \"" + dir.getName() + "\" was not found!");
25+
return;
4226
}
4327

44-
System.out.println("----------------- END PARSING TEST ------------------\n");
28+
Course c = RepoCacher.retrieveLocalCourseData(List.of(dir)).get(0);
29+
4530

31+
if (showDetails) {
32+
System.out.println("----------------- START PARSED FIELDS ------------------");
4633

47-
System.out.println("----------------- START PARSED FIELDS ------------------");
48-
for(Course c : courses) {
4934
String s = c.toString()
5035
//.replaceAll(", ", "\n")
5136
.replaceAll("super=.*?\\(", "")
@@ -55,48 +40,108 @@ public static void main(String[] args){
5540
.replaceAll("Exercise\\(", "\nExercise:\n");
5641

5742
System.out.println(s);
43+
44+
System.out.println("----------------- END PARSED FIELDS ------------------\n\n");
45+
}
46+
47+
System.out.println("----------------- START PARSING TEST ------------------");
48+
49+
int err = 0;
50+
int ascnt = 0;
51+
int excnt = 0;
52+
53+
54+
err += testCourse(c);
55+
ascnt += c.getAssignments().size();
56+
57+
for (Assignment a : c.getAssignments()) {
58+
err += testAssignment(a);
59+
excnt += a.getExercises().size();
60+
for (Exercise e : a.getExercises()) {
61+
err += testExercise(e);
62+
}
63+
}
64+
65+
66+
System.out.println("------------------------------------------------------");
67+
System.out.println("Found " + ascnt + " total Assignments");
68+
System.out.println("Found " + excnt + " total Exercises");
69+
System.out.println("------------------------------------------------------");
70+
System.out.println(("Test ended with " + err + " errors"));
71+
if (err == 0) {
72+
System.out.println("Everything seems to be fine :)");
73+
} else {
74+
System.out.println("You have some errors to fix :(");
5875
}
59-
System.out.println("----------------- START PARSED FIELDS ------------------");
76+
77+
System.out.println("----------------- END PARSING TEST ------------------\n");
6078
}
6179

62-
public static void testCourse(Course c){
63-
if(c.getTitle() == null)
80+
private static int testCourse(Course c) {
81+
if (c.getTitle() == null) {
6482
System.out.println("ERROR: Course 'title' must not be empty!");
83+
return 1;
84+
}
6585

66-
if(c.getStartDate() == null)
86+
if (c.getStartDate() == null) {
6787
System.out.println("ERROR: Course 'startDate' must not be empty! (" + c.getTitle() + ")");
88+
return 1;
89+
}
6890

69-
if(c.getEndDate() == null)
91+
if (c.getEndDate() == null) {
7092
System.out.println("ERROR: Course 'endDate' must not be empty! (" + c.getTitle() + ")");
93+
return 1;
94+
}
95+
96+
return 0;
7197
}
7298

73-
public static void testAssignment(Assignment a){
74-
if(a.getTitle() == null)
75-
System.out.println("ERROR: Assignment 'title' must not be empty! (" + a.getTitle() + ")");
99+
private static int testAssignment(Assignment a) {
76100

77-
if(a.getPublishDate() == null)
78-
System.out.println("ERROR: Assignment 'publishDate' must not be empty! (" + a.getTitle() + ")");
101+
if (a.getTitle() == null) {
102+
System.out.println("ERROR: Assignment 'title' must not be empty! (Assignment " + a.getIndex() + " - " + a.getTitle() + ")");
103+
return 1;
104+
}
79105

80-
if(a.getDueDate()== null)
81-
System.out.println("ERROR: Assignment 'dueDate' must not be empty! (" + a.getTitle() + ")");
106+
if (a.getPublishDate() == null) {
107+
System.out.println("ERROR: Assignment 'publishDate' must not be empty! (Assignment " + a.getIndex() + " - " + a.getTitle() + ")");
108+
return 1;
109+
}
110+
111+
if (a.getDueDate() == null) {
112+
System.out.println("ERROR: Assignment 'dueDate' must not be empty! (Assignment " + a.getIndex() + " - " + a.getTitle() + ")");
113+
return 1;
114+
}
115+
116+
return 0;
82117
}
83118

84-
public static void testExercise(Exercise e){
85-
if(e.getType() == null) {
86-
System.out.println("ERROR: Exercise 'type' must not be empty! (Exercise " + e.getIndex() + ")");
119+
private static int testExercise(Exercise e) {
120+
Assignment a = e.getAssignment();
87121

88-
}else{
122+
if (e.getType() == null) {
123+
System.out.println("ERROR: Exercise 'type' must not be empty! (Assignment " + a.getIndex() + " / Exercise " + e.getIndex() + ")");
124+
return 1;
125+
} else {
89126
if ((e.getType() == ExerciseType.code || e.getType() == ExerciseType.codeSnippet) &&
90-
e.getLanguage() == null)
91-
System.out.println("ERROR: Exercise of type 'code' and 'codeSnippet' needs to provide a 'language'! (Exercise " + e.getIndex() + ")");
127+
e.getLanguage() == null) {
128+
System.out.println("ERROR: Exercise of type 'code' and 'codeSnippet' needs to provide a 'language'! (Assignment " + a.getIndex() + " / Exercise " + e.getIndex() + ")");
129+
return 1;
130+
}
92131

93132
if ((e.getType() == ExerciseType.multipleChoice || e.getType() == ExerciseType.singleChoice) &&
94-
e.getOptions() == null)
95-
System.out.println("ERROR: Exercise of type 'singleChoice' and 'multipleChoice' must provide ¨options¨! (Exercise " + e.getIndex() + ")");
133+
e.getOptions() == null) {
134+
System.out.println("ERROR: Exercise of type 'singleChoice' and 'multipleChoice' must provide ¨options¨! (Assignment " + a.getIndex() + " / Exercise " + e.getIndex() + ")");
135+
return 1;
136+
}
96137

97138
if ((e.getType() == ExerciseType.multipleChoice || e.getType() == ExerciseType.singleChoice || e.getType() == ExerciseType.text) &&
98-
e.getSolutions() == null)
99-
System.out.println("ERROR: Exercise of type 'singleChoice', 'multipleChoice' and 'text' must provide 'solutions'! (Exercise " + e.getIndex() + ")");
139+
e.getSolutions() == null) {
140+
System.out.println("ERROR: Exercise of type 'singleChoice', 'multipleChoice' and 'text' must provide 'solutions'! (Assignment " + a.getIndex() + " / Exercise " + e.getIndex() + ")");
141+
return 1;
142+
}
100143
}
144+
145+
return 0;
101146
}
102147
}

src/main/java/ch/uzh/ifi/access/student/config/SchedulingConfig.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public SchedulingConfig(EvalMachineRepoService machineRepository) {
2727
@Scheduled(fixedDelay = FIXED_DELAY_IN_MINUTES * 60000)
2828
public void cleanUpRepo() {
2929
Instant threshold = Instant.now().minus(5, ChronoUnit.MINUTES);
30-
logger.debug("Starting state machine cleanup. Repository size {}, removing machine older than {}", machineRepository.count(), threshold);
30+
logger.info("Starting state machine cleanup. Repository size {}, removing machine older than {}", machineRepository.count(), threshold);
3131
machineRepository.removeMachinesOlderThan(threshold);
32-
logger.debug("Completed cleanup. Repository size {}", machineRepository.count());
32+
logger.info("Completed cleanup. Repository size {}", machineRepository.count());
3333
}
3434
}

0 commit comments

Comments
 (0)