init impl
This commit is contained in:
parent
b48e5191d7
commit
7366e68ea6
4
Dockerfile
Normal file
4
Dockerfile
Normal file
@ -0,0 +1,4 @@
|
||||
FROM openjdk:17-jdk-slim
|
||||
COPY target/*.jar app.jar
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["java", "-jar", "/app.jar"]
|
29
pom.xml
29
pom.xml
@ -88,6 +88,35 @@
|
||||
<artifactId>vaadin-testbench-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.openhft</groupId>
|
||||
<artifactId>compiler</artifactId>
|
||||
<version>2.26ea0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hilerio</groupId>
|
||||
<artifactId>ace-widget</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.hypersistence</groupId>
|
||||
<artifactId>hypersistence-utils-hibernate-63</artifactId>
|
||||
<version>3.7.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>in.virit</groupId>
|
||||
<artifactId>viritin</artifactId>
|
||||
<version>2.8.22</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.flowingcode.addons</groupId>
|
||||
<artifactId>simple-timer</artifactId>
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
32
src/main/bundles/README.md
Normal file
32
src/main/bundles/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
This directory is automatically generated by Vaadin and contains the pre-compiled
|
||||
frontend files/resources for your project (frontend development bundle).
|
||||
|
||||
It should be added to Version Control System and committed, so that other developers
|
||||
do not have to compile it again.
|
||||
|
||||
Frontend development bundle is automatically updated when needed:
|
||||
- an npm/pnpm package is added with @NpmPackage or directly into package.json
|
||||
- CSS, JavaScript or TypeScript files are added with @CssImport, @JsModule or @JavaScript
|
||||
- Vaadin add-on with front-end customizations is added
|
||||
- Custom theme imports/assets added into 'theme.json' file
|
||||
- Exported web component is added.
|
||||
|
||||
If your project development needs a hot deployment of the frontend changes,
|
||||
you can switch Flow to use Vite development server (default in Vaadin 23.3 and earlier versions):
|
||||
- set `vaadin.frontend.hotdeploy=true` in `application.properties`
|
||||
- configure `vaadin-maven-plugin`:
|
||||
```
|
||||
<configuration>
|
||||
<frontendHotdeploy>true</frontendHotdeploy>
|
||||
</configuration>
|
||||
```
|
||||
- configure `jetty-maven-plugin`:
|
||||
```
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<vaadin.frontend.hotdeploy>true</vaadin.frontend.hotdeploy>
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
Read more [about Vaadin development mode](https://vaadin.com/docs/next/flow/configuration/development-mode#precompiled-bundle).
|
BIN
src/main/bundles/dev.bundle
Normal file
BIN
src/main/bundles/dev.bundle
Normal file
Binary file not shown.
23
src/main/frontend/index.html
Normal file
23
src/main/frontend/index.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
This file is auto-generated by Vaadin.
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style>
|
||||
body, #outlet {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<!-- index.ts is included here automatically (either by the dev server or during the build) -->
|
||||
</head>
|
||||
<body>
|
||||
<!-- This outlet div is where the views are rendered -->
|
||||
<div id="outlet"></div>
|
||||
</body>
|
||||
</html>
|
23
src/main/java/com/primefactorsolutions/model/AppUser.java
Normal file
23
src/main/java/com/primefactorsolutions/model/AppUser.java
Normal file
@ -0,0 +1,23 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AppUser extends BaseEntity {
|
||||
private String email;
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "appUser")
|
||||
private List<Assessment> assessments;
|
||||
}
|
64
src/main/java/com/primefactorsolutions/model/Assessment.java
Normal file
64
src/main/java/com/primefactorsolutions/model/Assessment.java
Normal file
@ -0,0 +1,64 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Assessment extends BaseEntity {
|
||||
@OneToMany(fetch = FetchType.EAGER)
|
||||
private List<Question> questions;
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "assessment", cascade = {CascadeType.ALL})
|
||||
private List<Submission> submissions;
|
||||
|
||||
@ManyToOne
|
||||
private AppUser appUser;
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "assessment", cascade = {CascadeType.ALL})
|
||||
private List<AssessmentEvent> assessmentEvents = List.of();
|
||||
|
||||
public Submission getCurrentSubmission() {
|
||||
return submissions.getLast();
|
||||
}
|
||||
|
||||
public Long getRemainingTimeSeconds() {
|
||||
final Optional<Instant> started = assessmentEvents.stream().filter(e -> e.getStatus() == AssessmentStatus.STARTED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst();
|
||||
|
||||
return started.map(instant -> 300L - (Instant.now().getEpochSecond() - instant.getEpochSecond()))
|
||||
.orElse(300L);
|
||||
}
|
||||
|
||||
public Instant getStartingTime() {
|
||||
final Optional<Instant> started = assessmentEvents.stream().filter(e -> e.getStatus() == AssessmentStatus.STARTED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst();
|
||||
|
||||
return started.orElse(null);
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return assessmentEvents.stream().filter(e -> e.getStatus() == AssessmentStatus.COMPLETED)
|
||||
.map(AssessmentEvent::getTimestamp)
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
public boolean isFirst(Submission currSubmission) {
|
||||
return getQuestions().indexOf(currSubmission.getQuestion()) == 0;
|
||||
}
|
||||
|
||||
public boolean isLast(Submission currSubmission) {
|
||||
return getQuestions().indexOf(currSubmission.getQuestion()) == getQuestions().size() - 1;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class AssessmentEvent extends BaseEntity {
|
||||
private Instant timestamp;
|
||||
private AssessmentStatus status;
|
||||
|
||||
@ManyToOne
|
||||
private Assessment assessment;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum AssessmentStatus {
|
||||
CREATED,
|
||||
STARTED,
|
||||
COMPLETED
|
||||
}
|
51
src/main/java/com/primefactorsolutions/model/BaseEntity.java
Normal file
51
src/main/java/com/primefactorsolutions/model/BaseEntity.java
Normal file
@ -0,0 +1,51 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.Version;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private UUID id;
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (getId() != null) {
|
||||
return getId().hashCode();
|
||||
}
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof BaseEntity that)) {
|
||||
return false; // null or not an AbstractEntity class
|
||||
}
|
||||
if (getId() != null) {
|
||||
return getId().equals(that.getId());
|
||||
}
|
||||
return super.equals(that);
|
||||
}
|
||||
}
|
16
src/main/java/com/primefactorsolutions/model/Question.java
Normal file
16
src/main/java/com/primefactorsolutions/model/Question.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Question extends BaseEntity{
|
||||
private String content;
|
||||
}
|
34
src/main/java/com/primefactorsolutions/model/Submission.java
Normal file
34
src/main/java/com/primefactorsolutions/model/Submission.java
Normal file
@ -0,0 +1,34 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
import io.hypersistence.utils.hibernate.type.json.JsonType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Submission extends BaseEntity {
|
||||
@ManyToOne
|
||||
private Question question;
|
||||
|
||||
private String response;
|
||||
|
||||
@Type(JsonType.class)
|
||||
@Column(columnDefinition = "json")
|
||||
private Map<String, Object> results;
|
||||
|
||||
private SubmissionStatus submissionStatus;
|
||||
|
||||
@ManyToOne
|
||||
private Assessment assessment;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.primefactorsolutions.model;
|
||||
|
||||
public enum SubmissionStatus {
|
||||
OK,
|
||||
FAIL
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.primefactorsolutions.repositories;
|
||||
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface AssessmentRepository extends JpaRepository<Assessment, UUID> {
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.primefactorsolutions.repositories;
|
||||
|
||||
import com.primefactorsolutions.model.AppUser;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface UserRepository extends JpaRepository<AppUser, UUID> {
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.repositories.AssessmentRepository;
|
||||
import lombok.Data;
|
||||
import org.checkerframework.checker.units.qual.A;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.swing.text.html.Option;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@Data
|
||||
public class AssessmentService {
|
||||
|
||||
private final AssessmentRepository assessmentRepository;
|
||||
|
||||
public Assessment createOrUpdate(final Assessment assessment) {
|
||||
final Assessment saved = assessmentRepository.save(assessment);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
public List<Assessment> getAssessments() {
|
||||
return assessmentRepository.findAll();
|
||||
}
|
||||
|
||||
public Assessment getAssessment(final UUID id) {
|
||||
return assessmentRepository.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
public Assessment startAssessment(final UUID id) {
|
||||
final Assessment assessment = assessmentRepository.findById(id).get();
|
||||
final Optional<AssessmentEvent> started = assessment.getAssessmentEvents().stream()
|
||||
.filter(e -> e.getStatus() == AssessmentStatus.STARTED)
|
||||
.findFirst();
|
||||
|
||||
if (started.isPresent()) {
|
||||
return assessment;
|
||||
}
|
||||
|
||||
assessment.getAssessmentEvents().add(new AssessmentEvent(Instant.now(), AssessmentStatus.STARTED, assessment));
|
||||
assessmentRepository.save(assessment);
|
||||
|
||||
return assessment;
|
||||
}
|
||||
|
||||
public Submission getNextSubmission(final Assessment assessment, final Submission currSubmission) {
|
||||
if (currSubmission == null) {
|
||||
if (assessment.isCompleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question firstQuestion = assessment.getQuestions().stream().findFirst().get();
|
||||
final Submission submissionToReturn = new Submission(firstQuestion, firstQuestion.getContent(), Map.of(), SubmissionStatus.FAIL, assessment);
|
||||
assessment.getSubmissions().add(submissionToReturn);
|
||||
assessmentRepository.save(assessment);
|
||||
|
||||
return submissionToReturn;
|
||||
}
|
||||
|
||||
final Question currQuestion = currSubmission.getQuestion();
|
||||
int idx = assessment.getQuestions().indexOf(currQuestion);
|
||||
|
||||
if (idx == assessment.getQuestions().size() - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question nextQuestion = assessment.getQuestions().get(idx + 1);
|
||||
|
||||
final Optional<Submission> submissionToReturn = assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(nextQuestion))
|
||||
.findFirst();
|
||||
|
||||
final Submission result;
|
||||
if (submissionToReturn.isEmpty()) {
|
||||
result = new Submission(nextQuestion, nextQuestion.getContent(), Map.of(), SubmissionStatus.FAIL, assessment);
|
||||
assessment.getSubmissions().add(result);
|
||||
} else {
|
||||
result = submissionToReturn.get();
|
||||
}
|
||||
|
||||
assessmentRepository.save(assessment);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Submission getPrevSubmission(final Assessment assessment, final Submission currSubmission) {
|
||||
if (currSubmission == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question currQuestion = currSubmission.getQuestion();
|
||||
int idx = assessment.getQuestions().indexOf(currQuestion);
|
||||
|
||||
if (idx == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Question prevQuestion = assessment.getQuestions().get(idx - 1);
|
||||
|
||||
assessmentRepository.save(assessment);
|
||||
|
||||
|
||||
return assessment.getSubmissions().stream()
|
||||
.filter(s -> s.getQuestion().equals(prevQuestion))
|
||||
.findFirst().orElseThrow(() -> new IllegalStateException("submission invalid"));
|
||||
}
|
||||
|
||||
public Assessment completeAssessment(final UUID id, final Submission submission) {
|
||||
Assessment assessment = assessmentRepository.findById(id).get();
|
||||
Optional<AssessmentEvent> completed = assessment.getAssessmentEvents().stream()
|
||||
.filter(e -> e.getStatus() == AssessmentStatus.COMPLETED)
|
||||
.findFirst();
|
||||
|
||||
if (completed.isPresent()) {
|
||||
return assessment;
|
||||
}
|
||||
|
||||
assessment.getAssessmentEvents().add(new AssessmentEvent(Instant.now(), AssessmentStatus.COMPLETED, assessment));
|
||||
assessmentRepository.save(assessment);
|
||||
|
||||
return assessment;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.service.compiler.InMemoryClass;
|
||||
import com.primefactorsolutions.service.compiler.InMemoryFileManager;
|
||||
import com.primefactorsolutions.service.compiler.JavaSourceFromString;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.ToolProvider;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class CompilerService {
|
||||
private static final Map<Boolean, String> RESULT_MESSAGE = Map.of(Boolean.TRUE, "OK", Boolean.FALSE, "ERROR");
|
||||
|
||||
@SneakyThrows
|
||||
public Optional<String> doCompile(final String javaCode) {
|
||||
final String qualifiedClassName = "com.primefactorsolutions.TestClass";
|
||||
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
|
||||
final InMemoryFileManager manager = new InMemoryFileManager(compiler.getStandardFileManager(null, null, null));
|
||||
|
||||
final List<JavaFileObject> sourceFiles = Collections.singletonList(new JavaSourceFromString(qualifiedClassName, javaCode));
|
||||
final JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnostics,
|
||||
List.of("-proc:full", "-Xlint:-options"), null, sourceFiles);
|
||||
|
||||
boolean result = task.call();
|
||||
|
||||
if (!result) {
|
||||
final String errors = diagnostics.getDiagnostics().stream()
|
||||
.map(String::valueOf)
|
||||
.collect(Collectors.joining("\n"));
|
||||
|
||||
return Optional.of(errors);
|
||||
} else {
|
||||
final ClassLoader classLoader = manager.getClassLoader(null);
|
||||
final Class<?> clazz = classLoader.loadClass(qualifiedClassName);
|
||||
final InMemoryClass instanceOfClass = (InMemoryClass) clazz.newInstance();
|
||||
final Map<String, Boolean> results = instanceOfClass.runCode();
|
||||
|
||||
return Optional.of(results.entrySet().stream()
|
||||
.map(e -> String.format("%-50s ... %4s", e.getKey(), RESULT_MESSAGE.get(e.getValue())))
|
||||
.collect(Collectors.joining("\n")));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.primefactorsolutions.service;
|
||||
|
||||
import com.primefactorsolutions.model.AppUser;
|
||||
import com.primefactorsolutions.repositories.UserRepository;
|
||||
import lombok.Data;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@Data
|
||||
public class UserService {
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public AppUser createOrUpdate(final AppUser assessment) {
|
||||
final AppUser saved = userRepository.save(assessment);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
public List<AppUser> getUsers() {
|
||||
return userRepository.findAll();
|
||||
}
|
||||
|
||||
public AppUser getUser(final UUID id) {
|
||||
return userRepository.findById(id).orElse(null);
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.primefactorsolutions.service.compiler;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface InMemoryClass {
|
||||
|
||||
Map<String, Boolean> runCode();
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.primefactorsolutions.service.compiler;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class InMemoryClassLoader extends ClassLoader {
|
||||
|
||||
private final InMemoryFileManager manager;
|
||||
|
||||
public InMemoryClassLoader(ClassLoader parent, InMemoryFileManager manager) {
|
||||
super(parent);
|
||||
this.manager = requireNonNull(manager, "manager must not be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
|
||||
Map<String, JavaClassAsBytes> compiledClasses = manager
|
||||
.getBytesMap();
|
||||
|
||||
if (compiledClasses.containsKey(name)) {
|
||||
byte[] bytes = compiledClasses.get(name)
|
||||
.getBytes();
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
} else {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.primefactorsolutions.service.compiler;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.ForwardingJavaFileManager;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.JavaFileObject.Kind;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
|
||||
public class InMemoryFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
|
||||
private final Map<String, JavaClassAsBytes> compiledClasses;
|
||||
private final ClassLoader loader;
|
||||
|
||||
public InMemoryFileManager(StandardJavaFileManager standardManager) {
|
||||
super(standardManager);
|
||||
this.compiledClasses = new Hashtable<>();
|
||||
this.loader = new InMemoryClassLoader(this.getClass()
|
||||
.getClassLoader(),
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get the class loader for our compiled class. It creates an anonymous class extending
|
||||
* the SecureClassLoader which uses the byte code created by the compiler and stored in the
|
||||
* JavaClassObject, and returns the Class for it
|
||||
*
|
||||
* @param location where to place or search for file objects.
|
||||
*/
|
||||
@Override
|
||||
public ClassLoader getClassLoader(Location location) {
|
||||
return loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind,
|
||||
FileObject sibling) {
|
||||
|
||||
JavaClassAsBytes classAsBytes = new JavaClassAsBytes(
|
||||
className, kind);
|
||||
compiledClasses.put(className, classAsBytes);
|
||||
|
||||
return classAsBytes;
|
||||
}
|
||||
|
||||
public Map<String, JavaClassAsBytes> getBytesMap() {
|
||||
return compiledClasses;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.primefactorsolutions.service.compiler;
|
||||
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
|
||||
public class JavaClassAsBytes extends SimpleJavaFileObject {
|
||||
|
||||
protected ByteArrayOutputStream bos =
|
||||
new ByteArrayOutputStream();
|
||||
|
||||
public JavaClassAsBytes(String name, Kind kind) {
|
||||
super(URI.create("string:///" + name.replace('.', '/')
|
||||
+ kind.extension), kind);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutputStream() {
|
||||
return bos;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.primefactorsolutions.service.compiler;
|
||||
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import java.net.URI;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class JavaSourceFromString extends SimpleJavaFileObject {
|
||||
|
||||
private String sourceCode;
|
||||
|
||||
public JavaSourceFromString(String name, String sourceCode) {
|
||||
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension),
|
||||
Kind.SOURCE);
|
||||
this.sourceCode = requireNonNull(sourceCode, "sourceCode must not be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||
return sourceCode;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.router.BeforeEvent;
|
||||
import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/assessments", layout = MainLayout.class)
|
||||
public class AssessmentView extends BeanValidationForm<Assessment> implements HasUrlParameter<String> {
|
||||
private final AssessmentService assessmentService;
|
||||
|
||||
private final TextField appUserName = new TextField();
|
||||
|
||||
public AssessmentView(AssessmentService assessmentService) {
|
||||
super(Assessment.class);
|
||||
setEntityWithEnabledSave(new Assessment());
|
||||
this.assessmentService = assessmentService;
|
||||
|
||||
setSavedHandler((SavedHandler<Assessment>) assessment -> {
|
||||
System.out.println(assessment);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String s) {
|
||||
if (StringUtils.isNotBlank(s) && !"new".equals(s)) {
|
||||
var assessment = assessmentService.getAssessment(UUID.fromString(s));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(appUserName);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.Assessment;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.data.provider.DataProvider;
|
||||
import com.vaadin.flow.data.provider.DataProviderListener;
|
||||
import com.vaadin.flow.data.provider.Query;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/assessments", layout = MainLayout.class)
|
||||
public class AssessmentsListView extends Main {
|
||||
|
||||
public AssessmentsListView(final AssessmentService assessmentService) {
|
||||
final VGrid<Assessment> grid = new VGrid<>(Assessment.class);
|
||||
grid.setDataProvider(new DataProvider<Assessment, Object>() {
|
||||
@Override
|
||||
public boolean isInMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(Query<Assessment, Object> query) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Assessment> fetch(Query<Assessment, Object> query) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshItem(Assessment assessment) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAll() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registration addDataProviderListener(DataProviderListener<Assessment> dataProviderListener) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
add(grid);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.Text;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
|
||||
@PageTitle("Assessment")
|
||||
@Route(value = "/evaluation/completed", layout = MainLayout.class)
|
||||
public class EvaluationCompletedView extends Main {
|
||||
public EvaluationCompletedView() {
|
||||
add(new Text("La evaluacion ha sido completada. Nos contactaremos con usted."));
|
||||
}
|
||||
}
|
414
src/main/java/com/primefactorsolutions/views/EvaluationView.java
Normal file
414
src/main/java/com/primefactorsolutions/views/EvaluationView.java
Normal file
@ -0,0 +1,414 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.flowingcode.vaadin.addons.simpletimer.SimpleTimer;
|
||||
import com.hilerio.ace.AceEditor;
|
||||
import com.hilerio.ace.AceMode;
|
||||
import com.hilerio.ace.AceTheme;
|
||||
import com.primefactorsolutions.model.*;
|
||||
import com.primefactorsolutions.service.AssessmentService;
|
||||
import com.primefactorsolutions.service.CompilerService;
|
||||
import com.vaadin.flow.component.*;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.contextmenu.MenuItem;
|
||||
import com.vaadin.flow.component.dialog.Dialog;
|
||||
import com.vaadin.flow.component.html.*;
|
||||
import com.vaadin.flow.component.html.DescriptionList.Description;
|
||||
import com.vaadin.flow.component.html.DescriptionList.Term;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.menubar.MenuBar;
|
||||
import com.vaadin.flow.component.menubar.MenuBarVariant;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.router.*;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Background;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.BoxSizing;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Display;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Flex;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.FlexDirection;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.FontSize;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.FontWeight;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Gap;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Height;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Margin;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Overflow;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.Padding;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility.TextColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@PageTitle("Evaluacion")
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@Route(value = "/evaluation", layout = MainLayout.class)
|
||||
public class EvaluationView extends Main implements HasUrlParameter<String> {
|
||||
|
||||
final CompilerService compilerService;
|
||||
final AssessmentService assessmentService;
|
||||
|
||||
AceEditor questionEditor = null;
|
||||
Dialog dialog = null;
|
||||
Dialog completeDialog = null;
|
||||
AceEditor result = null;
|
||||
|
||||
Assessment assessment = null;
|
||||
Submission currSubmission = null;
|
||||
Boolean isCompleted = false;
|
||||
|
||||
Button start = null;
|
||||
MenuItem prev = null;
|
||||
MenuItem next = null;
|
||||
Section sidebar = null;
|
||||
SimpleTimer timer = null;
|
||||
|
||||
DescriptionList dl = null;
|
||||
|
||||
Section editorSection = null;
|
||||
Section startSection = null;
|
||||
|
||||
Section completedSection = null;
|
||||
|
||||
public EvaluationView(final CompilerService compilerService, final AssessmentService assessmentService) {
|
||||
this.compilerService = compilerService;
|
||||
this.assessmentService = assessmentService;
|
||||
|
||||
addClassNames(Display.FLEX, Flex.GROW, Height.FULL);
|
||||
|
||||
initStartSection();
|
||||
initCompletedSection();
|
||||
initEditorSection();
|
||||
initResultDialog();
|
||||
initCompleteDialog();
|
||||
initSidebar();
|
||||
|
||||
add(completedSection, startSection, editorSection, sidebar, dialog);
|
||||
|
||||
updateUI();
|
||||
}
|
||||
|
||||
private void initResultDialog() {
|
||||
dialog = new Dialog();
|
||||
dialog.setHeaderTitle("Resultados");
|
||||
|
||||
result = new AceEditor();
|
||||
result.setTheme(AceTheme.xcode);
|
||||
result.setMode(AceMode.text);
|
||||
result.setFontSize(12);
|
||||
result.setHeight("100%");
|
||||
result.setWidth("100%");
|
||||
result.setReadOnly(true);
|
||||
result.setMinlines(20);
|
||||
result.setMaxlines(30);
|
||||
result.setDisplayIndentGuides(false);
|
||||
result.setShowGutter(false);
|
||||
|
||||
final VerticalLayout dialogLayout = new VerticalLayout(result);
|
||||
dialogLayout.setPadding(false);
|
||||
dialogLayout.setSpacing(false);
|
||||
dialogLayout.setAlignItems(FlexComponent.Alignment.STRETCH);
|
||||
dialogLayout.getStyle()
|
||||
.set("width", "800px")
|
||||
.set("max-width", "100%");
|
||||
|
||||
dialog.add(dialogLayout);
|
||||
|
||||
final Button saveButton = new Button("Guardar y Siguiente", e -> {
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
goToNext();
|
||||
dialog.close();
|
||||
});
|
||||
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
final Button closeButton = new Button("Cerrar", e -> dialog.close());
|
||||
|
||||
dialog.getFooter().add(closeButton, saveButton);
|
||||
}
|
||||
|
||||
private void initCompleteDialog() {
|
||||
completeDialog = new Dialog();
|
||||
completeDialog.setHeaderTitle("Terminar Evaluacion");
|
||||
|
||||
final VerticalLayout dialogLayout = new VerticalLayout(new Text("Desea terminar la evaluacion? No podra editar las respuestas despues de terminar."));
|
||||
dialogLayout.setPadding(false);
|
||||
dialogLayout.setSpacing(false);
|
||||
dialogLayout.setAlignItems(FlexComponent.Alignment.STRETCH);
|
||||
dialogLayout.getStyle()
|
||||
.set("width", "800px")
|
||||
.set("max-width", "100%");
|
||||
|
||||
completeDialog.add(dialogLayout);
|
||||
|
||||
final Button completeButton = new Button("Terminar", e -> {
|
||||
completeDialog.close();
|
||||
this.assessment = assessmentService.completeAssessment(assessment.getId(), currSubmission);
|
||||
goToCompleted();
|
||||
updateUI();
|
||||
});
|
||||
completeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
|
||||
final Button closeButton = new Button("Cerrar", e -> completeDialog.close());
|
||||
|
||||
completeDialog.getFooter().add(closeButton, completeButton);
|
||||
}
|
||||
|
||||
private void initEditorSection() {
|
||||
editorSection = new Section();
|
||||
editorSection.addClassNames(Display.FLEX, FlexDirection.COLUMN, Flex.GROW, Height.FULL);
|
||||
questionEditor = getCodeEditor();
|
||||
|
||||
final MenuBar runMenuBar = new MenuBar();
|
||||
runMenuBar.addThemeVariants(MenuBarVariant.LUMO_PRIMARY);
|
||||
runMenuBar.addItem("Verificar/Ejecutar", (ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
final String javaCode = questionEditor.getValue();
|
||||
final Optional<String> result = this.compilerService.doCompile(javaCode);
|
||||
|
||||
if (result.isPresent()) {
|
||||
this.result.setValue(result.get());
|
||||
this.dialog.open();
|
||||
}
|
||||
});
|
||||
|
||||
final MenuBar navMenuBar = new MenuBar();
|
||||
prev = navMenuBar.addItem("Anterior pregunta", (ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
System.out.println(">>> prev");
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
this.currSubmission = this.assessmentService.getPrevSubmission(assessment, this.currSubmission);
|
||||
updateUI();
|
||||
});
|
||||
next = navMenuBar.addItem("Siguiente pregunta", (ComponentEventListener<ClickEvent<MenuItem>>) menuItemClickEvent -> {
|
||||
this.currSubmission.setResponse(this.questionEditor.getValue());
|
||||
goToNext();
|
||||
});
|
||||
|
||||
final Div menuBar = new Div();
|
||||
menuBar.add(runMenuBar, navMenuBar);
|
||||
|
||||
setInlineBlock(runMenuBar);
|
||||
setInlineBlock(navMenuBar);
|
||||
|
||||
editorSection.add(menuBar, questionEditor);
|
||||
}
|
||||
|
||||
private void initCompletedSection() {
|
||||
completedSection = new Section();
|
||||
completedSection.addClassNames(Display.FLEX, FlexDirection.COLUMN, Flex.GROW, Height.FULL);
|
||||
completedSection.setVisible(false);
|
||||
Div completedHelp = new Div();
|
||||
completedHelp.add(new Text("Evaluacion ha sido completada. Gracias!"));
|
||||
|
||||
completedSection.add(completedHelp);
|
||||
}
|
||||
|
||||
private void initStartSection() {
|
||||
startSection = new Section();
|
||||
startSection.addClassNames(Display.FLEX, FlexDirection.COLUMN, Flex.GROW, Height.FULL);
|
||||
|
||||
Div startHelp = new Div();
|
||||
startHelp.add(new Text("Bienvenido(a) al examen de evaluacion de PFS. Apriete el boton 'Empezar' para empezar la evaluacion. Tiene 30 minutos para completar."));
|
||||
startHelp.add(new HtmlComponent("br"));
|
||||
startHelp.add(new Text("Las preguntas son de implementacion de codigo en JAVA. Las preguntas deben poder compilar y pasar los tests incluidos en el codigo. Puede pasar un pregunta o volver a una pregunta anterior."));
|
||||
|
||||
TextField tf = new TextField();
|
||||
tf.setLabel("Ingrese su email (donde recibio el link de la evaluacion):");
|
||||
|
||||
start = new Button("Empezar");
|
||||
start.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
|
||||
start.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
System.out.println(">>> start");
|
||||
this.assessment = this.assessmentService.startAssessment(this.assessment.getId());
|
||||
|
||||
if (tf.getValue().trim().equalsIgnoreCase(this.assessment.getAppUser().getEmail())) {
|
||||
this.currSubmission = this.assessmentService.getNextSubmission(assessment, this.currSubmission);
|
||||
updateUI();
|
||||
} else {
|
||||
Notification notification = new Notification();
|
||||
notification.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
notification.setPosition(Notification.Position.TOP_CENTER);
|
||||
|
||||
Div text = new Div(new Text("Email invalido. Verifique que el email corresponde al email donde recibio el enlace a la evaluacion."));
|
||||
|
||||
Button closeButton = new Button(new Icon("lumo", "cross"));
|
||||
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE);
|
||||
closeButton.setAriaLabel("Close");
|
||||
closeButton.addClickListener(event -> {
|
||||
notification.close();
|
||||
});
|
||||
|
||||
HorizontalLayout layout = new HorizontalLayout(text, closeButton);
|
||||
layout.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||
|
||||
notification.add(layout);
|
||||
notification.open();
|
||||
}
|
||||
});
|
||||
|
||||
startSection.add(startHelp, tf, start);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private AceEditor getCodeEditor() {
|
||||
final AceEditor questionEditor;
|
||||
questionEditor = new AceEditor();
|
||||
questionEditor.setTheme(AceTheme.xcode);
|
||||
questionEditor.setMode(AceMode.java);
|
||||
questionEditor.setFontSize(15);
|
||||
questionEditor.setHeight("100%");
|
||||
questionEditor.setWidth("100%");
|
||||
|
||||
questionEditor.setReadOnly(false);
|
||||
questionEditor.setShowInvisibles(true);
|
||||
questionEditor.setShowGutter(false);
|
||||
questionEditor.setShowPrintMargin(false);
|
||||
questionEditor.setDisplayIndentGuides(false);
|
||||
questionEditor.setUseWorker(false);
|
||||
|
||||
questionEditor.setSofttabs(true);
|
||||
questionEditor.setTabSize(4);
|
||||
questionEditor.setWrap(true);
|
||||
questionEditor.setMinlines(20);
|
||||
questionEditor.setMaxlines(30);
|
||||
// aceEditor.setPlaceholder("... Edit ...");
|
||||
questionEditor.setAutoComplete(true);
|
||||
questionEditor.setCustomAutoCompletion(new String[] { "TEST", "TEST2", "TEST3" });
|
||||
questionEditor.setHighlightActiveLine(true);
|
||||
questionEditor.setHighlightSelectedWord(false);
|
||||
return questionEditor;
|
||||
}
|
||||
|
||||
private void setInlineBlock(MenuBar menuBar) {
|
||||
menuBar.getStyle().set("display", "inline-block");
|
||||
}
|
||||
|
||||
private void goToNext() {
|
||||
System.out.println(">>> next");
|
||||
this.currSubmission = this.assessmentService.getNextSubmission(assessment, this.currSubmission);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
private void initSidebar() {
|
||||
sidebar = new Section();
|
||||
sidebar.addClassNames(Background.CONTRAST_5, BoxSizing.BORDER, Display.FLEX, FlexDirection.COLUMN,
|
||||
Flex.SHRINK_NONE, Overflow.AUTO, Padding.LARGE);
|
||||
sidebar.setWidth("256px");
|
||||
|
||||
dl = new DescriptionList();
|
||||
dl.addClassNames(Display.FLEX, FlexDirection.COLUMN, Gap.LARGE, Margin.Bottom.SMALL, Margin.Top.NONE,
|
||||
FontSize.SMALL);
|
||||
|
||||
final Text text = new Text("Tiempo restante:");
|
||||
|
||||
timer = new SimpleTimer(0);
|
||||
timer.setMinutes(true);
|
||||
timer.addTimerEndEvent((ComponentEventListener<SimpleTimer.TimerEndedEvent>) timerEndedEvent -> {
|
||||
Notification.show("Tiempo completado.", 5, Notification.Position.TOP_CENTER);
|
||||
this.assessment = assessmentService.completeAssessment(assessment.getId(), currSubmission);
|
||||
goToCompleted();
|
||||
updateUI();
|
||||
});
|
||||
timer.setFractions(false);
|
||||
|
||||
final Button completeButton = new Button("Terminar evaluacion");
|
||||
completeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
|
||||
completeButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
|
||||
this.completeDialog.open();
|
||||
});
|
||||
|
||||
// Add it all together
|
||||
sidebar.add(dl, text, timer, completeButton);
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
if (currSubmission == null) {
|
||||
editorSection.setVisible(false);
|
||||
startSection.setVisible(true);
|
||||
sidebar.setVisible(false);
|
||||
} else {
|
||||
questionEditor.setValue(this.currSubmission.getResponse());
|
||||
editorSection.setVisible(true);
|
||||
startSection.setVisible(false);
|
||||
sidebar.setVisible(true);
|
||||
}
|
||||
|
||||
if (this.assessment != null) {
|
||||
prev.setEnabled(currSubmission != null && !assessment.isFirst(currSubmission));
|
||||
next.setEnabled(currSubmission == null || !assessment.isLast(currSubmission));
|
||||
|
||||
if (this.assessment.isCompleted()) {
|
||||
goToCompleted();
|
||||
this.editorSection.setVisible(false);
|
||||
this.sidebar.setVisible(false);
|
||||
this.startSection.setVisible(false);
|
||||
this.completedSection.setVisible(true);
|
||||
}
|
||||
|
||||
if (dl.getChildren().collect(Collectors.toList()).isEmpty()) {
|
||||
dl.add(
|
||||
createItem("Candidato:", assessment.getAppUser().getEmail()),
|
||||
createItem("Hora comienzo:", Optional.ofNullable(assessment.getStartingTime())
|
||||
.map(t -> ZonedDateTime.ofInstant(t, ZoneId.of("GMT-4")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
|
||||
.orElse("N/A"))
|
||||
);
|
||||
}
|
||||
|
||||
timer.pause();
|
||||
timer.setStartTime(this.assessment.getRemainingTimeSeconds());
|
||||
timer.setMinutes(true);
|
||||
timer.reset();
|
||||
timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
private Div createItem(String label, String value) {
|
||||
return new Div(createTerm(label), createDescription(value));
|
||||
}
|
||||
|
||||
// private Div createBadgeItem(String label, String value) {
|
||||
// return new Div(createTerm(label), createDescription(value, "badge"));
|
||||
// }
|
||||
|
||||
private Term createTerm(String label) {
|
||||
Term term = new Term(label);
|
||||
term.addClassNames(FontWeight.MEDIUM, TextColor.SECONDARY);
|
||||
return term;
|
||||
}
|
||||
|
||||
private Description createDescription(String value, String... themeNames) {
|
||||
Description desc = new Description(value);
|
||||
desc.addClassName(Margin.Left.NONE);
|
||||
|
||||
for (String themeName : themeNames) {
|
||||
desc.getElement().getThemeList().add(themeName);
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(BeforeEvent beforeEvent, String s) {
|
||||
this.assessment = this.assessmentService.getAssessment(UUID.fromString(s));
|
||||
|
||||
if (this.assessment.isCompleted()) {
|
||||
goToCompleted();
|
||||
}
|
||||
|
||||
this.currSubmission = this.assessment.getSubmissions().isEmpty()
|
||||
? null
|
||||
: this.assessmentService.getNextSubmission(assessment, null);
|
||||
|
||||
updateUI();
|
||||
}
|
||||
|
||||
private void goToCompleted() {
|
||||
this.isCompleted = true;
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.views.codeeditor.CodeEditorView;
|
||||
import com.vaadin.flow.component.applayout.AppLayout;
|
||||
import com.vaadin.flow.component.applayout.DrawerToggle;
|
||||
import com.vaadin.flow.component.html.Footer;
|
||||
@ -9,10 +8,8 @@ import com.vaadin.flow.component.html.Header;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.orderedlayout.Scroller;
|
||||
import com.vaadin.flow.component.sidenav.SideNav;
|
||||
import com.vaadin.flow.component.sidenav.SideNavItem;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||
import org.vaadin.lineawesome.LineAwesomeIcon;
|
||||
|
||||
/**
|
||||
* The main view is a top-level placeholder for other views.
|
||||
@ -50,7 +47,7 @@ public class MainLayout extends AppLayout {
|
||||
private SideNav createNavigation() {
|
||||
SideNav nav = new SideNav();
|
||||
|
||||
nav.addItem(new SideNavItem("CodeEditor", CodeEditorView.class, LineAwesomeIcon.EDIT.create()));
|
||||
//nav.addItem(new SideNavItem("CodeEditor", CodeEditorView.class, LineAwesomeIcon.EDIT.create()));
|
||||
|
||||
return nav;
|
||||
}
|
||||
|
14
src/main/java/com/primefactorsolutions/views/MainView.java
Normal file
14
src/main/java/com/primefactorsolutions/views/MainView.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.vaadin.flow.component.Text;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
|
||||
@PageTitle("Assessment")
|
||||
@Route(value = "", layout = MainLayout.class)
|
||||
public class MainView extends Main {
|
||||
public MainView() {
|
||||
add(new Text("welcome"));
|
||||
}
|
||||
}
|
51
src/main/java/com/primefactorsolutions/views/UserView.java
Normal file
51
src/main/java/com/primefactorsolutions/views/UserView.java
Normal file
@ -0,0 +1,51 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.AppUser;
|
||||
import com.primefactorsolutions.service.UserService;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.router.BeforeEvent;
|
||||
import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.form.BeanValidationForm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Assessments")
|
||||
@Route(value = "/users", layout = MainLayout.class)
|
||||
public class UserView extends BeanValidationForm<AppUser> implements HasUrlParameter<String> {
|
||||
private final UserService userService;
|
||||
|
||||
private final TextField appUserName = new TextField();
|
||||
|
||||
public UserView(UserService userService) {
|
||||
super(AppUser.class);
|
||||
setEntityWithEnabledSave(new AppUser("foo@bar.com", List.of()));
|
||||
this.userService = userService;
|
||||
|
||||
setSavedHandler((SavedHandler<AppUser>) appUser -> {
|
||||
System.out.println(appUser);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setParameter(final BeforeEvent beforeEvent, final String s) {
|
||||
if (StringUtils.isNotBlank(s) && !"new".equals(s)) {
|
||||
var assessment = userService.getUser(UUID.fromString(s));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Component> getFormComponents() {
|
||||
return List.of(appUserName);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.primefactorsolutions.views;
|
||||
|
||||
import com.primefactorsolutions.model.AppUser;
|
||||
import com.vaadin.flow.component.html.Main;
|
||||
import com.vaadin.flow.data.provider.DataProvider;
|
||||
import com.vaadin.flow.data.provider.DataProviderListener;
|
||||
import com.vaadin.flow.data.provider.Query;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import com.vaadin.flow.spring.annotation.SpringComponent;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.vaadin.firitin.components.grid.VGrid;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SpringComponent
|
||||
@Scope("prototype")
|
||||
@PageTitle("Users")
|
||||
@Route(value = "/users", layout = MainLayout.class)
|
||||
public class UsersListView extends Main {
|
||||
|
||||
public UsersListView() {
|
||||
final VGrid<AppUser> grid = new VGrid<>(AppUser.class);
|
||||
|
||||
grid.setDataProvider(new DataProvider<AppUser, Object>() {
|
||||
@Override
|
||||
public boolean isInMemory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size(Query<AppUser, Object> query) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<AppUser> fetch(Query<AppUser, Object> query) {
|
||||
int limit = query.getLimit();
|
||||
int pagerSize = query.getPageSize();
|
||||
int page = query.getPage();
|
||||
return Stream.of(new AppUser("foo@bar.com", List.of()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshItem(AppUser appUser) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAll() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registration addDataProviderListener(DataProviderListener<AppUser> dataProviderListener) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
add(grid);
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -6,5 +6,13 @@ spring.mustache.check-template-location = false
|
||||
vaadin.launch-browser=true
|
||||
# To improve the performance during development.
|
||||
# For more information https://vaadin.com/docs/latest/integrations/spring/configuration#special-configuration-parameters
|
||||
vaadin.allowed-packages = com.vaadin,org.vaadin,com.primefactorsolutions
|
||||
vaadin.allowed-packages = com.vaadin,org.vaadin,com.primefactorsolutions,com.hilerio.ace,com.flowingcode.vaadin
|
||||
|
||||
|
||||
spring.datasource.url=jdbc:h2:mem:testdb
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=sa
|
||||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||
spring.jpa.defer-datasource-initialization = true
|
||||
spring.h2.console.enabled=true
|
||||
|
10
src/main/resources/data.sql
Normal file
10
src/main/resources/data.sql
Normal file
@ -0,0 +1,10 @@
|
||||
insert into app_user (id, version, email) values ('23471ab3-f639-4d2b-9541-7227f4ea7ee6', 1, 'foo@bar.com');
|
||||
|
||||
insert into question (id, version, content) values ('a7e00ff8-da41-4624-b31c-1b13c3f2e3ae', 1, 'foo bar');
|
||||
insert into question (id, version, content) values ('8a4b213c-ca81-4c38-b56d-d7028c2dde88', 1, 'foo buzz');
|
||||
|
||||
insert into assessment(id, version, app_user_id) values ('46b153f4-23fd-462f-8430-fbe67b83caab', 1, '23471ab3-f639-4d2b-9541-7227f4ea7ee6');
|
||||
|
||||
insert into ASSESSMENT_QUESTIONS (assessment_id, questions_id) values ('46b153f4-23fd-462f-8430-fbe67b83caab', 'a7e00ff8-da41-4624-b31c-1b13c3f2e3ae');
|
||||
|
||||
insert into ASSESSMENT_QUESTIONS (assessment_id, questions_id) values ('46b153f4-23fd-462f-8430-fbe67b83caab', '8a4b213c-ca81-4c38-b56d-d7028c2dde88');
|
55
src/main/resources/questions/fibonaci.java.txt
Normal file
55
src/main/resources/questions/fibonaci.java.txt
Normal file
@ -0,0 +1,55 @@
|
||||
package com.primefactorsolutions;
|
||||
|
||||
import com.primefactorsolutions.service.compiler.InMemoryClass;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TestClass implements InMemoryClass {
|
||||
|
||||
// ------------ IMPLEMENTAR AQUI --------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static int getFibonaci(int n) {
|
||||
// TODO: implementar aca
|
||||
throw new RuntimeException("not implemented yet");
|
||||
}
|
||||
|
||||
|
||||
// ------------- NO MODIFICAR DESDE ESTA LINEA --------------------
|
||||
|
||||
public static class Tests {
|
||||
public static Map.Entry<String, Boolean> testFibonaciSingle() {
|
||||
boolean result = getFibonaci(1) == 0;
|
||||
|
||||
return Map.entry("fibonaci primer", result);
|
||||
}
|
||||
|
||||
public static Map.Entry<String, Boolean> testFibonaciInvalido() {
|
||||
try {
|
||||
getFibonaci(-1);
|
||||
return Map.entry("palindrome vacio es valido", false);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Map.entry("palindrome vacio es valido", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Boolean> runCode() {
|
||||
Map<String, Boolean> results = Arrays.stream(Tests.class.getDeclaredMethods())
|
||||
.map(m -> {
|
||||
try {
|
||||
return (Map.Entry<String, Boolean>) m.invoke(null);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
60
src/main/resources/questions/palindroma.java.txt
Normal file
60
src/main/resources/questions/palindroma.java.txt
Normal file
@ -0,0 +1,60 @@
|
||||
package com.primefactorsolutions;
|
||||
|
||||
import com.primefactorsolutions.service.compiler.InMemoryClass;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TestClass implements InMemoryClass {
|
||||
|
||||
// ------------ IMPLEMENTAR AQUI --------------------------------
|
||||
|
||||
public static String getPalindroma(String s) {
|
||||
return "TODO";
|
||||
}
|
||||
|
||||
|
||||
// ------------- NO MODIFICAR DESDE ESTA LINEA --------------------
|
||||
|
||||
public static class Tests {
|
||||
public static Map.Entry<String, Boolean> testPalindromaValido() {
|
||||
boolean result = getPalindroma("Ab").equals("ABBA");
|
||||
|
||||
return Map.entry("palindrome es valido", result);
|
||||
}
|
||||
|
||||
public static Map.Entry<String, Boolean> testPalindromaVacio() {
|
||||
boolean result = getPalindroma("").equals("");
|
||||
|
||||
return Map.entry("palindrome vacio es valido", result);
|
||||
}
|
||||
|
||||
public static Map.Entry<String, Boolean> testPalindromaUnico() {
|
||||
boolean result = getPalindroma("z").equals("ZZ");
|
||||
|
||||
return Map.entry("palindrome una solo caracter es valido", result);
|
||||
}
|
||||
|
||||
public static Map.Entry<String, Boolean> testPalindromaNull() {
|
||||
boolean result = getPalindroma(null) == null;
|
||||
|
||||
return Map.entry("palindrome null es valido", result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Boolean> runCode() {
|
||||
Map<String, Boolean> results = Arrays.stream(Tests.class.getDeclaredMethods())
|
||||
.map(m -> {
|
||||
try {
|
||||
return (Map.Entry<String, Boolean>) m.invoke(null);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user