diff --git a/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/Assignment.java b/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/Assignment.java index a06a7d0..fa1726e 100644 --- a/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/Assignment.java +++ b/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/Assignment.java @@ -33,6 +33,12 @@ public class Assignment { @Column() private int maxPoints; + @Column() + private boolean quiz; + + @Column() + private int jointId; + /* HashMap is used to store JSON for daily "stats" "stats": { "2022-11-13": { @@ -45,10 +51,13 @@ public class Assignment { @Column(columnDefinition = "jsonb") private Map> submissions = new HashMap<>(); - public Assignment(String title, String desc, String link, int maxPoints) { + public Assignment(String title, String desc, String link, int maxPoints, int jointId) { this.title = title; this.desc = desc; this.link = link; this.maxPoints = maxPoints; + this.quiz = false; + this.jointId = jointId; + //this.id = id; } } diff --git a/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/AssignmentApiController.java b/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/AssignmentApiController.java index 1ab0539..4795b0c 100644 --- a/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/AssignmentApiController.java +++ b/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/AssignmentApiController.java @@ -10,7 +10,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; - +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Optional; @@ -24,26 +24,32 @@ public class AssignmentApiController { // Autowired enables Control to connect URI request and POJO Object to easily for Database CRUD operations @Autowired - private AssignmentJpaRepository repository; + private AssignmentJpaRepository assignmentRepository; + + @Autowired + private QuizJpaRepository quizRepository; @Autowired private GradeJpaRepository gradeRepository; - /* GET List of Jokes - * @GetMapping annotation is used for mapping HTTP GET requests onto specific handler methods. - */ + int idCount = 1; //Needed to prevent id overlap with quizzes and assignments + @CrossOrigin(origins = "*", allowedHeaders = "*") @GetMapping("/") - public ResponseEntity> getJokes() { - // ResponseEntity returns List of Jokes provide by JPA findAll() - return new ResponseEntity<>( repository.findAll(), HttpStatus.OK); + public ResponseEntity> getRepositories() { + List combined = new ArrayList<>(); + combined.addAll(assignmentRepository.findAll()); + combined.addAll(quizRepository.findAll()); + return new ResponseEntity<>(combined, HttpStatus.OK); } + + @CrossOrigin(origins = "*", allowedHeaders = "*") @GetMapping("/{id}") public ResponseEntity getById(@PathVariable Long id) { - Optional assignmentOptional = repository.findById(id); + Optional assignmentOptional = assignmentRepository.findById(id); return assignmentOptional.map(assignment -> new ResponseEntity<>(assignment, HttpStatus.OK)) .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); @@ -69,13 +75,71 @@ public ResponseEntity postPerson( return new ResponseEntity<>("maxPoints must be positive", HttpStatus.BAD_REQUEST); } - repository.save(new Assignment(title, desc, link, maxPoints)); + assignmentRepository.save(new Assignment(title, desc, link, maxPoints, idCount)); + idCount++; + return new ResponseEntity<>("Created successfully", HttpStatus.CREATED); + } + + @CrossOrigin(origins = "*", allowedHeaders = "*") + @PostMapping("/postQuiz") + public ResponseEntity postQuiz( + @RequestParam("title") String title, + @RequestParam("desc") String desc, + @RequestParam("maxPoints") int maxPoints, + @RequestBody final Map questions) { + if(title.length() < 3 || title.length() > 100) { + return new ResponseEntity<>("Title is less than 3 or longer than 100 characters", HttpStatus.BAD_REQUEST); + } + if(desc.length() < 3 || desc.length() > 50000) { + return new ResponseEntity<>("Desc is less than 3 or longer than 500000 characters", HttpStatus.BAD_REQUEST); + } + if(maxPoints <= 0) { + return new ResponseEntity<>("maxPoints must be positive", HttpStatus.BAD_REQUEST); + } + + //Check Questions + // Extract Attributes from JSON + + Map attributeMap = new HashMap<>(); + int questionNum = 1; + for (Map.Entry questionTemp : questions.entrySet()) { + if (!(questionTemp.getValue() instanceof HashMap)) { + return new ResponseEntity<>("Questions should contain hashmaps", HttpStatus.BAD_REQUEST); + } + Map question = ((Map)questionTemp.getValue()); + + Map subAttributeMap = new HashMap<>(); + + + for (Map.Entry attribute : question.entrySet()) { + if(attribute.getKey().equals("answers")) + if (attribute.getValue() instanceof List) { + subAttributeMap.put(attribute.getKey(), attribute.getValue()); + } else { + return new ResponseEntity<>("Answers attribute should be a list", HttpStatus.BAD_REQUEST); + } + + if(attribute.getKey().equals("correctAnswer")) + subAttributeMap.put(attribute.getKey(), attribute.getValue()); + + if(attribute.getKey().equals("desc")) + subAttributeMap.put(attribute.getKey(), attribute.getValue()); + + } + //Does it have all attributes? + if(!(subAttributeMap.containsKey("answers") && subAttributeMap.containsKey("correctAnswer") && subAttributeMap.containsKey("desc"))) { + return new ResponseEntity<>("Missing attributes for question #" + questionNum + "answers, correctAnswer, and desc are required: " + subAttributeMap, HttpStatus.BAD_REQUEST); + } + + //Add question data to main attribute map + attributeMap.put(Integer.toString(questionNum), subAttributeMap); + questionNum++; + } + + quizRepository.save(new Quiz(title, desc, maxPoints, idCount, attributeMap)); + idCount++; return new ResponseEntity<>("Created successfully", HttpStatus.CREATED); } - /* Update Like - * @PutMapping annotation is used for mapping HTTP PUT requests onto specific handler methods. - * @PathVariable annotation extracts the templated part {id}, from the URI - */ @PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity personStats(@RequestBody final Map request_map) { @@ -83,8 +147,11 @@ public ResponseEntity personStats(@RequestBody final Map if (!(request_map.get("id") instanceof String)){ return new ResponseEntity<>("id should be a String", HttpStatus.BAD_REQUEST); } - long id=Long.parseLong((String)request_map.get("id")); - Optional optional = repository.findById((id)); + int id=Integer.parseInt((String)request_map.get("id")); + + List assignments = assignmentRepository.findByJointId(id); + Optional optional = assignments.isEmpty() ? Optional.empty() : Optional.of(assignments.get(0)); + if (optional.isPresent()) { // Good ID Assignment assignment = optional.get(); // value from findByID @@ -93,35 +160,25 @@ public ResponseEntity personStats(@RequestBody final Map for (Map.Entry submission : request_map.entrySet()) { // Add needed attributes to attributeMap - if(submission.getKey().equals("contributors")) + if(submission.getKey().equals("answers")) if (submission.getValue() instanceof List) { attributeMap.put(submission.getKey(), submission.getValue()); } else { return new ResponseEntity<>("Contributors attribute should be a list", HttpStatus.BAD_REQUEST); } - - if(submission.getKey().equals("title")) - attributeMap.put(submission.getKey(), submission.getValue()); - - if(submission.getKey().equals("desc")) - attributeMap.put(submission.getKey(), submission.getValue()); - - if(submission.getKey().equals("link")) - attributeMap.put(submission.getKey(), submission.getValue()); - } //Does it have all attributes? - if(!(attributeMap.containsKey("contributors") && attributeMap.containsKey("title") && attributeMap.containsKey("desc") && attributeMap.containsKey("link"))) { - return new ResponseEntity<>("Missing attributes. username, contributors, title, desc, and link are required" + attributeMap, HttpStatus.BAD_REQUEST); + if(!(attributeMap.containsKey("answers") && attributeMap.containsKey("username"))) { + return new ResponseEntity<>("Missing attributes. username and answers are required" + attributeMap, HttpStatus.BAD_REQUEST); } // Set Date and Attributes to SQL HashMap Map> date_map = assignment.getSubmissions(); - date_map.put( (String) request_map.get("username"), attributeMap ); + date_map.put( (String) request_map.get("username"), attributeMap); assignment.setSubmissions(date_map); - repository.save(assignment); + assignmentRepository.save(assignment); //Save grade as well for each contributor List contributors = (List) attributeMap.get("contributors"); @@ -135,6 +192,65 @@ public ResponseEntity personStats(@RequestBody final Map // return Person with update Stats return new ResponseEntity<>(assignment, HttpStatus.OK); } + + //Check for quiz + + List quizzes = quizRepository.findByJointId(id); + Optional optional2 = quizzes.isEmpty() ? Optional.empty() : Optional.of(quizzes.get(0)); + + if (optional2.isPresent()) { // Good ID + Quiz quiz = optional2.get(); // value from findByID + + // Extract Attributes from JSON + Map attributeMap = new HashMap<>(); + for (Map.Entry submission : request_map.entrySet()) { + // Add needed attributes to attributeMap + + if(submission.getKey().equals("answers")) + if (submission.getValue() instanceof List) { + attributeMap.put(submission.getKey(), submission.getValue()); + } else { + return new ResponseEntity<>("Contributors attribute should be a list", HttpStatus.BAD_REQUEST); + } + + if(submission.getKey().equals("username")) + attributeMap.put(submission.getKey(), submission.getValue()); + } + + //Does it have all attributes? + if(!(attributeMap.containsKey("answers") && attributeMap.containsKey("username"))) { + return new ResponseEntity<>("Missing attributes. username and answers are required" + attributeMap, HttpStatus.BAD_REQUEST); + } + + // Set Date and Attributes to SQL HashMap + Map> date_map = quiz.getSubmissions(); + date_map.put( (String) request_map.get("username"), attributeMap ); + + quiz.setSubmissions(date_map); + quizRepository.save(quiz); + + //Grade it + /*ArrayList submittedAnswers = (ArrayList)attributeMap.get("answers"); + Map questions = quizRepository.getById(Long.parseLong(Integer.toString(id))).getQuestions(); + + int score = 0; + int i = 0; + for(Map.Entry question : questions.entrySet()) { + if(((Map)question.getValue()).get("correctAnswer") == submittedAnswers.get(i)) { + score++; + } else { + + } + i++; + } + + Grade grade = new Grade((String)attributeMap.get("username"), "temp", quiz.getTitle(), quiz.getMaxPoints(), score); + gradeRepository.save(grade);*/ + + // return Person with update Stats + return new ResponseEntity<>(quiz, HttpStatus.OK); + } + // return Bad ID return new ResponseEntity<>("Bad ID", HttpStatus.BAD_REQUEST); } diff --git a/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/AssignmentJpaRepository.java b/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/AssignmentJpaRepository.java index 942f342..fee0fe1 100644 --- a/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/AssignmentJpaRepository.java +++ b/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/AssignmentJpaRepository.java @@ -1,5 +1,7 @@ package com.nighthawk.spring_portfolio.mvc.assignment; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; // JPA is an object-relational mapping (ORM) to persistent data, originally relational databases (SQL). Today JPA implementations has been extended for NoSQL. @@ -8,4 +10,5 @@ public interface AssignmentJpaRepository extends JpaRepository The below custom methods are prototyped for this application */ + List findByJointId(int jointId); } diff --git a/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/Quiz.java b/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/Quiz.java new file mode 100644 index 0000000..63b9891 --- /dev/null +++ b/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/Quiz.java @@ -0,0 +1,63 @@ +package com.nighthawk.spring_portfolio.mvc.assignment; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.HashMap; +import java.util.Map; + +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +import jakarta.persistence.*; + +@Data // Annotations to simplify writing code (ie constructors, setters) +@NoArgsConstructor +@AllArgsConstructor +@Entity // Annotation to simplify creating an entity, which is a lightweight persistence domain object. Typically, an entity represents a table in a relational database, and each entity instance corresponds to a row in that table. +public class Quiz { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column() + private String title; + + @Column() + private String desc; + + @Column() + private int maxPoints; + + @Column() + private boolean quiz; + + @Column() + private int jointId; + + /* HashMap is used to store JSON for daily "stats" + "stats": { + "2022-11-13": { + "calories": 2200 + "steps": 8000 + } + } + */ + @JdbcTypeCode(SqlTypes.JSON) + @Column(columnDefinition = "jsonb") + private Map> submissions = new HashMap<>(); + + @JdbcTypeCode(SqlTypes.JSON) + @Column(columnDefinition = "jsonb") + private Map questions = new HashMap<>(); + + public Quiz(String title, String desc, int maxPoints, int jointId, Map questions) { + this.title = title; + this.desc = desc; + this.maxPoints = maxPoints; + this.quiz = true; + this.jointId = jointId; + this.questions = questions; + } +} diff --git a/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/QuizJpaRepository.java b/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/QuizJpaRepository.java new file mode 100644 index 0000000..350b39b --- /dev/null +++ b/src/main/java/com/nighthawk/spring_portfolio/mvc/assignment/QuizJpaRepository.java @@ -0,0 +1,19 @@ +package com.nighthawk.spring_portfolio.mvc.assignment; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import com.nighthawk.spring_portfolio.mvc.grade.Grade; +import com.nighthawk.spring_portfolio.mvc.jokes.Jokes; + +// JPA is an object-relational mapping (ORM) to persistent data, originally relational databases (SQL). Today JPA implementations has been extended for NoSQL. +public interface QuizJpaRepository extends JpaRepository { + /* JPA has many built in methods: https://www.tutorialspoint.com/spring_boot_jpa/spring_boot_jpa_repository_methods.htm + The below custom methods are prototyped for this application + */ + //public List findByJointId(int jointId); + + List findByJointId(int jointId); +}