Add period companion wellbeing tools
All checks were successful
Build Org Backend / build (push) Successful in 19s
All checks were successful
Build Org Backend / build (push) Successful in 19s
This commit is contained in:
@@ -17,15 +17,20 @@ import org.zaine.app.dto.PeriodCompanionDTO.DailyLogDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.DailyLogRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.DashboardDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.InsightsDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.FastingLogDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.FastingLogRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.PeriodCycleDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.PeriodCycleRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.PeriodEntryRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.PredictionDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.ReflectionDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.ReflectionRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SettingsDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SettingsRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SupportMessageDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SupportMessageRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SymptomDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.WellbeingDTO;
|
||||
import org.zaine.app.service.PeriodCompanionService;
|
||||
|
||||
import java.time.LocalDate;
|
||||
@@ -117,6 +122,30 @@ public class PeriodCompanionController {
|
||||
return periodCompanionService.getInsights();
|
||||
}
|
||||
|
||||
@Operation(summary = "Get cycle-aware wellbeing guidance")
|
||||
@GetMapping("/wellbeing")
|
||||
public WellbeingDTO getWellbeing() {
|
||||
return periodCompanionService.getWellbeing();
|
||||
}
|
||||
|
||||
@Operation(summary = "Get fasting make-up logs")
|
||||
@GetMapping("/fasting-logs")
|
||||
public List<FastingLogDTO> getFastingLogs() {
|
||||
return periodCompanionService.getFastingLogs();
|
||||
}
|
||||
|
||||
@Operation(summary = "Create or update a fasting make-up log")
|
||||
@PostMapping("/fasting-logs")
|
||||
public ResponseEntity<FastingLogDTO> saveFastingLog(@RequestBody FastingLogRequest request) {
|
||||
return ResponseEntity.ok(periodCompanionService.saveFastingLog(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "Create or update a daily reflection")
|
||||
@PostMapping("/reflections")
|
||||
public ResponseEntity<ReflectionDTO> saveReflection(@RequestBody ReflectionRequest request) {
|
||||
return ResponseEntity.ok(periodCompanionService.saveReflection(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "Get period companion settings")
|
||||
@GetMapping("/settings")
|
||||
public SettingsDTO getSettings() {
|
||||
|
||||
@@ -89,4 +89,19 @@ public final class PeriodCompanionDTO {
|
||||
public record CreateSymptomRequest(String name) {}
|
||||
public record SettingsDTO(int averageCycleLength, int averagePeriodLength, OffsetDateTime updatedAt) {}
|
||||
public record SettingsRequest(Integer averageCycleLength, Integer averagePeriodLength) {}
|
||||
public record FastingLogDTO(Long id, LocalDate fastDate, String status, String notes, OffsetDateTime createdAt, OffsetDateTime updatedAt) {}
|
||||
public record FastingLogRequest(LocalDate fastDate, String status, String notes) {}
|
||||
public record ReflectionDTO(Long id, LocalDate date, String prompt, String response, OffsetDateTime createdAt, OffsetDateTime updatedAt) {}
|
||||
public record ReflectionRequest(LocalDate date, String prompt, String response) {}
|
||||
public record WellbeingDTO(
|
||||
String phase,
|
||||
String bodyInsight,
|
||||
String supportiveAction,
|
||||
String spiritualReminder,
|
||||
String fiqhNote,
|
||||
ReflectionDTO todayReflection,
|
||||
List<ReflectionDTO> recentReflections,
|
||||
List<FastingLogDTO> fastingLogs,
|
||||
long missedFastCount,
|
||||
long madeUpFastCount) {}
|
||||
}
|
||||
|
||||
58
src/main/java/org/zaine/app/model/PeriodDailyReflection.java
Normal file
58
src/main/java/org/zaine/app/model/PeriodDailyReflection.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package org.zaine.app.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.PreUpdate;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "pc_daily_reflections")
|
||||
public class PeriodDailyReflection {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private LocalDate date;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String prompt;
|
||||
|
||||
@Column(name = "response", columnDefinition = "text")
|
||||
private String response;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private OffsetDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
void onInsert() {
|
||||
OffsetDateTime now = OffsetDateTime.now();
|
||||
this.createdAt = now;
|
||||
this.updatedAt = now;
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
void onUpdate() {
|
||||
this.updatedAt = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
public Long getId() { return id; }
|
||||
public LocalDate getDate() { return date; }
|
||||
public void setDate(LocalDate date) { this.date = date; }
|
||||
public String getPrompt() { return prompt; }
|
||||
public void setPrompt(String prompt) { this.prompt = prompt; }
|
||||
public String getResponse() { return response; }
|
||||
public void setResponse(String response) { this.response = response; }
|
||||
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||
public OffsetDateTime getUpdatedAt() { return updatedAt; }
|
||||
}
|
||||
58
src/main/java/org/zaine/app/model/PeriodFastingLog.java
Normal file
58
src/main/java/org/zaine/app/model/PeriodFastingLog.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package org.zaine.app.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.PreUpdate;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "pc_fasting_logs")
|
||||
public class PeriodFastingLog {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "fast_date", nullable = false, unique = true)
|
||||
private LocalDate fastDate;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String status;
|
||||
|
||||
@Column(name = "notes", columnDefinition = "text")
|
||||
private String notes;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private OffsetDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private OffsetDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
void onInsert() {
|
||||
OffsetDateTime now = OffsetDateTime.now();
|
||||
this.createdAt = now;
|
||||
this.updatedAt = now;
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
void onUpdate() {
|
||||
this.updatedAt = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
public Long getId() { return id; }
|
||||
public LocalDate getFastDate() { return fastDate; }
|
||||
public void setFastDate(LocalDate fastDate) { this.fastDate = fastDate; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getNotes() { return notes; }
|
||||
public void setNotes(String notes) { this.notes = notes; }
|
||||
public OffsetDateTime getCreatedAt() { return createdAt; }
|
||||
public OffsetDateTime getUpdatedAt() { return updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.zaine.app.repositories;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.zaine.app.model.PeriodDailyReflection;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface PeriodDailyReflectionRepository extends JpaRepository<PeriodDailyReflection, Long> {
|
||||
Optional<PeriodDailyReflection> findByDate(LocalDate date);
|
||||
List<PeriodDailyReflection> findTop14ByOrderByDateDesc();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.zaine.app.repositories;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.zaine.app.model.PeriodFastingLog;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface PeriodFastingLogRepository extends JpaRepository<PeriodFastingLog, Long> {
|
||||
Optional<PeriodFastingLog> findByFastDate(LocalDate fastDate);
|
||||
List<PeriodFastingLog> findTop50ByOrderByFastDateDesc();
|
||||
long countByStatus(String status);
|
||||
}
|
||||
@@ -10,26 +10,35 @@ import org.zaine.app.dto.PeriodCompanionDTO.CycleTrendDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.DailyLogDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.DailyLogRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.DashboardDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.FastingLogDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.FastingLogRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.InsightsDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.MoodTrendDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.PeriodCycleDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.PeriodCycleRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.PeriodEntryRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.PredictionDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.ReflectionDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.ReflectionRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SettingsDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SettingsRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SupportMessageDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SupportMessageRequest;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SymptomDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.SymptomTrendDTO;
|
||||
import org.zaine.app.dto.PeriodCompanionDTO.WellbeingDTO;
|
||||
import org.zaine.app.model.PeriodCycle;
|
||||
import org.zaine.app.model.PeriodDailyReflection;
|
||||
import org.zaine.app.model.PeriodDailyLog;
|
||||
import org.zaine.app.model.PeriodFastingLog;
|
||||
import org.zaine.app.model.PeriodPredictionSnapshot;
|
||||
import org.zaine.app.model.PeriodSettings;
|
||||
import org.zaine.app.model.PeriodSupportMessage;
|
||||
import org.zaine.app.model.PeriodSymptom;
|
||||
import org.zaine.app.repositories.PeriodCycleRepository;
|
||||
import org.zaine.app.repositories.PeriodDailyReflectionRepository;
|
||||
import org.zaine.app.repositories.PeriodDailyLogRepository;
|
||||
import org.zaine.app.repositories.PeriodFastingLogRepository;
|
||||
import org.zaine.app.repositories.PeriodPredictionSnapshotRepository;
|
||||
import org.zaine.app.repositories.PeriodSettingsRepository;
|
||||
import org.zaine.app.repositories.PeriodSupportMessageRepository;
|
||||
@@ -51,6 +60,8 @@ import java.util.stream.Collectors;
|
||||
public class PeriodCompanionService {
|
||||
private final PeriodCycleRepository cycleRepository;
|
||||
private final PeriodDailyLogRepository dailyLogRepository;
|
||||
private final PeriodDailyReflectionRepository dailyReflectionRepository;
|
||||
private final PeriodFastingLogRepository fastingLogRepository;
|
||||
private final PeriodSymptomRepository symptomRepository;
|
||||
private final PeriodPredictionSnapshotRepository predictionRepository;
|
||||
private final PeriodSettingsRepository settingsRepository;
|
||||
@@ -60,6 +71,8 @@ public class PeriodCompanionService {
|
||||
public PeriodCompanionService(
|
||||
PeriodCycleRepository cycleRepository,
|
||||
PeriodDailyLogRepository dailyLogRepository,
|
||||
PeriodDailyReflectionRepository dailyReflectionRepository,
|
||||
PeriodFastingLogRepository fastingLogRepository,
|
||||
PeriodSymptomRepository symptomRepository,
|
||||
PeriodPredictionSnapshotRepository predictionRepository,
|
||||
PeriodSettingsRepository settingsRepository,
|
||||
@@ -67,6 +80,8 @@ public class PeriodCompanionService {
|
||||
PeriodPredictionService predictionService) {
|
||||
this.cycleRepository = cycleRepository;
|
||||
this.dailyLogRepository = dailyLogRepository;
|
||||
this.dailyReflectionRepository = dailyReflectionRepository;
|
||||
this.fastingLogRepository = fastingLogRepository;
|
||||
this.symptomRepository = symptomRepository;
|
||||
this.predictionRepository = predictionRepository;
|
||||
this.settingsRepository = settingsRepository;
|
||||
@@ -230,6 +245,76 @@ public class PeriodCompanionService {
|
||||
return new InsightsDTO(cycleTrends, symptomTrends, moodTrends);
|
||||
}
|
||||
|
||||
public WellbeingDTO getWellbeing() {
|
||||
LocalDate today = LocalDate.now();
|
||||
List<PeriodCycle> cycles = cycleRepository.findAllByOrderByStartDateAsc();
|
||||
PeriodSettings settings = getOrCreateSettings();
|
||||
String phase = predictionService.currentCycle(
|
||||
cycles,
|
||||
today,
|
||||
settings.getAverageCycleLength(),
|
||||
settings.getAveragePeriodLength()).phase();
|
||||
ReflectionDTO todayReflection = dailyReflectionRepository.findByDate(today)
|
||||
.map(this::toReflectionDto)
|
||||
.orElseGet(() -> new ReflectionDTO(null, today, defaultReflectionPrompt(phase), null, null, null));
|
||||
|
||||
return new WellbeingDTO(
|
||||
phase,
|
||||
bodyInsight(phase),
|
||||
supportiveAction(phase),
|
||||
spiritualReminder(phase),
|
||||
fiqhNote(phase),
|
||||
todayReflection,
|
||||
dailyReflectionRepository.findTop14ByOrderByDateDesc().stream().map(this::toReflectionDto).toList(),
|
||||
getFastingLogs(),
|
||||
fastingLogRepository.countByStatus("MISSED"),
|
||||
fastingLogRepository.countByStatus("MADE_UP"));
|
||||
}
|
||||
|
||||
public List<FastingLogDTO> getFastingLogs() {
|
||||
return fastingLogRepository.findTop50ByOrderByFastDateDesc().stream().map(this::toFastingLogDto).toList();
|
||||
}
|
||||
|
||||
public FastingLogDTO saveFastingLog(FastingLogRequest request) {
|
||||
if (request == null) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "fasting log payload is required");
|
||||
}
|
||||
LocalDate fastDate = requiredDate(request.fastDate(), "fastDate");
|
||||
String status = requiredText(request.status(), "status").toUpperCase(Locale.ROOT);
|
||||
if (!status.equals("MISSED") && !status.equals("MADE_UP")) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "status must be MISSED or MADE_UP");
|
||||
}
|
||||
PeriodFastingLog log = fastingLogRepository.findByFastDate(fastDate).orElseGet(PeriodFastingLog::new);
|
||||
log.setFastDate(fastDate);
|
||||
log.setStatus(status);
|
||||
log.setNotes(blankToNull(request.notes()));
|
||||
return toFastingLogDto(fastingLogRepository.save(log));
|
||||
}
|
||||
|
||||
public ReflectionDTO saveReflection(ReflectionRequest request) {
|
||||
if (request == null) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "reflection payload is required");
|
||||
}
|
||||
LocalDate date = request.date() == null ? LocalDate.now() : request.date();
|
||||
String prompt = blankToNull(request.prompt());
|
||||
if (prompt == null) {
|
||||
List<PeriodCycle> cycles = cycleRepository.findAllByOrderByStartDateAsc();
|
||||
PeriodSettings settings = getOrCreateSettings();
|
||||
String phase = predictionService.currentCycle(
|
||||
cycles,
|
||||
date,
|
||||
settings.getAverageCycleLength(),
|
||||
settings.getAveragePeriodLength()).phase();
|
||||
prompt = defaultReflectionPrompt(phase);
|
||||
}
|
||||
|
||||
PeriodDailyReflection reflection = dailyReflectionRepository.findByDate(date).orElseGet(PeriodDailyReflection::new);
|
||||
reflection.setDate(date);
|
||||
reflection.setPrompt(prompt);
|
||||
reflection.setResponse(blankToNull(request.response()));
|
||||
return toReflectionDto(dailyReflectionRepository.save(reflection));
|
||||
}
|
||||
|
||||
public SettingsDTO getSettings() {
|
||||
return toSettingsDto(getOrCreateSettings());
|
||||
}
|
||||
@@ -366,10 +451,71 @@ public class PeriodCompanionService {
|
||||
return new SupportMessageDTO(message.getId(), message.getAuthorName(), message.getMessage(), message.getCreatedAt());
|
||||
}
|
||||
|
||||
private FastingLogDTO toFastingLogDto(PeriodFastingLog log) {
|
||||
return new FastingLogDTO(log.getId(), log.getFastDate(), log.getStatus(), log.getNotes(), log.getCreatedAt(), log.getUpdatedAt());
|
||||
}
|
||||
|
||||
private ReflectionDTO toReflectionDto(PeriodDailyReflection reflection) {
|
||||
return new ReflectionDTO(
|
||||
reflection.getId(),
|
||||
reflection.getDate(),
|
||||
reflection.getPrompt(),
|
||||
reflection.getResponse(),
|
||||
reflection.getCreatedAt(),
|
||||
reflection.getUpdatedAt());
|
||||
}
|
||||
|
||||
private SettingsDTO toSettingsDto(PeriodSettings settings) {
|
||||
return new SettingsDTO(settings.getAverageCycleLength(), settings.getAveragePeriodLength(), settings.getUpdatedAt());
|
||||
}
|
||||
|
||||
private String bodyInsight(String phase) {
|
||||
return switch (phase) {
|
||||
case "Menstrual" -> "Energy can be lower during hayd. Rest, warmth, hydration, and lighter plans may feel better.";
|
||||
case "Follicular" -> "Energy often rebuilds after bleeding. This can be a good time for planning, movement, and fresh routines.";
|
||||
case "Ovulation" -> "Some people feel more social and alert around ovulation, while others notice cramps or tenderness.";
|
||||
case "Luteal" -> "Mood, sleep, cravings, and focus can shift in the luteal phase. Gentler expectations can help.";
|
||||
default -> "Track a few cycles and the guidance will become more specific to your rhythm.";
|
||||
};
|
||||
}
|
||||
|
||||
private String supportiveAction(String phase) {
|
||||
return switch (phase) {
|
||||
case "Menstrual" -> "Choose one kind thing for the body today: heat, a slow walk, extra water, or an early night.";
|
||||
case "Follicular" -> "Pick one task that benefits future you while your energy is returning.";
|
||||
case "Ovulation" -> "Use the clearer moments for connection, but keep space for rest if your body asks for it.";
|
||||
case "Luteal" -> "Reduce friction where possible: prepare simple meals, lower the pace, and write down what feels heavy.";
|
||||
default -> "Make one small note about what your body is telling you today.";
|
||||
};
|
||||
}
|
||||
|
||||
private String spiritualReminder(String phase) {
|
||||
return switch (phase) {
|
||||
case "Menstrual" -> "Even when salah and fasting pause, dhikr, dua, gratitude, and reflection remain open doors.";
|
||||
case "Follicular" -> "As energy returns, renew intentions gently rather than trying to catch up all at once.";
|
||||
case "Ovulation" -> "Use moments of ease for shukr and sincere dua.";
|
||||
case "Luteal" -> "When emotions feel louder, small consistent remembrance can be grounding.";
|
||||
default -> "Notice the body as an amanah and meet it with mercy.";
|
||||
};
|
||||
}
|
||||
|
||||
private String fiqhNote(String phase) {
|
||||
if ("Menstrual".equals(phase)) {
|
||||
return "Hayd-related rulings can vary by madhhab and situation. For spotting, istihadah, ghusl, or confusing bleeding, check a trusted scholar or teacher.";
|
||||
}
|
||||
return "For fasting make-ups, spotting, istihadah, and ghusl questions, use this as a reminder tool and confirm details with trusted fiqh guidance.";
|
||||
}
|
||||
|
||||
private String defaultReflectionPrompt(String phase) {
|
||||
return switch (phase) {
|
||||
case "Menstrual" -> "What would mercy toward your body look like today?";
|
||||
case "Follicular" -> "What intention do you want to rebuild gently this week?";
|
||||
case "Ovulation" -> "Where can you use today’s energy with gratitude and balance?";
|
||||
case "Luteal" -> "What can you simplify before your next period begins?";
|
||||
default -> "What did you notice in your body and heart today?";
|
||||
};
|
||||
}
|
||||
|
||||
private void validateRange(LocalDate from, LocalDate to) {
|
||||
requiredDate(from, "from");
|
||||
requiredDate(to, "to");
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
CREATE TABLE IF NOT EXISTS pc_fasting_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
fast_date DATE NOT NULL UNIQUE,
|
||||
status VARCHAR(32) NOT NULL,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT chk_pc_fasting_logs_status CHECK (status IN ('MISSED', 'MADE_UP'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pc_fasting_logs_fast_date
|
||||
ON pc_fasting_logs (fast_date DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pc_daily_reflections (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
date DATE NOT NULL UNIQUE,
|
||||
prompt VARCHAR(255) NOT NULL,
|
||||
response TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pc_daily_reflections_date
|
||||
ON pc_daily_reflections (date DESC);
|
||||
|
||||
INSERT INTO pc_symptoms (name)
|
||||
VALUES
|
||||
('Acne'),
|
||||
('Appetite changes'),
|
||||
('Constipation'),
|
||||
('Diarrhea'),
|
||||
('Dizziness'),
|
||||
('Insomnia'),
|
||||
('Joint pain'),
|
||||
('Mood swings'),
|
||||
('Pelvic pain'),
|
||||
('Sleepiness'),
|
||||
('Spotting'),
|
||||
('Tenderness'),
|
||||
('Digestive discomfort'),
|
||||
('Sugar cravings')
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
Reference in New Issue
Block a user