1
0
Fork 0
timelinize/datasources/dummydata/dummydata.go
2026-02-24 15:22:31 +00:00

133 lines
3.7 KiB
Go

// 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,
},
},
}
}