From 4b7cf62ce8c051850d3bde0c9fba83a440caf060 Mon Sep 17 00:00:00 2001 From: Shane Jaroch Date: Sun, 25 Jan 2026 23:11:46 -0500 Subject: [PATCH] config update, gitignore, docs feature roadmap --- .gitignore | 23 +++++++++++++++ CMakeLists.txt | 57 +++++++++++++++--------------------- docs/todo.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 33 deletions(-) create mode 100644 docs/todo.md diff --git a/.gitignore b/.gitignore index b25244e..2c49320 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,26 @@ # C++ stuff build/ + +# CMake +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +install_manifest.txt +CTestTestfile.cmake +Makefile + +# Qt +*_autogen/ +*.qrc.cpp + +# Generated binaries and libs +*.a +*.so +*.dylib +*.exe + +# Specific executables (if in-source build happens) +/nutra +/test_* +/nutra.desktop diff --git a/CMakeLists.txt b/CMakeLists.txt index 08ba242..8b80074 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,9 +13,13 @@ set(CMAKE_AUTOUIC ON) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Sql) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Sql) + +# Sources file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "src/*.cpp") file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS "include/*.h") -set(PROJECT_SOURCES ${SOURCES} ${HEADERS} "resources.qrc") + +# Filter out main.cpp for the library +list(FILTER SOURCES EXCLUDE REGEX ".*src/main\\.cpp$") # Versioning if(NOT NUTRA_VERSION) @@ -34,43 +38,30 @@ if(NOT NUTRA_VERSION) endif() add_compile_definitions(NUTRA_VERSION_STRING="${NUTRA_VERSION}") +# Core Library +add_library(nutra_lib STATIC ${SOURCES} ${HEADERS} "resources.qrc") +target_include_directories(nutra_lib PUBLIC ${CMAKE_SOURCE_DIR}/include) +target_link_libraries(nutra_lib PUBLIC Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Sql) +# Main Executable +add_executable(nutra src/main.cpp) +target_link_libraries(nutra PRIVATE nutra_lib) - - - -add_executable(nutra - ${PROJECT_SOURCES} -) - -target_include_directories(nutra PRIVATE ${CMAKE_SOURCE_DIR}/include) - -target_link_libraries(nutra PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Sql) - +# Testing enable_testing() find_package(Qt${QT_VERSION_MAJOR}Test REQUIRED) -add_executable(test_nutra EXCLUDE_FROM_ALL tests/test_foodrepository.cpp src/db/databasemanager.cpp src/db/foodrepository.cpp src/utils/string_utils.cpp) -target_include_directories(test_nutra PRIVATE ${CMAKE_SOURCE_DIR}/include) -target_link_libraries(test_nutra PRIVATE Qt${QT_VERSION_MAJOR}::Test Qt${QT_VERSION_MAJOR}::Sql) - -add_test(NAME FoodRepoTest COMMAND test_nutra) - -file(GLOB_RECURSE TEST_DB_SOURCES - tests/test_databasemanager.cpp - tests/test_databasemanager.h - src/db/*.cpp - src/utils/*.cpp -) -add_executable(test_databasemanager ${TEST_DB_SOURCES}) -target_include_directories(test_databasemanager PRIVATE ${CMAKE_SOURCE_DIR}/include) -target_link_libraries(test_databasemanager PRIVATE Qt${QT_VERSION_MAJOR}::Test Qt${QT_VERSION_MAJOR}::Sql) -add_test(NAME DatabaseManagerTest COMMAND test_databasemanager) - -add_executable(test_calculations tests/test_calculations.cpp tests/test_calculations.h) -target_include_directories(test_calculations PRIVATE ${CMAKE_SOURCE_DIR}/include) -target_link_libraries(test_calculations PRIVATE Qt${QT_VERSION_MAJOR}::Test) -add_test(NAME CalculationsTest COMMAND test_calculations) +# Dynamic Test Discovery +file(GLOB TEST_SOURCES "tests/test_*.cpp") + +foreach(TEST_SOURCE ${TEST_SOURCES}) + get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) + + add_executable(${TEST_NAME} EXCLUDE_FROM_ALL ${TEST_SOURCE}) + target_link_libraries(${TEST_NAME} PRIVATE nutra_lib Qt${QT_VERSION_MAJOR}::Test) + + add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) +endforeach() include(GNUInstallDirs) diff --git a/docs/todo.md b/docs/todo.md new file mode 100644 index 0000000..8f1bea9 --- /dev/null +++ b/docs/todo.md @@ -0,0 +1,79 @@ +Based on your current setup—which utilizes `usda-sqlite` to process the USDA SR-Legacy database—you have a solid foundation for commodity ingredients (raw fruits, vegetables, meats). However, SR-Legacy is static (stopped updating in 2018) and lacks the vast universe of branded products (UPC scanning) and modern micronutrient density data. + +To augment this for a "good user experience" (effortless search + rich data), you should integrate the following open-source and public APIs. + +### 1. USDA FoodData Central (FDC) API + +Since your project is already built on USDA logic, this is the most seamless integration. SR-Legacy (which you currently use) is now just one subset of FoodData Central. + +* **Why use it:** It includes **"Branded Foods"** (over 300,000 items from labels) and **"Foundation Foods"** (newer, highly granular micronutrient data that replaces SR-Legacy). +* **Integration Strategy:** +* Your `food_des` table uses `fdgrp_id`. FDC uses `fdcId`. You can create a mapping table to link your legacy IDs to new FDC IDs. +* **Search:** The FDC API allows for keyword search which returns `fdcId`. You can use this to backfill missing nutrients in your `nut_data` table. + +* **Cost/License:** Free, Public Domain (U.S. Government). + +### 2. Open Food Facts (OFF) + +This is the "Wikipedia of food." It is the best open-source resource for scanning barcodes and finding branded products internationally. + +* **Why use it:** +* **Barcodes:** It relies heavily on UPC/EAN codes. If your UX involves a camera/scanner, this is essential. +* **Crowdsourced:** It covers niche brands that the USDA might miss. +* **Nutri-Score:** It provides calculated scores (Nutri-Score, NOVA group) which you can store in your `food_des` or a new `food_metadata` table. + +* **Integration Strategy:** +* Query by barcode: `https://world.openfoodfacts.org/api/v0/product/[barcode].json` +* Map their JSON `nutriments` object to your `nutr_def` IDs (e.g., map OFF `saturated-fat_100g` to your `nutr_def` ID for saturated fat). + +* **Cost/License:** Free, Open Database License (ODbL). + +### 3. SQLite FTS5 (Full-Text Search) + +You are currently using SQLite. To make search "effortless" without calling an external API for every keystroke, you should utilize SQLite's native Full-Text Search extension. + +* **Why use it:** Your `food_des` table contains `long_desc`, `shrt_desc`, and `com_name`. Standard SQL `LIKE` queries are slow and bad at matching "natural" language. FTS5 allows for lightning-fast, ranked search results (e.g., typing "chk brst" finds "Chicken Breast"). +* **Integration:** +* Modify your `sql/tables.sql` to include a virtual table: + +```sql +CREATE VIRTUAL TABLE food_search USING fts5(long_desc, shrt_desc, com_name, content=food_des, content_rowid=id); + +``` + +* Add a trigger to keep it updated. This will make your local search instant. + +### 4. Natural Language Processing (NLP) Parsers + +If "effortless search" means the user types "1 cup of oatmeal with 2 tbsp honey," you need a parser, not just a database. + +* **New York Times Ingredient Phrase Tagger (CRF++):** A structured learning model released by NYT to parse ingredient lines into amount, unit, and food. +* **Price:** Open Source (Apache 2.0). +* **Integration:** You can run this as a microservice (Python) alongside your `process.py`. When a user types a sentence, parse it to extract the quantity (to calculate `gram` weight) and the food subject (to query your SQLite DB). + +### Recommended Architecture Update + +Given your file structure, here is how you should integrate these sources: + +1. **Augment `nutr_def`:** Ensure your `nutr_def` table aligns with Open Food Facts tag naming conventions (add a column `off_tag` to map `fat` -> `fat_100g`). +2. **New Table `external_links`:** +Don't pollute `food_des` with mixed data. Create a linking table: + +```sql +CREATE TABLE external_links ( + local_food_id INT, + service_name TEXT, -- 'FDC', 'OFF' + external_id TEXT, -- '123456' or UPC + last_updated DATETIME, + FOREIGN KEY(local_food_id) REFERENCES food_des(id) +); + +``` + +1. **Python Script (`data/process.py` extension):** +Write a new script (e.g., `fetch_external.py`) that: + +* Takes a user query. +* Checks local SQLite FTS5 first. +* If no hit, queries Open Food Facts API. +* Inserts the result into `food_des` and `nut_data`, and saves the link in `external_links`. -- 2.52.0