]> Nutra Git (v2) - nutratech/gui.git/commitdiff
version/release workflows. ui updates
authorShane Jaroch <chown_tee@proton.me>
Wed, 21 Jan 2026 20:34:29 +0000 (15:34 -0500)
committerShane Jaroch <chown_tee@proton.me>
Wed, 21 Jan 2026 20:34:29 +0000 (15:34 -0500)
.github/workflows/release.yml [new file with mode: 0644]
.github/workflows/version-bump.yml
include/db/foodrepository.h
src/db/foodrepository.cpp
src/widgets/dailylogwidget.cpp
src/widgets/searchwidget.cpp

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644 (file)
index 0000000..ef4c207
--- /dev/null
@@ -0,0 +1,176 @@
+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 }}
index 2019c8e0af0cf921bf6fe53c496f212d6e7d10d0..1714614ef2bf9de2380790ddc42cff647417457a 100644 (file)
@@ -1,10 +1,10 @@
-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
@@ -12,54 +12,89 @@ on:
           - 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 }}
index cb2d2660be2146663f2aedb6ff51fb5558a5fe85..3a27f47a5e76ad0e657c77d9276c47564f3d5290 100644 (file)
@@ -20,6 +20,7 @@ struct ServingWeight {
 
 struct FoodItem {
     int id;
+    int foodGroupId;
     QString description;
     QString foodGroupName;
     int nutrientCount;
index 05d4d328407982d4d2a33c9642120567e0da25aa..646d19587a4c19b76ddb56a4f47fc3d7a7bc3b88 100644 (file)
@@ -24,7 +24,7 @@ void FoodRepository::ensureCacheLoaded() {
 
     // 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);
@@ -41,6 +41,7 @@ void FoodRepository::ensureCacheLoaded() {
         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);
index 40b0cc5f44f13e3d6e3ebd7ddb59a79ae349413d..b0b98709bb662711429b5d3e82db6e8d7591e44c 100644 (file)
@@ -15,11 +15,11 @@ DailyLogWidget::DailyLogWidget(QWidget* parent) : QWidget(parent) {
 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));
@@ -33,11 +33,11 @@ void DailyLogWidget::setupUi() {
     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
index 81683106fd34bd15234f277e343fd89c9deac541..a4a0b805d3ef16afa0eb2d817324524b2a0615b3 100644 (file)
@@ -64,8 +64,42 @@ void SearchWidget::performSearch() {
     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)));