- 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>
227 lines
7.7 KiB
Swift
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)) ?? []
|
|
}
|
|
}
|