From e9853651d01005c73d09d9d672b44ec1a26f7744 Mon Sep 17 00:00:00 2001 From: Emily Johnson Date: Thu, 21 Aug 2025 14:21:49 -0700 Subject: [PATCH] Initial commit --- java-spring-api/build.gradle | 2 + .../java/com/elsevier/javaspringapi/App.java | 239 ++++++++++++++++++ 2 files changed, 241 insertions(+) diff --git a/java-spring-api/build.gradle b/java-spring-api/build.gradle index 6d8fc9f..2f513a1 100644 --- a/java-spring-api/build.gradle +++ b/java-spring-api/build.gradle @@ -6,6 +6,8 @@ apply plugin: 'java-library' dependencies { implementation 'org.springframework.boot:spring-boot-starter-web:2.7.2' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.hibernate.validator:hibernate-validator:6.1.5.Final' runtimeOnly 'org.springframework.boot:spring-boot-devtools:2.7.2' testImplementation 'org.springframework.boot:spring-boot-starter-test:2.7.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2' diff --git a/java-spring-api/src/main/java/com/elsevier/javaspringapi/App.java b/java-spring-api/src/main/java/com/elsevier/javaspringapi/App.java index e3b6e87..fe8c09e 100644 --- a/java-spring-api/src/main/java/com/elsevier/javaspringapi/App.java +++ b/java-spring-api/src/main/java/com/elsevier/javaspringapi/App.java @@ -1,7 +1,30 @@ package com.elsevier.javaspringapi; +import java.util.List; +import javax.persistence.Id; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.transaction.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.http.HttpStatus; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpServerErrorException; @SpringBootApplication public class App { @@ -9,5 +32,221 @@ public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } +} + +// Controller +@RestController +public class QuestionController { + + @Autowired + private final QuestionService questionService; + @Autowired + private final UserRepository userRepository; + @Autowired + private QuestionAnswerRepository questionAnswerRepository; + private Logger log = LoggerFactory.getLogger(QuestionController.class); + + public QuestionController(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @PostMapping("/submitQuestion") + public QuestionAnswer submitQuestion(@RequestBody QuestionAnswer req) { + log.info("Submitting question {}", req); + return questionService.submitQuestion(req); + } + + @PostMapping("/users/{userId}/exams/{examId}/scoreExam") + public double scoreExam(@PathVariable String userId, @PathVariable String examId) { + log.info("Score exam {} for user {}", examId, userId); + return questionService.scoreExam(examId); + } + @GetMapping("/getinstitutions/answers") + public List getInstituitionAnswers(@RequestParam List institutionIds) { + List questionAnswers; + for (Long institutionId : institutionIds) { + log.info("Get all answers for institution {}", institutionId); + List allByInstitutionId = userRepository.getAllByInstitutionId(institutionId); + + for (User user : allByInstitutionId) { + log.info("User: {}", user); + questionAnswers = questionAnswerRepository.getQuestionAnswerByUser(user.id); + } + } + return questionAnswers; + } + + @GetMapping("/users/{userId}/exams/{examId}/deleteExam") + public void deleteExam(@PathVariable Long userId, @PathVariable Long examId) { + int count = questionAnswerRepository.deleteByExamId(examId); + } } + +// Service +public interface QuestionService { + + QuestionAnswer submitQuestion(QuestionAnswer answer); + + double scoreExam(long examId); +} + +@Service +public class QuestionServiceImpl implements QuestionService { + + private static final Logger LOGGER = LoggerFactory.getLogger(QuestionService.class); + + @Autowired + private UserRepository userRepo; + @Autowired + private QuestionRepository questionRepo; + @Autowired + private QuestionAnswerRepository answerRepo; + @Autowired + private JdbcTemplate jdbcTemplate; + + public QuestionAnswer submitQuestion(QuestionAnswer answer) { + LOGGER.info("Submitting question for user {}", answer.user.id); + User user = userRepo.findById(answer.userId).orElse(null); + if (user == null) { + return null; + } + LOGGER.info("Submitting question {}", answer.question.id); + Question question = questionRepo.findById(answer.questionId).orElse(null); + if (question == null) { + LOGGER.warn("Submitted question is {}", question); + throw new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR); + } + + boolean isCorrect = question.getCorrectAnswer().equalsIgnoreCase(answer.submittedAnswer); + answer.isCorrect = isCorrect; + LOGGER.info("Submitted question is correct? {}", isCorrect); + + return answerRepo.save(answer); + } + + @Transactional + public double scoreExam(String examId) { + String sql = "SELECT * FROM question_answer WHERE examId = " + examId; + List answers = jdbcTemplate.query( + sql, + ids.toArray(), + (rs, rowNum) -> { + return rs.getBoolean("correct"); + } + ); + return new ScoreSummer(answers).getScore(); + } +} + +class ScoreSummer { + + public static List answers; + + public ScoreSummer(List answers) { + this.answers = answers; + } + + public double getScore() { + double score = 0; + double total = 0; + for (Boolean answer : answers) { + if (answer) { + score += 1; + } + total++; + } + return score / total; + } +} + +// Repositories +interface UserRepository extends JpaRepository { + + List getAllByInstitutionId(Long institutionId); + +} + +interface QuestionRepository extends JpaRepository { + +} + +interface QuestionAnswerRepository extends JpaRepository { + + int deleteByExamId(Long examId); +} + +// Entities +@Entity +class Institution { + + @Id + @GeneratedValue + public int id; + public String name; + public String address; + public String secretKey; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + public List users; + + @Override + public String toString() { + return "Institution{" + + "id=" + id + + ", name='" + name + '\'' + + ", address='" + address + '\'' + + ", secretKey='" + secretKey + '\''; + } +} + +@Entity +class User { + + @Id + @GeneratedValue + public int id; + public String username; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + public List institutions; + + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + public List questionAnswers; + + +} + +@Entity +class Question { + + @Id + @GeneratedValue + public int id; + public String content; + public String correctAnswer; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + public List questionAnswers; + +} + +@Entity +class QuestionAnswer { + + @Id + @GeneratedValue + public int id; + + public Long examId; + + @ManyToOne(fetch = FetchType.EAGER) + public User user; + + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + public Question question; + + public String submittedAnswer; + public boolean correct; + +} \ No newline at end of file