// Package dummydata is a development-only data source that emits a hardcoded // set of ClassMessage graph nodes to exercise the Conversations UI without // needing a real data export. Triggered by any file named // IMPORT_FOR_DUMMY_DATA.txt; the file contents are ignored. package dummydata import ( "context" "strings" "time" "github.com/timelinize/timelinize/timeline" "go.uber.org/zap" ) func init() { err := timeline.RegisterDataSource(timeline.DataSource{ Name: "dummy_data", Title: "Dummy Data", Description: "Emits hardcoded message graphs for UI development. Triggered by IMPORT_FOR_DUMMY_DATA.txt.", NewFileImporter: func() timeline.FileImporter { return new(Importer) }, }) if err != nil { timeline.Log.Fatal("registering data source", zap.Error(err)) } } // Importer is the dummy data importer. type Importer struct{} // Recognize returns a confident match for any file named IMPORT_FOR_DUMMY_DATA.txt. func (Importer) Recognize(_ context.Context, dirEntry timeline.DirEntry, _ timeline.RecognizeParams) (timeline.Recognition, error) { if strings.EqualFold(dirEntry.Name(), "IMPORT_FOR_DUMMY_DATA.txt") { return timeline.Recognition{Confidence: 1}, nil } return timeline.Recognition{}, nil } // FileImport emits a hardcoded conversation between three participants. // The file contents are ignored. func (imp *Importer) FileImport(ctx context.Context, _ timeline.DirEntry, params timeline.ImportParams) error { alice := entity("Alice Smith", "alice.smith") bob := entity("Bob Jones", "bob.jones") charlie := entity("Charlie Lee", "charlie.lee") type msg struct { sender timeline.Entity recipients []timeline.Entity text string ts time.Time } base := time.Date(2024, 6, 14, 10, 0, 0, 0, time.UTC) messages := []msg{ { sender: alice, recipients: []timeline.Entity{bob, charlie}, text: "Hey everyone, are we still on for Friday?", ts: base, }, { sender: bob, recipients: []timeline.Entity{alice, charlie}, text: "Yep! I'll be there. Should I bring anything?", ts: base.Add(3 * time.Minute), }, { sender: charlie, recipients: []timeline.Entity{alice, bob}, text: "Really sorry, I can't make it this time 😢", ts: base.Add(7 * time.Minute), }, { sender: alice, recipients: []timeline.Entity{bob, charlie}, text: "Aw no! We'll miss you Charlie. Bob, just bring yourself 😄", ts: base.Add(9 * time.Minute), }, { sender: bob, recipients: []timeline.Entity{alice, charlie}, text: "Sounds good, see you Friday Alice!", ts: base.Add(12 * time.Minute), }, { sender: charlie, recipients: []timeline.Entity{alice, bob}, text: "Have fun you two! Let's catch up the week after.", ts: base.Add(15 * time.Minute), }, } for _, m := range messages { if err := ctx.Err(); err != nil { return err } item := &timeline.Item{ Classification: timeline.ClassMessage, Timestamp: m.ts, Owner: m.sender, Content: timeline.ItemData{ Data: timeline.StringData(m.text), MediaType: "text/plain", }, } g := &timeline.Graph{Item: item} for _, recip := range m.recipients { r := recip // avoid loop-variable capture g.ToEntity(timeline.RelSent, &r) } params.Pipeline <- g } return nil } // entity constructs a named Entity with a datasource-scoped identity attribute. func entity(name, handle string) timeline.Entity { return timeline.Entity{ Name: name, Attributes: []timeline.Attribute{ { Name: "dummy_handle", Value: handle, Identity: true, }, }, } }