diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..093b320
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,4 @@
+FROM openjdk:17-jdk-slim
+COPY target/*.jar app.jar
+EXPOSE 8080
+ENTRYPOINT ["java", "-jar", "/app.jar"]
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2b4df90..861bd33 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,6 +88,35 @@
vaadin-testbench-junit5
test
+
+ net.openhft
+ compiler
+ 2.26ea0
+
+
+ com.hilerio
+ ace-widget
+ 2.0.0
+
+
+ io.hypersistence
+ hypersistence-utils-hibernate-63
+ 3.7.3
+
+
+ in.virit
+ viritin
+ 2.8.22
+
+
+ com.flowingcode.addons
+ simple-timer
+ 2.2.0
+
+
+ org.projectlombok
+ lombok
+
diff --git a/src/main/bundles/README.md b/src/main/bundles/README.md
new file mode 100644
index 0000000..c9737d1
--- /dev/null
+++ b/src/main/bundles/README.md
@@ -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`:
+```
+
+ true
+
+```
+- configure `jetty-maven-plugin`:
+```
+
+
+ true
+
+
+```
+
+Read more [about Vaadin development mode](https://vaadin.com/docs/next/flow/configuration/development-mode#precompiled-bundle).
\ No newline at end of file
diff --git a/src/main/bundles/dev.bundle b/src/main/bundles/dev.bundle
new file mode 100644
index 0000000..14f10fc
Binary files /dev/null and b/src/main/bundles/dev.bundle differ
diff --git a/src/main/frontend/index.html b/src/main/frontend/index.html
new file mode 100644
index 0000000..d36e593
--- /dev/null
+++ b/src/main/frontend/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/primefactorsolutions/model/AppUser.java b/src/main/java/com/primefactorsolutions/model/AppUser.java
new file mode 100644
index 0000000..73dc753
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/model/AppUser.java
@@ -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 assessments;
+}
diff --git a/src/main/java/com/primefactorsolutions/model/Assessment.java b/src/main/java/com/primefactorsolutions/model/Assessment.java
new file mode 100644
index 0000000..c682bbf
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/model/Assessment.java
@@ -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 questions;
+
+ @OneToMany(fetch = FetchType.EAGER, mappedBy = "assessment", cascade = {CascadeType.ALL})
+ private List submissions;
+
+ @ManyToOne
+ private AppUser appUser;
+
+ @OneToMany(fetch = FetchType.EAGER, mappedBy = "assessment", cascade = {CascadeType.ALL})
+ private List assessmentEvents = List.of();
+
+ public Submission getCurrentSubmission() {
+ return submissions.getLast();
+ }
+
+ public Long getRemainingTimeSeconds() {
+ final Optional 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 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;
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/model/AssessmentEvent.java b/src/main/java/com/primefactorsolutions/model/AssessmentEvent.java
new file mode 100644
index 0000000..8da7084
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/model/AssessmentEvent.java
@@ -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;
+}
diff --git a/src/main/java/com/primefactorsolutions/model/AssessmentStatus.java b/src/main/java/com/primefactorsolutions/model/AssessmentStatus.java
new file mode 100644
index 0000000..430e37f
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/model/AssessmentStatus.java
@@ -0,0 +1,7 @@
+package com.primefactorsolutions.model;
+
+public enum AssessmentStatus {
+ CREATED,
+ STARTED,
+ COMPLETED
+}
diff --git a/src/main/java/com/primefactorsolutions/model/BaseEntity.java b/src/main/java/com/primefactorsolutions/model/BaseEntity.java
new file mode 100644
index 0000000..ddc143e
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/model/BaseEntity.java
@@ -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);
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/model/Question.java b/src/main/java/com/primefactorsolutions/model/Question.java
new file mode 100644
index 0000000..475713d
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/model/Question.java
@@ -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;
+}
diff --git a/src/main/java/com/primefactorsolutions/model/Submission.java b/src/main/java/com/primefactorsolutions/model/Submission.java
new file mode 100644
index 0000000..749c5c6
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/model/Submission.java
@@ -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 results;
+
+ private SubmissionStatus submissionStatus;
+
+ @ManyToOne
+ private Assessment assessment;
+}
diff --git a/src/main/java/com/primefactorsolutions/model/SubmissionStatus.java b/src/main/java/com/primefactorsolutions/model/SubmissionStatus.java
new file mode 100644
index 0000000..c6424ac
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/model/SubmissionStatus.java
@@ -0,0 +1,6 @@
+package com.primefactorsolutions.model;
+
+public enum SubmissionStatus {
+ OK,
+ FAIL
+}
diff --git a/src/main/java/com/primefactorsolutions/repositories/AssessmentRepository.java b/src/main/java/com/primefactorsolutions/repositories/AssessmentRepository.java
new file mode 100644
index 0000000..81813d8
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/repositories/AssessmentRepository.java
@@ -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 {
+}
diff --git a/src/main/java/com/primefactorsolutions/repositories/UserRepository.java b/src/main/java/com/primefactorsolutions/repositories/UserRepository.java
new file mode 100644
index 0000000..70f46d0
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/repositories/UserRepository.java
@@ -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 {
+}
diff --git a/src/main/java/com/primefactorsolutions/service/AssessmentService.java b/src/main/java/com/primefactorsolutions/service/AssessmentService.java
new file mode 100644
index 0000000..f92bd9c
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/service/AssessmentService.java
@@ -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 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 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 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 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;
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/service/CompilerService.java b/src/main/java/com/primefactorsolutions/service/CompilerService.java
new file mode 100644
index 0000000..f1440cd
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/service/CompilerService.java
@@ -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 RESULT_MESSAGE = Map.of(Boolean.TRUE, "OK", Boolean.FALSE, "ERROR");
+
+ @SneakyThrows
+ public Optional doCompile(final String javaCode) {
+ final String qualifiedClassName = "com.primefactorsolutions.TestClass";
+ final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ final DiagnosticCollector diagnostics = new DiagnosticCollector<>();
+ final InMemoryFileManager manager = new InMemoryFileManager(compiler.getStandardFileManager(null, null, null));
+
+ final List 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 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")));
+ }
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/service/UserService.java b/src/main/java/com/primefactorsolutions/service/UserService.java
new file mode 100644
index 0000000..dfb1797
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/service/UserService.java
@@ -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 getUsers() {
+ return userRepository.findAll();
+ }
+
+ public AppUser getUser(final UUID id) {
+ return userRepository.findById(id).orElse(null);
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/service/compiler/InMemoryClass.java b/src/main/java/com/primefactorsolutions/service/compiler/InMemoryClass.java
new file mode 100644
index 0000000..801bc40
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/service/compiler/InMemoryClass.java
@@ -0,0 +1,8 @@
+package com.primefactorsolutions.service.compiler;
+
+import java.util.Map;
+
+public interface InMemoryClass {
+
+ Map runCode();
+}
diff --git a/src/main/java/com/primefactorsolutions/service/compiler/InMemoryClassLoader.java b/src/main/java/com/primefactorsolutions/service/compiler/InMemoryClassLoader.java
new file mode 100644
index 0000000..926bb97
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/service/compiler/InMemoryClassLoader.java
@@ -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 compiledClasses = manager
+ .getBytesMap();
+
+ if (compiledClasses.containsKey(name)) {
+ byte[] bytes = compiledClasses.get(name)
+ .getBytes();
+ return defineClass(name, bytes, 0, bytes.length);
+ } else {
+ throw new ClassNotFoundException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/primefactorsolutions/service/compiler/InMemoryFileManager.java b/src/main/java/com/primefactorsolutions/service/compiler/InMemoryFileManager.java
new file mode 100644
index 0000000..8602a35
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/service/compiler/InMemoryFileManager.java
@@ -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 {
+
+ private final Map 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 getBytesMap() {
+ return compiledClasses;
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/service/compiler/JavaClassAsBytes.java b/src/main/java/com/primefactorsolutions/service/compiler/JavaClassAsBytes.java
new file mode 100644
index 0000000..6a2d2dd
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/service/compiler/JavaClassAsBytes.java
@@ -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;
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/service/compiler/JavaSourceFromString.java b/src/main/java/com/primefactorsolutions/service/compiler/JavaSourceFromString.java
new file mode 100644
index 0000000..6fa41ac
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/service/compiler/JavaSourceFromString.java
@@ -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;
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/views/AssessmentView.java b/src/main/java/com/primefactorsolutions/views/AssessmentView.java
new file mode 100644
index 0000000..3f927e1
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/views/AssessmentView.java
@@ -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 implements HasUrlParameter {
+ 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 -> {
+ 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 getFormComponents() {
+ return List.of(appUserName);
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/views/AssessmentsListView.java b/src/main/java/com/primefactorsolutions/views/AssessmentsListView.java
new file mode 100644
index 0000000..5856050
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/views/AssessmentsListView.java
@@ -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 grid = new VGrid<>(Assessment.class);
+ grid.setDataProvider(new DataProvider() {
+ @Override
+ public boolean isInMemory() {
+ return false;
+ }
+
+ @Override
+ public int size(Query query) {
+ return 0;
+ }
+
+ @Override
+ public Stream fetch(Query query) {
+ return null;
+ }
+
+ @Override
+ public void refreshItem(Assessment assessment) {
+
+ }
+
+ @Override
+ public void refreshAll() {
+
+ }
+
+ @Override
+ public Registration addDataProviderListener(DataProviderListener dataProviderListener) {
+ return null;
+ }
+ });
+
+ add(grid);
+ }
+
+}
diff --git a/src/main/java/com/primefactorsolutions/views/EvaluationCompletedView.java b/src/main/java/com/primefactorsolutions/views/EvaluationCompletedView.java
new file mode 100644
index 0000000..5e4ee89
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/views/EvaluationCompletedView.java
@@ -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."));
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/views/EvaluationView.java b/src/main/java/com/primefactorsolutions/views/EvaluationView.java
new file mode 100644
index 0000000..b66ba4f
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/views/EvaluationView.java
@@ -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 {
+
+ 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>) menuItemClickEvent -> {
+ final String javaCode = questionEditor.getValue();
+ final Optional 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>) 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>) 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>) 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) 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>) 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;
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/views/MainLayout.java b/src/main/java/com/primefactorsolutions/views/MainLayout.java
index ae3b8a7..abdcfee 100644
--- a/src/main/java/com/primefactorsolutions/views/MainLayout.java
+++ b/src/main/java/com/primefactorsolutions/views/MainLayout.java
@@ -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;
}
diff --git a/src/main/java/com/primefactorsolutions/views/MainView.java b/src/main/java/com/primefactorsolutions/views/MainView.java
new file mode 100644
index 0000000..0ca481c
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/views/MainView.java
@@ -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"));
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/views/UserView.java b/src/main/java/com/primefactorsolutions/views/UserView.java
new file mode 100644
index 0000000..e9e49ed
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/views/UserView.java
@@ -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 implements HasUrlParameter {
+ 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 -> {
+ 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 getFormComponents() {
+ return List.of(appUserName);
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/views/UsersListView.java b/src/main/java/com/primefactorsolutions/views/UsersListView.java
new file mode 100644
index 0000000..1c86f0b
--- /dev/null
+++ b/src/main/java/com/primefactorsolutions/views/UsersListView.java
@@ -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 grid = new VGrid<>(AppUser.class);
+
+ grid.setDataProvider(new DataProvider() {
+ @Override
+ public boolean isInMemory() {
+ return false;
+ }
+
+ @Override
+ public int size(Query query) {
+ return 1;
+ }
+
+ @Override
+ public Stream fetch(Query 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 dataProviderListener) {
+ return null;
+ }
+ });
+
+ add(grid);
+ }
+}
diff --git a/src/main/java/com/primefactorsolutions/views/codeeditor/CodeEditorView.java b/src/main/java/com/primefactorsolutions/views/codeeditor/CodeEditorView.java
deleted file mode 100644
index 73eeac1..0000000
--- a/src/main/java/com/primefactorsolutions/views/codeeditor/CodeEditorView.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.primefactorsolutions.views.codeeditor;
-
-import com.primefactorsolutions.views.MainLayout;
-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.richtexteditor.RichTextEditor;
-import com.vaadin.flow.component.richtexteditor.RichTextEditorVariant;
-import com.vaadin.flow.component.select.Select;
-import com.vaadin.flow.router.PageTitle;
-import com.vaadin.flow.router.Route;
-import com.vaadin.flow.router.RouteAlias;
-import com.vaadin.flow.theme.lumo.LumoUtility.Accessibility;
-import com.vaadin.flow.theme.lumo.LumoUtility.Background;
-import com.vaadin.flow.theme.lumo.LumoUtility.Border;
-import com.vaadin.flow.theme.lumo.LumoUtility.BorderColor;
-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;
-
-@PageTitle("CodeEditor")
-@Route(value = "", layout = MainLayout.class)
-@RouteAlias(value = "", layout = MainLayout.class)
-public class CodeEditorView extends Main {
-
- public CodeEditorView() {
- addClassNames(Display.FLEX, Flex.GROW, Height.FULL);
-
- // Editor
- RichTextEditor editor = new RichTextEditor();
- editor.addClassNames(Border.RIGHT, BorderColor.CONTRAST_10, Flex.GROW);
- editor.addThemeVariants(RichTextEditorVariant.LUMO_NO_BORDER);
- editor.asDelta().setValue(
- "[{\"insert\":\"High quality rich text editor for the web\"},{\"attributes\":{\"header\":2},\"insert\":\"\\n\"},{\"insert\":\" is a Web Component providing rich text editor functionality, part of the \"},{\"attributes\":{\"link\":\"https://vaadin.com/components\"},\"insert\":\"Vaadin components\"},{\"insert\":\".\\nIt handles the following formatting:\\n\"},{\"attributes\":{\"bold\":true},\"insert\":\"Bold\"},{\"attributes\":{\"list\":\"bullet\"},\"insert\":\"\\n\"},{\"attributes\":{\"italic\":true},\"insert\":\"Italic\"},{\"attributes\":{\"list\":\"bullet\"},\"insert\":\"\\n\"},{\"attributes\":{\"underline\":true},\"insert\":\"Underline\"},{\"attributes\":{\"list\":\"bullet\"},\"insert\":\"\\n\"},{\"attributes\":{\"strike\":true},\"insert\":\"Strike-through\"},{\"attributes\":{\"list\":\"bullet\"},\"insert\":\"\\n\"},{\"insert\":\"Headings (H1, H2, H3)\"},{\"attributes\":{\"list\":\"bullet\"},\"insert\":\"\\n\"},{\"insert\":\"Lists (ordered and unordered)\"},{\"attributes\":{\"list\":\"bullet\"},\"insert\":\"\\n\"},{\"insert\":\"Text align (left, center, right)\"},{\"attributes\":{\"list\":\"bullet\"},\"insert\":\"\\n\"},{\"attributes\":{\"script\":\"sub\"},\"insert\":\"Sub\"},{\"insert\":\"script and \"},{\"attributes\":{\"script\":\"super\"},\"insert\":\"super\"},{\"insert\":\"script\"},{\"attributes\":{\"list\":\"bullet\"},\"insert\":\"\\n\"},{\"attributes\":{\"link\":\"https://vaadin.com\"},\"insert\":\"Hyperlink\"},{\"attributes\":{\"list\":\"bullet\"},\"insert\":\"\\n\"},{\"insert\":\"In addition to text formatting, additional content blocks can be added.\\nImages\"},{\"attributes\":{\"header\":3},\"insert\":\"\\n\"},{\"insert\":{\"image\":\"data:image/png;base64,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\"}},{\"insert\":\"\\nBlockquotes\"},{\"attributes\":{\"header\":3},\"insert\":\"\\n\"},{\"insert\":\"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\"},{\"attributes\":{\"blockquote\":true},\"insert\":\"\\n\"},{\"insert\":\"Code blocks\"},{\"attributes\":{\"header\":3},\"insert\":\"\\n\"},{\"insert\":\"\"},{\"attributes\":{\"code-block\":true},\"insert\":\"\\n\"},{\"insert\":\" \"},{\"attributes\":{\"code-block\":true},\"insert\":\"\\n\"},{\"insert\":\"\"},{\"attributes\":{\"code-block\":true},\"insert\":\"\\n\"},{\"insert\":\"\\n\"}]");
-
- // Sidebar
- add(editor, createSidebar());
- }
-
- private Section createSidebar() {
- Section sidebar = new Section();
- sidebar.addClassNames(Background.CONTRAST_5, BoxSizing.BORDER, Display.FLEX, FlexDirection.COLUMN,
- Flex.SHRINK_NONE, Overflow.AUTO, Padding.LARGE);
- sidebar.setWidth("256px");
-
- H2 title = new H2("Project details");
- title.addClassName(Accessibility.SCREEN_READER_ONLY);
-
- DescriptionList dl = new DescriptionList();
- dl.addClassNames(Display.FLEX, FlexDirection.COLUMN, Gap.LARGE, Margin.Bottom.SMALL, Margin.Top.NONE,
- FontSize.SMALL);
-
- dl.add(createItem("Owner", "My Name"), createItem("Created", "2021-08-14 14:48"),
- createItem("Last modified", "2021-08-14 14:50"), createBadgeItem("Status", "Draft"));
-
- Select select = new Select();
- select.setLabel("Project");
- select.setItems("My Project", "Your Project", "Their Project");
- select.setValue("My Project");
-
- // Add it all together
- sidebar.add(title, dl, select);
- return sidebar;
- }
-
- 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;
- }
-}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index ed37e71..1f266a2 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -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
diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql
new file mode 100644
index 0000000..41ddc80
--- /dev/null
+++ b/src/main/resources/data.sql
@@ -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');
\ No newline at end of file
diff --git a/src/main/resources/questions/fibonaci.java.txt b/src/main/resources/questions/fibonaci.java.txt
new file mode 100644
index 0000000..037876c
--- /dev/null
+++ b/src/main/resources/questions/fibonaci.java.txt
@@ -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 testFibonaciSingle() {
+ boolean result = getFibonaci(1) == 0;
+
+ return Map.entry("fibonaci primer", result);
+ }
+
+ public static Map.Entry 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 runCode() {
+ Map results = Arrays.stream(Tests.class.getDeclaredMethods())
+ .map(m -> {
+ try {
+ return (Map.Entry) 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;
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/questions/palindroma.java.txt b/src/main/resources/questions/palindroma.java.txt
new file mode 100644
index 0000000..b1462d1
--- /dev/null
+++ b/src/main/resources/questions/palindroma.java.txt
@@ -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 testPalindromaValido() {
+ boolean result = getPalindroma("Ab").equals("ABBA");
+
+ return Map.entry("palindrome es valido", result);
+ }
+
+ public static Map.Entry testPalindromaVacio() {
+ boolean result = getPalindroma("").equals("");
+
+ return Map.entry("palindrome vacio es valido", result);
+ }
+
+ public static Map.Entry testPalindromaUnico() {
+ boolean result = getPalindroma("z").equals("ZZ");
+
+ return Map.entry("palindrome una solo caracter es valido", result);
+ }
+
+ public static Map.Entry testPalindromaNull() {
+ boolean result = getPalindroma(null) == null;
+
+ return Map.entry("palindrome null es valido", result);
+ }
+ }
+
+ @Override
+ public Map runCode() {
+ Map results = Arrays.stream(Tests.class.getDeclaredMethods())
+ .map(m -> {
+ try {
+ return (Map.Entry) 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;
+ }
+}
\ No newline at end of file