diff --git a/.gitignore b/.gitignore
index 657a4bf6..2670cfb6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,4 @@ cp.iml
CP.iml
bin/
.vscode/
-.DS_Store
\ No newline at end of file
+**/application-dev.properties
diff --git a/pom.xml b/pom.xml
index 1157fd26..a84daea5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,12 @@
spring-boot-starter-test
provided
+
+ com.mysql
+ mysql-connector-j
+ 8.2.0
+ runtime
+
diff --git a/src/main/java/com/coderscampus/cp/config/NoOpSecurityConfig.java b/src/main/java/com/coderscampus/cp/config/NoOpSecurityConfig.java
new file mode 100644
index 00000000..b4faf9c6
--- /dev/null
+++ b/src/main/java/com/coderscampus/cp/config/NoOpSecurityConfig.java
@@ -0,0 +1,14 @@
+package com.coderscampus.cp.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+//This file completely disables any cors security, for dev only!
+@Configuration
+public class NoOpSecurityConfig implements WebMvcConfigurer {
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**").allowedMethods("*");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/coderscampus/cp/domain/Checkin.java b/src/main/java/com/coderscampus/cp/domain/Checkin.java
index 959fb208..676162c5 100644
--- a/src/main/java/com/coderscampus/cp/domain/Checkin.java
+++ b/src/main/java/com/coderscampus/cp/domain/Checkin.java
@@ -2,6 +2,8 @@
import jakarta.persistence.*;
+import java.math.BigInteger;
+import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -22,6 +24,8 @@ public class Checkin {
private Instant endTime;
private CodingType codingType;
private Integer issueNumber;
+ private BigInteger timeInClassInSeconds;
+
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "student_id")
private Student student;
@@ -138,12 +142,28 @@ public void setStudent(Student student) {
this.student = student;
}
+ public Boolean getSetUp() {
+ return isSetUp;
+ }
+
+ public void setSetUp(Boolean setUp) {
+ isSetUp = setUp;
+ }
+
+ public BigInteger getTimeInClassInSeconds() {
+ return timeInClassInSeconds;
+ }
+
+ public void setTimeInClassInSeconds(BigInteger timeInClassInSeconds) {
+ this.timeInClassInSeconds = timeInClassInSeconds;
+ }
+
// ENUMS
public enum CodingType{
FOUNDATIONS, CRUD, CODE_REVIEW, DESIGN, DOCUMENTATION
}
public enum Role{
- FOUNDATIONS, OBSERVER, CODER, GUIDE, SCRUM_MASTER, PRODUCT_OWNER
+ FOUNDATIONS, OBSERVER, CODER, GUIDE, SCRUM_MASTER, PRODUCT_OWNER
}
@Override
@@ -152,7 +172,7 @@ public String toString() {
"id=" + id +
", uid='" + uid + '\'' +
", date=" + date +
- ", assignment=" + nextAssignment +
+ ", nextAssignment=" + nextAssignment +
", blockers=" + blockers +
", blockerDescription='" + blockerDescription + '\'' +
", isSetUp=" + isSetUp +
@@ -160,9 +180,18 @@ public String toString() {
", role=" + role +
", startTime=" + startTime +
", endTime=" + endTime +
- ", issueNumber=" + issueNumber +
", codingType=" + codingType +
- ", student=" + student +
+ ", issueNumber=" + issueNumber +
+ ", timeInClassInSeconds=" + timeInClassInSeconds +
'}';
}
+
+ public void calculateTimeInClass() {
+ if (startTime != null && endTime != null) {
+ long seconds = Duration.between(startTime, endTime).getSeconds();
+ setTimeInClassInSeconds(BigInteger.valueOf(seconds));
+ } else {
+ setTimeInClassInSeconds(BigInteger.ZERO);
+ }
+ }
}
diff --git a/src/main/java/com/coderscampus/cp/domain/CheckinListener.java b/src/main/java/com/coderscampus/cp/domain/CheckinListener.java
new file mode 100644
index 00000000..942ed6dd
--- /dev/null
+++ b/src/main/java/com/coderscampus/cp/domain/CheckinListener.java
@@ -0,0 +1,12 @@
+package com.coderscampus.cp.domain;
+
+import jakarta.persistence.PrePersist;
+import jakarta.persistence.PreUpdate;
+
+public class CheckinListener {
+ @PrePersist
+ @PreUpdate
+ public void beforeSave(Checkin checkin) {
+ checkin.calculateTimeInClass();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/coderscampus/cp/domain/Student.java b/src/main/java/com/coderscampus/cp/domain/Student.java
index f53ecb16..0a3e568b 100644
--- a/src/main/java/com/coderscampus/cp/domain/Student.java
+++ b/src/main/java/com/coderscampus/cp/domain/Student.java
@@ -3,13 +3,8 @@
import java.util.ArrayList;
import java.util.List;
-import jakarta.persistence.CascadeType;
-import jakarta.persistence.Entity;
-import jakarta.persistence.FetchType;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
-import jakarta.persistence.OneToMany;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.*;
@Entity
public class Student {
@@ -21,6 +16,9 @@ public class Student {
private String name;
private Integer assignmentNum;
private String ide;
+ @Lob
+ @Column(name = "student_photo", columnDefinition="MEDIUMBLOB")
+ private byte[] studentPhoto;
// @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
// private GitHub githubHandle;
// @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@@ -37,7 +35,8 @@ public class Student {
// private Networking networking;
// @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
// private Website website;
- @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+ @OneToMany(mappedBy = "student", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+ @JsonIgnore
private List checkin = new ArrayList();
@@ -89,7 +88,15 @@ public String getIde() {
public void setIde(String ide) {
this.ide = ide;
}
-// public GitHub getGithubHandle() {
+
+ public byte[] getStudentPhoto() {
+ return studentPhoto;
+ }
+
+ public void setStudentPhoto(byte[] studentPhoto) {
+ this.studentPhoto = studentPhoto;
+ }
+ // public GitHub getGithubHandle() {
// return githubHandle;
// }
//
@@ -158,6 +165,7 @@ public List getCheckin() {
return checkin;
}
+
public void setCheckin(List checkin) {
this.checkin = checkin;
}
diff --git a/src/main/java/com/coderscampus/cp/react/CheckinRestController.java b/src/main/java/com/coderscampus/cp/react/CheckinRestController.java
new file mode 100644
index 00000000..4023e9e7
--- /dev/null
+++ b/src/main/java/com/coderscampus/cp/react/CheckinRestController.java
@@ -0,0 +1,35 @@
+package com.coderscampus.cp.react;
+
+import com.coderscampus.cp.domain.Checkin;
+import com.coderscampus.cp.service.CheckinService;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@ComponentScan
+@RestController
+@RequestMapping("/api/check-in")
+
+public class CheckinRestController {
+ private final CheckinService checkinService;
+
+ public CheckinRestController(CheckinService checkinService) {
+ this.checkinService = checkinService;
+ }
+ @PostMapping("/start-check-in")
+ public ResponseEntity createAndStartStudentCheckin(
+ @RequestParam Long studentId) {
+ Checkin newCheckin = checkinService.createStudentCheckin(studentId);
+ return ResponseEntity.ok(newCheckin);
+ }
+ @PostMapping("/finish-check-in")
+ public ResponseEntity finishStudentCheckin(
+ @RequestParam Long studentId) {
+ Checkin finishedCheckin = checkinService.endStudentCheckin(studentId);
+ System.out.println("Controller: Finished Checkin with ID: " + finishedCheckin.getId());
+ return ResponseEntity.ok(finishedCheckin);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/coderscampus/cp/react/StudentRestController.java b/src/main/java/com/coderscampus/cp/react/StudentRestController.java
new file mode 100644
index 00000000..2945f41b
--- /dev/null
+++ b/src/main/java/com/coderscampus/cp/react/StudentRestController.java
@@ -0,0 +1,56 @@
+package com.coderscampus.cp.react;
+
+import com.coderscampus.cp.domain.Student;
+import com.coderscampus.cp.service.StudentService;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.List;
+
+@ComponentScan
+@RestController
+@RequestMapping("/api/student")
+public class StudentRestController {
+ private final ObjectMapper objectMapper;
+ private final StudentService studentService;
+
+ public StudentRestController(ObjectMapper objectMapper, StudentService studentService) {
+ this.objectMapper = objectMapper;
+ this.studentService = studentService;
+ }
+ @GetMapping("/get-one-student")
+ public ResponseEntity getOneStudentById(
+ @RequestParam Long studentId) {
+ Student requestedStudent = studentService.findById(studentId);
+ System.out.println("Returning get-one-student: " + requestedStudent);
+ return ResponseEntity.ok(requestedStudent);
+ }
+ @GetMapping("/get-featured-students")
+ public ResponseEntity> getFeaturedStudentList() {
+// List featuredStudentList = studentService.getFeaturedStudents();
+// return ResponseEntity.ok(featuredStudentList);
+ return null;
+ }
+ @PostMapping("/create-student")
+ public ResponseEntity createStudent(@RequestBody String rawJson) throws JsonProcessingException {
+ System.out.println("Received JSON: " + rawJson);
+ // Convert rawJson back to Student object manually for debugging purposes
+ Student createStudentRequest = objectMapper.readValue(rawJson, Student.class);
+ System.out.println("Create student request name: " + createStudentRequest.getName());
+ Student newStudent = studentService.save(createStudentRequest);
+ return ResponseEntity.ok(newStudent);
+ }
+ @PostMapping("/update-student-photo")
+ public ResponseEntity updatePhoto(
+ @RequestParam("studentId") Long studentId,
+ @RequestParam("studentImage") MultipartFile studentImage) throws IOException {
+ System.out.println("Create student request id: " + studentId);
+ Student updatedStudent = studentService.updateStudentPhoto(studentId, studentImage.getBytes());
+ return ResponseEntity.ok(updatedStudent);
+ }
+}
diff --git a/src/main/java/com/coderscampus/cp/repository/CheckinRepository.java b/src/main/java/com/coderscampus/cp/repository/CheckinRepository.java
index e3cd087a..4b4bf157 100644
--- a/src/main/java/com/coderscampus/cp/repository/CheckinRepository.java
+++ b/src/main/java/com/coderscampus/cp/repository/CheckinRepository.java
@@ -1,10 +1,15 @@
package com.coderscampus.cp.repository;
import com.coderscampus.cp.domain.Checkin;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
+import java.util.Optional;
+
@Repository
public interface CheckinRepository extends JpaRepository {
+ @Query("SELECT c FROM Checkin c WHERE c.student.id = :studentId AND c.endTime IS NULL")
+ Optional findByStudentIdAndCheckoutTimeIsNull(Long studentId);
}
diff --git a/src/main/java/com/coderscampus/cp/service/CheckinService.java b/src/main/java/com/coderscampus/cp/service/CheckinService.java
index aafc7a2c..e4f1a6f7 100644
--- a/src/main/java/com/coderscampus/cp/service/CheckinService.java
+++ b/src/main/java/com/coderscampus/cp/service/CheckinService.java
@@ -4,10 +4,13 @@
import com.coderscampus.cp.domain.Student;
import com.coderscampus.cp.repository.CheckinRepository;
import com.coderscampus.cp.repository.StudentRepository;
-import org.springframework.beans.factory.annotation.Autowired;
+import jakarta.persistence.EntityNotFoundException;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
-import java.time.LocalDateTime;
+import java.math.BigInteger;
+import java.time.*;
+import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@@ -15,12 +18,15 @@
@Service
public class CheckinService {
- @Autowired
private CheckinRepository checkinRepo;
-
- @Autowired
private StudentRepository studentRepo;
+ public CheckinService(CheckinRepository checkinRepo, StudentRepository studentRepo) {
+ this.checkinRepo = checkinRepo;
+ this.studentRepo = studentRepo;
+ }
+
+
public Checkin save(Checkin checkin) {
if (checkin.getDate() == null) {
checkin.setDate(LocalDateTime.now());
@@ -52,4 +58,129 @@ public void delete(Checkin checkin) {
checkinRepo.delete(checkin);
}
+ @Transactional
+ public Checkin createStudentCheckin(Long studentId) {
+ Instant now = Instant.now();
+// Prevents accidental opening of multiple check-ins
+ if(checkinRepo.findByStudentIdAndCheckoutTimeIsNull(studentId).isPresent()){
+ System.out.println("Unclosed Checkin Found for Student with ID: " + studentId);
+ endStudentCheckin(studentId);
+ }
+
+ System.out.println("Creating Checkin for Student with ID: " + studentId);
+ Checkin newCheckin = new Checkin();
+ Student existingStudent = studentRepo.findById(studentId)
+ .orElseThrow(() -> new EntityNotFoundException("Student not found with id: " + studentId));
+ newCheckin.setStudent(existingStudent);
+ newCheckin.setStartTime(now);
+ checkinRepo.save(setCheckinTypeByDayHour(now, newCheckin));
+ existingStudent.getCheckin().add(newCheckin);
+ studentRepo.save(existingStudent);
+ return newCheckin;
+ }
+ @Transactional
+ public Checkin endStudentCheckin(Long studentId) {
+ Instant now = Instant.now();
+ System.out.println("Creating Checkout for Student with ID: " + studentId);
+ Student existingStudent = studentRepo.findById(studentId)
+ .orElseThrow(() -> new EntityNotFoundException(
+ "Student not found with id: " + studentId));
+ Checkin unclosedCheckin = checkinRepo.findByStudentIdAndCheckoutTimeIsNull(studentId)
+ .orElseThrow(() -> new EntityNotFoundException(
+ "Unclosed Check-in not found for student with id: " + studentId));
+ unclosedCheckin.setEndTime(now);
+ checkinRepo.save(unclosedCheckin);
+ BigInteger oneHourInSeconds = BigInteger.valueOf(3600);
+ //Sets max checkin time to 1 hour:
+ if(unclosedCheckin.getTimeInClassInSeconds().compareTo(oneHourInSeconds) > 0){
+ unclosedCheckin.setTimeInClassInSeconds(oneHourInSeconds);
+ }
+ existingStudent.getCheckin().add(unclosedCheckin);
+ System.out.println("Service: Saving finished checkin with ID: " + unclosedCheckin.getId());
+ return unclosedCheckin;
+ }
+
+ public Checkin setCheckinTypeByDayHour(Instant now, Checkin newCheckin){
+ Instant nowMinus10Minutes = now.minus(10, ChronoUnit.MINUTES);
+ //The time shift means students who checkin a bit early get credit for the checkin type,
+ //while only students who arrive in the last 10 minutes of class do not get a checkin type
+ ZonedDateTime zdt = nowMinus10Minutes.atZone(ZoneId.of("America/Chicago"));
+ DayOfWeek dayOfWeek = zdt.getDayOfWeek();
+ Integer hour = zdt.getHour();
+ switch (dayOfWeek) {
+ case SUNDAY:
+ if (hour == 12) {
+ newCheckin.setCodingType(Checkin.CodingType.FOUNDATIONS);
+ } else {
+ System.out.println("No Coding Type Set");
+ }
+ break;
+ case MONDAY:
+ if (hour == 8) {
+ newCheckin.setCodingType(Checkin.CodingType.DESIGN);
+ }
+ if (hour == 9){
+ newCheckin.setCodingType(Checkin.CodingType.FOUNDATIONS);
+ }
+ if (hour == 18){
+ newCheckin.setCodingType(Checkin.CodingType.FOUNDATIONS);
+ }
+ if (hour == 19){
+ newCheckin.setCodingType(Checkin.CodingType.CRUD);
+ } else {
+ System.out.println("No Coding Type Set");
+ }
+ break;
+ case TUESDAY:
+ if (hour == 12){
+ newCheckin.setCodingType(Checkin.CodingType.CRUD);
+ } else {
+ System.out.println("No Coding Type Set");
+ }
+ break;
+ case WEDNESDAY:
+ if (hour == 8) {
+ newCheckin.setCodingType(Checkin.CodingType.DESIGN);
+ }
+ if (hour == 18){
+ newCheckin.setCodingType(Checkin.CodingType.FOUNDATIONS);
+ }
+ if (hour == 19){
+ newCheckin.setCodingType(Checkin.CodingType.CRUD);
+ } else {
+ System.out.println("No Coding Type Set");
+ }
+ break;
+ case THURSDAY:
+ if (hour == 12) {
+ newCheckin.setCodingType(Checkin.CodingType.CRUD);
+ }
+ if (hour == 19){
+ newCheckin.setCodingType(Checkin.CodingType.CODE_REVIEW);
+ } else {
+ System.out.println("No Coding Type Set");
+ }
+ break;
+ case FRIDAY:
+ if (hour == 8) {
+ newCheckin.setCodingType(Checkin.CodingType.DESIGN);
+ }
+ if (hour == 9){
+ newCheckin.setCodingType(Checkin.CodingType.FOUNDATIONS);
+ }
+ if (hour == 18){
+ newCheckin.setCodingType(Checkin.CodingType.FOUNDATIONS);
+ } else {
+ System.out.println("No Coding Type Set");
+ }
+ break;
+ case SATURDAY:
+ System.out.println("Saturday, no class!");
+ break;
+ default:
+ System.out.println("Not a day.");
+ break;
+ }
+ return newCheckin;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/coderscampus/cp/service/StudentService.java b/src/main/java/com/coderscampus/cp/service/StudentService.java
index 5ea4919f..cb007e9f 100644
--- a/src/main/java/com/coderscampus/cp/service/StudentService.java
+++ b/src/main/java/com/coderscampus/cp/service/StudentService.java
@@ -2,7 +2,7 @@
import com.coderscampus.cp.domain.Student;
import com.coderscampus.cp.repository.StudentRepository;
-import org.springframework.beans.factory.annotation.Autowired;
+import jakarta.persistence.EntityNotFoundException;
import org.springframework.stereotype.Service;
import javax.management.RuntimeErrorException;
@@ -12,17 +12,16 @@
@Service
public class StudentService {
- @Autowired
- private StudentRepository studentRepo;
-
+ private final StudentRepository studentRepo;
+ public StudentService(StudentRepository studentRepo) {
+ this.studentRepo = studentRepo;
+ }
public Student save(Student student) {
if (isValidNewStudent(student)) {
-
return studentRepo.save(student);
}
if (isValidStudentUpdateOrDelete(student)) {
-
return studentRepo.save(student);
}
return null;
@@ -38,7 +37,6 @@ boolean isValidStudentUpdateOrDelete(Student student) {
if (existingStudent.isPresent() && existingStudent.get().getUid() != null
&& existingStudent.get().getUid().equals(student.getUid())) {
-
return true;
}
return false;
@@ -47,14 +45,11 @@ boolean isValidStudentUpdateOrDelete(Student student) {
boolean isValidNewStudent(Student student) {
List students = studentRepo.findByUid(student.getUid());
if (students.size() > 0) {
-
return false;
}
return student.getId() == 0;
}
-
public List findAll() {
-
return studentRepo.findAll();
}
@@ -82,8 +77,14 @@ public boolean delete(Student student) {
System.err.println(e);
return false;
}
-
return true;
}
-
+ public Student updateStudentPhoto(Long studentId, byte[] photoData) {
+ Student existingStudent = studentRepo.findById(studentId)
+ .orElseThrow(() -> new EntityNotFoundException(
+ "Check-in not found for student with id: " + studentId));
+ existingStudent.setStudentPhoto(photoData);
+ studentRepo.save(existingStudent);
+ return existingStudent;
+ }
}
diff --git a/src/main/java/com/coderscampus/cp/web/CheckinController.java b/src/main/java/com/coderscampus/cp/web/CheckinController.java
index 00e7f1d6..90d9845e 100644
--- a/src/main/java/com/coderscampus/cp/web/CheckinController.java
+++ b/src/main/java/com/coderscampus/cp/web/CheckinController.java
@@ -25,7 +25,7 @@ public String home(ModelMap model) {
model.put("isCheckin", true);
return "checkin/read";
}
-
+
@GetMapping("/create")
public String getCreate (ModelMap model) {
Checkin checkin = new Checkin();
diff --git a/src/main/java/com/coderscampus/cp/web/SpringProjectController.java b/src/main/java/com/coderscampus/cp/web/SpringProjectController.java
index 409283c7..01894713 100644
--- a/src/main/java/com/coderscampus/cp/web/SpringProjectController.java
+++ b/src/main/java/com/coderscampus/cp/web/SpringProjectController.java
@@ -2,9 +2,11 @@
import java.util.List;
+import com.coderscampus.cp.domain.Checkin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
+import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import com.coderscampus.cp.domain.SpringProject;
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
new file mode 100644
index 00000000..649da07b
--- /dev/null
+++ b/src/main/resources/application-dev.properties
@@ -0,0 +1,9 @@
+spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
+spring.datasource.url=jdbc:mysql://localhost:3306/coder_packaging?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC
+server.address=0.0.0.0
+spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
+spring.datasource.username=xyz
+logging.level.org.hibernate.SQL=ERROR
+logging.level.org.hibernate.type.descriptor.sql=ERROR
+spring.jpa.hibernate.ddl-auto=update
+spring.datasource.password = FhwzV&Ynq0
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 785fc4d9..d491f56d 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,5 +1,5 @@
+spring.config.import=optional:application-dev.properties
spring.thymeleaf.cache = false
-
spring.datasource.url=jdbc:h2:file:./target/h2console/h2datacp
spring.datasource.driverClassName=org.h2.Driver
spring.jpa.hibernate.ddl-auto=create-drop