Spaces:
Sleeping
Sleeping
KevanSoon
commited on
Commit
·
fc9a733
1
Parent(s):
0048178
added integration with frontend and python backend
Browse files- src/main/java/com/cs102/attendance/config/SecurityConfig.java +23 -0
- src/main/java/com/cs102/attendance/controller/FastApiCallerController.java +9 -6
- src/main/java/com/cs102/attendance/controller/HealthController.java +42 -42
- src/main/java/com/cs102/attendance/controller/TestController.java +62 -62
- src/main/java/com/cs102/attendance/entity/TestConnection.java +51 -51
- src/main/java/com/cs102/attendance/repository/TestConnectionRepository.java +10 -10
- src/main/java/com/cs102/attendance/service/FastApiCallerService.java +32 -24
- src/test/java/com/cs102/attendance/Cs102AttendanceProjectApplicationTests.java +41 -41
- src/test/java/com/cs102/attendance/controller/TestControllerTest.java +58 -58
- video-object-detection/main.js +58 -20
src/main/java/com/cs102/attendance/config/SecurityConfig.java
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
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 |
+
}
|
src/main/java/com/cs102/attendance/controller/FastApiCallerController.java
CHANGED
|
@@ -1,12 +1,15 @@
|
|
| 1 |
-
// FastApiCallerController.java
|
| 2 |
package com.cs102.attendance.controller;
|
| 3 |
|
| 4 |
-
import org.springframework.web.bind.annotation.
|
|
|
|
|
|
|
| 5 |
import org.springframework.web.bind.annotation.RestController;
|
|
|
|
| 6 |
|
| 7 |
import com.cs102.attendance.service.FastApiCallerService;
|
| 8 |
|
| 9 |
@RestController
|
|
|
|
| 10 |
public class FastApiCallerController {
|
| 11 |
|
| 12 |
private final FastApiCallerService fastApiCallerService;
|
|
@@ -15,13 +18,13 @@ public class FastApiCallerController {
|
|
| 15 |
this.fastApiCallerService = fastApiCallerService;
|
| 16 |
}
|
| 17 |
|
| 18 |
-
@
|
| 19 |
-
public String callFaceRecognition() {
|
| 20 |
try {
|
| 21 |
-
return fastApiCallerService.
|
| 22 |
} catch (Exception e) {
|
| 23 |
e.printStackTrace();
|
| 24 |
-
return "Error
|
| 25 |
}
|
| 26 |
}
|
| 27 |
}
|
|
|
|
|
|
|
| 1 |
package com.cs102.attendance.controller;
|
| 2 |
|
| 3 |
+
import org.springframework.web.bind.annotation.CrossOrigin;
|
| 4 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 5 |
+
import org.springframework.web.bind.annotation.RequestParam;
|
| 6 |
import org.springframework.web.bind.annotation.RestController;
|
| 7 |
+
import org.springframework.web.multipart.MultipartFile;
|
| 8 |
|
| 9 |
import com.cs102.attendance.service.FastApiCallerService;
|
| 10 |
|
| 11 |
@RestController
|
| 12 |
+
@CrossOrigin(origins = "https://cs-102-vert.vercel.app/")
|
| 13 |
public class FastApiCallerController {
|
| 14 |
|
| 15 |
private final FastApiCallerService fastApiCallerService;
|
|
|
|
| 18 |
this.fastApiCallerService = fastApiCallerService;
|
| 19 |
}
|
| 20 |
|
| 21 |
+
@PostMapping("/call-face-recognition")
|
| 22 |
+
public String callFaceRecognition(@RequestParam("image") MultipartFile imageFile) {
|
| 23 |
try {
|
| 24 |
+
return fastApiCallerService.callFaceRecognitionWithImage(imageFile);
|
| 25 |
} catch (Exception e) {
|
| 26 |
e.printStackTrace();
|
| 27 |
+
return "Error processing image: " + e.getMessage();
|
| 28 |
}
|
| 29 |
}
|
| 30 |
}
|
src/main/java/com/cs102/attendance/controller/HealthController.java
CHANGED
|
@@ -1,49 +1,49 @@
|
|
| 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 |
-
|
| 20 |
-
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
}
|
|
|
|
| 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 |
+
// }
|
src/main/java/com/cs102/attendance/controller/TestController.java
CHANGED
|
@@ -1,75 +1,75 @@
|
|
| 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 |
-
|
| 18 |
-
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
|
| 43 |
-
|
| 44 |
-
|
| 45 |
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
}
|
|
|
|
| 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 |
+
// }
|
src/main/java/com/cs102/attendance/entity/TestConnection.java
CHANGED
|
@@ -1,53 +1,53 @@
|
|
| 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 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
}
|
|
|
|
| 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/repository/TestConnectionRepository.java
CHANGED
|
@@ -1,13 +1,13 @@
|
|
| 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 |
-
|
| 12 |
-
|
| 13 |
-
}
|
|
|
|
| 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/FastApiCallerService.java
CHANGED
|
@@ -1,42 +1,50 @@
|
|
| 1 |
-
// FastApiCallerService.java
|
| 2 |
package com.cs102.attendance.service;
|
| 3 |
|
| 4 |
-
import
|
| 5 |
-
import
|
| 6 |
-
import
|
| 7 |
-
import
|
| 8 |
-
|
|
|
|
| 9 |
import org.springframework.stereotype.Service;
|
| 10 |
-
|
| 11 |
-
import
|
| 12 |
-
import
|
| 13 |
|
| 14 |
@Service
|
| 15 |
public class FastApiCallerService {
|
| 16 |
|
|
|
|
| 17 |
private static final String FASTAPI_URL = "https://kevansoon-java-facerecognition-endpoint.hf.space/face-recognition";
|
| 18 |
|
| 19 |
-
private final HttpClient httpClient;
|
| 20 |
-
private final ObjectMapper objectMapper;
|
| 21 |
-
|
| 22 |
public FastApiCallerService() {
|
| 23 |
-
this.
|
| 24 |
-
this.objectMapper = new ObjectMapper();
|
| 25 |
}
|
| 26 |
|
| 27 |
-
public String
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
-
|
| 34 |
|
| 35 |
-
if (response.
|
| 36 |
-
|
| 37 |
-
return jsonNode.path("result").asText();
|
| 38 |
} else {
|
| 39 |
-
throw new RuntimeException("Failed to call FastAPI: HTTP " + response.
|
| 40 |
}
|
| 41 |
}
|
| 42 |
}
|
|
|
|
|
|
|
| 1 |
package com.cs102.attendance.service;
|
| 2 |
|
| 3 |
+
import org.springframework.core.io.ByteArrayResource;
|
| 4 |
+
import org.springframework.http.HttpEntity;
|
| 5 |
+
import org.springframework.http.HttpHeaders;
|
| 6 |
+
import org.springframework.http.MediaType;
|
| 7 |
+
import org.springframework.http.ResponseEntity;
|
| 8 |
+
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
| 9 |
import org.springframework.stereotype.Service;
|
| 10 |
+
import org.springframework.util.LinkedMultiValueMap;
|
| 11 |
+
import org.springframework.web.client.RestTemplate;
|
| 12 |
+
import org.springframework.web.multipart.MultipartFile;
|
| 13 |
|
| 14 |
@Service
|
| 15 |
public class FastApiCallerService {
|
| 16 |
|
| 17 |
+
private final RestTemplate restTemplate;
|
| 18 |
private static final String FASTAPI_URL = "https://kevansoon-java-facerecognition-endpoint.hf.space/face-recognition";
|
| 19 |
|
|
|
|
|
|
|
|
|
|
| 20 |
public FastApiCallerService() {
|
| 21 |
+
this.restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory());
|
|
|
|
| 22 |
}
|
| 23 |
|
| 24 |
+
public String callFaceRecognitionWithImage(MultipartFile image) throws Exception {
|
| 25 |
+
// Wrap MultipartFile bytes with filename
|
| 26 |
+
ByteArrayResource imageAsResource = new ByteArrayResource(image.getBytes()) {
|
| 27 |
+
@Override
|
| 28 |
+
public String getFilename() {
|
| 29 |
+
return image.getOriginalFilename();
|
| 30 |
+
}
|
| 31 |
+
};
|
| 32 |
+
|
| 33 |
+
// Prepare multipart form data
|
| 34 |
+
LinkedMultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
| 35 |
+
body.add("image", imageAsResource);
|
| 36 |
+
|
| 37 |
+
HttpHeaders headers = new HttpHeaders();
|
| 38 |
+
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
| 39 |
+
|
| 40 |
+
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
| 41 |
|
| 42 |
+
ResponseEntity<String> response = restTemplate.postForEntity(FASTAPI_URL, requestEntity, String.class);
|
| 43 |
|
| 44 |
+
if (response.getStatusCode().is2xxSuccessful()) {
|
| 45 |
+
return response.getBody(); // Or parse JSON if needed
|
|
|
|
| 46 |
} else {
|
| 47 |
+
throw new RuntimeException("Failed to call FastAPI: HTTP " + response.getStatusCodeValue());
|
| 48 |
}
|
| 49 |
}
|
| 50 |
}
|
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 |
-
|
| 18 |
-
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 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 |
+
// }
|
src/test/java/com/cs102/attendance/controller/TestControllerTest.java
CHANGED
|
@@ -1,69 +1,69 @@
|
|
| 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 |
-
|
| 21 |
-
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
}
|
|
|
|
| 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 |
+
// }
|
video-object-detection/main.js
CHANGED
|
@@ -61,6 +61,37 @@ const COLOURS = [
|
|
| 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) => {
|
|
@@ -69,21 +100,23 @@ async function sendCanvasImageToAPI(canvas) {
|
|
| 69 |
return;
|
| 70 |
}
|
| 71 |
|
| 72 |
-
const
|
| 73 |
|
| 74 |
try {
|
| 75 |
-
|
| 76 |
-
|
| 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 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
frame2: file2,
|
| 86 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
resolve(result);
|
| 88 |
} catch (err) {
|
| 89 |
reject(err);
|
|
@@ -92,6 +125,7 @@ async function sendCanvasImageToAPI(canvas) {
|
|
| 92 |
});
|
| 93 |
}
|
| 94 |
|
|
|
|
| 95 |
// Variables to store current bounding box and label elements
|
| 96 |
let currentBoxElement = null;
|
| 97 |
let currentLabelElement = null;
|
|
@@ -135,15 +169,19 @@ function renderBox([xmin, ymin, xmax, ymax, score, id], [w, h]) {
|
|
| 135 |
if (!hasSent) {
|
| 136 |
hasSent = true;
|
| 137 |
sendCanvasImageToAPI(canvas)
|
| 138 |
-
.then((
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
const
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
const
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
if (decimalNumber !== null && decimalNumber > 0.80) {
|
| 149 |
// --- MODIFICATION START ---
|
|
|
|
| 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) => {
|
|
|
|
| 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("http://localhost:8080/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);
|
|
|
|
| 125 |
});
|
| 126 |
}
|
| 127 |
|
| 128 |
+
|
| 129 |
// Variables to store current bounding box and label elements
|
| 130 |
let currentBoxElement = null;
|
| 131 |
let currentLabelElement = null;
|
|
|
|
| 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 ---
|