feat(api): restructure api with axios and chainage related changes
This commit is contained in:
58
src/services/api/chainage.service.ts
Normal file
58
src/services/api/chainage.service.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import axiosClient from "../axios/axios";
|
||||
import {
|
||||
Chainage, ChainageCreate, ChainageUpdate,
|
||||
PaginatedResponse, PaginationParams
|
||||
} from "@/types";
|
||||
|
||||
/**
|
||||
* Chainage Service
|
||||
*/
|
||||
export const chainageService = {
|
||||
/**
|
||||
* Fetch all chainages
|
||||
*/
|
||||
getChainages: async (params?: PaginationParams): Promise<PaginatedResponse<Chainage>> => {
|
||||
const skip = params?.skip ?? 0;
|
||||
const limit = params?.limit ?? 100;
|
||||
const response = await axiosClient.get<PaginatedResponse<Chainage>>(`/chainages/`, {
|
||||
params: { skip, limit }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch chainages filtered by package ID
|
||||
*/
|
||||
getChainagesByPackage: async (packageId: string, params?: PaginationParams): Promise<PaginatedResponse<Chainage>> => {
|
||||
const skip = params?.skip ?? 0;
|
||||
const limit = params?.limit ?? 100;
|
||||
const response = await axiosClient.get<PaginatedResponse<Chainage>>(`/chainages/`, {
|
||||
params: { package_id: packageId, skip, limit }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new chainage
|
||||
*/
|
||||
createChainage: async (data: ChainageCreate): Promise<Chainage> => {
|
||||
const response = await axiosClient.post<Chainage>("/chainages/", data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update an existing chainage
|
||||
*/
|
||||
updateChainage: async (chainageId: string, data: ChainageUpdate): Promise<Chainage> => {
|
||||
const response = await axiosClient.put<Chainage>(`/chainages/${chainageId}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a chainage
|
||||
*/
|
||||
deleteChainage: async (chainageId: string): Promise<{ message: string }> => {
|
||||
const response = await axiosClient.delete<{ message: string }>(`/chainages/${chainageId}`);
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
56
src/services/api/detection.service.ts
Normal file
56
src/services/api/detection.service.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import axiosClient from "../axios/axios";
|
||||
import { Detection } from "@/types";
|
||||
import { projectService } from "./project.service";
|
||||
|
||||
/**
|
||||
* Detection Service
|
||||
*/
|
||||
export const detectionService = {
|
||||
/**
|
||||
* Fetch all detections from completed videos
|
||||
* Uses the summary endpoint to get detections for each project
|
||||
*/
|
||||
getAllDetections: async (): Promise<Detection[]> => {
|
||||
try {
|
||||
// First get all projects
|
||||
const projectsResponse = await projectService.getProjects();
|
||||
const projects = projectsResponse.items;
|
||||
|
||||
// Then fetch detections for each project
|
||||
const allDetections: Detection[] = []
|
||||
|
||||
for (const project of projects) {
|
||||
try {
|
||||
const response = await axiosClient.get<{
|
||||
packages: {
|
||||
[key: string]: {
|
||||
chainages: {
|
||||
[key: string]: {
|
||||
detections: Detection[]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}>(`/summary/projects/${project.id}`);
|
||||
|
||||
const summary = response.data;
|
||||
|
||||
// Extract detections from the nested structure
|
||||
for (const pkg of Object.values(summary.packages || {})) {
|
||||
for (const loc of Object.values(pkg.chainages || {})) {
|
||||
allDetections.push(...(loc.detections || []))
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Skip projects that fail to load
|
||||
console.warn(`Failed to load detections for project ${project.id}:`, e)
|
||||
}
|
||||
}
|
||||
|
||||
return allDetections;
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch all detections:", e)
|
||||
return []
|
||||
}
|
||||
}
|
||||
};
|
||||
7
src/services/api/index.ts
Normal file
7
src/services/api/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from "./project.service";
|
||||
export * from "./package.service";
|
||||
export { chainageService } from "./chainage.service";
|
||||
export * from "./video.service";
|
||||
export * from "./detection.service";
|
||||
export * from "./session.service";
|
||||
export { projectDataService } from "./project.service";
|
||||
58
src/services/api/package.service.ts
Normal file
58
src/services/api/package.service.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import axiosClient from "../axios/axios";
|
||||
import {
|
||||
Package, PackageCreate, PackageUpdate,
|
||||
PaginatedResponse, PaginationParams
|
||||
} from "@/types";
|
||||
|
||||
/**
|
||||
* Package Service
|
||||
*/
|
||||
export const packageService = {
|
||||
/**
|
||||
* Fetch all packages
|
||||
*/
|
||||
getPackages: async (params?: PaginationParams): Promise<PaginatedResponse<Package>> => {
|
||||
const skip = params?.skip ?? 0;
|
||||
const limit = params?.limit ?? 100;
|
||||
const response = await axiosClient.get<PaginatedResponse<Package>>(`/packages/`, {
|
||||
params: { skip, limit }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch packages filtered by project ID
|
||||
*/
|
||||
getPackagesByProject: async (projectId: string, params?: PaginationParams): Promise<PaginatedResponse<Package>> => {
|
||||
const skip = params?.skip ?? 0;
|
||||
const limit = params?.limit ?? 100;
|
||||
const response = await axiosClient.get<PaginatedResponse<Package>>(`/packages/`, {
|
||||
params: { project_id: projectId, skip, limit }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new package
|
||||
*/
|
||||
createPackage: async (data: PackageCreate): Promise<Package> => {
|
||||
const response = await axiosClient.post<Package>("/packages/", data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update an existing package
|
||||
*/
|
||||
updatePackage: async (packageId: string, data: PackageUpdate): Promise<Package> => {
|
||||
const response = await axiosClient.put<Package>(`/packages/${packageId}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a package
|
||||
*/
|
||||
deletePackage: async (packageId: string): Promise<{ message: string }> => {
|
||||
const response = await axiosClient.delete<{ message: string }>(`/packages/${packageId}`);
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
100
src/services/api/project.service.ts
Normal file
100
src/services/api/project.service.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import axiosClient from "../axios/axios";
|
||||
import {
|
||||
Project, ProjectCreate, ProjectUpdate,
|
||||
PaginatedResponse, PaginationParams
|
||||
} from "@/types";
|
||||
|
||||
/**
|
||||
* Project Service
|
||||
*/
|
||||
export const projectService = {
|
||||
/**
|
||||
* Fetch all projects
|
||||
*/
|
||||
getProjects: async (params?: PaginationParams): Promise<PaginatedResponse<Project>> => {
|
||||
const skip = params?.skip ?? 0;
|
||||
const limit = params?.limit ?? 100;
|
||||
const response = await axiosClient.get<PaginatedResponse<Project>>(`/projects/`, {
|
||||
params: { skip, limit }
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new project
|
||||
*/
|
||||
createProject: async (data: ProjectCreate): Promise<Project> => {
|
||||
const response = await axiosClient.post<Project>("/projects/", data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update an existing project
|
||||
*/
|
||||
updateProject: async (projectId: string, data: ProjectUpdate): Promise<Project> => {
|
||||
const response = await axiosClient.put<Project>(`/projects/${projectId}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a project
|
||||
*/
|
||||
deleteProject: async (projectId: string): Promise<{ message: string }> => {
|
||||
const response = await axiosClient.delete<{ message: string }>(`/projects/${projectId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch project summary (detections across packages and chainages)
|
||||
*/
|
||||
getProjectSummary: async (projectId: string): Promise<any> => {
|
||||
const response = await axiosClient.get(`/summary/projects/${projectId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch project summary filtered by video ID
|
||||
*/
|
||||
getProjectSummaryByVideo: async (projectId: string, videoId: string): Promise<any> => {
|
||||
const response = await axiosClient.get(`/summary/projects/${projectId}`, {
|
||||
params: { video_id: videoId }
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Service to handle data extraction from project summaries
|
||||
* Moved from legacy project-service.ts
|
||||
*/
|
||||
export const projectDataService = {
|
||||
/**
|
||||
* Extracts all detections from a project summary, optionally filtered by package and chainage
|
||||
*/
|
||||
extractDetections(
|
||||
projectSummary: any,
|
||||
selectedPackageId?: string | null,
|
||||
selectedChainageId?: string | null
|
||||
): any[] {
|
||||
if (!projectSummary) return []
|
||||
|
||||
const detections: any[] = []
|
||||
const packagesToProcess = selectedPackageId && selectedPackageId !== "all"
|
||||
? { [selectedPackageId]: projectSummary.packages[selectedPackageId] }
|
||||
: projectSummary.packages || {}
|
||||
|
||||
for (const [pkgName, pkg] of Object.entries(packagesToProcess)) {
|
||||
const chainagesToProcess = selectedChainageId && selectedChainageId !== "all"
|
||||
? { [selectedChainageId]: (pkg as any).chainages[selectedChainageId] }
|
||||
: (pkg as any).chainages || {}
|
||||
|
||||
for (const [chnName, chn] of Object.entries(chainagesToProcess)) {
|
||||
if (!chn) continue
|
||||
const chainageDetections = (chn as any).detections || []
|
||||
detections.push(...chainageDetections)
|
||||
}
|
||||
}
|
||||
|
||||
return detections
|
||||
}
|
||||
};
|
||||
73
src/services/api/session.service.ts
Normal file
73
src/services/api/session.service.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { SessionContext, VideoResultData, emptySessionContext } from "@/types";
|
||||
|
||||
// Session Storage Keys
|
||||
const SESSION_KEY = "visionroad_session";
|
||||
const VIDEO_DATA_KEY = "visionroad_video_data";
|
||||
const DETECTION_TYPE_KEY = "visionroad_detection_type";
|
||||
|
||||
/**
|
||||
* Session Service
|
||||
*/
|
||||
export const sessionService = {
|
||||
/**
|
||||
* Save session to localStorage
|
||||
*/
|
||||
saveSession: (session: SessionContext): void => {
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.setItem(SESSION_KEY, JSON.stringify(session))
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Load session from localStorage
|
||||
*/
|
||||
loadSession: (): SessionContext => {
|
||||
if (typeof window !== "undefined") {
|
||||
const stored = localStorage.getItem(SESSION_KEY)
|
||||
if (stored) {
|
||||
return JSON.parse(stored)
|
||||
}
|
||||
}
|
||||
return emptySessionContext;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear all session data
|
||||
*/
|
||||
clearSession: (): void => {
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.removeItem(SESSION_KEY)
|
||||
localStorage.removeItem(VIDEO_DATA_KEY)
|
||||
localStorage.removeItem(DETECTION_TYPE_KEY)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Save video result data
|
||||
*/
|
||||
saveVideoData: (data: VideoResultData): void => {
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.setItem(VIDEO_DATA_KEY, JSON.stringify(data))
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Load video result data
|
||||
*/
|
||||
loadVideoData: (): VideoResultData | null => {
|
||||
if (typeof window !== "undefined") {
|
||||
const stored = localStorage.getItem(VIDEO_DATA_KEY)
|
||||
if (stored) {
|
||||
return JSON.parse(stored)
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if session is complete
|
||||
*/
|
||||
isSessionValid: (session: SessionContext): boolean => {
|
||||
return !!(session.projectId && session.packageId && session.chainageId)
|
||||
}
|
||||
};
|
||||
79
src/services/api/video.service.ts
Normal file
79
src/services/api/video.service.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import axiosClient from "../axios/axios";
|
||||
import { Video, PaginationParams } from "@/types";
|
||||
|
||||
/**
|
||||
* Video Service
|
||||
*/
|
||||
export const videoService = {
|
||||
/**
|
||||
* Fetch all videos and transform them to match the Video interface
|
||||
*/
|
||||
getVideos: async (params?: PaginationParams): Promise<Video[]> => {
|
||||
const skip = params?.skip ?? 0;
|
||||
const limit = params?.limit ?? 100;
|
||||
|
||||
const response = await axiosClient.get<{
|
||||
videos: Array<{
|
||||
video_id: string;
|
||||
status: string;
|
||||
progress: number;
|
||||
summary?: {
|
||||
unique_defected_sign_board?: number;
|
||||
unique_pothole?: number;
|
||||
unique_road_crack?: number;
|
||||
unique_damaged_road_marking?: number;
|
||||
unique_good_sign_board?: number;
|
||||
total_road_damage?: number;
|
||||
total_detections?: number;
|
||||
};
|
||||
}>;
|
||||
}>(`/videos`, {
|
||||
params: { skip, limit }
|
||||
});
|
||||
|
||||
// Transform the response to match our Video interface
|
||||
return response.data.videos.map(v => ({
|
||||
id: v.video_id,
|
||||
filename: v.video_id,
|
||||
detection_type: "pot-sign-detection" as const,
|
||||
status: v.status as Video["status"],
|
||||
unique_defected_sign_board: v.summary?.unique_defected_sign_board,
|
||||
unique_pothole: v.summary?.unique_pothole,
|
||||
unique_road_crack: v.summary?.unique_road_crack,
|
||||
unique_damaged_road_marking: v.summary?.unique_damaged_road_marking,
|
||||
unique_good_sign_board: v.summary?.unique_good_sign_board,
|
||||
total_road_damage: v.summary?.total_road_damage,
|
||||
total_detections: v.summary?.total_detections,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Upload a video for processing
|
||||
*/
|
||||
uploadVideo: async (formData: FormData): Promise<any> => {
|
||||
const response = await axiosClient.post("/upload", formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get processing status of a video
|
||||
*/
|
||||
getVideoStatus: async (videoId: string): Promise<any> => {
|
||||
const response = await axiosClient.get(`/status/${videoId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get analysis results for a video
|
||||
*/
|
||||
getVideoResults: async (videoId: string): Promise<any> => {
|
||||
const response = await axiosClient.get(`/results/${videoId}`);
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user