package processor import ( "context" "fmt" "screenmark/screenmark/.gen/haystack/haystack/model" "screenmark/screenmark/agents" "screenmark/screenmark/models" "screenmark/screenmark/notifications" "sync" "github.com/charmbracelet/log" ) const STACK_PROCESS_AT_A_TIME = 10 // TODO: // This processor contains a lot of shared stuff. // If we ever want to do more generic stuff with "in-progress" and stuff // we can extract that into a common thing // // However, this will require a pretty big DB shuffle. type StackProcessor struct { stackModel models.StackModel logger *log.Logger stackAgent agents.CreateListAgent Processor *Processor[model.Stacks] notifier *notifications.Notifier[notifications.Notification] } func (p *StackProcessor) setStackToProcess(ctx context.Context, stack model.Stacks) { err := p.stackModel.UpdateProcess(ctx, stack.ID, model.Progress_InProgress) if err != nil { // TODO: what can we actually do here for the errors? // We can't stop the work for the others p.logger.Error("failed to update stack", "err", err) // TODO: we can use context here to actually pass some information through return } } func (p *StackProcessor) extractInfo(ctx context.Context, stack model.Stacks) { err := p.stackAgent.CreateList(p.logger, stack.UserID, stack.ID, stack.Name, stack.Description) if err != nil { // Again, wtf do we do? // Although i think the agent actually returns an error when it's finished p.logger.Error("failed to process image", "err", err) return } } func (p *StackProcessor) processImage(stack model.Stacks) { p.logger.Info("Processing image", "ID", stack.ID) ctx := context.Background() p.setStackToProcess(ctx, stack) var wg sync.WaitGroup // Future proofing! wg.Add(1) stackNotification := notifications.GetStackNotification(notifications.StackNotification{ Type: notifications.STACK_TYPE, Status: string(model.Progress_InProgress), StackID: stack.ID, Name: stack.Name, }) err := p.notifier.SendAndCreate(stack.UserID.String(), stackNotification) if err != nil { p.logger.Error("sending in progress notification", "err", err) return } go func() { p.extractInfo(ctx, stack) wg.Done() }() wg.Wait() // TODO: there is some repeated code here. The ergonomicts of the notifications, // isn't the best. stackNotification = notifications.GetStackNotification(notifications.StackNotification{ Type: notifications.STACK_TYPE, Status: string(model.Progress_Complete), StackID: stack.ID, Name: stack.Name, }) err = p.notifier.SendAndCreate(stack.UserID.String(), stackNotification) if err != nil { p.logger.Error("sending done notification", "err", err) return } } func NewStackProcessor( logger *log.Logger, stackModel models.StackModel, notifier *notifications.Notifier[notifications.Notification], ) (StackProcessor, error) { if notifier == nil { return StackProcessor{}, fmt.Errorf("notifier is nil") } stackAgent := agents.NewCreateListAgent(logger, stackModel) imageProcessor := StackProcessor{ logger: logger, stackModel: stackModel, stackAgent: stackAgent, notifier: notifier, } imageProcessor.Processor = NewProcessor(int(IMAGE_PROCESS_AT_A_TIME), imageProcessor.processImage) return imageProcessor, nil }