Skip to content
GitLab
Explore
Projects
Groups
Snippets
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CoTA
cota-backend
Commits
c1884045
Commit
c1884045
authored
7 months ago
by
mamunozgil
Browse files
Options
Download
Email Patches
Plain Diff
Refactor for backtracking manifesto
parent
c873f92d
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/main/java/de/hftstuttgart/dtabackend/utils/CompetencyAssessmentUtil.java
+169
-115
...tstuttgart/dtabackend/utils/CompetencyAssessmentUtil.java
src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java
+152
-193
...ava/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java
with
321 additions
and
308 deletions
+321
-308
src/main/java/de/hftstuttgart/dtabackend/utils/CompetencyAssessmentUtil.java
+
169
-
115
View file @
c1884045
...
...
@@ -3,134 +3,188 @@ package de.hftstuttgart.dtabackend.utils;
import
java.io.BufferedReader
;
import
java.io.FileReader
;
import
java.io.IOException
;
import
java.n
et.MalformedURLException
;
import
java.n
io.file.Files
;
import
java.nio.file.Path
;
import
java.io.File
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.stream.Collectors
;
import
java.util.stream.IntStream
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
com.fasterxml.jackson.core.exc.StreamReadException
;
import
com.fasterxml.jackson.databind.DatabindException
;
import
de.hftstuttgart.dtabackend.models.ExerciseCompetencyProfile
;
import
de.hftstuttgart.dtabackend.models.ICompetencyProfile
;
import
de.hftstuttgart.dtabackend.models.Result
;
import
de.hftstuttgart.dtabackend.models.ResultSummary
;
import
de.hftstuttgart.dtabackend.models.TestCompetencyProfile
;
import
java.io.FileNotFoundException
;
public
class
CompetencyAssessmentUtil
{
private
static
final
Logger
LOG
=
LogManager
.
getLogger
(
CompetencyAssessmentUtil
.
class
);
public
static
String
TEST_COMPETENCY_MANIFEST_FILE_NAME
=
"competency-tests.mft"
;
public
static
String
EXERCISE_COMPETENCY_MANIFEST_FILE_NAME
=
"exercise-tests.mft"
;
/*public static void main(String[] args) throws StreamReadException, DatabindException, MalformedURLException, IOException {
ResultSummary summary=ExecuteTestUtil.generateResult("1", Path.of(args[0]), Path.of(args[1]));
System.out.println(summary.successfulTestCompetencyProfile);
}
*/
public
static
float
[]
sumTestCompetencyProfiles
(
List
<
TestCompetencyProfile
>
testCompetencyProfiles
)
{
float
[]
tcpTotalProfile
=
new
float
[
ICompetencyProfile
.
MAX_COMPETENCY_DIMENSIONS
];
for
(
TestCompetencyProfile
currentProfile:
testCompetencyProfiles
)
{
tcpTotalProfile
=
ICompetencyProfile
.
competencySum
(
tcpTotalProfile
,
currentProfile
.
competencyAssessments
);
}
return
tcpTotalProfile
;
}
public
static
float
[]
sumSuccessfulCompetencyProfiles
(
List
<
TestCompetencyProfile
>
testCompetencyProfiles
,
ResultSummary
resultSummary
,
boolean
success
)
{
float
[]
sumSuccessful
=
new
float
[
ICompetencyProfile
.
MAX_COMPETENCY_DIMENSIONS
];
for
(
Result
currentResult:
resultSummary
.
results
)
{
boolean
isSuccess
=
Integer
.
valueOf
(
currentResult
.
state
).
equals
(
Result
.
State
.
SUCCESS
.
ordinal
());
if
(
isSuccess
==
success
)
{
TestCompetencyProfile
currentProfile
=
new
TestCompetencyProfile
();
currentProfile
.
testPackageName
=(
currentResult
.
packageName
!=
null
)?
currentResult
.
packageName
:
""
;
currentProfile
.
testClassName
=(
currentResult
.
className
!=
null
)?
currentResult
.
className
:
""
;
currentProfile
.
testName
=(
currentResult
.
name
!=
null
)?
currentResult
.
name
:
""
;
int
testIndex
=
testCompetencyProfiles
.
indexOf
(
currentProfile
);
if
(
testIndex
!=-
1
)
{
sumSuccessful
=
ICompetencyProfile
.
competencySum
(
sumSuccessful
,
testCompetencyProfiles
.
get
(
testIndex
).
competencyAssessments
);
}
}
}
return
sumSuccessful
;
}
public
static
List
<
TestCompetencyProfile
>
readTestCompetencyProfiles
(
Path
testPath
,
String
fileName
)
{
List
<
TestCompetencyProfile
>
testCompetencyProfiles
=
new
ArrayList
<
TestCompetencyProfile
>();
try
{
BufferedReader
testCompetencyManifest
=
new
BufferedReader
(
new
FileReader
(
new
File
(
testPath
.
toFile
(),
fileName
)));
String
testEntry
=
testCompetencyManifest
.
readLine
();
while
(
testEntry
!=
null
)
{
String
[]
testEntyComponents
=
testEntry
.
split
(
ICompetencyProfile
.
COMPETENCY_SEPARATOR
);
TestCompetencyProfile
currentProfile
=
new
TestCompetencyProfile
();
currentProfile
.
testPackageName
=
testEntyComponents
[
0
];
currentProfile
.
testClassName
=
testEntyComponents
[
1
];
currentProfile
.
testName
=
testEntyComponents
[
2
];
for
(
int
competencyIndex
=
0
;
competencyIndex
<
ICompetencyProfile
.
MAX_COMPETENCY_DIMENSIONS
;
competencyIndex
++)
{
currentProfile
.
competencyAssessments
[
competencyIndex
]=
Float
.
valueOf
(
testEntyComponents
[
competencyIndex
+
3
]);
}
testCompetencyProfiles
.
add
(
currentProfile
);
testEntry
=
testCompetencyManifest
.
readLine
();
}
testCompetencyManifest
.
close
();
LOG
.
info
(
"Added "
+
testCompetencyProfiles
.
size
()+
" test competency profiles from test competency manifest. Optional agent functionality enabled."
);
}
catch
(
FileNotFoundException
e
)
{
LOG
.
info
(
"Test competency manifest file for agent feedback not found. Skipping optional functionality."
);
testCompetencyProfiles
=
null
;
}
catch
(
IOException
e
)
{
LOG
.
info
(
"Test competency manifest file for agent feedback unreadable. Skipping optional functionality."
);
testCompetencyProfiles
=
null
;
}
return
testCompetencyProfiles
;
}
public
static
List
<
ExerciseCompetencyProfile
>
readExerciseCompetencyProfiles
(
Path
exercisePath
,
String
fileName
)
{
List
<
ExerciseCompetencyProfile
>
exerciseCompetencyProfiles
=
new
ArrayList
<>();
try
(
BufferedReader
exerciseCompetencyManifest
=
new
BufferedReader
(
new
FileReader
(
new
File
(
exercisePath
.
toFile
(),
fileName
))))
{
String
exerciseEntry
=
exerciseCompetencyManifest
.
readLine
();
while
(
exerciseEntry
!=
null
)
{
String
[]
exerciseEntyComponents
=
exerciseEntry
.
split
(
ExerciseCompetencyProfile
.
COMPETENCY_SEPARATOR
);
ExerciseCompetencyProfile
currentProfile
=
new
ExerciseCompetencyProfile
();
currentProfile
.
exerciseTopicName
=
exerciseEntyComponents
[
0
];
currentProfile
.
exerciseName
=
exerciseEntyComponents
[
1
];
currentProfile
.
exerciseURL
=
exerciseEntyComponents
[
2
];
for
(
int
competencyIndex
=
0
;
competencyIndex
<
ExerciseCompetencyProfile
.
MAX_COMPETENCY_DIMENSIONS
;
competencyIndex
++)
{
currentProfile
.
competencyAssessments
[
competencyIndex
]
=
Float
.
valueOf
(
exerciseEntyComponents
[
competencyIndex
+
3
]);
}
currentProfile
.
difficulty
=
Float
.
parseFloat
(
exerciseEntyComponents
[
19
]);
exerciseCompetencyProfiles
.
add
(
currentProfile
);
exerciseEntry
=
exerciseCompetencyManifest
.
readLine
();
}
exerciseCompetencyManifest
.
close
();
LOG
.
info
(
"Added "
+
exerciseCompetencyProfiles
.
size
()
+
" test competency profiles from exercise competency manifest."
);
}
catch
(
FileNotFoundException
e
)
{
LOG
.
info
(
"Exercise competency manifest file not found."
);
}
catch
(
IOException
e
)
{
LOG
.
info
(
"Exercise competency manifest file unreadable."
);
}
return
exerciseCompetencyProfiles
;
}
public
static
String
packFloats
(
float
[]
array
)
{
return
IntStream
.
range
(
0
,
array
.
length
)
.
mapToObj
(
i
->
String
.
valueOf
(
array
[
i
]))
.
collect
(
Collectors
.
joining
(
";"
));
}
private
static
final
Logger
LOG
=
LogManager
.
getLogger
(
CompetencyAssessmentUtil
.
class
);
public
static
final
String
TEST_COMPETENCY_MANIFEST_FILE_NAME
=
"competency-tests.mft"
;
public
static
final
String
EXERCISE_COMPETENCY_MANIFEST_FILE_NAME
=
"exercise-tests.mft"
;
/**
* Retrieves the base directory where the test competency manifest is located
* by traversing upwards until it finds the file "exercise-tests.mft".
*
* @param startDir Starting directory path to begin the search
* @return Path of the base directory if found; otherwise, null
*/
public
static
Path
getBaseDirectory
(
Path
startDir
)
{
Path
currentDir
=
startDir
;
while
(
currentDir
!=
null
)
{
Path
manifestPath
=
currentDir
.
resolve
(
EXERCISE_COMPETENCY_MANIFEST_FILE_NAME
);
if
(
Files
.
exists
(
manifestPath
))
{
return
currentDir
;
}
currentDir
=
currentDir
.
getParent
();
}
LOG
.
warn
(
"Base directory with "
+
EXERCISE_COMPETENCY_MANIFEST_FILE_NAME
+
" not found starting from "
+
startDir
);
return
null
;
}
/**
* Reads and loads test competency profiles from the base directory.
*
* @param exercisePath Path to the starting exercise directory
* @return List of TestCompetencyProfile instances loaded from the manifest
*/
public
static
List
<
TestCompetencyProfile
>
readTestCompetencyProfiles
(
Path
exercisePath
)
{
Path
baseDir
=
getBaseDirectory
(
exercisePath
);
if
(
baseDir
==
null
)
{
LOG
.
error
(
"Unable to locate the base directory for reading test competency profiles."
);
return
null
;
}
return
readTestCompetencyProfiles
(
baseDir
,
TEST_COMPETENCY_MANIFEST_FILE_NAME
);
}
/**
* Reads and loads exercise competency profiles from the specified exercise directory.
*
* @param exercisePath Path to the exercise directory
* @return List of ExerciseCompetencyProfile instances loaded from the manifest
*/
public
static
List
<
ExerciseCompetencyProfile
>
readExerciseCompetencyProfiles
(
Path
exercisePath
)
{
return
readExerciseCompetencyProfiles
(
exercisePath
,
EXERCISE_COMPETENCY_MANIFEST_FILE_NAME
);
}
private
static
List
<
TestCompetencyProfile
>
readTestCompetencyProfiles
(
Path
baseDir
,
String
fileName
)
{
List
<
TestCompetencyProfile
>
testCompetencyProfiles
=
new
ArrayList
<>();
Path
manifestPath
=
baseDir
.
resolve
(
fileName
);
try
(
BufferedReader
testCompetencyManifest
=
new
BufferedReader
(
new
FileReader
(
manifestPath
.
toFile
())))
{
String
testEntry
=
testCompetencyManifest
.
readLine
();
while
(
testEntry
!=
null
)
{
String
[]
testEntryComponents
=
testEntry
.
split
(
ICompetencyProfile
.
COMPETENCY_SEPARATOR
);
TestCompetencyProfile
currentProfile
=
new
TestCompetencyProfile
();
currentProfile
.
testPackageName
=
testEntryComponents
[
0
];
currentProfile
.
testClassName
=
testEntryComponents
[
1
];
currentProfile
.
testName
=
testEntryComponents
[
2
];
for
(
int
competencyIndex
=
0
;
competencyIndex
<
ICompetencyProfile
.
MAX_COMPETENCY_DIMENSIONS
;
competencyIndex
++)
{
currentProfile
.
competencyAssessments
[
competencyIndex
]
=
Float
.
valueOf
(
testEntryComponents
[
competencyIndex
+
3
]);
}
testCompetencyProfiles
.
add
(
currentProfile
);
testEntry
=
testCompetencyManifest
.
readLine
();
}
LOG
.
info
(
"Loaded "
+
testCompetencyProfiles
.
size
()
+
" test competency profiles from "
+
manifestPath
);
}
catch
(
IOException
e
)
{
LOG
.
error
(
"Error reading test competency manifest file at "
+
manifestPath
,
e
);
}
return
testCompetencyProfiles
;
}
private
static
List
<
ExerciseCompetencyProfile
>
readExerciseCompetencyProfiles
(
Path
exercisePath
,
String
fileName
)
{
List
<
ExerciseCompetencyProfile
>
exerciseCompetencyProfiles
=
new
ArrayList
<>();
Path
manifestPath
=
exercisePath
.
resolve
(
fileName
);
try
(
BufferedReader
exerciseCompetencyManifest
=
new
BufferedReader
(
new
FileReader
(
manifestPath
.
toFile
())))
{
String
exerciseEntry
=
exerciseCompetencyManifest
.
readLine
();
while
(
exerciseEntry
!=
null
)
{
String
[]
exerciseEntryComponents
=
exerciseEntry
.
split
(
ExerciseCompetencyProfile
.
COMPETENCY_SEPARATOR
);
ExerciseCompetencyProfile
currentProfile
=
new
ExerciseCompetencyProfile
();
currentProfile
.
exerciseTopicName
=
exerciseEntryComponents
[
0
];
currentProfile
.
exerciseName
=
exerciseEntryComponents
[
1
];
currentProfile
.
exerciseURL
=
exerciseEntryComponents
[
2
];
for
(
int
competencyIndex
=
0
;
competencyIndex
<
ExerciseCompetencyProfile
.
MAX_COMPETENCY_DIMENSIONS
;
competencyIndex
++)
{
currentProfile
.
competencyAssessments
[
competencyIndex
]
=
Float
.
valueOf
(
exerciseEntryComponents
[
competencyIndex
+
3
]);
}
currentProfile
.
difficulty
=
Float
.
parseFloat
(
exerciseEntryComponents
[
19
]);
exerciseCompetencyProfiles
.
add
(
currentProfile
);
exerciseEntry
=
exerciseCompetencyManifest
.
readLine
();
}
LOG
.
info
(
"Loaded "
+
exerciseCompetencyProfiles
.
size
()
+
" exercise competency profiles from "
+
manifestPath
);
}
catch
(
IOException
e
)
{
LOG
.
error
(
"Error reading exercise competency manifest file at "
+
manifestPath
,
e
);
}
return
exerciseCompetencyProfiles
;
}
/**
* Converts an array of floats into a semicolon-separated string.
*
* @param array Array of float values to pack
* @return Semicolon-separated string of float values
*/
public
static
String
packFloats
(
float
[]
array
)
{
return
IntStream
.
range
(
0
,
array
.
length
)
.
mapToObj
(
i
->
String
.
valueOf
(
array
[
i
]))
.
collect
(
Collectors
.
joining
(
";"
));
}
/**
* Sums the competency profiles across all test competency profiles.
*
* @param testCompetencyProfiles List of test competency profiles
* @return An array representing the sum of competencies
*/
public
static
float
[]
sumTestCompetencyProfiles
(
List
<
TestCompetencyProfile
>
testCompetencyProfiles
)
{
float
[]
tcpTotalProfile
=
new
float
[
ICompetencyProfile
.
MAX_COMPETENCY_DIMENSIONS
];
for
(
TestCompetencyProfile
currentProfile
:
testCompetencyProfiles
)
{
tcpTotalProfile
=
ICompetencyProfile
.
competencySum
(
tcpTotalProfile
,
currentProfile
.
competencyAssessments
);
}
return
tcpTotalProfile
;
}
/**
* Sums only the successful test competency profiles, based on the results summary.
*
* @param testCompetencyProfiles List of test competency profiles
* @param resultSummary The result summary containing test results
* @param success Indicates whether to sum successful (true) or unsuccessful (false) profiles
* @return An array representing the sum of competencies for successful or unsuccessful profiles
*/
public
static
float
[]
sumSuccessfulCompetencyProfiles
(
List
<
TestCompetencyProfile
>
testCompetencyProfiles
,
ResultSummary
resultSummary
,
boolean
success
)
{
float
[]
sumSuccessful
=
new
float
[
ICompetencyProfile
.
MAX_COMPETENCY_DIMENSIONS
];
for
(
Result
currentResult
:
resultSummary
.
results
)
{
boolean
isSuccess
=
Integer
.
valueOf
(
currentResult
.
state
).
equals
(
Result
.
State
.
SUCCESS
.
ordinal
());
if
(
isSuccess
==
success
)
{
TestCompetencyProfile
currentProfile
=
new
TestCompetencyProfile
();
currentProfile
.
testPackageName
=
(
currentResult
.
packageName
!=
null
)
?
currentResult
.
packageName
:
""
;
currentProfile
.
testClassName
=
(
currentResult
.
className
!=
null
)
?
currentResult
.
className
:
""
;
currentProfile
.
testName
=
(
currentResult
.
name
!=
null
)
?
currentResult
.
name
:
""
;
int
testIndex
=
testCompetencyProfiles
.
indexOf
(
currentProfile
);
if
(
testIndex
!=
-
1
)
{
sumSuccessful
=
ICompetencyProfile
.
competencySum
(
sumSuccessful
,
testCompetencyProfiles
.
get
(
testIndex
).
competencyAssessments
);
}
}
}
return
sumSuccessful
;
}
/**
* Checks if all elements in the competency profile array are zero (indicating full success).
*
* @param competencyProfile Array of competency values
* @return True if all values are zero; false otherwise
*/
public
static
boolean
isFullSuccess
(
float
[]
competencyProfile
)
{
for
(
float
value
:
competencyProfile
)
{
if
(
value
!=
0.0f
)
{
return
false
;
}
}
return
true
;
}
}
This diff is collapsed.
Click to expand it.
src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java
+
152
-
193
View file @
c1884045
...
...
@@ -7,7 +7,6 @@ import com.github.dockerjava.api.model.Bind;
import
com.github.dockerjava.api.model.Volume
;
import
de.hftstuttgart.dtabackend.models.ExerciseCompetencyProfile
;
import
de.hftstuttgart.dtabackend.models.ICompetencyProfile
;
import
de.hftstuttgart.dtabackend.models.Recommendation
;
import
de.hftstuttgart.dtabackend.models.ResultSummary
;
import
de.hftstuttgart.dtabackend.models.TestCompetencyProfile
;
...
...
@@ -22,10 +21,7 @@ import java.net.MalformedURLException;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.nio.file.Paths
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.*
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.stream.Collectors
;
...
...
@@ -38,217 +34,180 @@ public class ExecuteTestUtil {
private
final
String
assignmentBasePath
;
private
final
Path
testTmpPathHost
;
public
ExecuteTestUtil
(
Environment
env
,
DockerUtil
dockerUtil
)
{
public
ExecuteTestUtil
(
Environment
env
,
DockerUtil
dockerUtil
)
{
this
.
dockerUtil
=
dockerUtil
;
// set base path for assignments to be stored
Path
p
=
Paths
.
get
(
this
.
assignmentBasePath
=
Paths
.
get
(
env
.
getProperty
(
"data.dir"
),
env
.
getProperty
(
"data.dir.test.folder.name"
))
;
this
.
assignmentBasePath
=
p
.
toAbsolutePath
().
toString
();
env
.
getProperty
(
"data.dir.test.folder.name"
))
.
toAbsolutePath
().
toString
();
// set path of temporary directory on host _and_ inside our container, _must_ be identical
this
.
testTmpPathHost
=
Paths
.
get
(
env
.
getProperty
(
"host.tests.tmp.dir"
));
}
public
ResultSummary
runTests
(
String
assignmentId
,
Path
workDirectory
)
throws
IOException
,
InterruptedException
{
// define paths for the test, the submission and where the result is to be expected afterwards
Path
testPath
=
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"/test"
);
Path
srcPath
=
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"/src"
);
Path
resultPath
=
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"/result"
);
// clone stored test to tmpdir
LOG
.
debug
(
"copying pre-downloaded unitttest repo"
);
FileUtil
.
copyFolder
(
Paths
.
get
(
assignmentBasePath
,
assignmentId
),
testPath
);
LOG
.
debug
(
"copy test config"
);
Files
.
copy
(
Paths
.
get
(
assignmentBasePath
,
assignmentId
+
".txt"
),
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"config.txt"
));
// Define paths
Path
testPath
=
workDirectory
.
resolve
(
"test"
);
Path
srcPath
=
workDirectory
.
resolve
(
"src"
);
Path
resultPath
=
workDirectory
.
resolve
(
"result"
);
// Clone test to temporary directory
LOG
.
debug
(
"Copying pre-downloaded unittest repo"
);
FileUtil
.
copyFolder
(
Paths
.
get
(
assignmentBasePath
,
assignmentId
),
testPath
);
// Copy configuration file
LOG
.
debug
(
"Copy test config"
);
Files
.
copy
(
Paths
.
get
(
assignmentBasePath
,
assignmentId
+
".txt"
),
workDirectory
.
resolve
(
"config.txt"
));
Files
.
createDirectory
(
resultPath
);
LOG
.
info
(
"reading test config"
);
Matcher
config
=
RegexUtil
.
extractConfig
(
new
FileInputStream
(
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"config.txt"
).
toFile
()),
Pattern
.
compile
(
RegexUtil
.
DTA_TESTCONFIGREGEX
));
String
image
=
""
;
if
(
config
==
null
)
{
config
=
RegexUtil
.
extractConfig
(
new
FileInputStream
(
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"config.txt"
).
toFile
()),
Pattern
.
compile
(
RegexUtil
.
TESTCONFIGREGEX
));
if
(
config
==
null
)
{
throw
new
RuntimeException
(
"couldn't find repo config for unittest image extraction"
);
}
image
=
config
.
group
(
4
);
}
else
{
image
=
config
.
group
(
5
);
}
// define the paths to mount as Binds from Host to the test-container
Path
testPathHost
=
Paths
.
get
(
testTmpPathHost
.
toAbsolutePath
().
toString
(),
workDirectory
.
getName
(
workDirectory
.
getNameCount
()-
1
).
toString
(),
testPath
.
getName
(
testPath
.
getNameCount
()-
1
).
toString
()
);
Path
srcPathHost
=
Paths
.
get
(
testTmpPathHost
.
toAbsolutePath
().
toString
(),
workDirectory
.
getName
(
workDirectory
.
getNameCount
()-
1
).
toString
(),
srcPath
.
getName
(
srcPath
.
getNameCount
()-
1
).
toString
()
);
Path
resultPathHost
=
Paths
.
get
(
testTmpPathHost
.
toAbsolutePath
().
toString
(),
workDirectory
.
getName
(
workDirectory
.
getNameCount
()-
1
).
toString
(),
resultPath
.
getName
(
resultPath
.
getNameCount
()-
1
).
toString
()
);
// Load container image configuration
LOG
.
info
(
"Reading test config"
);
String
image
=
loadImageConfig
(
workDirectory
);
// Define paths to mount in the container
Path
testPathHost
=
testTmpPathHost
.
resolve
(
workDirectory
.
getFileName
()).
resolve
(
testPath
.
getFileName
());
Path
srcPathHost
=
testTmpPathHost
.
resolve
(
workDirectory
.
getFileName
()).
resolve
(
srcPath
.
getFileName
());
Path
resultPathHost
=
testTmpPathHost
.
resolve
(
workDirectory
.
getFileName
()).
resolve
(
resultPath
.
getFileName
());
//
s
tart
test-
container with
professor given image and bind mounts for test, submission and result
//
S
tart container with
mounts
dockerUtil
.
runContainer
(
image
,
new
Bind
(
testPathHost
.
toAbsolutePath
().
toString
(),
new
Volume
(
"/data/test"
)),
new
Bind
(
srcPathHost
.
toAbsolutePath
().
toString
(),
new
Volume
(
"/data/src"
)),
new
Bind
(
resultPathHost
.
toAbsolutePath
().
toString
(),
new
Volume
(
"/data/result"
))
new
Bind
(
testPathHost
.
toString
(),
new
Volume
(
"/data/test"
)),
new
Bind
(
srcPathHost
.
toString
(),
new
Volume
(
"/data/src"
)),
new
Bind
(
resultPathHost
.
toString
(),
new
Volume
(
"/data/result"
))
);
ResultSummary
resultSummary
=
generateResult
(
assignmentId
,
resultPath
,
testPathHost
);
return
resultSummary
;
return
generateResult
(
assignmentId
,
resultPath
,
testPathHost
);
}
private
String
loadImageConfig
(
Path
workDirectory
)
throws
FileNotFoundException
{
Matcher
config
=
RegexUtil
.
extractConfig
(
new
FileInputStream
(
workDirectory
.
resolve
(
"config.txt"
).
toFile
()),
Pattern
.
compile
(
RegexUtil
.
DTA_TESTCONFIGREGEX
));
if
(
config
==
null
)
{
config
=
RegexUtil
.
extractConfig
(
new
FileInputStream
(
workDirectory
.
resolve
(
"config.txt"
).
toFile
()),
Pattern
.
compile
(
RegexUtil
.
TESTCONFIGREGEX
));
if
(
config
==
null
)
{
throw
new
RuntimeException
(
"Couldn't find repo config for unittest image extraction"
);
}
return
config
.
group
(
4
);
}
return
config
.
group
(
5
);
}
private
ResultSummary
generateResult
(
String
assignmentId
,
Path
resultPath
,
Path
testPathHost
)
throws
IOException
,
StreamReadException
,
DatabindException
,
MalformedURLException
{
// define expected result file
File
resultFile
=
Paths
.
get
(
resultPath
.
toAbsolutePath
().
toString
(),
"result.json"
).
toFile
();
private
ResultSummary
generateResult
(
String
assignmentId
,
Path
resultPath
,
Path
testPathHost
)
throws
IOException
,
StreamReadException
,
DatabindException
,
MalformedURLException
{
File
resultFile
=
resultPath
.
resolve
(
"result.json"
).
toFile
();
// check if result file is there
if
(!
resultFile
.
exists
()
||
!
resultFile
.
isFile
())
{
LOG
.
error
(
String
.
format
(
"Could not find result file in %s"
,
resultFile
.
getAbsolutePath
()));
throw
new
RuntimeException
(
"
n
o resultfile found"
);
throw
new
RuntimeException
(
"
N
o result
file found"
);
}
LOG
.
debug
(
"
p
arse results
json
"
);
LOG
.
debug
(
"
P
arse results
JSON
"
);
ObjectMapper
objectMapper
=
new
ObjectMapper
();
ResultSummary
resultSummary
=
objectMapper
.
readValue
(
resultFile
.
toURI
().
toURL
(),
ResultSummary
.
class
);
LOG
.
debug
(
"result json returned time "
+
resultSummary
.
timestamp
+
" with "
+
resultSummary
.
results
.
size
()+
" test results."
);
LOG
.
debug
(
"Result JSON returned at "
+
resultSummary
.
timestamp
+
" with "
+
resultSummary
.
results
.
size
()
+
" test results."
);
LOG
.
info
(
"Checking for optional test competency profile information..."
);
List
<
TestCompetencyProfile
>
testCompetencyProfiles
=
CompetencyAssessmentUtil
.
readTestCompetencyProfiles
(
testPathHost
);
if
(
testCompetencyProfiles
!=
null
)
{
LOG
.
info
(
"Found test competency profiles, generating profile data..."
);
resultSummary
.
overallTestCompetencyProfile
=
CompetencyAssessmentUtil
.
packFloats
(
CompetencyAssessmentUtil
.
sumTestCompetencyProfiles
(
testCompetencyProfiles
));
resultSummary
.
successfulTestCompetencyProfile
=
CompetencyAssessmentUtil
.
packFloats
(
CompetencyAssessmentUtil
.
sumSuccessfulCompetencyProfiles
(
testCompetencyProfiles
,
resultSummary
,
true
));
Path
exerciseManifestFile
=
testPathHost
.
resolve
(
CompetencyAssessmentUtil
.
EXERCISE_COMPETENCY_MANIFEST_FILE_NAME
);
if
(
Files
.
exists
(
exerciseManifestFile
))
{
LOG
.
info
(
"Found exercise competency profiles, generating recommendations..."
);
resultSummary
.
recommendations
=
recommendNextExercises
(
assignmentId
,
testPathHost
,
testCompetencyProfiles
,
resultSummary
);
}
}
return
resultSummary
;
}
public
List
<
Recommendation
>
recommendNextExercises
(
String
assignmentId
,
Path
testPathHost
,
List
<
TestCompetencyProfile
>
testCompetencyProfiles
,
ResultSummary
resultSummary
)
throws
FileNotFoundException
{
String
testRepoURL
=
RegexUtil
.
extractConfig
(
new
FileInputStream
(
Paths
.
get
(
assignmentBasePath
,
assignmentId
+
".txt"
).
toFile
()),
Pattern
.
compile
(
RegexUtil
.
DTA_TESTCONFIGREGEX
)).
group
(
1
);
List
<
ExerciseCompetencyProfile
>
exerciseCompetencyProfiles
=
CompetencyAssessmentUtil
.
readExerciseCompetencyProfiles
(
testPathHost
);
int
currentTopicIndex
=
0
;
float
currentDifficulty
=
0.0f
;
Map
<
String
,
Integer
>
topicOrder
=
new
HashMap
<>();
int
order
=
1
;
// Determine currentTopicIndex and set currentDifficulty based on exercise profiles
for
(
ExerciseCompetencyProfile
e
:
exerciseCompetencyProfiles
)
{
if
(!
topicOrder
.
containsKey
(
e
.
exerciseTopicName
))
{
topicOrder
.
put
(
e
.
exerciseTopicName
,
order
++);
}
if
(
e
.
exerciseURL
.
equals
(
testRepoURL
))
{
currentTopicIndex
=
order
;
currentDifficulty
=
e
.
difficulty
;
// Directly assign to currentDifficulty
}
}
// Sum competencies for unsuccessful tests
float
[]
unsuccessful
=
CompetencyAssessmentUtil
.
sumSuccessfulCompetencyProfiles
(
testCompetencyProfiles
,
resultSummary
,
false
);
// Filter exercises based on topics and difficulty
List
<
ExerciseCompetencyProfile
>
filteredExercises
=
filterExercisesByTopicsAndDifficulty
(
exerciseCompetencyProfiles
,
topicOrder
,
currentTopicIndex
,
testRepoURL
,
currentDifficulty
,
unsuccessful
);
// Generate recommendations without using lambda expressions
List
<
Recommendation
>
recommendedExercises
=
new
ArrayList
<>();
for
(
ExerciseCompetencyProfile
profile
:
filteredExercises
)
{
float
score
=
calculateScore
(
profile
,
unsuccessful
,
topicOrder
,
currentDifficulty
);
Recommendation
recommendation
=
new
Recommendation
(
profile
.
exerciseTopicName
,
profile
.
exerciseURL
,
profile
.
exerciseName
,
profile
.
difficulty
,
score
);
recommendedExercises
.
add
(
recommendation
);
}
// Sort the recommendations using Collections.sort and a comparator
Collections
.
sort
(
recommendedExercises
,
Recommendation
.
COMPARE_BY_SCORE
);
return
recommendedExercises
;
}
public
static
List
<
ExerciseCompetencyProfile
>
filterExercisesByTopicsAndDifficulty
(
List
<
ExerciseCompetencyProfile
>
profiles
,
Map
<
String
,
Integer
>
topicOrder
,
int
currentTopicIndex
,
String
testRepoURL
,
float
currentDifficulty
,
float
[]
unsuccessful
)
{
LOG
.
info
(
"Checking for optional test competency profile information for paedagogical agent functionality..."
);
List
<
TestCompetencyProfile
>
testCompetencyProfiles
=
CompetencyAssessmentUtil
.
readTestCompetencyProfiles
(
testPathHost
,
CompetencyAssessmentUtil
.
TEST_COMPETENCY_MANIFEST_FILE_NAME
);
if
(
testCompetencyProfiles
!=
null
)
{
LOG
.
info
(
"Found optional test competency profiles, generating agent profile data..."
);
resultSummary
.
overallTestCompetencyProfile
=
CompetencyAssessmentUtil
.
packFloats
(
CompetencyAssessmentUtil
.
sumTestCompetencyProfiles
(
testCompetencyProfiles
));
resultSummary
.
successfulTestCompetencyProfile
=
CompetencyAssessmentUtil
.
packFloats
(
CompetencyAssessmentUtil
.
sumSuccessfulCompetencyProfiles
(
testCompetencyProfiles
,
resultSummary
,
true
));
LOG
.
info
(
"Checking for optional exercise competency profile information for paedagogical agent exercise recommendation functionality..."
);
//testPathHost or assignmentBasePath
Path
exerciseManifestFile
=
Paths
.
get
(
testPathHost
.
toAbsolutePath
().
toString
(),
CompetencyAssessmentUtil
.
EXERCISE_COMPETENCY_MANIFEST_FILE_NAME
);
if
(
Files
.
exists
(
exerciseManifestFile
))
{
LOG
.
info
(
"Found optional exercise competency profiles, generating recommendations..."
);
resultSummary
.
recommendations
=
recommendNextExercises
(
assignmentId
,
testPathHost
,
testCompetencyProfiles
,
resultSummary
);
}
List
<
ExerciseCompetencyProfile
>
filteredExercises
=
profiles
.
stream
()
.
filter
(
profile
->
topicOrder
.
get
(
profile
.
exerciseTopicName
)
<=
currentTopicIndex
)
.
collect
(
Collectors
.
toList
());
if
(
CompetencyAssessmentUtil
.
isFullSuccess
(
unsuccessful
))
{
filteredExercises
=
filteredExercises
.
stream
()
.
filter
(
profile
->
profile
.
difficulty
>=
currentDifficulty
&&
!
testRepoURL
.
equals
(
profile
.
exerciseURL
))
.
collect
(
Collectors
.
toList
());
}
else
{
filteredExercises
=
filteredExercises
.
stream
()
.
filter
(
profile
->
profile
.
difficulty
<=
currentDifficulty
)
.
collect
(
Collectors
.
toList
());
}
return
resultSummary
;
}
/*
* exercise recommendation part
*/
public
List
<
Recommendation
>
recommendNextExercises
(
String
assignmentId
,
Path
testPathHost
,
List
<
TestCompetencyProfile
>
testCompetencyProfiles
,
ResultSummary
resultSummary
)
throws
FileNotFoundException
{
// fetch repo url from original test upload
Pattern
pattern
=
Pattern
.
compile
(
RegexUtil
.
DTA_TESTCONFIGREGEX
);
File
file
=
Paths
.
get
(
assignmentBasePath
,
assignmentId
+
".txt"
).
toFile
();
FileInputStream
configFileStream
=
new
FileInputStream
(
file
);
Matcher
config
=
RegexUtil
.
extractConfig
(
configFileStream
,
pattern
);
String
testRepoURL
=
config
.
group
(
1
);
List
<
ExerciseCompetencyProfile
>
exerciseCompetencyProfiles
=
CompetencyAssessmentUtil
.
readExerciseCompetencyProfiles
(
testPathHost
,
CompetencyAssessmentUtil
.
EXERCISE_COMPETENCY_MANIFEST_FILE_NAME
);
int
currentTopicIndex
=
0
;
float
currentDifficulty
=
0.0f
;
//build course topic order
Map
<
String
,
Integer
>
topicOrder
=
new
HashMap
<>();
int
order
=
1
;
for
(
ExerciseCompetencyProfile
e
:
exerciseCompetencyProfiles
)
{
if
(!
topicOrder
.
containsKey
(
e
.
exerciseTopicName
))
{
topicOrder
.
put
(
e
.
exerciseTopicName
,
order
++);
}
if
(
e
.
exerciseURL
.
equals
(
testRepoURL
))
{
currentTopicIndex
=
order
;
currentDifficulty
=
e
.
difficulty
;
}
}
//filter exercises according to success
float
[]
unsuccessful
=
CompetencyAssessmentUtil
.
sumSuccessfulCompetencyProfiles
(
testCompetencyProfiles
,
resultSummary
,
false
);
List
<
ExerciseCompetencyProfile
>
filteredExercises
=
filterExercisesByTopicsAndDifficulty
(
exerciseCompetencyProfiles
,
topicOrder
,
currentTopicIndex
,
testRepoURL
,
currentDifficulty
,
unsuccessful
,
resultSummary
);
//compute recommendations
List
<
Recommendation
>
recommendedExercises
=
new
ArrayList
<>();
for
(
ExerciseCompetencyProfile
exerciseProfile
:
filteredExercises
)
{
Recommendation
recommendation
=
new
Recommendation
(
exerciseProfile
.
exerciseTopicName
,
exerciseProfile
.
exerciseURL
,
exerciseProfile
.
exerciseName
,
exerciseProfile
.
difficulty
,
calculateScore
(
exerciseProfile
,
unsuccessful
,
topicOrder
,
currentDifficulty
));
recommendedExercises
.
add
(
recommendation
);
LOG
.
info
(
"Recommending exercise "
+
recommendation
.
topic
+
"/"
+
recommendation
.
exerciseName
+
" with score "
+
recommendation
.
score
);
}
//sort the recommendations for successful or resilient learners, otherwise reverse in display
recommendedExercises
.
stream
().
sorted
(
Recommendation
.
COMPARE_BY_SCORE
).
collect
(
Collectors
.
toList
());
return
recommendedExercises
;
}
public
static
List
<
ExerciseCompetencyProfile
>
filterExercisesByTopicsAndDifficulty
(
List
<
ExerciseCompetencyProfile
>
exerciseCompetencyProfiles
,
Map
<
String
,
Integer
>
topicOrder
,
int
currentTopicIndex
,
String
testRepoURL
,
float
currentDifficulty
,
float
[]
unsuccessful
,
ResultSummary
resultSummary
)
{
//filter out all advanced topics in any case
//option for later: include next topic if fullsuccess and current difficulty == max difficulty
List
<
ExerciseCompetencyProfile
>
filteredExercises
=
exerciseCompetencyProfiles
.
stream
()
.
filter
(
testProfile
->
topicOrder
.
get
(
testProfile
.
exerciseTopicName
)
<=
currentTopicIndex
)
.
collect
(
Collectors
.
toList
());
//filter by difficulty according to success
if
(
isFullSuccess
(
unsuccessful
))
{
filteredExercises
=
filteredExercises
.
stream
().
filter
(
profile
->
profile
.
difficulty
>=
currentDifficulty
&&
!
testRepoURL
.
equals
(
profile
.
exerciseURL
)).
collect
(
Collectors
.
toList
());
}
else
{
filteredExercises
=
filteredExercises
.
stream
().
filter
(
profile
->
profile
.
difficulty
<=
currentDifficulty
).
collect
(
Collectors
.
toList
());
}
return
filteredExercises
;
}
public
static
boolean
isFullSuccess
(
float
[]
unsuccessful
)
{
for
(
float
value
:
unsuccessful
)
{
if
(
value
!=
0.0f
)
{
return
false
;
}
}
return
true
;
}
public
static
float
calculateScore
(
ExerciseCompetencyProfile
exerciseProfile
,
float
[]
unsuccessful
,
Map
<
String
,
Integer
>
topicOrder
,
float
currentDifficulty
)
{
//ensure factor 1 for full success not to blank out score, thus offset the base
float
score
=
1.0f
;
//competency profile difference to not fully achieved competencies component
for
(
int
i
=
0
;
i
<
exerciseProfile
.
competencyAssessments
.
length
-
1
;
i
++)
{
score
+=
exerciseProfile
.
competencyAssessments
[
i
]
*
unsuccessful
[
i
];
}
//difficulty component
score
=
score
*
(
exerciseProfile
.
difficulty
*(
0.5f
+
Math
.
abs
(
currentDifficulty
-
exerciseProfile
.
difficulty
)));
//topic component
score
*=
topicOrder
.
get
(
exerciseProfile
.
exerciseTopicName
);
score
=
Math
.
round
(
score
*
10.0f
)
/
10.0f
;
return
score
;
}
return
filteredExercises
;
}
public
static
float
calculateScore
(
ExerciseCompetencyProfile
profile
,
float
[]
unsuccessful
,
Map
<
String
,
Integer
>
topicOrder
,
float
currentDifficulty
)
{
float
score
=
1.0f
;
for
(
int
i
=
0
;
i
<
profile
.
competencyAssessments
.
length
-
1
;
i
++)
{
score
+=
profile
.
competencyAssessments
[
i
]
*
unsuccessful
[
i
];
}
score
*=
profile
.
difficulty
*
(
0.5f
+
Math
.
abs
(
currentDifficulty
-
profile
.
difficulty
));
score
*=
topicOrder
.
getOrDefault
(
profile
.
exerciseTopicName
,
1
);
return
Math
.
round
(
score
*
10.0f
)
/
10.0f
;
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Explore
Projects
Groups
Snippets