package labelstudio import ( "encoding/json" "fmt" "github.com/Gleipnir-Technology/nidus-sync/lint" ) type TaskResultValue struct { Text []string `json:"text"` } type TaskResult struct { ID string `json:"id"` FromName string `json:"from_name"` Origin string `json:"origin"` ToName string `json:"to_name"` Type string `json:"type"` Value TaskResultValue `json:"value"` } // TaskUpdate defines fields that can be updated in a task type TaskUpdate struct { // Fields that can be updated Annotations json.RawMessage `json:"annotations,omitempty"` Data *map[string]interface{} `json:"data,omitempty"` DraftExists *bool `json:"draft_exists,omitempty"` Drafts json.RawMessage `json:"drafts,omitempty"` GroundTruth *bool `json:"ground_truth,omitempty"` IsLabeled *bool `json:"is_labeled,omitempty"` Meta *map[string]interface{} `json:"meta,omitempty"` Predictions json.RawMessage `json:"predictions,omitempty"` Reviewed *bool `json:"reviewed"` // Internal tracking fieldsToUpdate map[string]bool } // NewTaskUpdate creates a new TaskUpdate builder func NewTaskUpdate() *TaskUpdate { return &TaskUpdate{ fieldsToUpdate: make(map[string]bool), } } func (t *TaskUpdate) MarshalJSON() ([]byte, error) { // Only include fields that are explicitly set updateMap := make(map[string]interface{}) if t.fieldsToUpdate["annotations"] { // Parse raw JSON back to interface{} to include in the map var annotations interface{} if err := json.Unmarshal(t.Annotations, &annotations); err != nil { return nil, err } updateMap["annotations"] = annotations } if t.fieldsToUpdate["data"] { updateMap["data"] = t.Data } if t.fieldsToUpdate["draft_exists"] { updateMap["draft_exists"] = t.DraftExists } if t.fieldsToUpdate["drafts"] { var drafts interface{} if err := json.Unmarshal(t.Drafts, &drafts); err != nil { return nil, err } updateMap["drafts"] = drafts } if t.fieldsToUpdate["ground_truth"] { updateMap["ground_truth"] = t.GroundTruth } if t.fieldsToUpdate["is_labeled"] { updateMap["is_labeled"] = t.IsLabeled } if t.fieldsToUpdate["meta"] { updateMap["meta"] = t.Meta } if t.fieldsToUpdate["predictions"] { var predictions interface{} if err := json.Unmarshal(t.Predictions, &predictions); err != nil { return nil, err } updateMap["predictions"] = predictions } if t.fieldsToUpdate["reviewed"] { updateMap["reviewed"] = t.Reviewed } return json.Marshal(updateMap) } func (t *TaskUpdate) SetAnnotations(annotations interface{}) *TaskUpdate { annotationsJSON, err := json.Marshal(annotations) if err != nil { // Handle error gracefully in a builder pattern // Could store the error and check it later return t } t.Annotations = annotationsJSON t.fieldsToUpdate["annotations"] = true return t } func (t *TaskUpdate) SetData(data map[string]interface{}) *TaskUpdate { t.Data = &data t.fieldsToUpdate["data"] = true return t } func (t *TaskUpdate) SetDraftExists(draftExists bool) *TaskUpdate { t.DraftExists = &draftExists t.fieldsToUpdate["draft_exists"] = true return t } func (t *TaskUpdate) SetDrafts(drafts interface{}) *TaskUpdate { draftsJSON, err := json.Marshal(drafts) if err != nil { return t } t.Drafts = draftsJSON t.fieldsToUpdate["drafts"] = true return t } func (t *TaskUpdate) SetGroundTruth(groundTruth bool) *TaskUpdate { t.GroundTruth = &groundTruth t.fieldsToUpdate["ground_truth"] = true return t } func (t *TaskUpdate) SetIsLabeled(isLabeled bool) *TaskUpdate { t.IsLabeled = &isLabeled t.fieldsToUpdate["is_labeled"] = true return t } func (t *TaskUpdate) SetMeta(meta map[string]interface{}) *TaskUpdate { t.Meta = &meta t.fieldsToUpdate["meta"] = true return t } func (t *TaskUpdate) SetPredictions(predictions interface{}) *TaskUpdate { predictionsJSON, err := json.Marshal(predictions) if err != nil { return t } t.Predictions = predictionsJSON t.fieldsToUpdate["predictions"] = true return t } func (t *TaskUpdate) SetReviewed(isReviewed bool) *TaskUpdate { t.Reviewed = &isReviewed t.fieldsToUpdate["reviewed"] = true return t } func (c *Client) TaskUpdate(taskID int, update *TaskUpdate) (*Task, error) { // Marshal the updates to JSON updateJSON, err := json.Marshal(update) if err != nil { return nil, fmt.Errorf("failed to marshal updates: %w", err) } // Create request path := fmt.Sprintf("/api/tasks/%d", taskID) resp, err := c.makeRequest("PATCH", path, updateJSON) if err != nil { return nil, fmt.Errorf("failed to PATCH %s: %w", path, err) } defer lint.LogOnErr(resp.Body.Close, "close response") // Parse response var updatedTask Task if err := json.NewDecoder(resp.Body).Decode(&updatedTask); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } return &updatedTask, nil }