diff --git a/bonus-modules/bonus-ai/pom.xml b/bonus-modules/bonus-ai/pom.xml index 06ce8b0..020f39a 100644 --- a/bonus-modules/bonus-ai/pom.xml +++ b/bonus-modules/bonus-ai/pom.xml @@ -41,8 +41,6 @@ spring-boot-starter-actuator - - com.mysql diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/AIConstants.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/AIConstants.java new file mode 100644 index 0000000..1646e60 --- /dev/null +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/AIConstants.java @@ -0,0 +1,5 @@ +package com.bonus.ai.client; + +public class AIConstants { + public static final String AUTHORIZATION_HEADER = "Authorization"; +} diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/AnnotationParam.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/AnnotationParam.java new file mode 100644 index 0000000..2ac8419 --- /dev/null +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/AnnotationParam.java @@ -0,0 +1,193 @@ +package com.bonus.ai.client; + +//public class AnnotationParam { +//} +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Data; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +@Data +public class AnnotationParam { + + @JsonProperty("id") + private int id; + + @JsonProperty("result") + private List result; + + @JsonProperty("created_username") + private String createdUsername; + + @JsonProperty("created_ago") + private String createdAgo; + + @JsonProperty("completed_by") + private int completedBy; + + @JsonProperty("was_cancelled") + private boolean wasCancelled; + + @JsonProperty("ground_truth") + private boolean groundTruth; + + @JsonProperty("created_at") + private String createdAt; + + @JsonProperty("updated_at") + private String updatedAt; + + @JsonProperty("draft_created_at") + private String draftCreatedAt; + + @JsonProperty("lead_time") + private String leadTime; + + @JsonProperty("import_id") + private String importId; + + @JsonProperty("last_action") + private String lastAction; + + @JsonProperty("task") + private int task; + + @JsonProperty("project") + private int project; + + @JsonProperty("updated_by") + private int updatedBy; + + @JsonProperty("parent_prediction") + private String parentPrediction; + + @JsonProperty("parent_annotation") + private String parentAnnotation; + + @JsonProperty("last_created_by") + private String lastCreatedBy; + + // Nested classes for Result and Value + @Data + public static class Result { + + @JsonProperty("original_width") + private int originalWidth; + + @JsonProperty("original_height") + private int originalHeight; + + @JsonProperty("image_rotation") + private int imageRotation; + + @JsonProperty("from_name") + private String fromName; + + @JsonProperty("to_name") + private String toName; + + @JsonProperty("type") + private String type; + + @JsonProperty("value") + private Value value; + + @Data + public static class Value { + + @JsonProperty("x") + private double x; + + @JsonProperty("y") + private double y; + + @JsonProperty("width") + private double width; + + @JsonProperty("height") + private double height; + + @JsonProperty("rotation") + private int rotation; + + @JsonProperty("values") + private Map> values; + + // Getters and setters + } + + // Getters and setters + } + + // JSON to class conversion + public static AnnotationParam fromJson(String json) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(json, AnnotationParam.class); + } + + // Class to JSON conversion + public String toJson() throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(this); + } + + // Getters and setters for outer class + // Use IDE to generate or add manually + public static void main(String[] args) throws IOException { + String json = "{\n" + + " \"id\": 6,\n" + + " \"result\": [\n" + + " {\n" + + " \"original_width\": 1920,\n" + + " \"original_height\": 1080,\n" + + " \"image_rotation\": 0,\n" + + " \"from_name\": \"bboxes\",\n" + + " \"to_name\": \"image\",\n" + + " \"type\": \"rectanglelabels\",\n" + + " \"value\": {\n" + + " \"x\": 20,\n" + + " \"y\": 30,\n" + + " \"width\": 50,\n" + + " \"height\": 60,\n" + + " \"rotation\": 0,\n" + + " \"values\": {\n" + + " \"rectanglelabels\": [\n" + + " \"Person\"\n" + + " ]\n" + + " }\n" + + " }\n" + + " }\n" + + " ],\n" + + " \"created_username\": \" wang272160587@gmail.com, 1\",\n" + + " \"created_ago\": \"0 minutes\",\n" + + " \"completed_by\": 1,\n" + + " \"was_cancelled\": false,\n" + + " \"ground_truth\": true,\n" + + " \"created_at\": \"2024-11-21T07:52:04.473353Z\",\n" + + " \"updated_at\": \"2024-11-21T07:52:04.473378Z\",\n" + + " \"draft_created_at\": null,\n" + + " \"lead_time\": null,\n" + + " \"import_id\": null,\n" + + " \"last_action\": null,\n" + + " \"task\": 6,\n" + + " \"project\": 4,\n" + + " \"updated_by\": 1,\n" + + " \"parent_prediction\": null,\n" + + " \"parent_annotation\": null,\n" + + " \"last_created_by\": null\n" + + "}"; + + // JSON to class + AnnotationParam annotation = AnnotationParam.fromJson(json); + System.out.println("Parsed Annotation Object: " + annotation); + System.out.println("result[0].toname:" + annotation.getResult().get(0).getToName()); + + // Class to JSON + String jsonString = annotation.toJson(); + System.out.println("Serialized JSON: " + jsonString); + } +} + diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/OnlineAnnotationFallbackFactory.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/OnlineAnnotationFallbackFactory.java new file mode 100644 index 0000000..cd734a6 --- /dev/null +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/OnlineAnnotationFallbackFactory.java @@ -0,0 +1,83 @@ +package com.bonus.ai.client; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + + +@Component +public class OnlineAnnotationFallbackFactory implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(OnlineAnnotationFallbackFactory.class); + + @Override + public OnlineAnnotationService create(Throwable throwable) { + + log.error("系统服务调用失败:{}", throwable.getMessage()); + return new OnlineAnnotationService() { + @Override + public String createProject(ProjectParam projectParam, String authorization) { + return null; + + } + + @Override + public String getProjectById(String id, String authorization) { + return ""; + } + + @Override + public boolean deleteProjectById(String id, String authorization) { + return false; + } + + @Override + public String updateProject(ProjectParam projectParam, String authorization) { + return ""; + } + + @Override + public String createTask(TaskParam task, String authorization) { + return null; + } + + @Override + public String getTaskById(String id, String authorization) { + return ""; + } + + @Override + public boolean deleteTaskById(String id, String authorization) { + return false; + } + + @Override + public String updateTask(TaskParam task, String authorization) { + return ""; + } + + @Override + public String createAnnotation(AnnotationParam annotationParam, String authorization) { + return ""; + } + + @Override + public String getAnnotationById(String id, String authorization) { + return ""; + } + + @Override + public boolean deleteAnnotationById(String id, String authorization) { + return false; + } + + @Override + public String updateAnnotation(AnnotationParam annotationParam, String authorization) { + return ""; + } + + + }; + } +} + diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/OnlineAnnotationService.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/OnlineAnnotationService.java new file mode 100644 index 0000000..4d921df --- /dev/null +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/OnlineAnnotationService.java @@ -0,0 +1,72 @@ +package com.bonus.ai.client; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + + +@FeignClient(name = "onlineAnnotationService", url = "${label-studio.url}") +public interface OnlineAnnotationService { + + /** + * project <------> 人工智能数据中心的标注任务 + * project 增删改查 + */ + @PostMapping("/api/projects/") + public String createProject(@RequestBody ProjectParam projectParam, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + @GetMapping("/api/projects/{id}/") + public String getProjectById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + @DeleteMapping("/api/projects/{id}/") + public boolean deleteProjectById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + @PatchMapping("/api/projects/") + public String updateProject(@RequestBody ProjectParam projectParam, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + /** + * task <------> 人工智能数据中心的具体的标注文件 + * task 增删改查 + */ + @PostMapping("/api/tasks/") + public String createTask(@RequestBody TaskParam task, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + @GetMapping("/api/tasks/{id}/") + public String getTaskById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + + @DeleteMapping("/api/tasks/{id}/") + public boolean deleteTaskById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + @PatchMapping("/api/tasks/") + public String updateTask(@RequestBody TaskParam task, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + + /** + * annotation <------> 人工智能数据中心的标注结果 + * annotation 增删改查 + */ + @PostMapping("/api/tasks/{id}/annotations/") + public String createAnnotation(@RequestBody AnnotationParam annotationParam, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + + @GetMapping("/api/annotations/{id}") + public String getAnnotationById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + @DeleteMapping("/api/annotations/{id}") + public boolean deleteAnnotationById(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + + @PatchMapping("/api/tasks/{id}/annotations/") + public String updateAnnotation(@RequestBody AnnotationParam annotationParam, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + /** + * 导出指定project的所有标注结果 + */ +// @GetMapping("/api/project/{id}/export") +// public String exportAnnotationByProjectId(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); +// +// @GetMapping("/api/annotations/{id}") +// public String exportAnnotationByTaskIds(@PathVariable("id") String id, @RequestHeader(AIConstants.AUTHORIZATION_HEADER) String authorization); + + +} diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/ProjectParam.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/ProjectParam.java new file mode 100644 index 0000000..4a99286 --- /dev/null +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/ProjectParam.java @@ -0,0 +1,207 @@ +package com.bonus.ai.client; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class ProjectParam { + + private int id; + private String title; + private String description; + @JsonProperty("label_config") + private String labelConfig; + @JsonProperty("expert_instruction") + private String expertInstruction; + @JsonProperty("show_instruction") + private boolean showInstruction; + @JsonProperty("show_skip_button") + private boolean showSkipButton; + @JsonProperty("enable_empty_annotation") + private boolean enableEmptyAnnotation; + @JsonProperty("show_annotation_history") + private boolean showAnnotationHistory; + private int organization; + private String color; + @JsonProperty("maximum_annotations") + private int maximumAnnotations; + @JsonProperty("is_published") + private boolean isPublished; + @JsonProperty("model_version") + private String modelVersion; + @JsonProperty("is_draft") + private boolean isDraft; + @JsonProperty("created_by") + private CreatedBy createdBy; + @JsonProperty("created_at") + private String createdAt; + @JsonProperty("min_annotations_to_start_training") + private int minAnnotationsToStartTraining; + @JsonProperty("start_training_on_annotation_update") + private boolean startTrainingOnAnnotationUpdate; + @JsonProperty("show_collab_predictions") + private boolean showCollabPredictions; + @JsonProperty("num_tasks_with_annotations") + private int numTasksWithAnnotations; + @JsonProperty("task_number") + private int taskNumber; + @JsonProperty("useful_annotation_number") + private int usefulAnnotationNumber; + @JsonProperty("ground_truth_number") + private int groundTruthNumber; + @JsonProperty("skipped_annotations_number") + private int skippedAnnotationsNumber; + @JsonProperty("total_annotations_number") + private int totalAnnotationsNumber; + @JsonProperty("total_predictions_number") + private int totalPredictionsNumber; + private String sampling; + @JsonProperty("show_ground_truth_first") + private boolean showGroundTruthFirst; + @JsonProperty("show_overlap_first") + private boolean showOverlapFirst; + @JsonProperty("overlap_cohort_percentage") + private int overlapCohortPercentage; + @JsonProperty("task_data_login") + private String taskDataLogin; + @JsonProperty("task_data_password") + private String taskDataPassword; + @JsonProperty("control_weights") + private Map controlWeights; + @JsonProperty("parsed_label_config") + private ParsedLabelConfig parsedLabelConfig; + @JsonProperty("evaluate_predictions_automatically") + private boolean evaluatePredictionsAutomatically; + @JsonProperty("config_has_control_tags") + private boolean configHasControlTags; + @JsonProperty("skip_queue") + private String skipQueue; + @JsonProperty("reveal_preannotations_interactively") + private boolean revealPreannotationsInteractively; + @JsonProperty("pinned_at") + private String pinnedAt; + @JsonProperty("finished_task_number") + private int finishedTaskNumber; + @JsonProperty("queue_total") + private int queueTotal; + @JsonProperty("queue_done") + private int queueDone; + + // Getters and Setters for all fields + @Data + public static class CreatedBy { + private int id; + @JsonProperty("first_name") + private String firstName; + @JsonProperty("last_name") + private String lastName; + private String email; + private String avatar; + + // Getters and Setters + } + + @Data + public static class ControlWeight { + private double overall; + private String type; + private Map labels; + + // Getters and Setters + } + + @Data + public static class ParsedLabelConfig { + private Map label; + + // Getters and Setters + } + + @Data + public static class LabelConfig { + private String type; + @JsonProperty("to_name") + private List toName; + private List inputs; + private List labels; + @JsonProperty("labels_attrs") + private Map labelsAttrs; + + // Getters and Setters + } + + @Data + public static class Input { + private String type; + private String value; + private String valueType; + + // Getters and Setters + } + + @Data + public static class LabelAttributes { + private String value; + private String background; + + // Getters and Setters + } + + public static ProjectParam fromJson(String json) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, ProjectParam.class); + } + + public String toJson() throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(this); + } + + public static void main(String[] args) throws JsonProcessingException { + String json = "{\n" + + " \"data\": {\n" + + " \"image\": \"https://example.com/image.jpg\",\n" + + " \"text\": \"Hello, AI!\"\n" + + " },\n" + + " \"id\": 1,\n" + + " \"meta\": {\n" + + " \"key\": \"value\"\n" + + " },\n" + + " \"created_at\": \"2024-06-18T23:45:46Z\",\n" + + " \"updated_at\": \"2024-06-18T23:45:46Z\",\n" + + " \"is_labeled\": false,\n" + + " \"overlap\": 1,\n" + + " \"inner_id\": 1,\n" + + " \"total_annotations\": 0,\n" + + " \"cancelled_annotations\": 0,\n" + + " \"total_predictions\": 0,\n" + + " \"comment_count\": 0,\n" + + " \"unresolved_comment_count\": 0,\n" + + " \"last_comment_updated_at\": \"2024-01-15T09:30:00Z\",\n" + + " \"project\": 1,\n" + + " \"updated_by\": [\n" + + " {\n" + + " \"user_id\": 1\n" + + " }\n" + + " ],\n" + + " \"file_upload\": \"42d46c4c-my-pic.jpeg\",\n" + + " \"comment_authors\": [\n" + + " 1\n" + + " ]\n" + + "}"; + + + System.out.println(json); + + TaskParam record = TaskParam.fromJson(json); + System.out.println("Create At: " + record.getData()); + System.out.println("Create At: " + record.getCreatedAt()); + System.out.println("Image URL: " + record.getData().getImage()); + } +} + diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/TaskParam.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/TaskParam.java new file mode 100644 index 0000000..3aee9e7 --- /dev/null +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/client/TaskParam.java @@ -0,0 +1,119 @@ +package com.bonus.ai.client; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.Data; + +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; + +//@JsonIgnoreProperties(ignoreUnknown = true) +@Data +public class TaskParam { + + private Data data; + private int id; + private Map meta; + @JsonProperty("created_at") + private String createdAt; +// private ZonedDateTime createdAt; + @JsonProperty("updated_at") + private String updatedAt; +// private ZonedDateTime updatedAt; + @JsonProperty("is_labeled") + private boolean isLabeled; + private int overlap; + @JsonProperty("inner_id") + private int innerId; + @JsonProperty("total_annotations") + private int totalAnnotations; + @JsonProperty("cancelled_annotations") + private int cancelledAnnotations; + @JsonProperty("total_predictions") + private int totalPredictions; + @JsonProperty("comment_count") + private int commentCount; + @JsonProperty("unresolved_comment_count") + private int unresolvedCommentCount; + @JsonProperty("last_comment_updated_at") + private String lastCommentUpdatedAt; + private int project; + @JsonProperty("updated_by") + private List updatedBy; + @JsonProperty("file_upload") + private String fileUpload; + @JsonProperty("comment_authors") + private List commentAuthors; + + // Getters and Setters for all fields + + @lombok.Data + public static class Data { + private String image; + private String text; + } + + public static class UpdatedBy { + @JsonProperty("user_id") + private int userId; + + // Getters and Setters + } + + public static TaskParam fromJson(String json) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + // 注册 Java 时间模块 + mapper.registerModule(new JavaTimeModule()); + return mapper.readValue(json, TaskParam.class); + } + + public String toJson() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(this); + } + + public static void main(String[] args) throws JsonProcessingException { + String json = "{\n" + + " \"data\": {\n" + + " \"image\": \"https://example.com/image.jpg\",\n" + + " \"text\": \"Hello, AI!\"\n" + + " },\n" + + " \"id\": 1,\n" + + " \"meta\": {\n" + + " \"key\": \"value\"\n" + + " },\n" + + " \"created_at\": \"2024-06-18T23:45:46Z\",\n" + + " \"updated_at\": \"2024-06-18T23:45:46Z\",\n" + + " \"is_labeled\": false,\n" + + " \"overlap\": 1,\n" + + " \"inner_id\": 1,\n" + + " \"total_annotations\": 0,\n" + + " \"cancelled_annotations\": 0,\n" + + " \"total_predictions\": 0,\n" + + " \"comment_count\": 0,\n" + + " \"unresolved_comment_count\": 0,\n" + + " \"last_comment_updated_at\": \"2024-01-15T09:30:00Z\",\n" + + " \"project\": 1,\n" + + " \"updated_by\": [\n" + + " {\n" + + " \"user_id\": 1\n" + + " }\n" + + " ],\n" + + " \"file_upload\": \"42d46c4c-my-pic.jpeg\",\n" + + " \"comment_authors\": [\n" + + " 1\n" + + " ]\n" + + "}"; + + + System.out.println(json); + + TaskParam record = TaskParam.fromJson(json); + System.out.println("Record ID: " + record.getId()); + System.out.println("Image URL: " + record.getData().getImage()); + } +} + diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/dataset/AnnotationTaskServiceImpl.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/dataset/AnnotationTaskServiceImpl.java index 79cd019..aa24b0f 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/dataset/AnnotationTaskServiceImpl.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/dataset/AnnotationTaskServiceImpl.java @@ -1,5 +1,6 @@ package com.bonus.ai.service.Impl.dataset; +import com.bonus.ai.client.OnlineAnnotationService; import com.bonus.ai.config.MinioConfig; import com.bonus.ai.config.OnlineAnnotateConfig; import com.bonus.ai.domain.dataset.AnnotationSubTaskEntity; @@ -8,7 +9,6 @@ import com.bonus.ai.domain.dataset.DataSetEntity; import com.bonus.ai.service.dataset.AnnotationTaskService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; @@ -16,36 +16,35 @@ import javax.annotation.Resource; @Service public class AnnotationTaskServiceImpl implements AnnotationTaskService { + @Resource - private RestTemplate restTemplate; + OnlineAnnotationService onlineAnnotationService; @Resource private OnlineAnnotateConfig onlineAnnotateConfig; - /** - * 手工标注 - */ - @Override - public void manualAnnotate(AnnotationSubTaskEntity subTask) { - - } - /** - * AI标注 + * 创建标注任务 */ @Override - public void aiAnnotate(AnnotationSubTaskEntity subTask) { - + public int createTask(AnnotationTaskEntity task) { + return 0; } + /** - * 获取任务列表 + * 获取任务列表,分配给我的标注任务,我创建的标注任务 */ @Override public DataSetEntity getTaskList(String type) { return null; } + /** + * 获取任务列表,分配给我的标注任务,我创建的标注任务 + */ + + /** * 更新任务 */ @@ -54,13 +53,6 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService { return 0; } - /** - * 创建标注任务 - */ - @Override - public int createTask(AnnotationTaskEntity task) { - return 0; - } /** * 根据taskid 删除任务 @@ -93,4 +85,21 @@ public class AnnotationTaskServiceImpl implements AnnotationTaskService { return false; } + + /** + * 手工标注 + */ + @Override + public void manualAnnotate(AnnotationSubTaskEntity subTask) { + + } + + /** + * AI标注 + */ + @Override + public void aiAnnotate(AnnotationSubTaskEntity subTask) { + + } + }