Files
Reflect/Shared/Persisence/DataControllerHelper.swift
Trey t 6a8a66546b Enrich test data, fix multi-page PDF export, and polish UI
- Populate debug test data with random notes, guided reflections, and weather
- Fix PDF export to use UIPrintPageRenderer for proper multi-page pagination
- Add journal/reflection indicator icons to day list entry cells
- Fix weather card icon contrast by using secondarySystemBackground
- Align Generate Report and Export PDF button widths in ReportsView

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 18:19:01 -05:00

227 lines
7.7 KiB
Swift

//
// DataControllerHelper.swift
// Reflect
//
// SwiftData helper and test data operations.
//
import SwiftData
import Foundation
extension DataController {
func randomEntries(count: Int) -> [MoodEntryModel] {
var entries = [MoodEntryModel]()
for idx in 0..<count {
let date = Calendar.current.date(byAdding: .day, value: -idx, to: Date())!
let entry = MoodEntryModel(
forDate: date,
mood: Mood.allValues.randomElement()!,
entryType: .listView
)
entry.timestamp = date
entries.append(entry)
}
return entries
}
func populateMemory() {
#if DEBUG
for idx in 1..<255 {
let date = Calendar.current.date(byAdding: .day, value: -idx, to: Date())!
var moodValue = Int.random(in: 2...4)
if idx % 5 == 0 {
moodValue = Int.random(in: 0...4)
}
let entry = MoodEntryModel(
forDate: date,
mood: Mood(rawValue: moodValue) ?? .average,
entryType: .listView
)
entry.timestamp = date
modelContext.insert(entry)
}
save()
#endif
}
/// Creates an entry that is NOT inserted into the context - used for UI placeholders
func generateObjectNotInArray(forDate date: Date = Date(), withMood mood: Mood = .placeholder) -> MoodEntryModel {
let entry = MoodEntryModel(
forDate: date,
moodValue: mood.rawValue,
entryType: EntryType.listView.rawValue,
canEdit: false,
canDelete: false
)
return entry
}
func populateTestData() {
clearDB()
for idx in 1..<1000 {
let date = Calendar.current.date(byAdding: .day, value: -idx, to: Date())!
let mood = Self.randomMood()
let entry = MoodEntryModel(
forDate: date,
mood: mood,
entryType: .listView,
notes: Self.randomNotes(),
weatherJSON: Self.randomWeatherJSON(for: date),
reflectionJSON: Self.randomReflectionJSON(for: mood, on: date)
)
modelContext.insert(entry)
}
saveAndRunDataListeners()
}
#if DEBUG
func populate2YearsData() {
clearDB()
for idx in 1...730 {
let date = Calendar.current.date(byAdding: .day, value: -idx, to: Date())!
let mood = Self.randomMood()
let entry = MoodEntryModel(
forDate: date,
mood: mood,
entryType: .listView,
notes: Self.randomNotes(),
weatherJSON: Self.randomWeatherJSON(for: date),
reflectionJSON: Self.randomReflectionJSON(for: mood, on: date)
)
modelContext.insert(entry)
}
saveAndRunDataListeners()
}
#endif
private static func randomMood() -> Mood {
var moodValue = Int.random(in: 3...4)
if Int.random(in: 0...400) % 5 == 0 {
moodValue = Int.random(in: 0...4)
}
return Mood(rawValue: moodValue) ?? .average
}
// ~40% of entries get notes
private static func randomNotes() -> String? {
guard Bool.random() && Bool.random() == false else { return nil }
return sampleNotes.randomElement()
}
private static let sampleNotes: [String] = [
"Had a productive morning and got a lot done.",
"Went for a long walk in the park today.",
"Feeling grateful for the little things.",
"Tough meeting at work but pushed through.",
"Spent quality time with family this evening.",
"Couldn't sleep well last night, feeling tired.",
"Great workout at the gym today!",
"Read a really interesting book chapter.",
"Cooked a new recipe and it turned out great.",
"Feeling a bit anxious about tomorrow.",
"Had coffee with an old friend, felt wonderful.",
"Rainy day but cozy inside with a good movie.",
"Got some bad news, trying to stay positive.",
"Meditation session helped clear my mind.",
"Busy day but managed to stay focused.",
"Enjoyed a quiet evening at home.",
"Felt overwhelmed by my to-do list.",
"Beautiful sunset on the drive home.",
"Tried journaling for the first time in a while.",
"Had a really meaningful conversation today.",
]
// ~25% of entries get guided reflections
private static func randomReflectionJSON(for mood: Mood, on date: Date) -> String? {
guard Int.random(in: 0...3) == 0 else { return nil }
let category = MoodCategory(from: mood)
let questions = GuidedReflection.questions(for: category)
let answers = sampleAnswers(for: category)
let responses = questions.enumerated().map { index, question in
GuidedReflection.Response(
id: index,
question: question,
answer: answers[index % answers.count]
)
}
let reflection = GuidedReflection(
moodCategory: category,
responses: responses,
completedAt: date
)
return reflection.encode()
}
private static func sampleAnswers(for category: MoodCategory) -> [String] {
switch category {
case .positive:
return [
"I felt a sense of calm and contentment throughout the day.",
"Connecting with a friend really lifted my spirits.",
"I want to keep making time for the things that bring me joy.",
]
case .neutral:
return [
"A mix of emotions today, nothing too strong in either direction.",
"Work was steady but unremarkable.",
"I'd like to be more intentional about how I spend my evenings.",
"Maybe adding a short walk after lunch could help.",
]
case .negative:
return [
"I've been worrying about things outside my control.",
"A stressful interaction this morning set the tone for the day.",
"I think I need some quiet time to recharge.",
"Taking a few deep breaths and stepping away from screens.",
]
}
}
private static let weatherConditions: [(symbol: String, condition: String)] = [
("sun.max.fill", "Clear"),
("cloud.sun.fill", "Partly Cloudy"),
("cloud.fill", "Cloudy"),
("cloud.rain.fill", "Rain"),
("cloud.heavyrain.fill", "Heavy Rain"),
("cloud.drizzle.fill", "Drizzle"),
("cloud.bolt.fill", "Thunderstorms"),
("cloud.snow.fill", "Snow"),
("cloud.fog.fill", "Foggy"),
("wind", "Windy"),
("sun.haze.fill", "Hazy"),
]
private static func randomWeatherJSON(for date: Date) -> String? {
let condition = weatherConditions.randomElement()!
let high = Double.random(in: 5...35)
let low = high - Double.random(in: 5...15)
let weather = WeatherData(
conditionSymbol: condition.symbol,
condition: condition.condition,
temperature: (high + low) / 2.0,
highTemperature: high,
lowTemperature: low,
humidity: Double.random(in: 0.2...0.95),
latitude: 37.7749,
longitude: -122.4194,
fetchedAt: date
)
return weather.encode()
}
func longestStreak() -> [MoodEntryModel] {
let descriptor = FetchDescriptor<MoodEntryModel>(
sortBy: [SortDescriptor(\.forDate, order: .forward)]
)
return (try? modelContext.fetch(descriptor)) ?? []
}
}