--- /dev/null
+name: Release Build
+
+on:
+ push:
+ tags:
+ - "v*"
+
+jobs:
+ build-linux-20-04:
+ name: "Linux (Ubuntu 20.04)"
+ runs-on: ubuntu-20.04
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
+ - name: Install Dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y git cmake build-essential \
+ qtbase5-dev libqt5sql5-sqlite libgl1-mesa-dev
+
+ - name: Build
+ run: make release
+
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: nutra-linux-20.04
+ path: build/nutra
+
+ build-linux-22-04:
+ name: "Linux (Ubuntu 22.04)"
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
+ - name: Install Dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y git cmake build-essential \
+ qt6-base-dev libqt6sql6 libqt6sql6-sqlite libgl1-mesa-dev
+
+ - name: Build
+ run: make release
+
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: nutra-linux-22.04
+ path: build/nutra
+
+ build-linux-24-04:
+ name: "Linux (Ubuntu 24.04)"
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
+ - name: Install Dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y git cmake build-essential \
+ qt6-base-dev libqt6sql6 libqt6sql6-sqlite libgl1-mesa-dev
+
+ - name: Build
+ run: make release
+
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: nutra-linux-24.04
+ path: build/nutra
+
+ build-windows:
+ name: "Windows"
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v3
+ with:
+ version: "6.5.0"
+ host: "windows"
+
+ - name: Build
+ run: make release
+
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: nutra-win64.exe
+ path: build/Release/nutra.exe
+
+ build-macos:
+ name: "macOS"
+ runs-on: macos-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v3
+ with:
+ version: "6.5.0"
+ host: "mac"
+
+ - name: Build
+ run: make release
+
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: nutra-macos.app
+ path: build/nutra.app
+
+ release:
+ name: "Create Release"
+ needs:
+ [
+ build-linux-20-04,
+ build-linux-22-04,
+ build-linux-24-04,
+ build-windows,
+ build-macos,
+ ]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Verify tag is on master
+ run: |
+ git fetch origin master
+ if ! git merge-base --is-ancestor ${{ github.ref_name }} origin/master; then
+ echo "Error: Tag ${{ github.ref_name }} is not on master branch."
+ exit 1
+ fi
+
+ - name: Check for Pre-release
+ id: check_prerelease
+ run: |
+ if [[ "${{ github.ref_name }}" == *"-"* ]]; then
+ echo "is_prerelease=true" >> $GITHUB_OUTPUT
+ echo "Detected pre-release tag."
+ else
+ echo "is_prerelease=false" >> $GITHUB_OUTPUT
+ echo "Detected stable release tag."
+ fi
+
+ - name: Download Artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: artifacts
+
+ - name: Create Release
+ uses: softprops/action-gh-release@v1
+ if: startsWith(github.ref, 'refs/tags/')
+ with:
+ prerelease: ${{ steps.check_prerelease.outputs.is_prerelease }}
+ files: |
+ nutra-linux-20.04/nutra
+ nutra-linux-22.04/nutra
+ nutra-linux-24.04/nutra
+ nutra-win64.exe/nutra.exe
+ nutra-macos.app/**
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-name: Manual Version Bump
+name: Bump Version
on:
workflow_dispatch:
inputs:
bump_type:
- description: "Type of bump"
+ description: "How to bump version"
required: true
default: "patch"
type: choice
- patch
- minor
- major
+ pre_release_type:
+ description: "Pre-release type (optional)"
+ required: false
+ default: "none"
+ type: choice
+ options:
+ - none
+ - beta
+ - rc
jobs:
- bump:
+ bump-version:
runs-on: ubuntu-latest
- permissions:
- contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- - name: Configure Git
+ - name: Git Config
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- - name: Calculate and Push New Tag
+ - name: Bump Version
run: |
- # Get current tag
- CURRENT_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
- echo "Current tag: $CURRENT_TAG"
+ # Get latest tag, remove 'v' prefix
+ LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
+ # Remove v prefix
+ VERSION=${LATEST_TAG#v}
+
+ # Parse current version
+ BASE_VERSION=$(echo "$VERSION" | cut -d'-' -f1)
+ PRERELEASE_PART=$(echo "$VERSION" | cut -d'-' -f2- -s)
- # Remove 'v' prefix
- VERSION=${CURRENT_TAG#v}
+ IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE_VERSION"
- # Split version
- IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
+ BUMP_TYPE="${{ inputs.bump_type }}"
+ PRE_TYPE="${{ inputs.pre_release_type }}"
- # Calculate next version
- case "${{ inputs.bump_type }}" in
- major)
- MAJOR=$((MAJOR + 1))
- MINOR=0
- PATCH=0
- ;;
- minor)
- MINOR=$((MINOR + 1))
- PATCH=0
- ;;
- patch)
+ # If currently no prerelease part, simple bump logic or start new prerelease
+ if [ -z "$PRERELEASE_PART" ]; then
+ if [ "$BUMP_TYPE" == "major" ]; then
+ MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0
+ elif [ "$BUMP_TYPE" == "minor" ]; then
+ MINOR=$((MINOR + 1)); PATCH=0
+ else
PATCH=$((PATCH + 1))
- ;;
- esac
+ fi
+
+ if [ "$PRE_TYPE" != "none" ]; then
+ NEW_TAG="v$MAJOR.$MINOR.$PATCH-$PRE_TYPE.1"
+ else
+ NEW_TAG="v$MAJOR.$MINOR.$PATCH"
+ fi
+ else
+ # Existing prerelease (e.g., 1.0.0-beta.1)
+ # Check if we are switching pre-release type or completing it
+ CURRENT_PRE_TYPE=$(echo "$PRERELEASE_PART" | cut -d'.' -f1)
+ CURRENT_PRE_NUM=$(echo "$PRERELEASE_PART" | cut -d'.' -f2)
+
+ if [ "$PRE_TYPE" == "none" ]; then
+ # Promotion to stable: 1.0.0-beta.1 -> 1.0.0
+ # Keep same MAJOR.MINOR.PATCH
+ NEW_TAG="v$MAJOR.$MINOR.$PATCH"
+ elif [ "$PRE_TYPE" == "$CURRENT_PRE_TYPE" ]; then
+ # Increment same prerelease type: 1.0.0-beta.1 -> 1.0.0-beta.2
+ NEW_NUM=$((CURRENT_PRE_NUM + 1))
+ NEW_TAG="v$MAJOR.$MINOR.$PATCH-$PRE_TYPE.$NEW_NUM"
+ else
+ # Switching type, restart count? e.g. beta.2 -> rc.1
+ NEW_TAG="v$MAJOR.$MINOR.$PATCH-$PRE_TYPE.1"
+ fi
+
+ # Note: If user explicitly requested BUMP_TYPE (major/minor/patch) on a pre-release,
+ # this logic might need refinement, but standard flow is:
+ # 1. Bump to new version (potentially starting beta)
+ # 2. Iterate on beta
+ # 3. Promote to stable (none)
+ fi
- NEW_TAG="v$MAJOR.$MINOR.$PATCH"
- echo "New tag: $NEW_TAG"
+ echo "Bumping from $LATEST_TAG to $NEW_TAG"
+ echo "NEW_TAG=$NEW_TAG" >> $GITHUB_ENV
- # Create and push tag
- git tag "$NEW_TAG"
- git push origin "$NEW_TAG"
+ git tag ${{ env.NEW_TAG }}
+ git push origin ${{ env.NEW_TAG }}
struct FoodItem {
int id;
+ int foodGroupId;
QString description;
QString foodGroupName;
int nutrientCount;
// 1. Load Food Items with Group Names
QSqlQuery query(
- "SELECT f.id, f.long_desc, g.fdgrp_desc "
+ "SELECT f.id, f.long_desc, g.fdgrp_desc, f.fdgrp_id "
"FROM food_des f "
"JOIN fdgrp g ON f.fdgrp_id = g.id",
db);
item.id = query.value(0).toInt();
item.description = query.value(1).toString();
item.foodGroupName = query.value(2).toString();
+ item.foodGroupId = query.value(3).toInt();
// Set counts from map (default 0 if not found)
auto it = nutrientCounts.find(item.id);
void DailyLogWidget::setupUi() {
auto* mainLayout = new QVBoxLayout(this);
- QSplitter* splitter = new QSplitter(Qt::Vertical, this);
+ auto* splitter = new QSplitter(Qt::Vertical, this);
mainLayout->addWidget(splitter);
// --- Top: Log Table ---
- QWidget* topWidget = new QWidget(this);
+ auto* topWidget = new QWidget(this);
auto* topLayout = new QVBoxLayout(topWidget);
topLayout->setContentsMargins(0, 0, 0, 0);
topLayout->addWidget(new QLabel("Today's Food Log", this));
splitter->addWidget(topWidget);
// --- Bottom: Analysis ---
- QWidget* bottomWidget = new QWidget(this);
+ auto* bottomWidget = new QWidget(this);
auto* bottomLayout = new QVBoxLayout(bottomWidget);
bottomLayout->setContentsMargins(0, 0, 0, 0);
- QGroupBox* analysisBox = new QGroupBox("Analysis (Projected)", this);
+ auto* analysisBox = new QGroupBox("Analysis (Projected)", this);
auto* analysisLayout = new QVBoxLayout(analysisBox);
// Analysis UI
for (int i = 0; i < static_cast<int>(results.size()); ++i) {
const auto& item = results[i];
resultsTable->setItem(i, 0, new QTableWidgetItem(QString::number(item.id)));
- resultsTable->setItem(i, 1, new QTableWidgetItem(item.description));
- resultsTable->setItem(i, 2, new QTableWidgetItem(item.foodGroupName));
+ static const std::map<int, QString> groupAbbreviations = {
+ {1100, "Vegetables"}, // Vegetables and Vegetable Products
+ {600, "Soups/Sauces"}, // Soups, Sauces, and Gravies
+ {1700, "Lamb/Veal/Game"}, // Lamb, Veal, and Game Products
+ {500, "Poultry"}, // Poultry Products
+ {700, "Sausages/Meats"}, // Sausages and Luncheon Meats
+ {800, "Cereals"}, // Breakfast Cereals
+ {900, "Fruits"}, // Fruits and Fruit Juices
+ {1200, "Nuts/Seeds"}, // Nut and Seed Products
+ {1400, "Beverages"}, // Beverages
+ {400, "Fats/Oils"}, // Fats and Oils
+ {1900, "Sweets"}, // Sweets
+ {1800, "Baked Prod."}, // Baked Products
+ {2100, "Fast Food"}, // Fast Foods
+ {2200, "Meals/Entrees"}, // Meals, Entrees, and Side Dishes
+ {2500, "Snacks"}, // Snacks
+ {3600, "Restaurant"}, // Restaurant Foods
+ {100, "Dairy/Egg"}, // Dairy and Egg Products
+ {1300, "Beef"}, // Beef Products
+ {1000, "Pork"}, // Pork Products
+ {2000, "Grains/Pasta"}, // Cereal Grains and Pasta
+ {1600, "Legumes"}, // Legumes and Legume Products
+ {1500, "Fish/Shellfish"}, // Finfish and Shellfish Products
+ {300, "Baby Food"}, // Baby Foods
+ {200, "Spices"}, // Spices and Herbs
+ {3500, "Native Foods"} // American Indian/Alaska Native Foods
+ };
+
+ QString group = item.foodGroupName;
+ auto it = groupAbbreviations.find(item.foodGroupId);
+ if (it != groupAbbreviations.end()) {
+ group = it->second;
+ } else if (group.length() > 20) {
+ group = group.left(17) + "...";
+ }
+ resultsTable->setItem(i, 2, new QTableWidgetItem(group));
resultsTable->setItem(i, 3, new QTableWidgetItem(QString::number(item.nutrientCount)));
resultsTable->setItem(i, 4, new QTableWidgetItem(QString::number(item.aminoCount)));
resultsTable->setItem(i, 5, new QTableWidgetItem(QString::number(item.flavCount)));