From: Shane Jaroch Date: Wed, 21 Jan 2026 19:27:46 +0000 (-0500) Subject: add daily log (wip), and fix ubuntu 20.04 build X-Git-Url: https://git.nutra.tk/v2?a=commitdiff_plain;h=90af78e96bad4ab9cc0a2427f28b074143120dbb;p=nutratech%2Fgui.git add daily log (wip), and fix ubuntu 20.04 build --- diff --git a/include/mainwindow.h b/include/mainwindow.h index 6668513..68f1524 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -6,6 +6,7 @@ #include #include +#include "widgets/dailylogwidget.h" #include "widgets/detailswidget.h" #include "widgets/mealwidget.h" #include "widgets/searchwidget.h" @@ -32,6 +33,7 @@ private: SearchWidget* searchWidget; DetailsWidget* detailsWidget; MealWidget* mealWidget; + DailyLogWidget* dailyLogWidget; FoodRepository repository; QMenu* recentFilesMenu; diff --git a/include/widgets/dailylogwidget.h b/include/widgets/dailylogwidget.h new file mode 100644 index 0000000..98a0b34 --- /dev/null +++ b/include/widgets/dailylogwidget.h @@ -0,0 +1,30 @@ +#ifndef DAILYLOGWIDGET_H +#define DAILYLOGWIDGET_H + +#include +#include +#include +#include + +#include "db/foodrepository.h" +#include "db/mealrepository.h" + +class DailyLogWidget : public QWidget { + Q_OBJECT + +public: + explicit DailyLogWidget(QWidget* parent = nullptr); + +public slots: + void refresh(); + +private: + void setupUi(); + void updateTable(); + + QTableWidget* logTable; + MealRepository m_mealRepo; + FoodRepository m_foodRepo; +}; + +#endif // DAILYLOGWIDGET_H diff --git a/include/widgets/mealwidget.h b/include/widgets/mealwidget.h index 041a4ba..e6c83df 100644 --- a/include/widgets/mealwidget.h +++ b/include/widgets/mealwidget.h @@ -24,8 +24,12 @@ public: void addFood(int foodId, const QString& foodName, double grams); +signals: + void logUpdated(); + private slots: void clearMeal(); + void onAddToLog(); private: void updateTotals(); diff --git a/src/db/mealrepository.cpp b/src/db/mealrepository.cpp index 3fe9bfc..eba8078 100644 --- a/src/db/mealrepository.cpp +++ b/src/db/mealrepository.cpp @@ -36,7 +36,7 @@ void MealRepository::addFoodLog(int foodId, double grams, int mealId, QDate date if (date == QDate::currentDate()) { timestamp = QDateTime::currentSecsSinceEpoch(); } else { - timestamp = date.startOfDay().toSecsSinceEpoch() + 43200; // Noon + timestamp = QDateTime(date, QTime(0, 0, 0)).toSecsSinceEpoch() + 43200; // Noon } QSqlQuery query(db); @@ -62,8 +62,8 @@ std::vector MealRepository::getDailyLogs(QDate date) { ensureMealNamesLoaded(); - qint64 startOfDay = date.startOfDay().toSecsSinceEpoch(); - qint64 endOfDay = date.endOfDay().toSecsSinceEpoch(); + qint64 startOfDay = QDateTime(date, QTime(0, 0, 0)).toSecsSinceEpoch(); + qint64 endOfDay = QDateTime(date, QTime(23, 59, 59)).toSecsSinceEpoch(); QSqlQuery query(userDb); query.prepare( @@ -128,8 +128,8 @@ void MealRepository::clearDailyLogs(QDate date) { QSqlDatabase db = DatabaseManager::instance().userDatabase(); if (!db.isOpen()) return; - qint64 startOfDay = date.startOfDay().toSecsSinceEpoch(); - qint64 endOfDay = date.endOfDay().toSecsSinceEpoch(); + qint64 startOfDay = QDateTime(date, QTime(0, 0, 0)).toSecsSinceEpoch(); + qint64 endOfDay = QDateTime(date, QTime(23, 59, 59)).toSecsSinceEpoch(); QSqlQuery query(db); query.prepare("DELETE FROM log_food WHERE date >= ? AND date <= ? AND profile_id = 1"); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0dfdf31..c03a20b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -89,9 +89,13 @@ void MainWindow::setupUi() { detailsWidget = new DetailsWidget(this); tabs->addTab(detailsWidget, "Analyze"); - // Meal Tab + // Meal Tab (Builder) mealWidget = new MealWidget(this); - tabs->addTab(mealWidget, "Meal Tracker"); + tabs->addTab(mealWidget, "Meal Builder"); + + // Daily Log Tab + dailyLogWidget = new DailyLogWidget(this); + tabs->addTab(dailyLogWidget, "Daily Log"); // Connect Analysis -> Meal connect(detailsWidget, &DetailsWidget::addToMeal, this, @@ -101,6 +105,9 @@ void MainWindow::setupUi() { // tabs->setCurrentWidget(mealWidget); }); + // Connect Meal Builder -> Daily Log + connect(mealWidget, &MealWidget::logUpdated, dailyLogWidget, &DailyLogWidget::refresh); + // Status Bar dbStatusLabel = new QLabel(this); dbStatusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); diff --git a/src/widgets/dailylogwidget.cpp b/src/widgets/dailylogwidget.cpp new file mode 100644 index 0000000..978d11e --- /dev/null +++ b/src/widgets/dailylogwidget.cpp @@ -0,0 +1,61 @@ +#include "widgets/dailylogwidget.h" + +#include +#include +#include + +DailyLogWidget::DailyLogWidget(QWidget* parent) : QWidget(parent) { + setupUi(); + refresh(); +} + +void DailyLogWidget::setupUi() { + auto* layout = new QVBoxLayout(this); + + layout->addWidget(new QLabel("Today's Food Log", this)); + + logTable = new QTableWidget(this); + logTable->setColumnCount(4); + logTable->setHorizontalHeaderLabels({"Meal", "Food", "Amount", "Calories"}); + logTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); + + layout->addWidget(logTable); +} + +void DailyLogWidget::refresh() { + updateTable(); +} + +void DailyLogWidget::updateTable() { + logTable->setRowCount(0); + + // Get logs for today + auto logs = m_mealRepo.getDailyLogs(QDate::currentDate()); + + for (const auto& log : logs) { + int row = logTable->rowCount(); + logTable->insertRow(row); + + // Meal Name + logTable->setItem(row, 0, new QTableWidgetItem(log.mealName)); + + // Food Name + logTable->setItem(row, 1, new QTableWidgetItem(log.foodName)); + + // Amount + logTable->setItem(row, 2, new QTableWidgetItem(QString::number(log.grams, 'f', 1) + " g")); + + // Calories Calculation + // TODO: optimize by fetching in batch or using repository better? + // For now, simple fetch is fine for valid DBs + auto nutrients = m_foodRepo.getFoodNutrients(log.foodId); + double kcal = 0; + for (const auto& nut : nutrients) { + if (nut.id == 208) { // KCAL + kcal = (nut.amount * log.grams) / 100.0; + break; + } + } + logTable->setItem(row, 3, new QTableWidgetItem(QString::number(kcal, 'f', 0) + " kcal")); + } +} diff --git a/src/widgets/mealwidget.cpp b/src/widgets/mealwidget.cpp index 2921932..03452bf 100644 --- a/src/widgets/mealwidget.cpp +++ b/src/widgets/mealwidget.cpp @@ -4,13 +4,14 @@ #include #include #include +#include #include MealWidget::MealWidget(QWidget* parent) : QWidget(parent) { auto* layout = new QVBoxLayout(this); // Items List - layout->addWidget(new QLabel("Meal Composition", this)); + layout->addWidget(new QLabel("Meal Composition (Builder)", this)); itemsTable = new QTableWidget(this); itemsTable->setColumnCount(3); itemsTable->setHorizontalHeaderLabels({"Food", "Grams", "Calories"}); @@ -18,53 +19,71 @@ MealWidget::MealWidget(QWidget* parent) : QWidget(parent) { layout->addWidget(itemsTable); // Controls - clearButton = new QPushButton("Clear Meal", this); + auto* buttonLayout = new QHBoxLayout(); + + auto* addToLogButton = new QPushButton("Add to Log", this); + connect(addToLogButton, &QPushButton::clicked, this, &MealWidget::onAddToLog); + buttonLayout->addWidget(addToLogButton); + + clearButton = new QPushButton("Clear Builder", this); connect(clearButton, &QPushButton::clicked, this, &MealWidget::clearMeal); - layout->addWidget(clearButton); + buttonLayout->addWidget(clearButton); + + layout->addLayout(buttonLayout); // Totals - layout->addWidget(new QLabel("Total Nutrition", this)); + layout->addWidget(new QLabel("Predicted Nutrition", this)); totalsTable = new QTableWidget(this); totalsTable->setColumnCount(3); totalsTable->setHorizontalHeaderLabels({"Nutrient", "Total", "Unit"}); totalsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); layout->addWidget(totalsTable); - refresh(); // Load initial state + refresh(); } void MealWidget::addFood(int foodId, const QString& foodName, double grams) { - // Default to meal_id 1 (e.g. Breakfast/General) for now - // TODO: Add UI to select meal - m_mealRepo.addFoodLog(foodId, grams, 1); + std::vector baseNutrients = repository.getFoodNutrients(foodId); + + MealItem item; + item.foodId = foodId; + item.name = foodName; + item.grams = grams; + item.nutrients_100g = baseNutrients; + mealItems.push_back(item); + refresh(); } -void MealWidget::refresh() { - mealItems.clear(); - itemsTable->setRowCount(0); +void MealWidget::onAddToLog() { + if (mealItems.empty()) return; - auto logs = m_mealRepo.getDailyLogs(); // defaults to today + // TODO: Add meal selection dialog? For now default to Breakfast/General (1) + int mealId = 1; - for (const auto& log : logs) { - std::vector baseNutrients = repository.getFoodNutrients(log.foodId); + for (const auto& item : mealItems) { + m_mealRepo.addFoodLog(item.foodId, item.grams, mealId); + } - MealItem item; - item.foodId = log.foodId; - item.name = log.foodName; - item.grams = log.grams; - item.nutrients_100g = baseNutrients; - mealItems.push_back(item); + emit logUpdated(); - // Update Items Table + mealItems.clear(); + refresh(); + + QMessageBox::information(this, "Logged", "Meal added to daily log."); +} + +void MealWidget::refresh() { + itemsTable->setRowCount(0); + + for (const auto& item : mealItems) { int row = itemsTable->rowCount(); itemsTable->insertRow(row); itemsTable->setItem(row, 0, new QTableWidgetItem(item.name)); itemsTable->setItem(row, 1, new QTableWidgetItem(QString::number(item.grams))); - // Calculate Calories (ID 208) double kcal = 0; - for (const auto& nut : baseNutrients) { + for (const auto& nut : item.nutrients_100g) { if (nut.id == 208) { kcal = (nut.amount * item.grams) / 100.0; break; @@ -77,8 +96,16 @@ void MealWidget::refresh() { } void MealWidget::clearMeal() { - m_mealRepo.clearDailyLogs(); - refresh(); + if (mealItems.empty()) return; + + auto reply = QMessageBox::question(this, "Clear Builder", + "Are you sure you want to clear the current meal builder?", + QMessageBox::Yes | QMessageBox::No); + + if (reply == QMessageBox::Yes) { + mealItems.clear(); + refresh(); + } } void MealWidget::updateTotals() {