diff --git a/pom.xml b/pom.xml index ff89545..46b5f08 100644 --- a/pom.xml +++ b/pom.xml @@ -25,13 +25,16 @@ com.h2database h2 - runtime org.springframework.boot spring-boot-starter-test test + + org.projectlombok + lombok + diff --git a/src/main/java/com/opinionowl/opinionowl/OpinionOwlApplication.java b/src/main/java/com/opinionowl/opinionowl/OpinionOwlApplication.java index c89dc91..1053dfc 100644 --- a/src/main/java/com/opinionowl/opinionowl/OpinionOwlApplication.java +++ b/src/main/java/com/opinionowl/opinionowl/OpinionOwlApplication.java @@ -3,6 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +/** + * The starting point for the application. + */ @SpringBootApplication public class OpinionOwlApplication { diff --git a/src/main/java/com/opinionowl/opinionowl/models/Answer.java b/src/main/java/com/opinionowl/opinionowl/models/Answer.java new file mode 100644 index 0000000..7c4f7b7 --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/models/Answer.java @@ -0,0 +1,51 @@ +package com.opinionowl.opinionowl.models; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * The class in charge of holding the indivdual answers for each response. + */ +@Entity +@Getter +@Setter +@NoArgsConstructor +public class Answer { + // The id of the answer. + @Id + @GeneratedValue + private Long id; + + // The response which the answer belongs to. + @ManyToOne + private Response response; + + // The id of the question. + private Long question; + + // The contents of the response. + private String content; + + /** + * The constructor for answer. + * @param response the response for which the answe belongs to. + * @param question the question id. + * @param content the content of the reply. + */ + public Answer(Response response, Long question, String content){ + this.response = response; + this.question = question; + this.content = content; + } + + /** + * @return the answer in string form. + */ + @Override + public String toString(){ + return "Answer#" + id + " value:" + content; + } + +} diff --git a/src/main/java/com/opinionowl/opinionowl/models/LongAnswerQuestion.java b/src/main/java/com/opinionowl/opinionowl/models/LongAnswerQuestion.java new file mode 100644 index 0000000..95c049f --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/models/LongAnswerQuestion.java @@ -0,0 +1,42 @@ +package com.opinionowl.opinionowl.models; + +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.Setter; + +/** + * The class used to describe the long answer questions. + */ +@Entity +@Getter +@Setter +public class LongAnswerQuestion extends Question{ + // The character limit for the question. + private int charLimit; + + /** + * The default constructor for the class. + */ + public LongAnswerQuestion(){ + this("", 0); + } + + /** + * The constructor for the class. + * @param prompt the prompt for the question + * @param charLimit the character limit for the response. + */ + public LongAnswerQuestion(String prompt, int charLimit){ + super(prompt, QuestionType.LONG_ANSWER); + this.charLimit = charLimit; + } + + /** + * @return the question in string form. + */ + @Override + public String toString(){ + return super.toString() + " charLimit:" + charLimit; + } + +} diff --git a/src/main/java/com/opinionowl/opinionowl/models/Question.java b/src/main/java/com/opinionowl/opinionowl/models/Question.java new file mode 100644 index 0000000..436edc5 --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/models/Question.java @@ -0,0 +1,45 @@ +package com.opinionowl.opinionowl.models; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import lombok.NoArgsConstructor; + +/** + * The Question class which keeps track of the survey questions. + */ +@Entity +@Getter +@Setter +@NoArgsConstructor +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +public class Question { + // The id of the question. + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + // The prompt for the question. + private String prompt; + + // The type of the question. + private QuestionType type; + + /** + * The constructor for the question class. + * @param prompt the prompt for the question. + * @param type the type of the question + */ + public Question(String prompt, QuestionType type){ + this.prompt = prompt; + this.type = type; + } + + /** + * @return the question in string form. + */ + @Override + public String toString(){ + return "Question id:" + id + " prompt:" + prompt; + } +} diff --git a/src/main/java/com/opinionowl/opinionowl/models/QuestionType.java b/src/main/java/com/opinionowl/opinionowl/models/QuestionType.java new file mode 100644 index 0000000..b655f43 --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/models/QuestionType.java @@ -0,0 +1,10 @@ +package com.opinionowl.opinionowl.models; + +/** + * An Enum to keep track of the different question types. + */ +public enum QuestionType { + LONG_ANSWER, + RADIO_CHOICE, + RANGE +} diff --git a/src/main/java/com/opinionowl/opinionowl/models/RadioChoiceQuestion.java b/src/main/java/com/opinionowl/opinionowl/models/RadioChoiceQuestion.java new file mode 100644 index 0000000..f167c20 --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/models/RadioChoiceQuestion.java @@ -0,0 +1,49 @@ +package com.opinionowl.opinionowl.models; + +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.Setter; + +/** + * The class used to describe the multiple choice questions. + */ +@Entity +@Getter +@Setter +public class RadioChoiceQuestion extends Question { + // The choices for the question. + private String[] choices; + + /** + * The default constructor for the class. + */ + public RadioChoiceQuestion() { + this("", new String[0]); + } + + /** + * The constructor for the class. + * @param prompt the prompt for the question. + * @param choices the choices for the question. + */ + public RadioChoiceQuestion(String prompt, String[] choices){ + super(prompt, QuestionType.RADIO_CHOICE); + this.choices = choices; + } + + /** + * @return the question in string form. + */ + @Override + public String toString(){ + String res = super.toString() + " choices:["; + if (choices.length > 0) { + for (int i = 0; i < choices.length - 1; i++){ + res += choices[i] + ", "; + } + res += choices[choices.length - 1]; + } + res += "]"; + return res; + } +} diff --git a/src/main/java/com/opinionowl/opinionowl/models/RangeQuestion.java b/src/main/java/com/opinionowl/opinionowl/models/RangeQuestion.java new file mode 100644 index 0000000..6bcdc47 --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/models/RangeQuestion.java @@ -0,0 +1,61 @@ +package com.opinionowl.opinionowl.models; + +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.Setter; + +/** + * The class used to describe the range questions. + */ +@Entity +@Getter +@Setter +public class RangeQuestion extends Question { + // The lower value for the range. + int lower; + + // the upper value for the range. + int upper; + + // the increment the range. + int increment; + + /** + * The default constructor for the class. + */ + public RangeQuestion(){ + this("", 0, 0); + } + + /** + * The constructor for the class without increment. + * @param prompt the prompt for the question. + * @param lower the lower value for the range. + * @param upper the upper value for the range. + */ + public RangeQuestion(String prompt, int lower, int upper){ + this(prompt, lower, upper, 1); + } + + /** + * The constructor for the class with increment. + * @param prompt the prompt for the question. + * @param lower the lower value for the range. + * @param upper the upper value for the range. + * @param increment the increment value for the range. + */ + public RangeQuestion(String prompt, int lower, int upper, int increment){ + super(prompt, QuestionType.RANGE); + this.lower = lower; + this.upper = upper; + this.increment = increment; + } + + /** + * @return the question in string form. + */ + @Override + public String toString(){ + return super.toString() + " lower:" + lower + " upper:" + upper + " increment:" + increment; + } +} diff --git a/src/main/java/com/opinionowl/opinionowl/models/Response.java b/src/main/java/com/opinionowl/opinionowl/models/Response.java new file mode 100644 index 0000000..d5d92d1 --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/models/Response.java @@ -0,0 +1,56 @@ +package com.opinionowl.opinionowl.models; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * The Response class in charge holding the survey response. + */ +@Entity +@Getter +@Setter +@NoArgsConstructor +public class Response { + // The id of the response. + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + // The survey object used for the response. + @ManyToOne + private Survey survey; + + // The list of the questions for the response. + @OneToMany(mappedBy = "response", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) + private List answers; + + /** + * The constructor for the Response class. + */ + public Response(Survey survey) { + this.survey = survey; + this.answers = new ArrayList<>(); + } + + /** + * Adds an answer to the survey. + * @param question the question id. + * @param content the content of the reply. + */ + public void addAnswer(Long question, String content){ + this.answers.add(new Answer(this, question, content)); + } + + /** + * @return the Response class in string form. + */ + @Override + public String toString(){ + return "Response #" + id + " answers:" + answers; + } +} diff --git a/src/main/java/com/opinionowl/opinionowl/models/Survey.java b/src/main/java/com/opinionowl/opinionowl/models/Survey.java new file mode 100644 index 0000000..349f7d8 --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/models/Survey.java @@ -0,0 +1,127 @@ +package com.opinionowl.opinionowl.models; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * The survey class which contains all the information needed to create survey. + */ +@Entity +@Getter +@Setter +@NoArgsConstructor +public class Survey { + // Keeps track of the Id of the survey. + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + // Keeps track of the questions of the survey. + @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) + private List questions; + + // Keeps track of the survey responses. + @OneToMany(mappedBy = "survey", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) + private List responses; + + // Keeps track of whether the survey is open or closed. + private boolean closed; + + // The title for the survey. + private String title; + + /** + * The default constructor for the survey. + */ + public Survey(String title){ + this.title = title; + this.questions = new ArrayList<>(); + this.responses = new ArrayList<>(); + this.closed = false; + } + + /** + * Adds a question to the survey. + * + * @param question the question to add to the survey. + */ + public boolean addQuestion(Question question){ + if (!closed) { + this.questions.add(question); + return true; + } + return false; + } + + /** + * Removes a question from the survey. + * @param questionId the Id of the question. + * @return true if successfully removed the question, false otherwise. + */ + public boolean removeQuestion(Long questionId){ + if (!closed) { + return this.questions.removeIf(q -> q.getId() == questionId); + } + return false; + } + + /** + * Adds a response to the survey. + * @param response the response to add to the survey. + */ + public boolean addResponse(Response response){ + if (!closed){ + this.responses.add(response); + return true; + } + return false; + } + + /** + * Removes the response from the survey. + * @param responseId the id of the response to remove. + * @return true if successfully remove, false otherwise. + */ + public boolean removeResponse(Long responseId){ + return this.responses.removeIf(r -> r.getId() == responseId); + } + + /** + * Returns a list of response for a specific question. + * @param questionId the id of the question. + * @return a list of the responses for that question. + */ + public List getResponsesForQuestion(Long questionId){ + List result = new ArrayList<>(); + for (Response r: responses){ + for (Answer a: r.getAnswers()){ + if (a.getQuestion() == questionId){ + result.add(a.getContent()); + } + } + } + return result; + } + + /** + * @return the survey in string form. + */ + @Override + public String toString(){ + String res = "Survey#" + id + " Title:" + title + " Closed?" + closed; + res += "\n-----Questions-----"; + for (Question q: questions){ + res += "\n" + q.toString(); + } + res += "\n-----Response-----"; + for (Response r: responses){ + res += "\n" + r.toString(); + } + return res; + } +} diff --git a/src/main/java/com/opinionowl/opinionowl/repos/AnswerRepository.java b/src/main/java/com/opinionowl/opinionowl/repos/AnswerRepository.java new file mode 100644 index 0000000..9054318 --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/repos/AnswerRepository.java @@ -0,0 +1,14 @@ +package com.opinionowl.opinionowl.repos; + +import com.opinionowl.opinionowl.models.Answer; +import org.springframework.data.repository.CrudRepository; + +import java.util.List; + +/** + * The repository in charge of managing the CRUD operations for Answer Entity. + */ +public interface AnswerRepository extends CrudRepository { + // Essentially performs SELECT * FROM answer + List findAll(); +} diff --git a/src/main/java/com/opinionowl/opinionowl/repos/QuestionRepository.java b/src/main/java/com/opinionowl/opinionowl/repos/QuestionRepository.java new file mode 100644 index 0000000..5dce970 --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/repos/QuestionRepository.java @@ -0,0 +1,14 @@ +package com.opinionowl.opinionowl.repos; + +import com.opinionowl.opinionowl.models.Question; +import org.springframework.data.repository.CrudRepository; + +import java.util.List; + +/** + * The repository in charge of managing the CRUD operations for Question Entity. + */ +public interface QuestionRepository extends CrudRepository { + // Essentially performs SELECT * FROM question + List findAll(); +} diff --git a/src/main/java/com/opinionowl/opinionowl/repos/ResponseRepository.java b/src/main/java/com/opinionowl/opinionowl/repos/ResponseRepository.java new file mode 100644 index 0000000..7d623b7 --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/repos/ResponseRepository.java @@ -0,0 +1,14 @@ +package com.opinionowl.opinionowl.repos; + +import com.opinionowl.opinionowl.models.Response; +import org.springframework.data.repository.CrudRepository; + +import java.util.List; + +/** + * The repository in charge of managing the CRUD operations for Response Entity. + */ +public interface ResponseRepository extends CrudRepository { + // Essentially performs SELECT * FROM response + List findAll(); +} diff --git a/src/main/java/com/opinionowl/opinionowl/repos/SurveyRepository.java b/src/main/java/com/opinionowl/opinionowl/repos/SurveyRepository.java new file mode 100644 index 0000000..99c686f --- /dev/null +++ b/src/main/java/com/opinionowl/opinionowl/repos/SurveyRepository.java @@ -0,0 +1,14 @@ +package com.opinionowl.opinionowl.repos; + +import com.opinionowl.opinionowl.models.Survey; +import org.springframework.data.repository.CrudRepository; + +import java.util.List; + +/** + * The repository in charge of managing the CRUD operations for Survey Entity. + */ +public interface SurveyRepository extends CrudRepository { + // Essentially performs SELECT * FROM survey + List findAll(); +} diff --git a/src/test/java/com/opinionowl/opinionowl/SurveyTest.java b/src/test/java/com/opinionowl/opinionowl/SurveyTest.java new file mode 100644 index 0000000..ef77739 --- /dev/null +++ b/src/test/java/com/opinionowl/opinionowl/SurveyTest.java @@ -0,0 +1,59 @@ +package com.opinionowl.opinionowl; + +import com.opinionowl.opinionowl.models.*; +import com.opinionowl.opinionowl.repos.SurveyRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; + +@SpringBootTest +public class SurveyTest { + @Autowired + private SurveyRepository surveyRepository; + + @Test + public void testPersist() { + Survey survey = new Survey("TEST_SURVEY"); + LongAnswerQuestion q1 = new LongAnswerQuestion("test1", 2); + RadioChoiceQuestion q2 = new RadioChoiceQuestion("test2", new String[]{"a", "c", "d"}); + RangeQuestion q3 = new RangeQuestion("test3", 1, 10); + + survey.addQuestion(q1); + survey.addQuestion(q2); + survey.addQuestion(q3); + + surveyRepository.save(survey); + + Response r1 = new Response(survey); + + r1.addAnswer(q1.getId(), "hi"); + r1.addAnswer(q2.getId(), "b"); + + survey.addResponse(r1); + + surveyRepository.save(survey); + + survey.setClosed(true); + + Response r2 = new Response(survey); + + r2.addAnswer(q1.getId(), "yo"); + r2.addAnswer(q2.getId(), "a"); + + surveyRepository.save(survey); + + List results = surveyRepository.findAll(); + for (Survey r : results){ + System.out.println(r.toString()); + + for (Question q : r.getQuestions()){ + if (q.getType() == QuestionType.LONG_ANSWER){ + LongAnswerQuestion laq = (LongAnswerQuestion) q; + System.out.println(laq.getCharLimit()); + } + } + } + } +}