KevanSoon commited on
Commit
03549e5
·
1 Parent(s): fc9a733

Added working database connection and facedection api

Browse files
Files changed (41) hide show
  1. src/main/java/com/cs102/attendance/DatabaseTestRunner.java +89 -0
  2. src/main/java/com/cs102/attendance/config/RecognitionProperties.java +28 -0
  3. src/main/java/com/cs102/attendance/config/SecurityConfig.java +12 -8
  4. src/main/java/com/cs102/attendance/controller/AttendanceController.java +82 -0
  5. src/main/java/com/cs102/attendance/controller/HealthController.java +177 -42
  6. src/main/java/com/cs102/attendance/controller/SessionController.java +113 -0
  7. src/main/java/com/cs102/attendance/controller/StudentController.java +168 -0
  8. src/main/java/com/cs102/attendance/controller/TestController.java +66 -63
  9. src/main/java/com/cs102/attendance/dto/MainViewDto.java +55 -0
  10. src/main/java/com/cs102/attendance/dto/SessionDto.java +101 -0
  11. src/main/java/com/cs102/attendance/dto/StudentRosterDto.java +54 -0
  12. src/main/java/com/cs102/attendance/entity/AttendanceRecord.java +93 -0
  13. src/main/java/com/cs102/attendance/entity/Entity.java +34 -0
  14. src/main/java/com/cs102/attendance/entity/FaceData.java +57 -0
  15. src/main/java/com/cs102/attendance/entity/Session.java +80 -0
  16. src/main/java/com/cs102/attendance/entity/Student.java +108 -0
  17. src/main/java/com/cs102/attendance/entity/TestConnection.java +0 -53
  18. src/main/java/com/cs102/attendance/enums/Method.java +6 -0
  19. src/main/java/com/cs102/attendance/enums/Status.java +7 -0
  20. src/main/java/com/cs102/attendance/repository/AttendanceRepository.java +16 -0
  21. src/main/java/com/cs102/attendance/repository/FaceDataRepository.java +10 -0
  22. src/main/java/com/cs102/attendance/repository/SessionRepository.java +13 -0
  23. src/main/java/com/cs102/attendance/repository/StudentRepository.java +12 -0
  24. src/main/java/com/cs102/attendance/repository/TestConnectionRepository.java +0 -13
  25. src/main/java/com/cs102/attendance/service/AttendanceService.java +13 -0
  26. src/main/java/com/cs102/attendance/service/AutoMarker.java +89 -0
  27. src/main/java/com/cs102/attendance/service/ManualMarker.java +62 -0
  28. src/main/java/com/cs102/attendance/service/SessionService.java +90 -0
  29. src/main/java/com/cs102/attendance/service/StudentService.java +83 -0
  30. src/main/resources/application.yml +58 -7
  31. src/test/java/com/cs102/attendance/Cs102AttendanceProjectApplicationTests.java +41 -41
  32. src/test/java/com/cs102/attendance/DatabaseIntegrationTest.java +108 -0
  33. src/test/java/com/cs102/attendance/controller/TestControllerTest.java +66 -58
  34. video-object-detection/facedetection.html +73 -0
  35. video-object-detection/index.html +263 -68
  36. video-object-detection/professor.html +285 -0
  37. video-object-detection/src/facedetection.js +285 -0
  38. video-object-detection/{main.js → src/script.js} +0 -0
  39. video-object-detection/student.html +115 -0
  40. video-object-detection/styles2.css +465 -0
  41. video-object-detection/vite.config.js +13 -2
src/main/java/com/cs102/attendance/DatabaseTestRunner.java ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance;
2
+
3
+ import com.cs102.attendance.entity.*;
4
+ import com.cs102.attendance.service.*;
5
+ import com.cs102.attendance.repository.*;
6
+ import org.springframework.beans.factory.annotation.Autowired;
7
+ import org.springframework.boot.CommandLineRunner;
8
+ import org.springframework.stereotype.Component;
9
+
10
+ import java.time.LocalDate;
11
+ import java.time.LocalTime;
12
+ import java.util.Optional;
13
+
14
+ @Component
15
+ public class DatabaseTestRunner implements CommandLineRunner {
16
+
17
+ @Autowired private StudentService studentService;
18
+ @Autowired private SessionService sessionService;
19
+ @Autowired private StudentRepository studentRepository;
20
+ @Autowired private SessionRepository sessionRepository;
21
+
22
+ @Override
23
+ public void run(String... args) throws Exception {
24
+ System.out.println("\nTESTING DATABASE OPERATIONS...\n");
25
+
26
+ try {
27
+ // 1. Test Student Creation (or find existing)
28
+ System.out.println("1. Checking/Creating test student...");
29
+ Student student;
30
+ Optional<Student> existingStudent = studentRepository.findByCode("TEST001");
31
+
32
+ if (existingStudent.isPresent()) {
33
+ student = existingStudent.get();
34
+ System.out.println("Found existing test student: " + student.getName() + " (ID: " + student.getId() + ")");
35
+ } else {
36
+ student = studentService.enrol("TEST001", "John Doe", "CS102", "Group A", "[email protected]", "123-456-7890");
37
+ System.out.println("Created new test student: " + student.getName() + " (ID: " + student.getId() + ")");
38
+ }
39
+
40
+ // 2. Test Session Creation (or find existing)
41
+ System.out.println("\n2. Checking/Creating test session...");
42
+ Session session;
43
+ Optional<Session> existingSession = sessionRepository.findByNameAndDate("Test Lecture", LocalDate.now());
44
+
45
+ if (existingSession.isPresent()) {
46
+ session = existingSession.get();
47
+ System.out.println("Found existing test session: " + session.getName() + " (ID: " + session.getId() + ")");
48
+ } else {
49
+ session = sessionService.createSession("Test Lecture", LocalDate.now(), LocalTime.of(9, 0), LocalTime.of(11, 0));
50
+ System.out.println("Created new test session: " + session.getName() + " (ID: " + session.getId() + ")");
51
+ }
52
+
53
+ // 3. Test Face Data Upload (check if already exists)
54
+ System.out.println("\n3. Checking/Uploading face data...");
55
+ try {
56
+ FaceData faceData = studentService.uploadFaceImage(student.getId(), "http://example.com/face.jpg");
57
+ System.out.println("Uploaded face data (ID: " + faceData.getId() + ")");
58
+ } catch (Exception e) {
59
+ System.out.println("Face data upload skipped (likely already exists): " + e.getMessage());
60
+ }
61
+
62
+ // 4. Test database queries (always run these tests)
63
+ System.out.println("\n4. Testing database queries...");
64
+ var students = studentService.getAllStudents();
65
+ System.out.println("Found " + students.size() + " student(s) in database");
66
+
67
+ // 5. Test today's sessions query
68
+ System.out.println("\n5. Testing session queries...");
69
+ var sessions = sessionService.getTodaySessions();
70
+ System.out.println("Found " + sessions.size() + " session(s) for today");
71
+
72
+ // 6. Test connection integrity
73
+ System.out.println("\n6. Testing connection integrity...");
74
+ Optional<Student> retrievedStudent = studentService.getStudentById(student.getId());
75
+ if (retrievedStudent.isPresent()) {
76
+ System.out.println("Student retrieval test: PASSED");
77
+ } else {
78
+ System.out.println("Student retrieval test: FAILED");
79
+ }
80
+
81
+ System.out.println("\nALL DATABASE TESTS PASSED!");
82
+ System.out.println("Database is working correctly with persistent data.\n");
83
+
84
+ } catch (Exception e) {
85
+ System.err.println("DATABASE TEST FAILED: " + e.getMessage());
86
+ e.printStackTrace();
87
+ }
88
+ }
89
+ }
src/main/java/com/cs102/attendance/config/RecognitionProperties.java ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.config;
2
+
3
+ import org.springframework.boot.context.properties.ConfigurationProperties;
4
+ import org.springframework.stereotype.Component;
5
+
6
+ @Component
7
+ @ConfigurationProperties(prefix = "recognition")
8
+ public class RecognitionProperties {
9
+
10
+ private double confidence = 0.70;
11
+ private long cooldownMs = 10000;
12
+
13
+ public double getConfidence() {
14
+ return confidence;
15
+ }
16
+
17
+ public void setConfidence(double confidence) {
18
+ this.confidence = confidence;
19
+ }
20
+
21
+ public long getCooldownMs() {
22
+ return cooldownMs;
23
+ }
24
+
25
+ public void setCooldownMs(long cooldownMs) {
26
+ this.cooldownMs = cooldownMs;
27
+ }
28
+ }
src/main/java/com/cs102/attendance/config/SecurityConfig.java CHANGED
@@ -2,22 +2,26 @@ package com.cs102.attendance.config;
2
 
3
  import org.springframework.context.annotation.Bean;
4
  import org.springframework.context.annotation.Configuration;
5
- import org.springframework.security.config.Customizer;
6
  import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 
7
  import org.springframework.security.web.SecurityFilterChain;
8
 
9
  @Configuration
 
10
  public class SecurityConfig {
11
 
12
  @Bean
13
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
14
  http
15
- .csrf(csrf -> csrf.disable()) // Disable CSRF for simplicity
16
- .authorizeHttpRequests(auth -> auth
17
- .requestMatchers("/call-face-recognition").permitAll()
18
- .anyRequest().authenticated()
19
- )
20
- .httpBasic(Customizer.withDefaults()); // or formLogin/custom config
 
 
 
21
  return http.build();
22
  }
23
- }
 
2
 
3
  import org.springframework.context.annotation.Bean;
4
  import org.springframework.context.annotation.Configuration;
 
5
  import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6
+ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
7
  import org.springframework.security.web.SecurityFilterChain;
8
 
9
  @Configuration
10
+ @EnableWebSecurity
11
  public class SecurityConfig {
12
 
13
  @Bean
14
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
15
  http
16
+ .csrf(csrf -> csrf.disable()) // Disable CSRF for API testing
17
+ .cors(cors -> cors.disable()) // Disable CORS for testing
18
+ .authorizeHttpRequests(authz -> authz
19
+ .requestMatchers("/api/**").permitAll() // Allow all API requests
20
+ .requestMatchers("/actuator/**").permitAll() // Allow actuator endpoints
21
+ .requestMatchers("/error").permitAll() // Allow error page
22
+ .anyRequest().permitAll() // Allow all requests for testing
23
+ );
24
+
25
  return http.build();
26
  }
27
+ }
src/main/java/com/cs102/attendance/controller/AttendanceController.java ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.controller;
2
+
3
+ import com.cs102.attendance.entity.AttendanceRecord;
4
+ import com.cs102.attendance.enums.Status;
5
+ import com.cs102.attendance.service.AttendanceService;
6
+ import org.springframework.beans.factory.annotation.Autowired;
7
+ import org.springframework.beans.factory.annotation.Qualifier;
8
+ import org.springframework.http.ResponseEntity;
9
+ import org.springframework.web.bind.annotation.*;
10
+
11
+ import java.util.UUID;
12
+
13
+ @RestController
14
+ @RequestMapping("/api/attendance")
15
+ @CrossOrigin(origins = "*")
16
+ public class AttendanceController {
17
+
18
+ @Autowired
19
+ @Qualifier("manual")
20
+ private AttendanceService manualMarker;
21
+
22
+ @Autowired
23
+ @Qualifier("auto")
24
+ private AttendanceService autoMarker;
25
+
26
+ // Mark attendance manually
27
+ @PostMapping("/manual")
28
+ public ResponseEntity<AttendanceRecord> markManualAttendance(@RequestBody MarkAttendanceRequest request) {
29
+ try {
30
+ AttendanceRecord record = manualMarker.markAttendance(
31
+ request.getStudentId(),
32
+ request.getSessionId(),
33
+ request.getStatus()
34
+ );
35
+ return ResponseEntity.ok(record);
36
+ } catch (RuntimeException e) {
37
+ return ResponseEntity.badRequest().body(null);
38
+ }
39
+ }
40
+
41
+ // Mark attendance automatically (with confidence)
42
+ @PostMapping("/auto")
43
+ public ResponseEntity<AttendanceRecord> markAutoAttendance(@RequestBody MarkAutoAttendanceRequest request) {
44
+ try {
45
+ // For auto marking, we need to cast to AutoMarker to access the confidence method
46
+ com.cs102.attendance.service.AutoMarker autoMarkerImpl =
47
+ (com.cs102.attendance.service.AutoMarker) autoMarker;
48
+
49
+ // First get the student and session entities, then call the method with confidence
50
+ AttendanceRecord record = autoMarkerImpl.markAttendance(
51
+ request.getStudentId(),
52
+ request.getSessionId(),
53
+ request.getStatus()
54
+ );
55
+ return ResponseEntity.ok(record);
56
+ } catch (RuntimeException e) {
57
+ return ResponseEntity.badRequest().body(null);
58
+ }
59
+ }
60
+
61
+ // DTOs for request bodies
62
+ public static class MarkAttendanceRequest {
63
+ private UUID studentId;
64
+ private UUID sessionId;
65
+ private Status status;
66
+
67
+ // Getters and setters
68
+ public UUID getStudentId() { return studentId; }
69
+ public void setStudentId(UUID studentId) { this.studentId = studentId; }
70
+ public UUID getSessionId() { return sessionId; }
71
+ public void setSessionId(UUID sessionId) { this.sessionId = sessionId; }
72
+ public Status getStatus() { return status; }
73
+ public void setStatus(Status status) { this.status = status; }
74
+ }
75
+
76
+ public static class MarkAutoAttendanceRequest extends MarkAttendanceRequest {
77
+ private double confidence;
78
+
79
+ public double getConfidence() { return confidence; }
80
+ public void setConfidence(double confidence) { this.confidence = confidence; }
81
+ }
82
+ }
src/main/java/com/cs102/attendance/controller/HealthController.java CHANGED
@@ -1,49 +1,184 @@
1
- // package com.cs102.attendance.controller;
2
 
3
- // import org.springframework.beans.factory.annotation.Autowired;
4
- // import org.springframework.http.ResponseEntity;
5
- // import org.springframework.web.bind.annotation.GetMapping;
6
- // import org.springframework.web.bind.annotation.RequestMapping;
7
- // import org.springframework.web.bind.annotation.RestController;
 
 
8
 
9
- // import javax.sql.DataSource;
10
- // import java.sql.Connection;
11
- // import java.sql.SQLException;
12
- // import java.util.HashMap;
13
- // import java.util.Map;
 
 
 
 
14
 
15
- // @RestController
16
- // @RequestMapping("/api/health")
17
- // public class HealthController {
18
 
19
- // @Autowired
20
- // private DataSource dataSource;
21
 
22
- // @GetMapping("/database")
23
- // public ResponseEntity<Map<String, Object>> checkDatabaseConnection() {
24
- // Map<String, Object> response = new HashMap<>();
25
 
26
- // try (Connection connection = dataSource.getConnection()) {
27
- // // Test the connection
28
- // boolean isValid = connection.isValid(5); // 5 second timeout
29
 
30
- // if (isValid) {
31
- // response.put("status", "UP");
32
- // response.put("database", "Connected");
33
- // response.put("url", connection.getMetaData().getURL());
34
- // response.put("driver", connection.getMetaData().getDriverName());
35
- // response.put("version", connection.getMetaData().getDatabaseProductVersion());
36
- // return ResponseEntity.ok(response);
37
- // } else {
38
- // response.put("status", "DOWN");
39
- // response.put("database", "Connection invalid");
40
- // return ResponseEntity.status(503).body(response);
41
- // }
42
- // } catch (SQLException e) {
43
- // response.put("status", "DOWN");
44
- // response.put("database", "Connection failed");
45
- // response.put("error", e.getMessage());
46
- // return ResponseEntity.status(503).body(response);
47
- // }
48
- // }
49
- // }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.controller;
2
 
3
+ import com.zaxxer.hikari.HikariDataSource;
4
+ import com.zaxxer.hikari.HikariPoolMXBean;
5
+ import org.springframework.beans.factory.annotation.Autowired;
6
+ import org.springframework.http.ResponseEntity;
7
+ import org.springframework.web.bind.annotation.GetMapping;
8
+ import org.springframework.web.bind.annotation.RequestMapping;
9
+ import org.springframework.web.bind.annotation.RestController;
10
 
11
+ import javax.management.JMX;
12
+ import javax.management.MBeanServer;
13
+ import javax.management.ObjectName;
14
+ import javax.sql.DataSource;
15
+ import java.lang.management.ManagementFactory;
16
+ import java.sql.Connection;
17
+ import java.sql.SQLException;
18
+ import java.util.HashMap;
19
+ import java.util.Map;
20
 
21
+ @RestController
22
+ @RequestMapping("/api/health")
23
+ public class HealthController {
24
 
25
+ @Autowired
26
+ private DataSource dataSource;
27
 
28
+ @GetMapping("/database")
29
+ public ResponseEntity<Map<String, Object>> checkDatabaseConnection() {
30
+ Map<String, Object> response = new HashMap<>();
31
 
32
+ try (Connection connection = dataSource.getConnection()) {
33
+ // Test the connection
34
+ boolean isValid = connection.isValid(5); // 5 second timeout
35
 
36
+ if (isValid) {
37
+ response.put("status", "UP");
38
+ response.put("database", "Connected");
39
+ response.put("url", connection.getMetaData().getURL());
40
+ response.put("driver", connection.getMetaData().getDriverName());
41
+ response.put("version", connection.getMetaData().getDatabaseProductVersion());
42
+ return ResponseEntity.ok(response);
43
+ } else {
44
+ response.put("status", "DOWN");
45
+ response.put("database", "Connection invalid");
46
+ return ResponseEntity.status(503).body(response);
47
+ }
48
+ } catch (SQLException e) {
49
+ response.put("status", "DOWN");
50
+ response.put("database", "Connection failed");
51
+ response.put("error", e.getMessage());
52
+ return ResponseEntity.status(503).body(response);
53
+ }
54
+ }
55
+
56
+ @GetMapping("/connection-pool")
57
+ public ResponseEntity<Map<String, Object>> checkConnectionPool() {
58
+ Map<String, Object> response = new HashMap<>();
59
+
60
+ try {
61
+ if (dataSource instanceof HikariDataSource) {
62
+ HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
63
+
64
+ response.put("status", "UP");
65
+ response.put("poolName", "AttendanceHikariCP");
66
+
67
+ // Basic configuration details (always available)
68
+ response.put("maximumPoolSize", hikariDataSource.getMaximumPoolSize());
69
+ response.put("minimumIdle", hikariDataSource.getMinimumIdle());
70
+ response.put("connectionTimeout", hikariDataSource.getConnectionTimeout());
71
+ response.put("idleTimeout", hikariDataSource.getIdleTimeout());
72
+ response.put("maxLifetime", hikariDataSource.getMaxLifetime());
73
+ response.put("leakDetectionThreshold", hikariDataSource.getLeakDetectionThreshold());
74
+
75
+ // Try to get HikariCP MBean for detailed metrics
76
+ try {
77
+ MBeanServer server = ManagementFactory.getPlatformMBeanServer();
78
+ ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (AttendanceHikariCP)");
79
+
80
+ if (server.isRegistered(poolName)) {
81
+ HikariPoolMXBean poolProxy = JMX.newMXBeanProxy(server, poolName, HikariPoolMXBean.class);
82
+
83
+ response.put("activeConnections", poolProxy.getActiveConnections());
84
+ response.put("idleConnections", poolProxy.getIdleConnections());
85
+ response.put("totalConnections", poolProxy.getTotalConnections());
86
+ response.put("threadsAwaitingConnection", poolProxy.getThreadsAwaitingConnection());
87
+
88
+ // Health indicators
89
+ int activeConnections = poolProxy.getActiveConnections();
90
+ int totalConnections = poolProxy.getTotalConnections();
91
+ int maxPoolSize = hikariDataSource.getMaximumPoolSize();
92
+
93
+ response.put("poolUtilization", String.format("%.2f%%",
94
+ (double) totalConnections / maxPoolSize * 100));
95
+ response.put("activeUtilization", String.format("%.2f%%",
96
+ (double) activeConnections / maxPoolSize * 100));
97
+
98
+ // Warning thresholds
99
+ if (totalConnections >= maxPoolSize * 0.9) {
100
+ response.put("warning", "Connection pool is near maximum capacity");
101
+ }
102
+ if (poolProxy.getThreadsAwaitingConnection() > 0) {
103
+ response.put("warning", "Threads are waiting for connections - possible bottleneck");
104
+ }
105
+ } else {
106
+ response.put("mbeanStatus", "MBean not registered - pool metrics unavailable");
107
+ response.put("note", "Pool configuration is available, but runtime metrics require MBean access");
108
+ }
109
+ } catch (Exception mbeanException) {
110
+ response.put("mbeanError", "Cannot access pool MBean: " + mbeanException.getMessage());
111
+ response.put("note", "Pool configuration is available, but runtime metrics are not accessible");
112
+ }
113
+
114
+ return ResponseEntity.ok(response);
115
+ } else {
116
+ response.put("status", "UNKNOWN");
117
+ response.put("message", "DataSource is not HikariCP");
118
+ return ResponseEntity.ok(response);
119
+ }
120
+ } catch (Exception e) {
121
+ response.put("status", "ERROR");
122
+ response.put("error", "Failed to get connection pool info: " + e.getMessage());
123
+ return ResponseEntity.status(500).body(response);
124
+ }
125
+ }
126
+
127
+ @GetMapping("/connection-test")
128
+ public ResponseEntity<Map<String, Object>> testConnectionLeak() {
129
+ Map<String, Object> response = new HashMap<>();
130
+
131
+ try {
132
+ // Test multiple rapid connections to check for leaks
133
+ for (int i = 0; i < 3; i++) {
134
+ try (Connection connection = dataSource.getConnection()) {
135
+ connection.isValid(1);
136
+ }
137
+ }
138
+
139
+ response.put("status", "UP");
140
+ response.put("message", "Connection leak test passed");
141
+ response.put("testConnections", 3);
142
+ return ResponseEntity.ok(response);
143
+
144
+ } catch (SQLException e) {
145
+ response.put("status", "DOWN");
146
+ response.put("error", "Connection leak test failed: " + e.getMessage());
147
+ return ResponseEntity.status(503).body(response);
148
+ }
149
+ }
150
+
151
+ @GetMapping("/pool-simple")
152
+ public ResponseEntity<Map<String, Object>> getSimplePoolInfo() {
153
+ Map<String, Object> response = new HashMap<>();
154
+
155
+ try {
156
+ if (dataSource instanceof HikariDataSource) {
157
+ HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
158
+
159
+ response.put("status", "UP");
160
+ response.put("dataSourceType", "HikariCP");
161
+ response.put("poolName", hikariDataSource.getPoolName());
162
+ response.put("jdbcUrl", hikariDataSource.getJdbcUrl());
163
+ response.put("maximumPoolSize", hikariDataSource.getMaximumPoolSize());
164
+ response.put("minimumIdle", hikariDataSource.getMinimumIdle());
165
+ response.put("connectionTimeout", hikariDataSource.getConnectionTimeout() + "ms");
166
+ response.put("idleTimeout", hikariDataSource.getIdleTimeout() + "ms");
167
+ response.put("maxLifetime", hikariDataSource.getMaxLifetime() + "ms");
168
+ response.put("leakDetectionThreshold", hikariDataSource.getLeakDetectionThreshold() + "ms");
169
+ response.put("isRunning", !hikariDataSource.isClosed());
170
+
171
+ return ResponseEntity.ok(response);
172
+ } else {
173
+ response.put("status", "UNKNOWN");
174
+ response.put("dataSourceType", dataSource.getClass().getSimpleName());
175
+ response.put("message", "Not using HikariCP");
176
+ return ResponseEntity.ok(response);
177
+ }
178
+ } catch (Exception e) {
179
+ response.put("status", "ERROR");
180
+ response.put("error", "Failed to get pool info: " + e.getMessage());
181
+ return ResponseEntity.status(500).body(response);
182
+ }
183
+ }
184
+ }
src/main/java/com/cs102/attendance/controller/SessionController.java ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.controller;
2
+
3
+ import com.cs102.attendance.entity.Session;
4
+ import com.cs102.attendance.dto.SessionDto;
5
+ import com.cs102.attendance.service.SessionService;
6
+ import org.springframework.beans.factory.annotation.Autowired;
7
+ import org.springframework.http.ResponseEntity;
8
+ import org.springframework.web.bind.annotation.*;
9
+
10
+ import java.time.LocalDate;
11
+ import java.time.LocalTime;
12
+ import java.util.List;
13
+ import java.util.UUID;
14
+ import java.util.Optional;
15
+
16
+ @RestController
17
+ @RequestMapping("/api/sessions")
18
+ @CrossOrigin(origins = "*")
19
+ public class SessionController {
20
+
21
+ @Autowired
22
+ private SessionService sessionService;
23
+
24
+ // Create a new session
25
+ @PostMapping
26
+ public ResponseEntity<Session> createSession(@RequestBody CreateSessionRequest request) {
27
+ try {
28
+ Session session = sessionService.createSession(
29
+ request.getName(),
30
+ request.getDate(),
31
+ request.getStartTime(),
32
+ request.getEndTime()
33
+ );
34
+ return ResponseEntity.ok(session);
35
+ } catch (Exception e) {
36
+ return ResponseEntity.badRequest().build();
37
+ }
38
+ }
39
+
40
+ // Get all sessions
41
+ @GetMapping
42
+ public ResponseEntity<List<Session>> getAllSessions() {
43
+ List<Session> sessions = sessionService.getAllSessions();
44
+ return ResponseEntity.ok(sessions);
45
+ }
46
+
47
+ // Get session by ID
48
+ @GetMapping("/{id}")
49
+ public ResponseEntity<Session> getSessionById(@PathVariable UUID id) {
50
+ Optional<Session> session = sessionService.getSessionById(id);
51
+ return session.map(ResponseEntity::ok)
52
+ .orElse(ResponseEntity.notFound().build());
53
+ }
54
+
55
+ // Get today's sessions
56
+ @GetMapping("/today")
57
+ public ResponseEntity<List<SessionDto>> getTodaySessions() {
58
+ List<SessionDto> sessions = sessionService.getTodaySessionDtos();
59
+ return ResponseEntity.ok(sessions);
60
+ }
61
+
62
+ // Get sessions for specific date
63
+ @GetMapping("/date/{date}")
64
+ public ResponseEntity<List<Session>> getSessionsByDate(@PathVariable String date) {
65
+ try {
66
+ LocalDate localDate = LocalDate.parse(date);
67
+ List<Session> sessions = sessionService.getTodaySessions(localDate);
68
+ return ResponseEntity.ok(sessions);
69
+ } catch (Exception e) {
70
+ return ResponseEntity.badRequest().build();
71
+ }
72
+ }
73
+
74
+ // Close session
75
+ @PutMapping("/{id}/close")
76
+ public ResponseEntity<Session> closeSession(@PathVariable UUID id) {
77
+ try {
78
+ Session session = sessionService.closeSession(id);
79
+ return ResponseEntity.ok(session);
80
+ } catch (RuntimeException e) {
81
+ return ResponseEntity.notFound().build();
82
+ }
83
+ }
84
+
85
+ // Delete session
86
+ @DeleteMapping("/{id}")
87
+ public ResponseEntity<Void> deleteSession(@PathVariable UUID id) {
88
+ try {
89
+ sessionService.deleteSession(id);
90
+ return ResponseEntity.ok().build();
91
+ } catch (Exception e) {
92
+ return ResponseEntity.notFound().build();
93
+ }
94
+ }
95
+
96
+ // DTO for request body
97
+ public static class CreateSessionRequest {
98
+ private String name;
99
+ private LocalDate date;
100
+ private LocalTime startTime;
101
+ private LocalTime endTime;
102
+
103
+ // Getters and setters
104
+ public String getName() { return name; }
105
+ public void setName(String name) { this.name = name; }
106
+ public LocalDate getDate() { return date; }
107
+ public void setDate(LocalDate date) { this.date = date; }
108
+ public LocalTime getStartTime() { return startTime; }
109
+ public void setStartTime(LocalTime startTime) { this.startTime = startTime; }
110
+ public LocalTime getEndTime() { return endTime; }
111
+ public void setEndTime(LocalTime endTime) { this.endTime = endTime; }
112
+ }
113
+ }
src/main/java/com/cs102/attendance/controller/StudentController.java ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.controller;
2
+
3
+ import com.cs102.attendance.entity.Student;
4
+ import com.cs102.attendance.entity.FaceData;
5
+ import com.cs102.attendance.service.StudentService;
6
+ import org.springframework.beans.factory.annotation.Autowired;
7
+ import org.springframework.http.ResponseEntity;
8
+ import org.springframework.web.bind.annotation.*;
9
+
10
+ import java.util.List;
11
+ import java.util.UUID;
12
+ import java.util.Optional;
13
+
14
+ @RestController
15
+ @RequestMapping("/api/students")
16
+ @CrossOrigin(origins = "*") // Allow CORS for testing
17
+ public class StudentController {
18
+
19
+ @Autowired
20
+ private StudentService studentService;
21
+
22
+ // Create a new student
23
+ @PostMapping
24
+ public ResponseEntity<Student> createStudent(@RequestBody CreateStudentRequest request) {
25
+ try {
26
+ Student student = studentService.enrol(
27
+ request.getCode(),
28
+ request.getName(),
29
+ request.getClassName(),
30
+ request.getStudentGroup(),
31
+ request.getEmail(),
32
+ request.getPhone()
33
+ );
34
+ return ResponseEntity.ok(student);
35
+ } catch (Exception e) {
36
+ return ResponseEntity.badRequest().build();
37
+ }
38
+ }
39
+
40
+ // Get all students
41
+ @GetMapping
42
+ public ResponseEntity<List<Student>> getAllStudents() {
43
+ try {
44
+ List<Student> students = studentService.getAllStudents();
45
+ return ResponseEntity.ok(students);
46
+ } catch (Exception e) {
47
+ e.printStackTrace();
48
+ return ResponseEntity.status(500).build();
49
+ }
50
+ }
51
+
52
+ // Simple test endpoint
53
+ @GetMapping("/health-check")
54
+ public ResponseEntity<String> testEndpoint() {
55
+ return ResponseEntity.ok("Students endpoint is working!");
56
+ }
57
+
58
+ // Count endpoint
59
+ @GetMapping("/count")
60
+ public ResponseEntity<Long> getStudentCount() {
61
+ try {
62
+ List<Student> students = studentService.getAllStudents();
63
+ return ResponseEntity.ok((long) students.size());
64
+ } catch (Exception e) {
65
+ e.printStackTrace();
66
+ return ResponseEntity.status(500).body(-1L);
67
+ }
68
+ }
69
+
70
+ // Get student by ID
71
+ @GetMapping("/{id}")
72
+ public ResponseEntity<Student> getStudentById(@PathVariable UUID id) {
73
+ Optional<Student> student = studentService.getStudentById(id);
74
+ return student.map(ResponseEntity::ok)
75
+ .orElse(ResponseEntity.notFound().build());
76
+ }
77
+
78
+ // Update student
79
+ @PutMapping("/{id}")
80
+ public ResponseEntity<Student> updateStudent(@PathVariable UUID id, @RequestBody UpdateStudentRequest request) {
81
+ try {
82
+ Student updated = studentService.updateProfile(
83
+ id,
84
+ request.getName(),
85
+ request.getClassName(),
86
+ request.getStudentGroup(),
87
+ request.getEmail(),
88
+ request.getPhone()
89
+ );
90
+ return ResponseEntity.ok(updated);
91
+ } catch (RuntimeException e) {
92
+ return ResponseEntity.notFound().build();
93
+ }
94
+ }
95
+
96
+ // Delete student
97
+ @DeleteMapping("/{id}")
98
+ public ResponseEntity<Void> deleteStudent(@PathVariable UUID id) {
99
+ try {
100
+ studentService.deleteStudent(id);
101
+ return ResponseEntity.ok().build();
102
+ } catch (Exception e) {
103
+ return ResponseEntity.notFound().build();
104
+ }
105
+ }
106
+
107
+ // Upload face image (URL)
108
+ @PostMapping("/{id}/face-image")
109
+ public ResponseEntity<FaceData> uploadFaceImage(@PathVariable UUID id, @RequestBody FaceImageRequest request) {
110
+ try {
111
+ FaceData faceData = studentService.uploadFaceImage(id, request.getImageUrl());
112
+ return ResponseEntity.ok(faceData);
113
+ } catch (RuntimeException e) {
114
+ return ResponseEntity.notFound().build();
115
+ }
116
+ }
117
+
118
+ // DTOs for request bodies
119
+ public static class CreateStudentRequest {
120
+ private String code;
121
+ private String name;
122
+ private String className;
123
+ private String studentGroup;
124
+ private String email;
125
+ private String phone;
126
+
127
+ // Getters and setters
128
+ public String getCode() { return code; }
129
+ public void setCode(String code) { this.code = code; }
130
+ public String getName() { return name; }
131
+ public void setName(String name) { this.name = name; }
132
+ public String getClassName() { return className; }
133
+ public void setClassName(String className) { this.className = className; }
134
+ public String getStudentGroup() { return studentGroup; }
135
+ public void setStudentGroup(String studentGroup) { this.studentGroup = studentGroup; }
136
+ public String getEmail() { return email; }
137
+ public void setEmail(String email) { this.email = email; }
138
+ public String getPhone() { return phone; }
139
+ public void setPhone(String phone) { this.phone = phone; }
140
+ }
141
+
142
+ public static class UpdateStudentRequest {
143
+ private String name;
144
+ private String className;
145
+ private String studentGroup;
146
+ private String email;
147
+ private String phone;
148
+
149
+ // Getters and setters
150
+ public String getName() { return name; }
151
+ public void setName(String name) { this.name = name; }
152
+ public String getClassName() { return className; }
153
+ public void setClassName(String className) { this.className = className; }
154
+ public String getStudentGroup() { return studentGroup; }
155
+ public void setStudentGroup(String studentGroup) { this.studentGroup = studentGroup; }
156
+ public String getEmail() { return email; }
157
+ public void setEmail(String email) { this.email = email; }
158
+ public String getPhone() { return phone; }
159
+ public void setPhone(String phone) { this.phone = phone; }
160
+ }
161
+
162
+ public static class FaceImageRequest {
163
+ private String imageUrl;
164
+
165
+ public String getImageUrl() { return imageUrl; }
166
+ public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
167
+ }
168
+ }
src/main/java/com/cs102/attendance/controller/TestController.java CHANGED
@@ -1,75 +1,78 @@
1
- // package com.cs102.attendance.controller;
2
 
3
- // import com.cs102.attendance.entity.TestConnection;
4
- // import com.cs102.attendance.repository.TestConnectionRepository;
5
- // import org.springframework.beans.factory.annotation.Autowired;
6
- // import org.springframework.http.ResponseEntity;
7
- // import org.springframework.web.bind.annotation.*;
8
 
9
- // import java.util.HashMap;
10
- // import java.util.List;
11
- // import java.util.Map;
12
 
13
- // @RestController
14
- // @RequestMapping("/api/test")
15
- // public class TestController {
16
 
17
- // @Autowired
18
- // private TestConnectionRepository testConnectionRepository;
19
 
20
- // @GetMapping("/database-operations")
21
- // public ResponseEntity<Map<String, Object>> testDatabaseOperations() {
22
- // Map<String, Object> response = new HashMap<>();
23
 
24
- // try {
25
- // // Test 1: Simple query
26
- // Integer queryResult = testConnectionRepository.testQuery();
27
- // response.put("simpleQuery", queryResult == 1 ? "PASS" : "FAIL");
 
 
28
 
29
- // // Test 2: Insert operation
30
- // TestConnection testEntity = new TestConnection("Connection test from Spring Boot");
31
- // TestConnection saved = testConnectionRepository.save(testEntity);
32
- // response.put("insertOperation", saved.getId() != null ? "PASS" : "FAIL");
33
 
34
- // // Test 3: Read operation
35
- // List<TestConnection> allRecords = testConnectionRepository.findAll();
36
- // response.put("readOperation", !allRecords.isEmpty() ? "PASS" : "FAIL");
37
- // response.put("recordCount", allRecords.size());
38
 
39
- // // Test 4: Delete operation
40
- // testConnectionRepository.deleteById(saved.getId());
41
- // response.put("deleteOperation", "PASS");
42
 
43
- // response.put("overallStatus", "SUCCESS");
44
- // response.put("message", "All database operations completed successfully");
45
-
46
- // return ResponseEntity.ok(response);
47
- // } catch (Exception e) {
48
- // response.put("overallStatus", "FAILED");
49
- // response.put("error", e.getMessage());
50
- // response.put("errorClass", e.getClass().getSimpleName());
51
- // return ResponseEntity.status(500).body(response);
52
- // }
53
- // }
54
 
55
- // @PostMapping("/insert-test-data")
56
- // public ResponseEntity<TestConnection> insertTestData(@RequestParam(defaultValue = "Test message") String message) {
57
- // try {
58
- // TestConnection testEntity = new TestConnection(message);
59
- // TestConnection saved = testConnectionRepository.save(testEntity);
60
- // return ResponseEntity.ok(saved);
61
- // } catch (Exception e) {
62
- // return ResponseEntity.status(500).build();
63
- // }
64
- // }
 
 
 
 
 
 
65
 
66
- // @GetMapping("/all-test-data")
67
- // public ResponseEntity<List<TestConnection>> getAllTestData() {
68
- // try {
69
- // List<TestConnection> allRecords = testConnectionRepository.findAll();
70
- // return ResponseEntity.ok(allRecords);
71
- // } catch (Exception e) {
72
- // return ResponseEntity.status(500).build();
73
- // }
74
- // }
75
- // }
 
1
+ package com.cs102.attendance.controller;
2
 
3
+ import com.cs102.attendance.entity.Student;
4
+ import com.cs102.attendance.repository.StudentRepository;
5
+ import org.springframework.beans.factory.annotation.Autowired;
6
+ import org.springframework.http.ResponseEntity;
7
+ import org.springframework.web.bind.annotation.*;
8
 
9
+ import java.util.HashMap;
10
+ import java.util.List;
11
+ import java.util.Map;
12
 
13
+ @RestController
14
+ @RequestMapping("/api/test")
15
+ public class TestController {
16
 
17
+ @Autowired
18
+ private StudentRepository studentRepository;
19
 
20
+ @GetMapping("/database-operations")
21
+ public ResponseEntity<Map<String, Object>> testDatabaseOperations() {
22
+ Map<String, Object> response = new HashMap<>();
23
 
24
+ try {
25
+ // Test 1: Insert operation
26
+ Student testStudent = new Student("TEST001", "Test Student");
27
+ testStudent.setEmail("[email protected]");
28
+ Student saved = studentRepository.save(testStudent);
29
+ response.put("insertOperation", saved.getId() != null ? "PASS" : "FAIL");
30
 
31
+ // Test 2: Read operation
32
+ List<Student> allRecords = studentRepository.findAll();
33
+ response.put("readOperation", !allRecords.isEmpty() ? "PASS" : "FAIL");
34
+ response.put("recordCount", allRecords.size());
35
 
36
+ // Test 3: Delete operation
37
+ studentRepository.deleteById(saved.getId());
38
+ response.put("deleteOperation", "PASS");
 
39
 
40
+ response.put("overallStatus", "SUCCESS");
41
+ response.put("message", "All database operations completed successfully");
 
42
 
43
+ return ResponseEntity.ok(response);
44
+ } catch (Exception e) {
45
+ response.put("overallStatus", "FAILED");
46
+ response.put("error", e.getMessage());
47
+ response.put("errorClass", e.getClass().getSimpleName());
48
+ return ResponseEntity.status(500).body(response);
49
+ }
50
+ }
 
 
 
51
 
52
+ @PostMapping("/insert-test-data")
53
+ public ResponseEntity<Student> insertTestData(
54
+ @RequestParam String code,
55
+ @RequestParam String name,
56
+ @RequestParam(required = false) String email) {
57
+ try {
58
+ Student testStudent = new Student(code, name);
59
+ if (email != null) {
60
+ testStudent.setEmail(email);
61
+ }
62
+ Student saved = studentRepository.save(testStudent);
63
+ return ResponseEntity.ok(saved);
64
+ } catch (Exception e) {
65
+ return ResponseEntity.status(500).build();
66
+ }
67
+ }
68
 
69
+ @GetMapping("/all-test-data")
70
+ public ResponseEntity<List<Student>> getAllTestData() {
71
+ try {
72
+ List<Student> allRecords = studentRepository.findAll();
73
+ return ResponseEntity.ok(allRecords);
74
+ } catch (Exception e) {
75
+ return ResponseEntity.status(500).build();
76
+ }
77
+ }
78
+ }
src/main/java/com/cs102/attendance/dto/MainViewDto.java ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.dto;
2
+
3
+ import java.util.List;
4
+
5
+ public class MainViewDto {
6
+ private List<SessionDto> todaySessions;
7
+ private List<StudentRosterDto> studentRoster;
8
+ private int totalStudents;
9
+ private int totalSessions;
10
+
11
+ // Constructors
12
+ public MainViewDto() {}
13
+
14
+ public MainViewDto(List<SessionDto> todaySessions, List<StudentRosterDto> studentRoster) {
15
+ this.todaySessions = todaySessions;
16
+ this.studentRoster = studentRoster;
17
+ this.totalStudents = studentRoster != null ? studentRoster.size() : 0;
18
+ this.totalSessions = todaySessions != null ? todaySessions.size() : 0;
19
+ }
20
+
21
+ // Getters and Setters
22
+ public List<SessionDto> getTodaySessions() {
23
+ return todaySessions;
24
+ }
25
+
26
+ public void setTodaySessions(List<SessionDto> todaySessions) {
27
+ this.todaySessions = todaySessions;
28
+ this.totalSessions = todaySessions != null ? todaySessions.size() : 0;
29
+ }
30
+
31
+ public List<StudentRosterDto> getStudentRoster() {
32
+ return studentRoster;
33
+ }
34
+
35
+ public void setStudentRoster(List<StudentRosterDto> studentRoster) {
36
+ this.studentRoster = studentRoster;
37
+ this.totalStudents = studentRoster != null ? studentRoster.size() : 0;
38
+ }
39
+
40
+ public int getTotalStudents() {
41
+ return totalStudents;
42
+ }
43
+
44
+ public void setTotalStudents(int totalStudents) {
45
+ this.totalStudents = totalStudents;
46
+ }
47
+
48
+ public int getTotalSessions() {
49
+ return totalSessions;
50
+ }
51
+
52
+ public void setTotalSessions(int totalSessions) {
53
+ this.totalSessions = totalSessions;
54
+ }
55
+ }
src/main/java/com/cs102/attendance/dto/SessionDto.java ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.dto;
2
+
3
+ import java.time.LocalDate;
4
+ import java.time.LocalTime;
5
+ import java.util.UUID;
6
+
7
+ public class SessionDto {
8
+ private UUID id;
9
+ private String name;
10
+ private LocalDate date;
11
+ private LocalTime startTime;
12
+ private LocalTime endTime;
13
+ private long presentCount;
14
+ private long absentCount;
15
+ private long lateCount;
16
+ private long totalStudents;
17
+
18
+ // Constructors
19
+ public SessionDto() {}
20
+
21
+ public SessionDto(UUID id, String name, LocalDate date, LocalTime startTime, LocalTime endTime) {
22
+ this.id = id;
23
+ this.name = name;
24
+ this.date = date;
25
+ this.startTime = startTime;
26
+ this.endTime = endTime;
27
+ }
28
+
29
+ // Getters and Setters
30
+ public UUID getId() {
31
+ return id;
32
+ }
33
+
34
+ public void setId(UUID id) {
35
+ this.id = id;
36
+ }
37
+
38
+ public String getName() {
39
+ return name;
40
+ }
41
+
42
+ public void setName(String name) {
43
+ this.name = name;
44
+ }
45
+
46
+ public LocalDate getDate() {
47
+ return date;
48
+ }
49
+
50
+ public void setDate(LocalDate date) {
51
+ this.date = date;
52
+ }
53
+
54
+ public LocalTime getStartTime() {
55
+ return startTime;
56
+ }
57
+
58
+ public void setStartTime(LocalTime startTime) {
59
+ this.startTime = startTime;
60
+ }
61
+
62
+ public LocalTime getEndTime() {
63
+ return endTime;
64
+ }
65
+
66
+ public void setEndTime(LocalTime endTime) {
67
+ this.endTime = endTime;
68
+ }
69
+
70
+ public long getPresentCount() {
71
+ return presentCount;
72
+ }
73
+
74
+ public void setPresentCount(long presentCount) {
75
+ this.presentCount = presentCount;
76
+ }
77
+
78
+ public long getAbsentCount() {
79
+ return absentCount;
80
+ }
81
+
82
+ public void setAbsentCount(long absentCount) {
83
+ this.absentCount = absentCount;
84
+ }
85
+
86
+ public long getLateCount() {
87
+ return lateCount;
88
+ }
89
+
90
+ public void setLateCount(long lateCount) {
91
+ this.lateCount = lateCount;
92
+ }
93
+
94
+ public long getTotalStudents() {
95
+ return totalStudents;
96
+ }
97
+
98
+ public void setTotalStudents(long totalStudents) {
99
+ this.totalStudents = totalStudents;
100
+ }
101
+ }
src/main/java/com/cs102/attendance/dto/StudentRosterDto.java ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.dto;
2
+
3
+ import com.cs102.attendance.enums.Status;
4
+ import java.util.UUID;
5
+
6
+ public class StudentRosterDto {
7
+ private UUID id;
8
+ private String code;
9
+ private String name;
10
+ private Status status;
11
+
12
+ // Constructors
13
+ public StudentRosterDto() {}
14
+
15
+ public StudentRosterDto(UUID id, String code, String name, Status status) {
16
+ this.id = id;
17
+ this.code = code;
18
+ this.name = name;
19
+ this.status = status;
20
+ }
21
+
22
+ // Getters and Setters
23
+ public UUID getId() {
24
+ return id;
25
+ }
26
+
27
+ public void setId(UUID id) {
28
+ this.id = id;
29
+ }
30
+
31
+ public String getCode() {
32
+ return code;
33
+ }
34
+
35
+ public void setCode(String code) {
36
+ this.code = code;
37
+ }
38
+
39
+ public String getName() {
40
+ return name;
41
+ }
42
+
43
+ public void setName(String name) {
44
+ this.name = name;
45
+ }
46
+
47
+ public Status getStatus() {
48
+ return status;
49
+ }
50
+
51
+ public void setStatus(Status status) {
52
+ this.status = status;
53
+ }
54
+ }
src/main/java/com/cs102/attendance/entity/AttendanceRecord.java ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.entity;
2
+
3
+ import com.cs102.attendance.enums.Status;
4
+ import com.cs102.attendance.enums.Method;
5
+ import jakarta.persistence.*;
6
+ import java.time.LocalDateTime;
7
+
8
+ @jakarta.persistence.Entity
9
+ @Table(name = "attendance_records")
10
+ public class AttendanceRecord extends Entity {
11
+
12
+ @ManyToOne(fetch = FetchType.LAZY)
13
+ @JoinColumn(name = "student_id", nullable = false)
14
+ private Student student;
15
+
16
+ @ManyToOne(fetch = FetchType.LAZY)
17
+ @JoinColumn(name = "session_id", nullable = false)
18
+ private Session session;
19
+
20
+ @Enumerated(EnumType.STRING)
21
+ @Column(nullable = false)
22
+ private Status status;
23
+
24
+ @Enumerated(EnumType.STRING)
25
+ @Column(nullable = false)
26
+ private Method method;
27
+
28
+ @Column
29
+ private Double confidence;
30
+
31
+ @Column(name = "marked_at", nullable = false)
32
+ private LocalDateTime markedAt;
33
+
34
+ // Constructors
35
+ public AttendanceRecord() {}
36
+
37
+ public AttendanceRecord(Student student, Session session, Status status, Method method) {
38
+ this.student = student;
39
+ this.session = session;
40
+ this.status = status;
41
+ this.method = method;
42
+ this.markedAt = LocalDateTime.now();
43
+ }
44
+
45
+ // Getters and Setters
46
+ public Student getStudent() {
47
+ return student;
48
+ }
49
+
50
+ public void setStudent(Student student) {
51
+ this.student = student;
52
+ }
53
+
54
+ public Session getSession() {
55
+ return session;
56
+ }
57
+
58
+ public void setSession(Session session) {
59
+ this.session = session;
60
+ }
61
+
62
+ public Status getStatus() {
63
+ return status;
64
+ }
65
+
66
+ public void setStatus(Status status) {
67
+ this.status = status;
68
+ }
69
+
70
+ public Method getMethod() {
71
+ return method;
72
+ }
73
+
74
+ public void setMethod(Method method) {
75
+ this.method = method;
76
+ }
77
+
78
+ public Double getConfidence() {
79
+ return confidence;
80
+ }
81
+
82
+ public void setConfidence(Double confidence) {
83
+ this.confidence = confidence;
84
+ }
85
+
86
+ public LocalDateTime getMarkedAt() {
87
+ return markedAt;
88
+ }
89
+
90
+ public void setMarkedAt(LocalDateTime markedAt) {
91
+ this.markedAt = markedAt;
92
+ }
93
+ }
src/main/java/com/cs102/attendance/entity/Entity.java ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.entity;
2
+
3
+ import jakarta.persistence.*;
4
+ import java.util.Objects;
5
+ import java.util.UUID;
6
+
7
+ @MappedSuperclass
8
+ public abstract class Entity {
9
+
10
+ @Id
11
+ @GeneratedValue(strategy = GenerationType.AUTO)
12
+ private UUID id;
13
+
14
+ public UUID getId() {
15
+ return id;
16
+ }
17
+
18
+ public void setId(UUID id) {
19
+ this.id = id;
20
+ }
21
+
22
+ @Override
23
+ public boolean equals(Object o) {
24
+ if (this == o) return true;
25
+ if (o == null || getClass() != o.getClass()) return false;
26
+ Entity entity = (Entity) o;
27
+ return Objects.equals(id, entity.id);
28
+ }
29
+
30
+ @Override
31
+ public int hashCode() {
32
+ return Objects.hash(id);
33
+ }
34
+ }
src/main/java/com/cs102/attendance/entity/FaceData.java ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.entity;
2
+
3
+ import jakarta.persistence.*;
4
+
5
+ @jakarta.persistence.Entity
6
+ @Table(name = "face_data")
7
+ public class FaceData extends Entity {
8
+
9
+ @ManyToOne(fetch = FetchType.LAZY)
10
+ @JoinColumn(name = "student_id", nullable = false)
11
+ private Student student;
12
+
13
+ @Column(name = "image_url")
14
+ private String imageUrl;
15
+
16
+ @Lob
17
+ @Column(name = "image_data")
18
+ private byte[] imageData;
19
+
20
+ // Constructors
21
+ public FaceData() {}
22
+
23
+ public FaceData(Student student, String imageUrl) {
24
+ this.student = student;
25
+ this.imageUrl = imageUrl;
26
+ }
27
+
28
+ public FaceData(Student student, byte[] imageData) {
29
+ this.student = student;
30
+ this.imageData = imageData;
31
+ }
32
+
33
+ // Getters and Setters
34
+ public Student getStudent() {
35
+ return student;
36
+ }
37
+
38
+ public void setStudent(Student student) {
39
+ this.student = student;
40
+ }
41
+
42
+ public String getImageUrl() {
43
+ return imageUrl;
44
+ }
45
+
46
+ public void setImageUrl(String imageUrl) {
47
+ this.imageUrl = imageUrl;
48
+ }
49
+
50
+ public byte[] getImageData() {
51
+ return imageData;
52
+ }
53
+
54
+ public void setImageData(byte[] imageData) {
55
+ this.imageData = imageData;
56
+ }
57
+ }
src/main/java/com/cs102/attendance/entity/Session.java ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.entity;
2
+
3
+ import com.fasterxml.jackson.annotation.JsonIgnore;
4
+ import jakarta.persistence.*;
5
+ import java.time.LocalDate;
6
+ import java.time.LocalTime;
7
+ import java.util.List;
8
+ import java.util.ArrayList;
9
+
10
+ @jakarta.persistence.Entity
11
+ @Table(name = "sessions")
12
+ public class Session extends Entity {
13
+
14
+ @Column(nullable = false)
15
+ private String name;
16
+
17
+ @Column(nullable = false)
18
+ private LocalDate date;
19
+
20
+ @Column(name = "start_time", nullable = false)
21
+ private LocalTime startTime;
22
+
23
+ @Column(name = "end_time", nullable = false)
24
+ private LocalTime endTime;
25
+
26
+ @OneToMany(mappedBy = "session", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
27
+ @JsonIgnore
28
+ private List<AttendanceRecord> attendanceRecords = new ArrayList<>();
29
+
30
+ // Constructors
31
+ public Session() {}
32
+
33
+ public Session(String name, LocalDate date, LocalTime startTime, LocalTime endTime) {
34
+ this.name = name;
35
+ this.date = date;
36
+ this.startTime = startTime;
37
+ this.endTime = endTime;
38
+ }
39
+
40
+ // Getters and Setters
41
+ public String getName() {
42
+ return name;
43
+ }
44
+
45
+ public void setName(String name) {
46
+ this.name = name;
47
+ }
48
+
49
+ public LocalDate getDate() {
50
+ return date;
51
+ }
52
+
53
+ public void setDate(LocalDate date) {
54
+ this.date = date;
55
+ }
56
+
57
+ public LocalTime getStartTime() {
58
+ return startTime;
59
+ }
60
+
61
+ public void setStartTime(LocalTime startTime) {
62
+ this.startTime = startTime;
63
+ }
64
+
65
+ public LocalTime getEndTime() {
66
+ return endTime;
67
+ }
68
+
69
+ public void setEndTime(LocalTime endTime) {
70
+ this.endTime = endTime;
71
+ }
72
+
73
+ public List<AttendanceRecord> getAttendanceRecords() {
74
+ return attendanceRecords;
75
+ }
76
+
77
+ public void setAttendanceRecords(List<AttendanceRecord> attendanceRecords) {
78
+ this.attendanceRecords = attendanceRecords;
79
+ }
80
+ }
src/main/java/com/cs102/attendance/entity/Student.java ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.entity;
2
+
3
+ import com.fasterxml.jackson.annotation.JsonIgnore;
4
+ import jakarta.persistence.*;
5
+ import java.util.List;
6
+ import java.util.ArrayList;
7
+
8
+ @jakarta.persistence.Entity
9
+ @Table(name = "students")
10
+ public class Student extends Entity {
11
+
12
+ @Column(unique = true, nullable = false)
13
+ private String code;
14
+
15
+ @Column(nullable = false)
16
+ private String name;
17
+
18
+ @Column(name = "class_name")
19
+ private String className;
20
+
21
+ @Column(name = "student_group")
22
+ private String studentGroup;
23
+
24
+ private String email;
25
+
26
+ private String phone;
27
+
28
+ @OneToMany(mappedBy = "student", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
29
+ @JsonIgnore
30
+ private List<FaceData> faceData = new ArrayList<>();
31
+
32
+ @OneToMany(mappedBy = "student", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
33
+ @JsonIgnore
34
+ private List<AttendanceRecord> attendanceRecords = new ArrayList<>();
35
+
36
+ // Constructors
37
+ public Student() {}
38
+
39
+ public Student(String code, String name) {
40
+ this.code = code;
41
+ this.name = name;
42
+ }
43
+
44
+ // Getters and Setters
45
+ public String getCode() {
46
+ return code;
47
+ }
48
+
49
+ public void setCode(String code) {
50
+ this.code = code;
51
+ }
52
+
53
+ public String getName() {
54
+ return name;
55
+ }
56
+
57
+ public void setName(String name) {
58
+ this.name = name;
59
+ }
60
+
61
+ public String getClassName() {
62
+ return className;
63
+ }
64
+
65
+ public void setClassName(String className) {
66
+ this.className = className;
67
+ }
68
+
69
+ public String getStudentGroup() {
70
+ return studentGroup;
71
+ }
72
+
73
+ public void setStudentGroup(String studentGroup) {
74
+ this.studentGroup = studentGroup;
75
+ }
76
+
77
+ public String getEmail() {
78
+ return email;
79
+ }
80
+
81
+ public void setEmail(String email) {
82
+ this.email = email;
83
+ }
84
+
85
+ public String getPhone() {
86
+ return phone;
87
+ }
88
+
89
+ public void setPhone(String phone) {
90
+ this.phone = phone;
91
+ }
92
+
93
+ public List<FaceData> getFaceData() {
94
+ return faceData;
95
+ }
96
+
97
+ public void setFaceData(List<FaceData> faceData) {
98
+ this.faceData = faceData;
99
+ }
100
+
101
+ public List<AttendanceRecord> getAttendanceRecords() {
102
+ return attendanceRecords;
103
+ }
104
+
105
+ public void setAttendanceRecords(List<AttendanceRecord> attendanceRecords) {
106
+ this.attendanceRecords = attendanceRecords;
107
+ }
108
+ }
src/main/java/com/cs102/attendance/entity/TestConnection.java DELETED
@@ -1,53 +0,0 @@
1
- // package com.cs102.attendance.entity;
2
-
3
- // import jakarta.persistence.*;
4
- // import java.time.LocalDateTime;
5
-
6
- // @Entity
7
- // @Table(name = "test_connections")
8
- // public class TestConnection {
9
-
10
- // @Id
11
- // @GeneratedValue(strategy = GenerationType.IDENTITY)
12
- // private Long id;
13
-
14
- // @Column(name = "test_message")
15
- // private String testMessage;
16
-
17
- // @Column(name = "created_at")
18
- // private LocalDateTime createdAt;
19
-
20
- // public TestConnection() {
21
- // this.createdAt = LocalDateTime.now();
22
- // }
23
-
24
- // public TestConnection(String testMessage) {
25
- // this.testMessage = testMessage;
26
- // this.createdAt = LocalDateTime.now();
27
- // }
28
-
29
- // // Getters and setters
30
- // public Long getId() {
31
- // return id;
32
- // }
33
-
34
- // public void setId(Long id) {
35
- // this.id = id;
36
- // }
37
-
38
- // public String getTestMessage() {
39
- // return testMessage;
40
- // }
41
-
42
- // public void setTestMessage(String testMessage) {
43
- // this.testMessage = testMessage;
44
- // }
45
-
46
- // public LocalDateTime getCreatedAt() {
47
- // return createdAt;
48
- // }
49
-
50
- // public void setCreatedAt(LocalDateTime createdAt) {
51
- // this.createdAt = createdAt;
52
- // }
53
- // }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/main/java/com/cs102/attendance/enums/Method.java ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.enums;
2
+
3
+ public enum Method {
4
+ AUTO,
5
+ MANUAL
6
+ }
src/main/java/com/cs102/attendance/enums/Status.java ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.enums;
2
+
3
+ public enum Status {
4
+ PRESENT,
5
+ ABSENT,
6
+ LATE
7
+ }
src/main/java/com/cs102/attendance/repository/AttendanceRepository.java ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.repository;
2
+
3
+ import com.cs102.attendance.entity.AttendanceRecord;
4
+ import com.cs102.attendance.entity.Session;
5
+ import com.cs102.attendance.entity.Student;
6
+ import com.cs102.attendance.enums.Status;
7
+ import org.springframework.data.jpa.repository.JpaRepository;
8
+ import org.springframework.stereotype.Repository;
9
+ import java.util.Optional;
10
+ import java.util.UUID;
11
+
12
+ @Repository
13
+ public interface AttendanceRepository extends JpaRepository<AttendanceRecord, UUID> {
14
+ Optional<AttendanceRecord> findBySessionAndStudent(Session session, Student student);
15
+ long countBySessionAndStatus(Session session, Status status);
16
+ }
src/main/java/com/cs102/attendance/repository/FaceDataRepository.java ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.repository;
2
+
3
+ import com.cs102.attendance.entity.FaceData;
4
+ import org.springframework.data.jpa.repository.JpaRepository;
5
+ import org.springframework.stereotype.Repository;
6
+ import java.util.UUID;
7
+
8
+ @Repository
9
+ public interface FaceDataRepository extends JpaRepository<FaceData, UUID> {
10
+ }
src/main/java/com/cs102/attendance/repository/SessionRepository.java ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.repository;
2
+
3
+ import com.cs102.attendance.entity.Session;
4
+ import org.springframework.data.jpa.repository.JpaRepository;
5
+ import org.springframework.stereotype.Repository;
6
+ import java.time.LocalDate;
7
+ import java.util.Optional;
8
+ import java.util.UUID;
9
+
10
+ @Repository
11
+ public interface SessionRepository extends JpaRepository<Session, UUID> {
12
+ Optional<Session> findByNameAndDate(String name, LocalDate date);
13
+ }
src/main/java/com/cs102/attendance/repository/StudentRepository.java ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.repository;
2
+
3
+ import com.cs102.attendance.entity.Student;
4
+ import org.springframework.data.jpa.repository.JpaRepository;
5
+ import org.springframework.stereotype.Repository;
6
+ import java.util.Optional;
7
+ import java.util.UUID;
8
+
9
+ @Repository
10
+ public interface StudentRepository extends JpaRepository<Student, UUID> {
11
+ Optional<Student> findByCode(String code);
12
+ }
src/main/java/com/cs102/attendance/repository/TestConnectionRepository.java DELETED
@@ -1,13 +0,0 @@
1
- // package com.cs102.attendance.repository;
2
-
3
- // import com.cs102.attendance.entity.TestConnection;
4
- // import org.springframework.data.jpa.repository.JpaRepository;
5
- // import org.springframework.data.jpa.repository.Query;
6
- // import org.springframework.stereotype.Repository;
7
-
8
- // @Repository
9
- // public interface TestConnectionRepository extends JpaRepository<TestConnection, Long> {
10
-
11
- // @Query(value = "SELECT 1", nativeQuery = true)
12
- // Integer testQuery();
13
- // }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/main/java/com/cs102/attendance/service/AttendanceService.java ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.service;
2
+
3
+ import com.cs102.attendance.entity.AttendanceRecord;
4
+ import com.cs102.attendance.entity.Session;
5
+ import com.cs102.attendance.entity.Student;
6
+ import com.cs102.attendance.enums.Status;
7
+
8
+ import java.util.UUID;
9
+
10
+ public interface AttendanceService {
11
+ AttendanceRecord markAttendance(UUID studentId, UUID sessionId, Status status);
12
+ AttendanceRecord markAttendance(Student student, Session session, Status status);
13
+ }
src/main/java/com/cs102/attendance/service/AutoMarker.java ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.service;
2
+
3
+ import com.cs102.attendance.entity.AttendanceRecord;
4
+ import com.cs102.attendance.entity.Session;
5
+ import com.cs102.attendance.entity.Student;
6
+ import com.cs102.attendance.enums.Method;
7
+ import com.cs102.attendance.enums.Status;
8
+ import com.cs102.attendance.repository.AttendanceRepository;
9
+ import com.cs102.attendance.repository.StudentRepository;
10
+ import com.cs102.attendance.repository.SessionRepository;
11
+ import com.cs102.attendance.config.RecognitionProperties;
12
+ import org.springframework.beans.factory.annotation.Autowired;
13
+ import org.springframework.beans.factory.annotation.Qualifier;
14
+ import org.springframework.stereotype.Component;
15
+ import org.springframework.transaction.annotation.Transactional;
16
+
17
+ import java.time.LocalDateTime;
18
+ import java.util.Optional;
19
+ import java.util.UUID;
20
+
21
+ @Component
22
+ @Qualifier("auto")
23
+ @Transactional
24
+ public class AutoMarker implements AttendanceService {
25
+
26
+ @Autowired
27
+ private AttendanceRepository attendanceRepository;
28
+
29
+ @Autowired
30
+ private StudentRepository studentRepository;
31
+
32
+ @Autowired
33
+ private SessionRepository sessionRepository;
34
+
35
+ @Autowired
36
+ private RecognitionProperties recognitionProperties;
37
+
38
+ @Override
39
+ public AttendanceRecord markAttendance(UUID studentId, UUID sessionId, Status status) {
40
+ Optional<Student> student = studentRepository.findById(studentId);
41
+ Optional<Session> session = sessionRepository.findById(sessionId);
42
+
43
+ if (student.isPresent() && session.isPresent()) {
44
+ return markAttendance(student.get(), session.get(), status);
45
+ }
46
+
47
+ throw new RuntimeException("Student or Session not found");
48
+ }
49
+
50
+ @Override
51
+ public AttendanceRecord markAttendance(Student student, Session session, Status status) {
52
+ return markAttendance(student, session, status, recognitionProperties.getConfidence());
53
+ }
54
+
55
+ public AttendanceRecord markAttendance(Student student, Session session, Status status, double confidence) {
56
+ // Check confidence threshold
57
+ if (confidence < recognitionProperties.getConfidence()) {
58
+ throw new RuntimeException("Recognition confidence too low: " + confidence);
59
+ }
60
+
61
+ // Check if attendance record already exists and cooldown period
62
+ Optional<AttendanceRecord> existingRecord = attendanceRepository.findBySessionAndStudent(session, student);
63
+
64
+ if (existingRecord.isPresent()) {
65
+ AttendanceRecord record = existingRecord.get();
66
+ LocalDateTime lastMarked = record.getMarkedAt();
67
+ LocalDateTime now = LocalDateTime.now();
68
+
69
+ // Check cooldown period
70
+ long timeSinceLastMark = java.time.Duration.between(lastMarked, now).toMillis();
71
+ if (timeSinceLastMark < recognitionProperties.getCooldownMs()) {
72
+ throw new RuntimeException("Cooldown period not elapsed. Please wait " +
73
+ (recognitionProperties.getCooldownMs() - timeSinceLastMark) + "ms");
74
+ }
75
+
76
+ // Update existing record
77
+ record.setStatus(status);
78
+ record.setMethod(Method.AUTO);
79
+ record.setConfidence(confidence);
80
+ record.setMarkedAt(now);
81
+ return attendanceRepository.save(record);
82
+ } else {
83
+ // Create new record
84
+ AttendanceRecord record = new AttendanceRecord(student, session, status, Method.AUTO);
85
+ record.setConfidence(confidence);
86
+ return attendanceRepository.save(record);
87
+ }
88
+ }
89
+ }
src/main/java/com/cs102/attendance/service/ManualMarker.java ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.service;
2
+
3
+ import com.cs102.attendance.entity.AttendanceRecord;
4
+ import com.cs102.attendance.entity.Session;
5
+ import com.cs102.attendance.entity.Student;
6
+ import com.cs102.attendance.enums.Method;
7
+ import com.cs102.attendance.enums.Status;
8
+ import com.cs102.attendance.repository.AttendanceRepository;
9
+ import com.cs102.attendance.repository.StudentRepository;
10
+ import com.cs102.attendance.repository.SessionRepository;
11
+ import org.springframework.beans.factory.annotation.Autowired;
12
+ import org.springframework.beans.factory.annotation.Qualifier;
13
+ import org.springframework.stereotype.Component;
14
+ import org.springframework.transaction.annotation.Transactional;
15
+
16
+ import java.util.Optional;
17
+ import java.util.UUID;
18
+
19
+ @Component
20
+ @Qualifier("manual")
21
+ @Transactional
22
+ public class ManualMarker implements AttendanceService {
23
+
24
+ @Autowired
25
+ private AttendanceRepository attendanceRepository;
26
+
27
+ @Autowired
28
+ private StudentRepository studentRepository;
29
+
30
+ @Autowired
31
+ private SessionRepository sessionRepository;
32
+
33
+ @Override
34
+ public AttendanceRecord markAttendance(UUID studentId, UUID sessionId, Status status) {
35
+ Optional<Student> student = studentRepository.findById(studentId);
36
+ Optional<Session> session = sessionRepository.findById(sessionId);
37
+
38
+ if (student.isPresent() && session.isPresent()) {
39
+ return markAttendance(student.get(), session.get(), status);
40
+ }
41
+
42
+ throw new RuntimeException("Student or Session not found");
43
+ }
44
+
45
+ @Override
46
+ public AttendanceRecord markAttendance(Student student, Session session, Status status) {
47
+ // Check if attendance record already exists
48
+ Optional<AttendanceRecord> existingRecord = attendanceRepository.findBySessionAndStudent(session, student);
49
+
50
+ AttendanceRecord record;
51
+ if (existingRecord.isPresent()) {
52
+ record = existingRecord.get();
53
+ record.setStatus(status);
54
+ record.setMethod(Method.MANUAL);
55
+ record.setConfidence(null); // Manual marking doesn't have confidence
56
+ } else {
57
+ record = new AttendanceRecord(student, session, status, Method.MANUAL);
58
+ }
59
+
60
+ return attendanceRepository.save(record);
61
+ }
62
+ }
src/main/java/com/cs102/attendance/service/SessionService.java ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.service;
2
+
3
+ import com.cs102.attendance.entity.Session;
4
+ import com.cs102.attendance.repository.SessionRepository;
5
+ import com.cs102.attendance.repository.AttendanceRepository;
6
+ import com.cs102.attendance.dto.SessionDto;
7
+ import com.cs102.attendance.enums.Status;
8
+ import org.springframework.beans.factory.annotation.Autowired;
9
+ import org.springframework.stereotype.Service;
10
+ import org.springframework.transaction.annotation.Transactional;
11
+
12
+ import java.time.LocalDate;
13
+ import java.time.LocalTime;
14
+ import java.util.List;
15
+ import java.util.UUID;
16
+ import java.util.Optional;
17
+ import java.util.stream.Collectors;
18
+
19
+ @Service
20
+ @Transactional
21
+ public class SessionService {
22
+
23
+ @Autowired
24
+ private SessionRepository sessionRepository;
25
+
26
+ @Autowired
27
+ private AttendanceRepository attendanceRepository;
28
+
29
+ public Session createSession(String name, LocalDate date, LocalTime startTime, LocalTime endTime) {
30
+ Session session = new Session(name, date, startTime, endTime);
31
+ return sessionRepository.save(session);
32
+ }
33
+
34
+ public Session closeSession(UUID sessionId) {
35
+ Optional<Session> optionalSession = sessionRepository.findById(sessionId);
36
+ if (optionalSession.isPresent()) {
37
+ Session session = optionalSession.get();
38
+ // Session is considered closed when end time has passed
39
+ // Additional logic can be added here if needed
40
+ return session;
41
+ }
42
+ throw new RuntimeException("Session not found with id: " + sessionId);
43
+ }
44
+
45
+ public List<Session> getTodaySessions() {
46
+ return getTodaySessions(LocalDate.now());
47
+ }
48
+
49
+ public List<Session> getTodaySessions(LocalDate date) {
50
+ return sessionRepository.findAll().stream()
51
+ .filter(session -> session.getDate().equals(date))
52
+ .collect(Collectors.toList());
53
+ }
54
+
55
+ public SessionDto getSessionDto(Session session) {
56
+ SessionDto dto = new SessionDto(
57
+ session.getId(),
58
+ session.getName(),
59
+ session.getDate(),
60
+ session.getStartTime(),
61
+ session.getEndTime()
62
+ );
63
+
64
+ // Calculate attendance counts
65
+ dto.setPresentCount(attendanceRepository.countBySessionAndStatus(session, Status.PRESENT));
66
+ dto.setAbsentCount(attendanceRepository.countBySessionAndStatus(session, Status.ABSENT));
67
+ dto.setLateCount(attendanceRepository.countBySessionAndStatus(session, Status.LATE));
68
+ dto.setTotalStudents(dto.getPresentCount() + dto.getAbsentCount() + dto.getLateCount());
69
+
70
+ return dto;
71
+ }
72
+
73
+ public List<SessionDto> getTodaySessionDtos() {
74
+ return getTodaySessions().stream()
75
+ .map(this::getSessionDto)
76
+ .collect(Collectors.toList());
77
+ }
78
+
79
+ public List<Session> getAllSessions() {
80
+ return sessionRepository.findAll();
81
+ }
82
+
83
+ public Optional<Session> getSessionById(UUID id) {
84
+ return sessionRepository.findById(id);
85
+ }
86
+
87
+ public void deleteSession(UUID id) {
88
+ sessionRepository.deleteById(id);
89
+ }
90
+ }
src/main/java/com/cs102/attendance/service/StudentService.java ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance.service;
2
+
3
+ import com.cs102.attendance.entity.Student;
4
+ import com.cs102.attendance.entity.FaceData;
5
+ import com.cs102.attendance.repository.StudentRepository;
6
+ import com.cs102.attendance.repository.FaceDataRepository;
7
+ import org.springframework.beans.factory.annotation.Autowired;
8
+ import org.springframework.stereotype.Service;
9
+ import org.springframework.transaction.annotation.Transactional;
10
+
11
+ import java.util.List;
12
+ import java.util.UUID;
13
+ import java.util.Optional;
14
+
15
+ @Service
16
+ @Transactional
17
+ public class StudentService {
18
+
19
+ @Autowired
20
+ private StudentRepository studentRepository;
21
+
22
+ @Autowired
23
+ private FaceDataRepository faceDataRepository;
24
+
25
+ public Student enrol(String code, String name, String className, String studentGroup, String email, String phone) {
26
+ Student student = new Student();
27
+ student.setCode(code);
28
+ student.setName(name);
29
+ student.setClassName(className);
30
+ student.setStudentGroup(studentGroup);
31
+ student.setEmail(email);
32
+ student.setPhone(phone);
33
+
34
+ return studentRepository.save(student);
35
+ }
36
+
37
+ public Student updateProfile(UUID studentId, String name, String className, String studentGroup, String email, String phone) {
38
+ Optional<Student> optionalStudent = studentRepository.findById(studentId);
39
+ if (optionalStudent.isPresent()) {
40
+ Student student = optionalStudent.get();
41
+ if (name != null) student.setName(name);
42
+ if (className != null) student.setClassName(className);
43
+ if (studentGroup != null) student.setStudentGroup(studentGroup);
44
+ if (email != null) student.setEmail(email);
45
+ if (phone != null) student.setPhone(phone);
46
+
47
+ return studentRepository.save(student);
48
+ }
49
+ throw new RuntimeException("Student not found with id: " + studentId);
50
+ }
51
+
52
+ public FaceData uploadFaceImage(UUID studentId, String imageUrl) {
53
+ Optional<Student> optionalStudent = studentRepository.findById(studentId);
54
+ if (optionalStudent.isPresent()) {
55
+ Student student = optionalStudent.get();
56
+ FaceData faceData = new FaceData(student, imageUrl);
57
+ return faceDataRepository.save(faceData);
58
+ }
59
+ throw new RuntimeException("Student not found with id: " + studentId);
60
+ }
61
+
62
+ public FaceData uploadFaceImage(UUID studentId, byte[] imageData) {
63
+ Optional<Student> optionalStudent = studentRepository.findById(studentId);
64
+ if (optionalStudent.isPresent()) {
65
+ Student student = optionalStudent.get();
66
+ FaceData faceData = new FaceData(student, imageData);
67
+ return faceDataRepository.save(faceData);
68
+ }
69
+ throw new RuntimeException("Student not found with id: " + studentId);
70
+ }
71
+
72
+ public List<Student> getAllStudents() {
73
+ return studentRepository.findAll();
74
+ }
75
+
76
+ public Optional<Student> getStudentById(UUID id) {
77
+ return studentRepository.findById(id);
78
+ }
79
+
80
+ public void deleteStudent(UUID id) {
81
+ studentRepository.deleteById(id);
82
+ }
83
+ }
src/main/resources/application.yml CHANGED
@@ -4,10 +4,32 @@ spring:
4
  username: ${SPRING_DATASOURCE_USERNAME}
5
  password: ${SPRING_DATASOURCE_PASSWORD}
6
  driver-class-name: org.postgresql.Driver
 
 
7
  hikari:
8
- maximum-pool-size: 10
9
- connection-timeout: 30000
10
- idle-timeout: 600000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  jpa:
12
  hibernate:
13
  ddl-auto: update
@@ -15,13 +37,42 @@ spring:
15
  properties:
16
  hibernate:
17
  dialect: org.hibernate.dialect.PostgreSQLDialect
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- # Expose metrics for demo proof
20
- management.endpoints.web.exposure.include: health,info,metrics,prometheus
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  # Externalised CV thresholds
23
  recognition:
24
- confidence: 0.6
25
  cooldownMs: 10000
26
  face-service:
27
- url: http://localhost:5001 # backend face service url
 
4
  username: ${SPRING_DATASOURCE_USERNAME}
5
  password: ${SPRING_DATASOURCE_PASSWORD}
6
  driver-class-name: org.postgresql.Driver
7
+ lifecycle:
8
+ timeout-per-shutdown-phase: 30000
9
  hikari:
10
+ maximum-pool-size: 5
11
+ minimum-idle: 1
12
+ idle-timeout: 300000
13
+ max-lifetime: 1800000
14
+ connection-timeout: 20000
15
+
16
+ # Leak detection and validation
17
+ leak-detection-threshold: 15000
18
+ validation-timeout: 5000
19
+
20
+ # Connection testing
21
+ connection-test-query: SELECT 1
22
+
23
+ # Pool name for monitoring
24
+ pool-name: AttendanceHikariCP
25
+
26
+ # Additional connection properties for PostgreSQL/Supabase
27
+ data-source-properties:
28
+ socketTimeout: 30
29
+ loginTimeout: 10
30
+ tcpKeepAlive: true
31
+ ApplicationName: cs102-attendance-app
32
+
33
  jpa:
34
  hibernate:
35
  ddl-auto: update
 
37
  properties:
38
  hibernate:
39
  dialect: org.hibernate.dialect.PostgreSQLDialect
40
+ connection.handling_mode: delayed_acquisition_and_release_after_transaction
41
+ query.timeout: 30000
42
+
43
+ server:
44
+ shutdown: graceful
45
+
46
+ # Enhanced logging for connection monitoring
47
+ logging:
48
+ level:
49
+ org.hibernate.SQL: DEBUG
50
+ org.hibernate.type.descriptor.sql.BasicBinder: TRACE
51
+ com.zaxxer.hikari: DEBUG
52
+ com.zaxxer.hikari.HikariConfig: DEBUG
53
+ com.zaxxer.hikari.HikariDataSource: DEBUG
54
+ com.zaxxer.hikari.pool: DEBUG
55
 
56
+ # Expose metrics for connection pool monitoring
57
+ management:
58
+ endpoints:
59
+ web:
60
+ exposure:
61
+ include: health,info,metrics,prometheus,hikaricp
62
+ endpoint:
63
+ health:
64
+ show-details: always
65
+ metrics:
66
+ export:
67
+ prometheus:
68
+ enabled: true
69
+ jmx:
70
+ exposure:
71
+ include: "*"
72
 
73
  # Externalised CV thresholds
74
  recognition:
75
+ confidence: 0.70
76
  cooldownMs: 10000
77
  face-service:
78
+ url: http://localhost:5001 # to insert backend face service url
src/test/java/com/cs102/attendance/Cs102AttendanceProjectApplicationTests.java CHANGED
@@ -1,50 +1,50 @@
1
- // package com.cs102.attendance;
2
 
3
- // import com.cs102.attendance.repository.TestConnectionRepository;
4
- // import org.junit.jupiter.api.Test;
5
- // import org.springframework.beans.factory.annotation.Autowired;
6
- // import org.springframework.boot.test.context.SpringBootTest;
7
 
8
- // import javax.sql.DataSource;
9
- // import java.sql.Connection;
10
- // import java.sql.SQLException;
11
 
12
- // import static org.junit.jupiter.api.Assertions.*;
13
 
14
- // @SpringBootTest
15
- // class Cs102AttendanceProjectApplicationTests {
16
 
17
- // @Autowired
18
- // private DataSource dataSource;
19
 
20
- // @Autowired
21
- // private TestConnectionRepository testConnectionRepository;
22
 
23
- // @Test
24
- // void contextLoads() {
25
- // }
26
 
27
- // @Test
28
- // void testDatabaseConnection() throws SQLException {
29
- // // Test that we can get a connection from the DataSource
30
- // try (Connection connection = dataSource.getConnection()) {
31
- // assertNotNull(connection, "Database connection should not be null");
32
- // assertTrue(connection.isValid(5), "Database connection should be valid");
33
 
34
- // // Print connection details for verification
35
- // System.out.println("Database URL: " + connection.getMetaData().getURL());
36
- // System.out.println("Database Driver: " + connection.getMetaData().getDriverName());
37
- // System.out.println("Database Version: " + connection.getMetaData().getDatabaseProductVersion());
38
- // }
39
- // }
40
-
41
- // @Test
42
- // void testRepositoryConnection() {
43
- // // Test that the repository can execute queries
44
- // assertDoesNotThrow(() -> {
45
- // Integer result = testConnectionRepository.testQuery();
46
- // assertEquals(1, result, "Test query should return 1");
47
- // }, "Repository test query should not throw an exception");
48
- // }
49
-
50
- // }
 
1
+ package com.cs102.attendance;
2
 
3
+ import com.cs102.attendance.repository.StudentRepository;
4
+ import org.junit.jupiter.api.Test;
5
+ import org.springframework.beans.factory.annotation.Autowired;
6
+ import org.springframework.boot.test.context.SpringBootTest;
7
 
8
+ import javax.sql.DataSource;
9
+ import java.sql.Connection;
10
+ import java.sql.SQLException;
11
 
12
+ import static org.junit.jupiter.api.Assertions.*;
13
 
14
+ @SpringBootTest
15
+ class Cs102AttendanceProjectApplicationTests {
16
 
17
+ @Autowired
18
+ private DataSource dataSource;
19
 
20
+ @Autowired
21
+ private StudentRepository studentRepository;
22
 
23
+ @Test
24
+ void contextLoads() {
25
+ }
26
 
27
+ @Test
28
+ void testDatabaseConnection() throws SQLException {
29
+ // Test that we can get a connection from the DataSource
30
+ try (Connection connection = dataSource.getConnection()) {
31
+ assertNotNull(connection, "Database connection should not be null");
32
+ assertTrue(connection.isValid(5), "Database connection should be valid");
33
 
34
+ // Print connection details for verification
35
+ System.out.println("Database URL: " + connection.getMetaData().getURL());
36
+ System.out.println("Database Driver: " + connection.getMetaData().getDriverName());
37
+ System.out.println("Database Version: " + connection.getMetaData().getDatabaseProductVersion());
38
+ }
39
+ }
40
+
41
+ @Test
42
+ void testRepositoryConnection() {
43
+ // Test that the repository can execute queries
44
+ assertDoesNotThrow(() -> {
45
+ long count = studentRepository.count();
46
+ assertTrue(count >= 0, "Student count should be non-negative");
47
+ }, "Repository test query should not throw an exception");
48
+ }
49
+
50
+ }
src/test/java/com/cs102/attendance/DatabaseIntegrationTest.java ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.cs102.attendance;
2
+
3
+ import com.cs102.attendance.entity.*;
4
+ import com.cs102.attendance.enums.Method;
5
+ import com.cs102.attendance.enums.Status;
6
+ import com.cs102.attendance.repository.*;
7
+ import com.cs102.attendance.service.*;
8
+ import org.junit.jupiter.api.Test;
9
+ import org.springframework.beans.factory.annotation.Autowired;
10
+ import org.springframework.boot.test.context.SpringBootTest;
11
+ import org.springframework.test.annotation.DirtiesContext;
12
+ import org.springframework.transaction.annotation.Transactional;
13
+
14
+ import java.time.LocalDate;
15
+ import java.time.LocalTime;
16
+
17
+ import static org.junit.jupiter.api.Assertions.*;
18
+
19
+ @SpringBootTest
20
+ @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
21
+ public class DatabaseIntegrationTest {
22
+
23
+ @Autowired private StudentRepository studentRepository;
24
+ @Autowired private SessionRepository sessionRepository;
25
+ @Autowired private AttendanceRepository attendanceRepository;
26
+ @Autowired private FaceDataRepository faceDataRepository;
27
+
28
+ @Autowired private StudentService studentService;
29
+ @Autowired private SessionService sessionService;
30
+
31
+ @Test
32
+ @Transactional
33
+ public void testCompleteWorkflow() {
34
+ System.out.println("Testing complete database workflow...");
35
+
36
+ // 1. Test Student Creation
37
+ System.out.println("1. Creating student...");
38
+ Student student = studentService.enrol("CS001", "John Doe", "CS102", "Group A", "[email protected]", "123-456-7890");
39
+ assertNotNull(student.getId());
40
+ assertEquals("CS001", student.getCode());
41
+ System.out.println("Student created: " + student.getName());
42
+
43
+ // 2. Test Session Creation
44
+ System.out.println("2. Creating session...");
45
+ Session session = sessionService.createSession("Morning Lecture", LocalDate.now(), LocalTime.of(9, 0), LocalTime.of(11, 0));
46
+ assertNotNull(session.getId());
47
+ assertEquals("Morning Lecture", session.getName());
48
+ System.out.println("Session created: " + session.getName());
49
+
50
+ // 3. Test Face Data Upload
51
+ System.out.println("3. Uploading face data...");
52
+ FaceData faceData = studentService.uploadFaceImage(student.getId(), "http://example.com/face.jpg");
53
+ assertNotNull(faceData.getId());
54
+ assertEquals(student.getId(), faceData.getStudent().getId());
55
+ System.out.println("Face data uploaded");
56
+
57
+ // 4. Test Attendance Record
58
+ System.out.println("4. Creating attendance record...");
59
+ AttendanceRecord record = new AttendanceRecord(student, session, Status.PRESENT, Method.MANUAL);
60
+ record = attendanceRepository.save(record);
61
+ assertNotNull(record.getId());
62
+ assertEquals(Status.PRESENT, record.getStatus());
63
+ System.out.println("Attendance record created");
64
+
65
+ // 5. Test Repository Queries
66
+ System.out.println("5. Testing repository queries...");
67
+ var foundRecord = attendanceRepository.findBySessionAndStudent(session, student);
68
+ assertTrue(foundRecord.isPresent());
69
+
70
+ long presentCount = attendanceRepository.countBySessionAndStatus(session, Status.PRESENT);
71
+ assertEquals(1, presentCount);
72
+ System.out.println("Repository queries working");
73
+
74
+ // 6. Test Relationships
75
+ System.out.println("6. Testing entity relationships...");
76
+ Student savedStudent = studentRepository.findById(student.getId()).orElse(null);
77
+ assertNotNull(savedStudent);
78
+ assertEquals(1, savedStudent.getFaceData().size());
79
+ assertEquals(1, savedStudent.getAttendanceRecords().size());
80
+ System.out.println("Entity relationships working");
81
+
82
+ System.out.println("All database tests passed.");
83
+ }
84
+
85
+ @Test
86
+ public void testRepositoryBasicOperations() {
87
+ System.out.println("Testing basic repository operations...");
88
+
89
+ // Test counts
90
+ long studentCount = studentRepository.count();
91
+ long sessionCount = sessionRepository.count();
92
+ long attendanceCount = attendanceRepository.count();
93
+ long faceDataCount = faceDataRepository.count();
94
+
95
+ System.out.println("Current counts:");
96
+ System.out.println(" Students: " + studentCount);
97
+ System.out.println(" Sessions: " + sessionCount);
98
+ System.out.println(" Attendance Records: " + attendanceCount);
99
+ System.out.println(" Face Data: " + faceDataCount);
100
+
101
+ assertTrue(studentCount >= 0);
102
+ assertTrue(sessionCount >= 0);
103
+ assertTrue(attendanceCount >= 0);
104
+ assertTrue(faceDataCount >= 0);
105
+
106
+ System.out.println("Repository basic operations working");
107
+ }
108
+ }
src/test/java/com/cs102/attendance/controller/TestControllerTest.java CHANGED
@@ -1,69 +1,77 @@
1
- // package com.cs102.attendance.controller;
2
 
3
- // import com.cs102.attendance.entity.TestConnection;
4
- // import com.cs102.attendance.repository.TestConnectionRepository;
5
- // import org.junit.jupiter.api.Test;
6
- // import org.springframework.beans.factory.annotation.Autowired;
7
- // import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
8
- // import org.springframework.test.context.bean.override.mockito.MockitoBean;
9
- // import org.springframework.http.MediaType;
10
- // import org.springframework.test.web.servlet.MockMvc;
11
 
12
- // import static org.mockito.ArgumentMatchers.any;
13
- // import static org.mockito.Mockito.when;
14
- // import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
15
- // import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
16
 
17
- // @WebMvcTest(TestController.class)
18
- // public class TestControllerTest {
19
 
20
- // @Autowired
21
- // private MockMvc mockMvc;
22
 
23
- // @MockitoBean
24
- // private TestConnectionRepository testConnectionRepository;
25
 
26
- // @Test
27
- // public void testInsertTestDataWithDefaultMessage() throws Exception {
28
- // // Given
29
- // TestConnection mockSavedEntity = new TestConnection("Test message");
30
- // mockSavedEntity.setId(1L);
31
- // when(testConnectionRepository.save(any(TestConnection.class))).thenReturn(mockSavedEntity);
32
 
33
- // // When & Then
34
- // mockMvc.perform(post("/api/test/insert-test-data")
35
- // .contentType(MediaType.APPLICATION_JSON))
36
- // .andExpect(status().isOk())
37
- // .andExpect(jsonPath("$.id").value(1L))
38
- // .andExpect(jsonPath("$.message").value("Test message"));
39
- // }
 
 
40
 
41
- // @Test
42
- // public void testInsertTestDataWithCustomMessage() throws Exception {
43
- // // Given
44
- // String customMessage = "Custom test message";
45
- // TestConnection mockSavedEntity = new TestConnection(customMessage);
46
- // mockSavedEntity.setId(2L);
47
- // when(testConnectionRepository.save(any(TestConnection.class))).thenReturn(mockSavedEntity);
48
 
49
- // // When & Then
50
- // mockMvc.perform(post("/api/test/insert-test-data")
51
- // .param("message", customMessage)
52
- // .contentType(MediaType.APPLICATION_JSON))
53
- // .andExpect(status().isOk())
54
- // .andExpect(jsonPath("$.id").value(2L))
55
- // .andExpect(jsonPath("$.message").value(customMessage));
56
- // }
 
 
 
57
 
58
- // @Test
59
- // public void testInsertTestDataWithException() throws Exception {
60
- // // Given
61
- // when(testConnectionRepository.save(any(TestConnection.class)))
62
- // .thenThrow(new RuntimeException("Database error"));
63
 
64
- // // When & Then
65
- // mockMvc.perform(post("/api/test/insert-test-data")
66
- // .contentType(MediaType.APPLICATION_JSON))
67
- // .andExpect(status().isInternalServerError());
68
- // }
69
- // }
 
 
 
1
+ package com.cs102.attendance.controller;
2
 
3
+ import com.cs102.attendance.entity.Student;
4
+ import com.cs102.attendance.repository.StudentRepository;
5
+ import org.junit.jupiter.api.Test;
6
+ import org.springframework.beans.factory.annotation.Autowired;
7
+ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
8
+ import org.springframework.test.context.bean.override.mockito.MockitoBean;
9
+ import org.springframework.http.MediaType;
10
+ import org.springframework.test.web.servlet.MockMvc;
11
 
12
+ import java.util.UUID;
13
+ import static org.mockito.ArgumentMatchers.any;
14
+ import static org.mockito.Mockito.when;
15
+ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
16
+ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
17
 
18
+ @WebMvcTest(TestController.class)
19
+ public class TestControllerTest {
20
 
21
+ @Autowired
22
+ private MockMvc mockMvc;
23
 
24
+ @MockitoBean
25
+ private StudentRepository studentRepository;
26
 
27
+ @Test
28
+ public void testInsertTestDataWithValidData() throws Exception {
29
+ // Given
30
+ Student mockSavedStudent = new Student("TEST001", "Test Student");
31
+ mockSavedStudent.setId(UUID.randomUUID());
32
+ when(studentRepository.save(any(Student.class))).thenReturn(mockSavedStudent);
33
 
34
+ // When & Then
35
+ mockMvc.perform(post("/api/test/insert-test-data")
36
+ .param("code", "TEST001")
37
+ .param("name", "Test Student")
38
+ .contentType(MediaType.APPLICATION_JSON))
39
+ .andExpect(status().isOk())
40
+ .andExpect(jsonPath("$.code").value("TEST001"))
41
+ .andExpect(jsonPath("$.name").value("Test Student"));
42
+ }
43
 
44
+ @Test
45
+ public void testInsertTestDataWithEmail() throws Exception {
46
+ // Given
47
+ Student mockSavedStudent = new Student("TEST002", "Test Student 2");
48
+ mockSavedStudent.setEmail("[email protected]");
49
+ mockSavedStudent.setId(UUID.randomUUID());
50
+ when(studentRepository.save(any(Student.class))).thenReturn(mockSavedStudent);
51
 
52
+ // When & Then
53
+ mockMvc.perform(post("/api/test/insert-test-data")
54
+ .param("code", "TEST002")
55
+ .param("name", "Test Student 2")
56
+ .param("email", "test@example.com")
57
+ .contentType(MediaType.APPLICATION_JSON))
58
+ .andExpect(status().isOk())
59
+ .andExpect(jsonPath("$.code").value("TEST002"))
60
+ .andExpect(jsonPath("$.name").value("Test Student 2"))
61
+ .andExpect(jsonPath("$.email").value("[email protected]"));
62
+ }
63
 
64
+ @Test
65
+ public void testInsertTestDataWithException() throws Exception {
66
+ // Given
67
+ when(studentRepository.save(any(Student.class)))
68
+ .thenThrow(new RuntimeException("Database error"));
69
 
70
+ // When & Then
71
+ mockMvc.perform(post("/api/test/insert-test-data")
72
+ .param("code", "TEST003")
73
+ .param("name", "Test Student 3")
74
+ .contentType(MediaType.APPLICATION_JSON))
75
+ .andExpect(status().isInternalServerError());
76
+ }
77
+ }
video-object-detection/facedetection.html ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Transformers.js | Real-time object detection</title>
7
+ <link rel="stylesheet" href="/style.css" />
8
+ </head>
9
+
10
+ <body>
11
+ <h1>
12
+ Real-time object detection w/ gelan-c_all
13
+ </h1>
14
+ <h4>
15
+ Runs locally in your browser, powered by
16
+ <a href="http://github.com/huggingface/transformers.js" target="_blank"
17
+ >🤗 Transformers.js</a
18
+ >
19
+ </h4>
20
+ <div id="container">
21
+ <video id="video" autoplay muted playsinline></video>
22
+ <canvas id="canvas" width="360" height="240"></canvas>
23
+ <div id="overlay"></div>
24
+ </div>
25
+ <div id="controls">
26
+ <div>
27
+ <label>Image size</label>
28
+ (<label id="size-value">96</label>)
29
+ <br />
30
+ <input
31
+ id="size"
32
+ type="range"
33
+ min="64"
34
+ max="256"
35
+ step="32"
36
+ value="96"
37
+ disabled
38
+ />
39
+ </div>
40
+ <div>
41
+ <label>Threshold</label>
42
+ (<label id="threshold-value">0.85</label>)
43
+ <br />
44
+ <input
45
+ id="threshold"
46
+ type="range"
47
+ min="0.01"
48
+ max="1"
49
+ step="0.01"
50
+ value="0.85"
51
+ disabled
52
+ />
53
+ </div>
54
+ <div>
55
+ <label>Scale</label>
56
+ (<label id="scale-value">0.5</label>)
57
+ <br />
58
+ <input
59
+ id="scale"
60
+ type="range"
61
+ min="0.01"
62
+ max="1"
63
+ step="0.01"
64
+ value="0.5"
65
+ disabled
66
+ />
67
+ </div>
68
+ </div>
69
+ <label id="status"></label>
70
+
71
+ <script type="module" src="/src/facedetection.js"></script>
72
+ </body>
73
+ </html>
video-object-detection/index.html CHANGED
@@ -1,73 +1,268 @@
1
- <!doctype html>
2
  <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Transformers.js | Real-time object detection</title>
7
- <link rel="stylesheet" href="/style.css" />
8
- </head>
9
-
10
- <body>
11
- <h1>
12
- Real-time object detection w/ gelan-c_all
13
- </h1>
14
- <h4>
15
- Runs locally in your browser, powered by
16
- <a href="http://github.com/huggingface/transformers.js" target="_blank"
17
- >🤗 Transformers.js</a
18
- >
19
- </h4>
20
- <div id="container">
21
- <video id="video" autoplay muted playsinline></video>
22
- <canvas id="canvas" width="360" height="240"></canvas>
23
- <div id="overlay"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  </div>
25
- <div id="controls">
26
- <div>
27
- <label>Image size</label>
28
- (<label id="size-value">128</label>)
29
- <br />
30
- <input
31
- id="size"
32
- type="range"
33
- min="64"
34
- max="256"
35
- step="32"
36
- value="128"
37
- disabled
38
- />
39
- </div>
40
- <div>
41
- <label>Threshold</label>
42
- (<label id="threshold-value">0.85</label>)
43
- <br />
44
- <input
45
- id="threshold"
46
- type="range"
47
- min="0.01"
48
- max="1"
49
- step="0.01"
50
- value="0.85"
51
- disabled
52
- />
53
- </div>
54
- <div>
55
- <label>Scale</label>
56
- (<label id="scale-value">0.5</label>)
57
- <br />
58
- <input
59
- id="scale"
60
- type="range"
61
- min="0.01"
62
- max="1"
63
- step="0.01"
64
- value="0.5"
65
- disabled
66
- />
67
- </div>
68
  </div>
69
- <label id="status"></label>
70
 
71
- <script type="module" src="/main.js"></script>
72
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  </html>
 
1
+ <!DOCTYPE html>
2
  <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Attendance Management System - Login</title>
7
+ <link rel="stylesheet" href="styles2.css">
8
+ <style>
9
+ /* Login-specific styles */
10
+ .login-container {
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+ min-height: 100vh;
15
+ background: #f8fafc;
16
+ }
17
+
18
+ .login-card {
19
+ background: white;
20
+ padding: 3rem;
21
+ border-radius: 16px;
22
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
23
+ width: 100%;
24
+ max-width: 400px;
25
+ }
26
+
27
+ .login-title {
28
+ text-align: center;
29
+ font-size: 1.5rem;
30
+ font-weight: 600;
31
+ color: #1e293b;
32
+ margin-bottom: 2rem;
33
+ }
34
+
35
+ .login-tabs {
36
+ display: flex;
37
+ background: #f1f5f9;
38
+ border-radius: 8px;
39
+ padding: 0.25rem;
40
+ margin-bottom: 2rem;
41
+ }
42
+
43
+ .tab {
44
+ flex: 1;
45
+ padding: 0.75rem;
46
+ text-align: center;
47
+ border: none;
48
+ background: transparent;
49
+ border-radius: 6px;
50
+ cursor: pointer;
51
+ font-weight: 500;
52
+ color: #64748b;
53
+ transition: all 0.2s;
54
+ }
55
+
56
+ .tab.active {
57
+ background: white;
58
+ color: #1e293b;
59
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
60
+ }
61
+ </style>
62
+ </head>
63
+ <body>
64
+ <div class="login-container">
65
+ <div class="login-card">
66
+ <h1 class="login-title">Attendance Management System</h1>
67
+
68
+ <div class="login-tabs">
69
+ <button class="tab active" onclick="switchTab('student')">Student</button>
70
+ <button class="tab" onclick="switchTab('professor')">Professor/TA</button>
71
+ </div>
72
+
73
+ <!-- Enhanced login form -->
74
+ <form id="loginForm" style="display: block;">
75
+ <div class="form-group">
76
+ <label class="form-label" for="loginEmail">Email</label>
77
+ <input type="email" id="loginEmail" class="form-input" required>
78
+ </div>
79
+ <div class="form-group">
80
+ <label class="form-label" for="loginPassword">Password</label>
81
+ <input type="password" id="loginPassword" class="form-input" required>
82
+ </div>
83
+ <button type="submit" class="btn btn-primary" style="width: 100%;">Login</button>
84
+
85
+ <!-- Added register hyperlink underneath login button -->
86
+ <div style="text-align: center; margin-top: 1rem;">
87
+ <span style="color: #64748b; font-size: 0.875rem;">Don't have an account? </span>
88
+ <a href="#" onclick="showRegisterForm()" style="color: #0ea5e9; text-decoration: none; font-weight: 500; font-size: 0.875rem;">Register here</a>
89
+ </div>
90
+ </form>
91
+
92
+ <!-- Enhanced registration form with role selection and removed toggle -->
93
+ <form id="registerForm" style="display: none;">
94
+ <div class="form-group">
95
+ <label class="form-label" for="registerName">Full Name</label>
96
+ <input type="text" id="registerName" class="form-input" required>
97
+ </div>
98
+ <div class="form-group">
99
+ <label class="form-label" for="registerEmail">Email</label>
100
+ <input type="email" id="registerEmail" class="form-input" required>
101
+ </div>
102
+ <div class="form-group">
103
+ <label class="form-label" for="registerRole">Role</label>
104
+ <select id="registerRole" class="form-input" required>
105
+ <option value="">Select your role</option>
106
+ <option value="student">Student</option>
107
+ <option value="professor">Professor/TA</option>
108
+ </select>
109
+ </div>
110
+ <div class="form-group">
111
+ <label class="form-label" for="registerPassword">Password</label>
112
+ <input type="password" id="registerPassword" class="form-input" required>
113
+ </div>
114
+ <div class="form-group">
115
+ <label class="form-label" for="confirmPassword">Confirm Password</label>
116
+ <input type="password" id="confirmPassword" class="form-input" required>
117
+ </div>
118
+ <div id="passwordError" style="color: #ef4444; font-size: 0.875rem; margin-bottom: 1rem; display: none;">
119
+ Passwords do not match
120
+ </div>
121
+ <button type="submit" class="btn btn-primary" style="width: 100%;">Register & Setup Face</button>
122
+
123
+ <!-- Added back to login link -->
124
+ <div style="text-align: center; margin-top: 1rem;">
125
+ <span style="color: #64748b; font-size: 0.875rem;">Already have an account? </span>
126
+ <a href="#" onclick="showLoginForm()" style="color: #0ea5e9; text-decoration: none; font-weight: 500; font-size: 0.875rem;">Login here</a>
127
+ </div>
128
+ </form>
129
+ </div>
130
  </div>
131
+
132
+ <!-- Enhanced Face Registration Modal with live camera -->
133
+ <div id="faceRegisterModal" class="modal">
134
+ <div class="modal-content" style="max-width: 500px;">
135
+ <div class="modal-header">
136
+ <h3 class="modal-title">Register Your Face</h3>
137
+ <button class="close-btn" onclick="closeFaceRegister()">&times;</button>
138
+ </div>
139
+ <div class="modal-body">
140
+ <p style="text-align: center; color: #64748b; margin-bottom: 1rem;">
141
+ Position your face within the frame and click capture to complete registration
142
+ </p>
143
+ <div class="camera-container" style="position: relative; background: #000; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
144
+ <video id="faceVideo" style="width: 100%; height: 300px; object-fit: cover;" autoplay muted></video>
145
+ <canvas id="faceCanvas" style="display: none;"></canvas>
146
+ <div class="scan-overlay" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 200px; height: 200px; border: 2px solid #22d3ee; border-radius: 50%; opacity: 0.8;"></div>
147
+ </div>
148
+ <div id="captureResult" style="text-align: center; margin-bottom: 1rem; display: none;">
149
+ <div style="color: #22c55e; font-weight: 500;">✓ Face captured successfully!</div>
150
+ </div>
151
+ <div style="display: flex; gap: 1rem;">
152
+ <button id="captureBtn" class="btn btn-primary" style="flex: 1;" onclick="captureFace()">Capture Face</button>
153
+ <button id="completeBtn" class="btn btn-success" style="flex: 1; display: none;" onclick="completeRegistration()">Complete Registration</button>
154
+ <button class="btn btn-secondary" style="flex: 1;" onclick="closeFaceRegister()">Cancel</button>
155
+ </div>
156
+ </div>
157
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  </div>
 
159
 
160
+ <script>
161
+ let currentUser = 'student';
162
+ let faceStream = null;
163
+ let faceCaptured = false;
164
+
165
+ function switchTab(userType) {
166
+ currentUser = userType;
167
+ const tabs = document.querySelectorAll('.tab');
168
+ tabs.forEach(tab => tab.classList.remove('active'));
169
+ event.target.classList.add('active');
170
+ }
171
+
172
+ function showRegisterForm() {
173
+ document.getElementById('loginForm').style.display = 'none';
174
+ document.getElementById('registerForm').style.display = 'block';
175
+ document.querySelector('.login-tabs').style.display = 'none';
176
+ }
177
+
178
+ function showLoginForm() {
179
+ document.getElementById('registerForm').style.display = 'none';
180
+ document.getElementById('loginForm').style.display = 'block';
181
+ document.querySelector('.login-tabs').style.display = 'flex';
182
+ }
183
+
184
+ async function showFaceRegister() {
185
+ document.getElementById('faceRegisterModal').classList.add('active');
186
+
187
+ try {
188
+ faceStream = await navigator.mediaDevices.getUserMedia({
189
+ video: {
190
+ width: 640,
191
+ height: 480,
192
+ facingMode: 'user'
193
+ }
194
+ });
195
+ document.getElementById('faceVideo').srcObject = faceStream;
196
+ } catch (error) {
197
+ console.error('Error accessing camera:', error);
198
+ alert('Unable to access camera. Please ensure camera permissions are granted.');
199
+ }
200
+ }
201
+
202
+ function closeFaceRegister() {
203
+ document.getElementById('faceRegisterModal').classList.remove('active');
204
+ if (faceStream) {
205
+ faceStream.getTracks().forEach(track => track.stop());
206
+ faceStream = null;
207
+ }
208
+ // Reset capture state
209
+ faceCaptured = false;
210
+ document.getElementById('captureResult').style.display = 'none';
211
+ document.getElementById('captureBtn').style.display = 'block';
212
+ document.getElementById('completeBtn').style.display = 'none';
213
+ }
214
+
215
+ function captureFace() {
216
+ const video = document.getElementById('faceVideo');
217
+ const canvas = document.getElementById('faceCanvas');
218
+ const ctx = canvas.getContext('2d');
219
+
220
+ canvas.width = video.videoWidth;
221
+ canvas.height = video.videoHeight;
222
+ ctx.drawImage(video, 0, 0);
223
+
224
+ // Simulate face detection processing
225
+ setTimeout(() => {
226
+ faceCaptured = true;
227
+ document.getElementById('captureResult').style.display = 'block';
228
+ document.getElementById('captureBtn').style.display = 'none';
229
+ document.getElementById('completeBtn').style.display = 'block';
230
+ }, 1000);
231
+ }
232
+
233
+ function completeRegistration() {
234
+ alert('Registration completed successfully! You can now login.');
235
+ closeFaceRegister();
236
+ showLoginForm();
237
+ }
238
+
239
+ document.getElementById('loginForm').addEventListener('submit', function(e) {
240
+ e.preventDefault();
241
+
242
+ if (currentUser === 'student') {
243
+ window.location.href = 'student.html';
244
+ } else {
245
+ window.location.href = 'professor.html';
246
+ }
247
+ });
248
+
249
+ document.getElementById('registerForm').addEventListener('submit', function(e) {
250
+ e.preventDefault();
251
+
252
+ const password = document.getElementById('registerPassword').value;
253
+ const confirmPassword = document.getElementById('confirmPassword').value;
254
+ const errorDiv = document.getElementById('passwordError');
255
+
256
+ if (password !== confirmPassword) {
257
+ errorDiv.style.display = 'block';
258
+ return;
259
+ }
260
+
261
+ errorDiv.style.display = 'none';
262
+ const selectedRole = document.getElementById('registerRole').value;
263
+ sessionStorage.setItem('registeringRole', selectedRole);
264
+ showFaceRegister();
265
+ });
266
+ </script>
267
+ </body>
268
  </html>
video-object-detection/professor.html ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Professor Dashboard - Attendance System</title>
7
+ <link rel="stylesheet" href="styles2.css">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <div class="header">
12
+ <div class="header-left">
13
+ <div class="logo-icon">👨‍🏫</div>
14
+ <h1 class="header-title">Professor Dashboard</h1>
15
+ </div>
16
+ <div class="header-right">
17
+ <span class="welcome-text">Welcome, Dr. Smith</span>
18
+ <button class="logout-btn" onclick="logout()">Logout</button>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="welcome-section">
23
+ <h2 class="welcome-title">Welcome back!</h2>
24
+ <p class="welcome-subtitle">Manage your classes and track student attendance</p>
25
+ </div>
26
+
27
+ <div class="stats-grid">
28
+ <div class="stat-card">
29
+ <div class="stat-icon blue">📊</div>
30
+ <div class="stat-content">
31
+ <h3>Average Attendance</h3>
32
+ <div class="stat-number">78%</div>
33
+ </div>
34
+ </div>
35
+ <div class="stat-card">
36
+ <div class="stat-icon orange">👥</div>
37
+ <div class="stat-content">
38
+ <h3>Total Students</h3>
39
+ <div class="stat-number">125</div>
40
+ </div>
41
+ </div>
42
+ <div class="stat-card">
43
+ <div class="stat-icon yellow">📅</div>
44
+ <div class="stat-content">
45
+ <h3>Active Classes</h3>
46
+ <div class="stat-number">3</div>
47
+ </div>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="section-card">
52
+ <div class="section-header">
53
+ <h3 class="section-title">Class Management</h3>
54
+ <p class="section-subtitle">Create and manage your class sessions</p>
55
+ </div>
56
+ <div class="section-content">
57
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
58
+ <button class="btn btn-primary" onclick="openCreateClass()">Create Class/Session</button>
59
+ <button class="btn btn-secondary" onclick="openManualAttendance()">Manual Attendance</button>
60
+ <button class="btn btn-secondary" onclick="openGenerateReport()">Generate Report</button>
61
+ <button class="btn btn-secondary" onclick="openStudentManagement()">Manage Students</button>
62
+ <button class="btn btn-secondary" onclick="openAttendanceCheck()">Check Total Attendance</button>
63
+ <button class="btn btn-danger" onclick="closeActiveSession()">Close Active Session</button>
64
+ </div>
65
+ </div>
66
+ </div>
67
+
68
+ <div class="section-card">
69
+ <div class="section-header">
70
+ <h3 class="section-title">Active Sessions</h3>
71
+ </div>
72
+ <div class="section-content" id="activeSessions">
73
+ <p class="welcome-subtitle">No active sessions at the moment</p>
74
+ </div>
75
+ </div>
76
+ </div>
77
+
78
+ <!-- All Professor Modals -->
79
+ <!-- Create Class Modal -->
80
+ <div id="createClassModal" class="modal">
81
+ <div class="modal-content">
82
+ <div class="modal-header">
83
+ <h3 class="modal-title">Create Class/Session</h3>
84
+ <button class="close-btn" onclick="closeCreateClass()">&times;</button>
85
+ </div>
86
+ <form id="createClassForm">
87
+ <div class="form-group">
88
+ <label class="form-label">Class Name</label>
89
+ <input type="text" class="form-input" placeholder="e.g., CS101 - Introduction to Programming" required>
90
+ </div>
91
+ <div class="form-group">
92
+ <label class="form-label">Session Date</label>
93
+ <input type="date" class="form-input" required>
94
+ </div>
95
+ <div class="form-group">
96
+ <label class="form-label">Start Time</label>
97
+ <input type="time" class="form-input" required>
98
+ </div>
99
+ <div class="form-group">
100
+ <label class="form-label">Duration (minutes)</label>
101
+ <input type="number" class="form-input" placeholder="90" required>
102
+ </div>
103
+ <div style="display: flex; gap: 1rem;">
104
+ <button type="submit" class="btn btn-primary" style="flex: 1;">Create Session</button>
105
+ <button type="button" class="btn btn-secondary" style="flex: 1;" onclick="closeCreateClass()">Cancel</button>
106
+ </div>
107
+ </form>
108
+ </div>
109
+ </div>
110
+
111
+ <!-- Manual Attendance Modal -->
112
+ <div id="manualAttendanceModal" class="modal">
113
+ <div class="modal-content">
114
+ <div class="modal-header">
115
+ <h3 class="modal-title">Manual Attendance Marking</h3>
116
+ <button class="close-btn" onclick="closeManualAttendance()">&times;</button>
117
+ </div>
118
+ <div class="form-group">
119
+ <label class="form-label">Select Class</label>
120
+ <select class="form-select">
121
+ <option>CS101 - Introduction to Programming</option>
122
+ <option>MATH201 - Calculus II</option>
123
+ <option>PHY301 - Quantum Physics</option>
124
+ </select>
125
+ </div>
126
+ <div class="student-list">
127
+ <div class="student-item">
128
+ <span>John Doe ([email protected])</span>
129
+ <div class="attendance-controls">
130
+ <button class="btn btn-success btn-sm">Present</button>
131
+ <button class="btn btn-danger btn-sm">Absent</button>
132
+ </div>
133
+ </div>
134
+ <div class="student-item">
135
+ <span>Jane Smith ([email protected])</span>
136
+ <div class="attendance-controls">
137
+ <button class="btn btn-success btn-sm">Present</button>
138
+ <button class="btn btn-danger btn-sm">Absent</button>
139
+ </div>
140
+ </div>
141
+ <div class="student-item">
142
+ <span>Mike Johnson ([email protected])</span>
143
+ <div class="attendance-controls">
144
+ <button class="btn btn-success btn-sm">Present</button>
145
+ <button class="btn btn-danger btn-sm">Absent</button>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ <div style="display: flex; gap: 1rem; margin-top: 1rem;">
150
+ <button class="btn btn-primary" style="flex: 1;">Save Attendance</button>
151
+ <button class="btn btn-secondary" style="flex: 1;" onclick="closeManualAttendance()">Cancel</button>
152
+ </div>
153
+ </div>
154
+ </div>
155
+
156
+ <!-- Generate Report Modal -->
157
+ <div id="generateReportModal" class="modal">
158
+ <div class="modal-content">
159
+ <div class="modal-header">
160
+ <h3 class="modal-title">Generate Attendance Report</h3>
161
+ <button class="close-btn" onclick="closeGenerateReport()">&times;</button>
162
+ </div>
163
+ <div class="form-group">
164
+ <label class="form-label">Select Class</label>
165
+ <select class="form-select">
166
+ <option>CS101 - Introduction to Programming</option>
167
+ <option>MATH201 - Calculus II</option>
168
+ <option>PHY301 - Quantum Physics</option>
169
+ </select>
170
+ </div>
171
+ <div class="form-group">
172
+ <label class="form-label">Date Range</label>
173
+ <div style="display: flex; gap: 1rem;">
174
+ <input type="date" class="form-input" style="flex: 1;">
175
+ <input type="date" class="form-input" style="flex: 1;">
176
+ </div>
177
+ </div>
178
+ <table class="attendance-table">
179
+ <thead>
180
+ <tr>
181
+ <th>Student</th>
182
+ <th>Total Classes</th>
183
+ <th>Present</th>
184
+ <th>Attendance Rate</th>
185
+ </tr>
186
+ </thead>
187
+ <tbody>
188
+ <tr>
189
+ <td>John Doe</td>
190
+ <td>20</td>
191
+ <td>18</td>
192
+ <td><span class="status-badge status-present">90%</span></td>
193
+ </tr>
194
+ <tr>
195
+ <td>Jane Smith</td>
196
+ <td>20</td>
197
+ <td>15</td>
198
+ <td><span class="status-badge status-absent">75%</span></td>
199
+ </tr>
200
+ </tbody>
201
+ </table>
202
+ <div class="export-buttons">
203
+ <button class="btn btn-success" onclick="exportCSV()">Export as CSV</button>
204
+ <button class="btn btn-danger" onclick="exportPDF()">Export as PDF</button>
205
+ <button class="btn btn-secondary" onclick="closeGenerateReport()">Close</button>
206
+ </div>
207
+ </div>
208
+ </div>
209
+
210
+ <!-- Student Management Modal -->
211
+ <div id="studentManagementModal" class="modal">
212
+ <div class="modal-content">
213
+ <div class="modal-header">
214
+ <h3 class="modal-title">Student Management</h3>
215
+ <button class="close-btn" onclick="closeStudentManagement()">&times;</button>
216
+ </div>
217
+ <div class="form-group">
218
+ <label class="form-label">Add Student to Class</label>
219
+ <div style="display: flex; gap: 1rem;">
220
+ <input type="email" class="form-input" placeholder="Student email" style="flex: 1;">
221
+ <select class="form-select" style="flex: 1;">
222
+ <option>Select Class</option>
223
+ <option>CS101</option>
224
+ <option>MATH201</option>
225
+ <option>PHY301</option>
226
+ </select>
227
+ <button class="btn btn-primary">Add</button>
228
+ </div>
229
+ </div>
230
+ <div class="student-list">
231
+ <div class="student-item">
232
+ <span>John Doe - CS101</span>
233
+ <button class="btn btn-danger btn-sm">Remove</button>
234
+ </div>
235
+ <div class="student-item">
236
+ <span>Jane Smith - CS101</span>
237
+ <button class="btn btn-danger btn-sm">Remove</button>
238
+ </div>
239
+ <div class="student-item">
240
+ <span>Mike Johnson - MATH201</span>
241
+ <button class="btn btn-danger btn-sm">Remove</button>
242
+ </div>
243
+ </div>
244
+ </div>
245
+ </div>
246
+
247
+ <!-- Attendance Check Modal -->
248
+ <div id="attendanceCheckModal" class="modal">
249
+ <div class="modal-content">
250
+ <div class="modal-header">
251
+ <h3 class="modal-title">Check Total Attendance</h3>
252
+ <button class="close-btn" onclick="closeAttendanceCheck()">&times;</button>
253
+ </div>
254
+ <div class="form-group">
255
+ <label class="form-label">Select Class</label>
256
+ <select class="form-select">
257
+ <option>CS101 - Introduction to Programming</option>
258
+ <option>MATH201 - Calculus II</option>
259
+ <option>PHY301 - Quantum Physics</option>
260
+ </select>
261
+ </div>
262
+ <div style="background: #f8fafc; padding: 1.5rem; border-radius: 8px; margin: 1rem 0;">
263
+ <h4 style="margin-bottom: 1rem; color: #1e293b;">Class Statistics</h4>
264
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem;">
265
+ <div>
266
+ <div style="font-size: 1.5rem; font-weight: 700; color: #0ea5e9;">25</div>
267
+ <div style="font-size: 0.875rem; color: #64748b;">Total Students</div>
268
+ </div>
269
+ <div>
270
+ <div style="font-size: 1.5rem; font-weight: 700; color: #10b981;">78%</div>
271
+ <div style="font-size: 0.875rem; color: #64748b;">Average Attendance</div>
272
+ </div>
273
+ <div>
274
+ <div style="font-size: 1.5rem; font-weight: 700; color: #f59e0b;">15</div>
275
+ <div style="font-size: 0.875rem; color: #64748b;">Total Sessions</div>
276
+ </div>
277
+ </div>
278
+ </div>
279
+ </div>
280
+ </div>
281
+
282
+ <script type="module" src="src/script.js"></script>
283
+
284
+ </body>
285
+ </html>
video-object-detection/src/facedetection.js ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { AutoModel, AutoProcessor, RawImage } from "@huggingface/transformers";
2
+ import { Client } from "@gradio/client";
3
+
4
+ // Reference the elements that we will need
5
+ const status = document.getElementById("status");
6
+ const container = document.getElementById("container");
7
+ const overlay = document.getElementById("overlay");
8
+ const canvas = document.getElementById("canvas");
9
+ const video = document.getElementById("video");
10
+ const thresholdSlider = document.getElementById("threshold");
11
+ const thresholdLabel = document.getElementById("threshold-value");
12
+ const sizeSlider = document.getElementById("size");
13
+ const sizeLabel = document.getElementById("size-value");
14
+ const scaleSlider = document.getElementById("scale");
15
+ const scaleLabel = document.getElementById("scale-value");
16
+
17
+ function setStreamSize(width, height) {
18
+ video.width = canvas.width = Math.round(width);
19
+ video.height = canvas.height = Math.round(height);
20
+ }
21
+
22
+ status.textContent = "Loading model...";
23
+
24
+ // Load model and processor
25
+ const model_id = "Xenova/gelan-c_all";
26
+ const model = await AutoModel.from_pretrained(model_id);
27
+ const processor = await AutoProcessor.from_pretrained(model_id);
28
+
29
+ // Set up controls
30
+ let scale = 0.5;
31
+ scaleSlider.addEventListener("input", () => {
32
+ scale = Number(scaleSlider.value);
33
+ setStreamSize(video.videoWidth * scale, video.videoHeight * scale);
34
+ scaleLabel.textContent = scale;
35
+ });
36
+ scaleSlider.disabled = false;
37
+
38
+ let threshold = 0.80;
39
+ thresholdSlider.addEventListener("input", () => {
40
+ threshold = Number(thresholdSlider.value);
41
+ thresholdLabel.textContent = threshold.toFixed(2);
42
+ });
43
+ thresholdSlider.disabled = false;
44
+
45
+ let size = 96;
46
+ processor.feature_extractor.size = { shortest_edge: size };
47
+ sizeSlider.addEventListener("input", () => {
48
+ size = Number(sizeSlider.value);
49
+ processor.feature_extractor.size = { shortest_edge: size };
50
+ sizeLabel.textContent = size;
51
+ });
52
+ sizeSlider.disabled = false;
53
+
54
+ status.textContent = "Ready";
55
+
56
+ const COLOURS = [
57
+ "#EF4444", "#4299E1", "#059669", "#FBBF24", "#4B52B1", "#7B3AC2",
58
+ "#ED507A", "#1DD1A1", "#F3873A", "#4B5563", "#DC2626", "#1852B4",
59
+ "#18A35D", "#F59E0B", "#4059BE", "#6027A5", "#D63D60", "#00AC9B",
60
+ "#E64A19", "#272A34",
61
+ ];
62
+
63
+ // Function to send canvas image to Gradio API
64
+ // async function sendCanvasImageToAPI(canvas) {
65
+ // return new Promise((resolve, reject) => {
66
+ // canvas.toBlob(async (blob) => {
67
+ // if (!blob) {
68
+ // reject("Failed to get Blob from canvas");
69
+ // return;
70
+ // }
71
+
72
+ // const file1 = new File([blob], "detected.png", { type: "image/png" });
73
+
74
+ // try {
75
+ // // Fetch image from URL and convert to Blob
76
+ // const response = await fetch("https://qvnhhditkzzeudppuezf.supabase.co/storage/v1/object/public/post-images/post-images/1752289670997-kevan.jpg");
77
+ // const frame2Blob = await response.blob();
78
+ // const file2 = new File([frame2Blob], "frame2.jpg", { type: frame2Blob.type });
79
+
80
+ // const client = await Client.connect("MiniAiLive/FaceRecognition-LivenessDetection-Demo");
81
+
82
+ // // Send canvas image as frame1, URL image as frame2
83
+ // const result = await client.predict("/face_compare", {
84
+ // frame1: file1,
85
+ // frame2: file2,
86
+ // });
87
+ // resolve(result);
88
+ // } catch (err) {
89
+ // reject(err);
90
+ // }
91
+ // }, "image/png");
92
+ // });
93
+ // }
94
+
95
+ async function sendCanvasImageToAPI(canvas) {
96
+ return new Promise((resolve, reject) => {
97
+ canvas.toBlob(async (blob) => {
98
+ if (!blob) {
99
+ reject("Failed to get Blob from canvas");
100
+ return;
101
+ }
102
+
103
+ const file = new File([blob], "detected.png", { type: "image/png" });
104
+
105
+ try {
106
+ const formData = new FormData();
107
+ formData.append("image", file);
108
+
109
+ const response = await fetch("https://kevansoon-java-endpoint.hf.space/call-face-recognition", {
110
+ method: "POST",
111
+ body: formData,
112
+ });
113
+
114
+ if (!response.ok) {
115
+ reject("Failed to send image: " + response.statusText);
116
+ return;
117
+ }
118
+
119
+ const result = await response.text(); // or JSON if backend returns JSON
120
+ resolve(result);
121
+ } catch (err) {
122
+ reject(err);
123
+ }
124
+ }, "image/png");
125
+ });
126
+ }
127
+
128
+
129
+ // Variables to store current bounding box and label elements
130
+ let currentBoxElement = null;
131
+ let currentLabelElement = null;
132
+
133
+ let hasSent = false; // Flag to send API request only once per detection
134
+
135
+ var color = "yellow";
136
+ var text = "Verifying..."
137
+ // Render a bounding box and label on the image
138
+ function renderBox([xmin, ymin, xmax, ymax, score, id], [w, h]) {
139
+ if (score < threshold) return; // Skip boxes with low confidence
140
+
141
+
142
+
143
+ // Create bounding box div
144
+ let boxElement = document.createElement("div");
145
+ boxElement.className = "bounding-box";
146
+ Object.assign(boxElement.style, {
147
+ borderColor: color,
148
+ left: (100 * xmin) / w + "%",
149
+ top: (100 * ymin) / h + "%",
150
+ width: (100 * (xmax - xmin)) / w + "%",
151
+ height: (100 * (ymax - ymin)) / h + "%",
152
+ });
153
+
154
+ // Create label span
155
+ let labelElement = document.createElement("span");
156
+ labelElement.textContent = text;
157
+ labelElement.className = "bounding-box-label";
158
+ labelElement.style.backgroundColor = color;
159
+ labelElement.style.color = "black";
160
+
161
+ boxElement.appendChild(labelElement);
162
+ overlay.appendChild(boxElement);
163
+
164
+ // Store references globally for updating after API response
165
+ currentBoxElement = boxElement;
166
+ currentLabelElement = labelElement;
167
+
168
+ // Send image to the API on first detection
169
+ if (!hasSent) {
170
+ hasSent = true;
171
+ sendCanvasImageToAPI(canvas)
172
+ .then((response) => {
173
+
174
+ const responseObj = JSON.parse(response);
175
+ const confidenceStr = responseObj.result; // "0.982708"
176
+
177
+ // Extract decimal part only:
178
+ // const decimalPart = confidenceStr.slice(confidenceStr.indexOf('.') + 1);
179
+ // console.log(decimalPart); // e.g., "982708"
180
+
181
+ // Convert to float for comparison
182
+ const decimalNumber = parseFloat(confidenceStr);
183
+ console.log(decimalNumber);
184
+
185
+
186
+ if (decimalNumber !== null && decimalNumber > 0.80) {
187
+ // --- MODIFICATION START ---
188
+ // Instead of removing and recreating the box, just update its style.
189
+ color = "green";
190
+ text = "Verified Successfully! " + decimalNumber;
191
+ currentLabelElement.style.color = "black"; // Or "white" if it looks better
192
+
193
+ console.log("Updated box to green");
194
+ // --- MODIFICATION END ---
195
+
196
+ } else if (decimalNumber !== null) {
197
+ // Not identified case - update existing box and label to red
198
+ color = "red";
199
+ text = "Failed to verify"
200
+ currentLabelElement.style.color = "black"; // Or "white" if it looks better
201
+
202
+ console.log("Updated box to red");
203
+ } else {
204
+ // Fallback yellow verifying state
205
+ currentLabelElement.textContent = "Verifying...";
206
+ currentLabelElement.style.backgroundColor = "yellow";
207
+ currentLabelElement.style.color = "black";
208
+ currentBoxElement.style.borderColor = "yellow";
209
+
210
+ console.log("Fallback to yellow");
211
+ }
212
+ })
213
+ .catch((err) => {
214
+ console.error("Error sending image to API:", err);
215
+ // On error, fallback to yellow verifying
216
+ if (currentLabelElement && currentBoxElement) {
217
+ currentLabelElement.textContent = "Verifying...";
218
+ currentLabelElement.style.backgroundColor = "yellow";
219
+ currentLabelElement.style.color = "black";
220
+ currentBoxElement.style.borderColor = "yellow";
221
+ }
222
+ });
223
+ }
224
+ }
225
+
226
+ let isProcessing = false;
227
+ let previousTime;
228
+ const context = canvas.getContext("2d", { willReadFrequently: true });
229
+
230
+ function updateCanvas() {
231
+ const { width, height } = canvas;
232
+ context.drawImage(video, 0, 0, width, height);
233
+
234
+ if (!isProcessing) {
235
+ isProcessing = true;
236
+ (async function () {
237
+ const pixelData = context.getImageData(0, 0, width, height).data;
238
+ const image = new RawImage(pixelData, width, height, 4);
239
+
240
+ const inputs = await processor(image);
241
+ const { outputs } = await model(inputs);
242
+
243
+ overlay.innerHTML = "";
244
+
245
+ const sizes = inputs.reshaped_input_sizes[0].reverse();
246
+ outputs.tolist().forEach((x) => renderBox(x, sizes));
247
+
248
+ if (previousTime !== undefined) {
249
+ const fps = 1000 / (performance.now() - previousTime);
250
+ status.textContent = `FPS: ${fps.toFixed(2)}`;
251
+ }
252
+ previousTime = performance.now();
253
+ isProcessing = false;
254
+ })();
255
+ }
256
+
257
+ window.requestAnimationFrame(updateCanvas);
258
+ }
259
+
260
+ // Start the video stream
261
+ navigator.mediaDevices
262
+ .getUserMedia({ video: true })
263
+ .then((stream) => {
264
+ video.srcObject = stream;
265
+ video.play();
266
+
267
+ const videoTrack = stream.getVideoTracks()[0];
268
+ const { width, height } = videoTrack.getSettings();
269
+
270
+ setStreamSize(width * scale, height * scale);
271
+
272
+ const ar = width / height;
273
+ const [cw, ch] = ar > 720 / 405 ? [720, 720 / ar] : [405 * ar, 405];
274
+ container.style.width = `${cw}px`;
275
+ container.style.height = `${ch}px`;
276
+
277
+ window.requestAnimationFrame(updateCanvas);
278
+ })
279
+ .catch((error) => {
280
+ alert(error);
281
+ });
282
+
283
+
284
+
285
+
video-object-detection/{main.js → src/script.js} RENAMED
File without changes
video-object-detection/student.html ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Student Dashboard - Attendance System</title>
7
+ <link rel="stylesheet" href="styles2.css">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <!-- Header design matching the image -->
12
+ <div class="header">
13
+ <div class="header-left">
14
+ <div class="logo-icon">👤</div>
15
+ <h1 class="header-title">Student Dashboard</h1>
16
+ </div>
17
+ <div class="header-right">
18
+ <span class="welcome-text">Welcome, a@a</span>
19
+ <button class="logout-btn" onclick="logout()">Logout</button>
20
+ </div>
21
+ </div>
22
+
23
+ <!-- Welcome section -->
24
+ <div class="welcome-section">
25
+ <h2 class="welcome-title">Welcome back!</h2>
26
+ <p class="welcome-subtitle">Manage your class attendance and view your records</p>
27
+ </div>
28
+
29
+ <!-- Stats cards matching the image -->
30
+ <div class="stats-grid">
31
+ <div class="stat-card">
32
+ <div class="stat-icon blue">✓</div>
33
+ <div class="stat-content">
34
+ <h3>Attendance Rate</h3>
35
+ <div class="stat-number">85%</div>
36
+ </div>
37
+ </div>
38
+ <div class="stat-card">
39
+ <div class="stat-icon orange">📚</div>
40
+ <div class="stat-content">
41
+ <h3>Total Classes</h3>
42
+ <div class="stat-number">24</div>
43
+ </div>
44
+ </div>
45
+ <div class="stat-card">
46
+ <div class="stat-icon yellow">🕐</div>
47
+ <div class="stat-content">
48
+ <h3>Classes Today</h3>
49
+ <div class="stat-number">3</div>
50
+ </div>
51
+ </div>
52
+ </div>
53
+
54
+ <!-- Class selection section -->
55
+ <div class="section-card">
56
+ <div class="section-header">
57
+ <h3 class="section-title">Select Class/Session</h3>
58
+ <p class="section-subtitle">Available Classes</p>
59
+ </div>
60
+ <div class="section-content">
61
+ <select class="form-select" id="classSelect" onchange="enableFaceScanning()">
62
+ <option value="">Select a class</option>
63
+ <option value="cs101">CS101 - Introduction to Programming</option>
64
+ <option value="math201">MATH201 - Calculus II</option>
65
+ <option value="phy301">PHY301 - Quantum Physics</option>
66
+ </select>
67
+ <button id="scanFaceBtn" class="btn btn-primary" style="width: 100%; margin-top: 1rem; display: none;" onclick="openFaceScanning()">
68
+ Scan Face for Attendance
69
+ </button>
70
+ </div>
71
+ </div>
72
+
73
+ <!-- Recent Attendance section -->
74
+ <div class="section-card">
75
+ <div class="section-header">
76
+ <h3 class="section-title">Recent Attendance</h3>
77
+ </div>
78
+ <div class="section-content">
79
+ <p class="welcome-subtitle">Your recent attendance records will appear here</p>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ <!-- Face Scanning Modal -->
85
+ <div id="faceScanModal" class="modal">
86
+ <div class="modal-content" style="max-width: 500px;">
87
+ <div class="modal-header">
88
+ <h3 class="modal-title">Scan Face for Attendance</h3>
89
+ <button class="close-btn" onclick="closeFaceScanning()">&times;</button>
90
+ </div>
91
+ <div class="modal-body">
92
+ <p style="text-align: center; color: #64748b; margin-bottom: 1rem;">
93
+ Position your face within the frame for attendance verification
94
+ </p>
95
+ <div class="camera-container" style="position: relative; background: #000; border-radius: 8px; overflow: hidden; margin-bottom: 1rem;">
96
+ <video id="attendanceVideo" style="width: 100%; height: 300px; object-fit: cover;" autoplay muted></video>
97
+ <canvas id="attendanceCanvas" style="display: none;"></canvas>
98
+ <div class="scan-overlay" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 200px; height: 200px; border: 2px solid #22d3ee; border-radius: 50%; opacity: 0.8;"></div>
99
+ </div>
100
+ <div id="scanResult" style="text-align: center; margin-bottom: 1rem; display: none;">
101
+ <div style="color: #22c55e; font-weight: 500;">✓ Face verified successfully!</div>
102
+ </div>
103
+ <div style="display: flex; gap: 1rem;">
104
+ <button id="scanBtn" class="btn btn-primary" style="flex: 1;" onclick="scanFace()">Scan Face</button>
105
+ <button id="markPresentBtn" class="btn btn-success" style="flex: 1; display: none;" onclick="markAttendance()">Mark Present</button>
106
+ <button class="btn btn-secondary" style="flex: 1;" onclick="closeFaceScanning()">Cancel</button>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+
112
+ <script type="module" src="src/script.js"></script>
113
+
114
+ </body>
115
+ </html>
video-object-detection/styles2.css ADDED
@@ -0,0 +1,465 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
9
+ background-color: #f8fafc;
10
+ color: #334155;
11
+ line-height: 1.6;
12
+ }
13
+
14
+ .container {
15
+ max-width: 1200px;
16
+ margin: 0 auto;
17
+ padding: 2rem;
18
+ }
19
+
20
+ /* Header design matching the image */
21
+ .header {
22
+ display: flex;
23
+ justify-content: space-between;
24
+ align-items: center;
25
+ margin-bottom: 3rem;
26
+ }
27
+
28
+ .header-left {
29
+ display: flex;
30
+ align-items: center;
31
+ gap: 1rem;
32
+ }
33
+
34
+ .logo-icon {
35
+ width: 48px;
36
+ height: 48px;
37
+ background: #0ea5e9;
38
+ border-radius: 50%;
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ color: white;
43
+ font-size: 1.5rem;
44
+ }
45
+
46
+ .header-title {
47
+ font-size: 1.5rem;
48
+ font-weight: 600;
49
+ color: #64748b;
50
+ }
51
+
52
+ .header-right {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 1rem;
56
+ }
57
+
58
+ .welcome-text {
59
+ color: #64748b;
60
+ font-size: 0.95rem;
61
+ }
62
+
63
+ .logout-btn {
64
+ color: #ef4444;
65
+ background: none;
66
+ border: none;
67
+ font-size: 0.95rem;
68
+ cursor: pointer;
69
+ font-weight: 500;
70
+ }
71
+
72
+ .logout-btn:hover {
73
+ text-decoration: underline;
74
+ }
75
+
76
+ /* Welcome section design */
77
+ .welcome-section {
78
+ margin-bottom: 3rem;
79
+ }
80
+
81
+ .welcome-title {
82
+ font-size: 2rem;
83
+ font-weight: 600;
84
+ color: #1e293b;
85
+ margin-bottom: 0.5rem;
86
+ }
87
+
88
+ .welcome-subtitle {
89
+ color: #64748b;
90
+ font-size: 1rem;
91
+ }
92
+
93
+ /* Stats cards design matching the image */
94
+ .stats-grid {
95
+ display: grid;
96
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
97
+ gap: 1.5rem;
98
+ margin-bottom: 3rem;
99
+ }
100
+
101
+ .stat-card {
102
+ background: white;
103
+ padding: 2rem;
104
+ border-radius: 12px;
105
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
106
+ display: flex;
107
+ align-items: center;
108
+ gap: 1rem;
109
+ }
110
+
111
+ .stat-icon {
112
+ width: 48px;
113
+ height: 48px;
114
+ border-radius: 12px;
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ font-size: 1.5rem;
119
+ }
120
+
121
+ .stat-icon.blue {
122
+ background: #dbeafe;
123
+ color: #2563eb;
124
+ }
125
+
126
+ .stat-icon.orange {
127
+ background: #fed7aa;
128
+ color: #ea580c;
129
+ }
130
+
131
+ .stat-icon.yellow {
132
+ background: #fef3c7;
133
+ color: #d97706;
134
+ }
135
+
136
+ .stat-content h3 {
137
+ font-size: 0.875rem;
138
+ color: #64748b;
139
+ font-weight: 500;
140
+ margin-bottom: 0.25rem;
141
+ }
142
+
143
+ .stat-content .stat-number {
144
+ font-size: 2rem;
145
+ font-weight: 700;
146
+ color: #1e293b;
147
+ }
148
+
149
+ /* Section cards design */
150
+ .section-card {
151
+ background: white;
152
+ border-radius: 12px;
153
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
154
+ margin-bottom: 2rem;
155
+ }
156
+
157
+ .section-header {
158
+ padding: 1.5rem 2rem;
159
+ border-bottom: 1px solid #e2e8f0;
160
+ }
161
+
162
+ .section-title {
163
+ font-size: 1.25rem;
164
+ font-weight: 600;
165
+ color: #1e293b;
166
+ margin-bottom: 0.5rem;
167
+ }
168
+
169
+ .section-subtitle {
170
+ color: #64748b;
171
+ font-size: 0.875rem;
172
+ }
173
+
174
+ .section-content {
175
+ padding: 2rem;
176
+ }
177
+
178
+ /* Form elements */
179
+ .form-select {
180
+ width: 100%;
181
+ padding: 0.75rem 1rem;
182
+ border: 1px solid #d1d5db;
183
+ border-radius: 8px;
184
+ background: white;
185
+ color: #374151;
186
+ font-size: 0.875rem;
187
+ cursor: pointer;
188
+ }
189
+
190
+ .form-select:focus {
191
+ outline: none;
192
+ border-color: #0ea5e9;
193
+ box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1);
194
+ }
195
+
196
+ .form-group {
197
+ margin-bottom: 1.5rem;
198
+ }
199
+
200
+ .form-label {
201
+ display: block;
202
+ margin-bottom: 0.5rem;
203
+ font-weight: 500;
204
+ color: #374151;
205
+ font-size: 0.875rem;
206
+ }
207
+
208
+ .form-input {
209
+ width: 100%;
210
+ padding: 0.75rem;
211
+ border: 1px solid #d1d5db;
212
+ border-radius: 8px;
213
+ font-size: 0.875rem;
214
+ transition: border-color 0.2s;
215
+ }
216
+
217
+ .form-input:focus {
218
+ outline: none;
219
+ border-color: #0ea5e9;
220
+ box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1);
221
+ }
222
+
223
+ /* Buttons */
224
+ .btn {
225
+ padding: 0.75rem 1.5rem;
226
+ border: none;
227
+ border-radius: 8px;
228
+ font-weight: 500;
229
+ cursor: pointer;
230
+ transition: all 0.2s;
231
+ font-size: 0.875rem;
232
+ }
233
+
234
+ .btn-primary {
235
+ background: #0ea5e9;
236
+ color: white;
237
+ }
238
+
239
+ .btn-primary:hover {
240
+ background: #0284c7;
241
+ }
242
+
243
+ .btn-secondary {
244
+ background: #f1f5f9;
245
+ color: #64748b;
246
+ border: 1px solid #e2e8f0;
247
+ }
248
+
249
+ .btn-secondary:hover {
250
+ background: #e2e8f0;
251
+ }
252
+
253
+ .btn-success {
254
+ background: #10b981;
255
+ color: white;
256
+ }
257
+
258
+ .btn-success:hover {
259
+ background: #059669;
260
+ }
261
+
262
+ .btn-danger {
263
+ background: #ef4444;
264
+ color: white;
265
+ }
266
+
267
+ .btn-danger:hover {
268
+ background: #dc2626;
269
+ }
270
+
271
+ .btn-sm {
272
+ padding: 0.375rem 0.75rem;
273
+ font-size: 0.75rem;
274
+ }
275
+
276
+ /* Modals */
277
+ .modal {
278
+ display: none;
279
+ position: fixed;
280
+ top: 0;
281
+ left: 0;
282
+ width: 100%;
283
+ height: 100%;
284
+ background: rgba(0, 0, 0, 0.5);
285
+ z-index: 1000;
286
+ justify-content: center;
287
+ align-items: center;
288
+ }
289
+
290
+ .modal.active {
291
+ display: flex;
292
+ }
293
+
294
+ .modal-content {
295
+ background: white;
296
+ padding: 2rem;
297
+ border-radius: 16px;
298
+ max-width: 500px;
299
+ width: 90%;
300
+ max-height: 80vh;
301
+ overflow-y: auto;
302
+ }
303
+
304
+ .modal-header {
305
+ display: flex;
306
+ justify-content: space-between;
307
+ align-items: center;
308
+ margin-bottom: 1.5rem;
309
+ }
310
+
311
+ .modal-title {
312
+ font-size: 1.25rem;
313
+ font-weight: 600;
314
+ color: #1e293b;
315
+ }
316
+
317
+ .close-btn {
318
+ background: none;
319
+ border: none;
320
+ font-size: 1.5rem;
321
+ cursor: pointer;
322
+ color: #64748b;
323
+ }
324
+
325
+ /* Camera and scanning */
326
+ .camera-container {
327
+ position: relative;
328
+ width: 100%;
329
+ max-width: 400px;
330
+ margin: 0 auto 1rem;
331
+ }
332
+
333
+ .camera-preview {
334
+ width: 100%;
335
+ height: 300px;
336
+ background: #f1f5f9;
337
+ border-radius: 8px;
338
+ display: flex;
339
+ align-items: center;
340
+ justify-content: center;
341
+ color: #64748b;
342
+ font-size: 0.875rem;
343
+ }
344
+
345
+ .scan-overlay {
346
+ position: absolute;
347
+ top: 50%;
348
+ left: 50%;
349
+ transform: translate(-50%, -50%);
350
+ width: 200px;
351
+ height: 200px;
352
+ border: 2px solid #0ea5e9;
353
+ border-radius: 8px;
354
+ pointer-events: none;
355
+ }
356
+
357
+ .scan-overlay::before {
358
+ content: "";
359
+ position: absolute;
360
+ top: -2px;
361
+ left: -2px;
362
+ right: -2px;
363
+ bottom: -2px;
364
+ border: 2px solid #0ea5e9;
365
+ border-radius: 8px;
366
+ animation: pulse 2s infinite;
367
+ }
368
+
369
+ @keyframes pulse {
370
+ 0% {
371
+ opacity: 1;
372
+ }
373
+ 50% {
374
+ opacity: 0.5;
375
+ }
376
+ 100% {
377
+ opacity: 1;
378
+ }
379
+ }
380
+
381
+ /* Tables */
382
+ .attendance-table {
383
+ width: 100%;
384
+ border-collapse: collapse;
385
+ margin-top: 1rem;
386
+ }
387
+
388
+ .attendance-table th,
389
+ .attendance-table td {
390
+ padding: 0.75rem;
391
+ text-align: left;
392
+ border-bottom: 1px solid #e2e8f0;
393
+ }
394
+
395
+ .attendance-table th {
396
+ background: #f8fafc;
397
+ font-weight: 600;
398
+ color: #374151;
399
+ }
400
+
401
+ .status-badge {
402
+ padding: 0.25rem 0.75rem;
403
+ border-radius: 9999px;
404
+ font-size: 0.75rem;
405
+ font-weight: 500;
406
+ }
407
+
408
+ .status-present {
409
+ background: #dcfce7;
410
+ color: #166534;
411
+ }
412
+
413
+ .status-absent {
414
+ background: #fee2e2;
415
+ color: #991b1b;
416
+ }
417
+
418
+ /* Lists */
419
+ .student-list {
420
+ max-height: 300px;
421
+ overflow-y: auto;
422
+ border: 1px solid #e2e8f0;
423
+ border-radius: 8px;
424
+ margin-top: 1rem;
425
+ }
426
+
427
+ .student-item {
428
+ display: flex;
429
+ justify-content: space-between;
430
+ align-items: center;
431
+ padding: 0.75rem;
432
+ border-bottom: 1px solid #e2e8f0;
433
+ }
434
+
435
+ .student-item:last-child {
436
+ border-bottom: none;
437
+ }
438
+
439
+ .attendance-controls {
440
+ display: flex;
441
+ gap: 0.5rem;
442
+ }
443
+
444
+ .export-buttons {
445
+ display: flex;
446
+ gap: 1rem;
447
+ margin-top: 1rem;
448
+ }
449
+
450
+ /* Responsive design */
451
+ @media (max-width: 768px) {
452
+ .container {
453
+ padding: 1rem;
454
+ }
455
+
456
+ .header {
457
+ flex-direction: column;
458
+ gap: 1rem;
459
+ text-align: center;
460
+ }
461
+
462
+ .stats-grid {
463
+ grid-template-columns: 1fr;
464
+ }
465
+ }
video-object-detection/vite.config.js CHANGED
@@ -1,6 +1,17 @@
1
- import { defineConfig } from "vite";
 
 
 
2
  export default defineConfig({
3
  build: {
4
  target: "esnext",
5
- },
 
 
 
 
 
 
 
 
6
  });
 
1
+ // vite.config.js
2
+ import { defineConfig } from 'vite';
3
+ import { resolve } from 'path';
4
+
5
  export default defineConfig({
6
  build: {
7
  target: "esnext",
8
+ rollupOptions: {
9
+ input: {
10
+ main: resolve(__dirname, 'index.html'),
11
+ professor: resolve(__dirname, 'professor.html'),
12
+ student: resolve(__dirname, 'student.html'),
13
+ facedetection: resolve(__dirname, 'facedetection.html'),
14
+ }
15
+ }
16
+ }
17
  });