Просмотр исходного кода

[#1] Fix version parsing, image queries, table formatting, and add Total row to Meal Planner

Lange François 3 недель назад
Родитель
Сommit
2cec419759
1 измененных файлов с 63 добавлено и 17 удалено
  1. 63 17
      app.py

+ 63 - 17
app.py

@@ -466,9 +466,10 @@ def render_version():
                         match = re.search(r'\$For' + r'mat:(.*?)\$', line)
                         match = re.search(r'\$For' + r'mat:(.*?)\$', line)
                         if match:
                         if match:
                             parts = match.group(1).split(':')
                             parts = match.group(1).split(':')
-                            if len(parts) >= 11 and not parts[2].startswith('%an'):
-                                git_version = f"{parts[7]}" # %cd (committer date)
-                                git_hash = parts[8][:7] if parts[8] else ""
+                            if len(parts) >= 13 and not parts[2].startswith('%an'):
+                                git_version = f"{parts[9]}:{parts[10]}:{parts[11]}"
+                                h = parts[12]
+                                git_hash = f"{h[:2]}{h[-2:]}" if h else ""
                                 break
                                 break
     except Exception:
     except Exception:
         pass
         pass
@@ -480,10 +481,12 @@ def render_version():
                 ['git', 'log', '-1', '--date=format:%Y/%m/%d %H:%M:%S', '--format=%cd', 'app.py'],
                 ['git', 'log', '-1', '--date=format:%Y/%m/%d %H:%M:%S', '--format=%cd', 'app.py'],
                 stderr=subprocess.DEVNULL
                 stderr=subprocess.DEVNULL
             ).decode('utf-8').strip()
             ).decode('utf-8').strip()
-            git_hash = subprocess.check_output(
-                ['git', 'log', '-1', '--format=%h', 'app.py'],
+            git_hash_full = subprocess.check_output(
+                ['git', 'log', '-1', '--format=%H', 'app.py'],
                 stderr=subprocess.DEVNULL
                 stderr=subprocess.DEVNULL
             ).decode('utf-8').strip()
             ).decode('utf-8').strip()
+            if git_hash_full:
+                git_hash = f"{git_hash_full[:2]}{git_hash_full[-2:]}"
         except Exception:
         except Exception:
             pass
             pass
 
 
@@ -769,8 +772,8 @@ with tab_explore:
                                min.calcium_100g, min.iron_100g, min.magnesium_100g, min.potassium_100g, min.zinc_100g
                                min.calcium_100g, min.iron_100g, min.magnesium_100g, min.potassium_100g, min.zinc_100g
                         FROM (
                         FROM (
                             SELECT code, product_name, generic_name, brands, ingredients_text,
                             SELECT code, product_name, generic_name, brands, ingredients_text,
-                                   NULL AS url, NULL AS image_url, NULL AS image_small_url, NULL AS image_ingredients_url, 
-                                   NULL AS image_ingredients_small_url, NULL AS image_nutrition_url, NULL AS image_nutrition_small_url
+                                   url, image_url, image_small_url, image_ingredients_url, 
+                                   image_ingredients_small_url, image_nutrition_url, image_nutrition_small_url
                             FROM food_db.products_core
                             FROM food_db.products_core
                             WHERE (MATCH(product_name, ingredients_text) AGAINST(%s IN BOOLEAN MODE) OR product_name LIKE %s)
                             WHERE (MATCH(product_name, ingredients_text) AGAINST(%s IN BOOLEAN MODE) OR product_name LIKE %s)
                             AND product_name IS NOT NULL AND product_name != '' AND product_name != 'None'
                             AND product_name IS NOT NULL AND product_name != '' AND product_name != 'None'
@@ -910,8 +913,8 @@ with tab_explore:
                         for col in df_display.columns:
                         for col in df_display.columns:
                             if 'image' in col.lower():
                             if 'image' in col.lower():
                                 df_display[col] = df_display[col].apply(lambda x: x if is_valid_image_url(x) else "")
                                 df_display[col] = df_display[col].apply(lambda x: x if is_valid_image_url(x) else "")
-                        # Replace None values with &nsbp
-                        df_display.replace(to_replace=r'^None$', value='&nsbp', regex=True, inplace=True)
+                        # Replace None values with  
+                        df_display.replace(to_replace=r'^None$', value=' ', regex=True, inplace=True)
                         # Only fillna with empty string on object columns to avoid Arrow float64 conversion errors
                         # Only fillna with empty string on object columns to avoid Arrow float64 conversion errors
                         for col in df_display.columns:
                         for col in df_display.columns:
                             if df_display[col].dtype == 'object':
                             if df_display[col].dtype == 'object':
@@ -1144,7 +1147,7 @@ with tab_plate:
                         sql = f"""
                         sql = f"""
                             SELECT c.code, c.product_name, c.image_small_url, c.image_ingredients_small_url, c.image_nutrition_small_url
                             SELECT c.code, c.product_name, c.image_small_url, c.image_ingredients_small_url, c.image_nutrition_small_url
                             FROM (
                             FROM (
-                                SELECT code, product_name, NULL AS image_small_url, NULL AS image_ingredients_small_url, NULL AS image_nutrition_small_url
+                                SELECT code, product_name, image_small_url, image_ingredients_small_url, image_nutrition_small_url
                                 FROM food_db.products_core
                                 FROM food_db.products_core
                                 WHERE MATCH({m_col}) AGAINST(%s IN BOOLEAN MODE)
                                 WHERE MATCH({m_col}) AGAINST(%s IN BOOLEAN MODE)
                                 AND product_name IS NOT NULL AND product_name != '' AND product_name != 'None'
                                 AND product_name IS NOT NULL AND product_name != '' AND product_name != 'None'
@@ -1186,7 +1189,8 @@ with tab_plate:
                             "Nutrition Image": r.get('image_nutrition_small_url') if is_valid_image_url(r.get('image_nutrition_small_url')) else "",
                             "Nutrition Image": r.get('image_nutrition_small_url') if is_valid_image_url(r.get('image_nutrition_small_url')) else "",
                         })
                         })
                     gallery_df = pd.DataFrame(df_rows)
                     gallery_df = pd.DataFrame(df_rows)
-                    gallery_df.replace(to_replace=r'^None$', value='&nsbp', regex=True, inplace=True)
+                    gallery_df.replace(to_replace=r'^None$', value=' ', regex=True, inplace=True)
+                    gallery_df.index = range(1, len(gallery_df) + 1)
                     st.dataframe(
                     st.dataframe(
                         gallery_df,
                         gallery_df,
                         column_config={
                         column_config={
@@ -1194,8 +1198,7 @@ with tab_plate:
                             "Ingredients Image": st.column_config.ImageColumn("Ingredients"),
                             "Ingredients Image": st.column_config.ImageColumn("Ingredients"),
                             "Nutrition Image": st.column_config.ImageColumn("Nutrition"),
                             "Nutrition Image": st.column_config.ImageColumn("Nutrition"),
                         },
                         },
-                        use_container_width=True,
-                        hide_index=True
+                        use_container_width=True
                     )
                     )
                     
                     
                     options = {f"{r['product_name']} ({r['code']})": r for r in search_res}
                     options = {f"{r['product_name']} ({r['code']})": r for r in search_res}
@@ -1289,14 +1292,57 @@ with tab_planner:
             # Stream the response instantly!
             # Stream the response instantly!
             try:
             try:
                 start_llm = time.time()
                 start_llm = time.time()
+                placeholder = st.empty()
                 response = ollama.chat(model=get_active_model(), messages=[
                 response = ollama.chat(model=get_active_model(), messages=[
                     {'role': 'system', 'content': sys_prompt},
                     {'role': 'system', 'content': sys_prompt},
                     {'role': 'user', 'content': 'Generate my meal plan as a markdown table.'}
                     {'role': 'user', 'content': 'Generate my meal plan as a markdown table.'}
                 ], stream=True)
                 ], stream=True)
                 raw_chunks = []
                 raw_chunks = []
                 clean_stream = filter_scratchpad_stream(response, raw_chunks)
                 clean_stream = filter_scratchpad_stream(response, raw_chunks)
-                ai_reply = st.write_stream(clean_stream)
+                ai_reply = placeholder.write_stream(clean_stream)
                 raw_reply = "".join(raw_chunks)
                 raw_reply = "".join(raw_chunks)
+                
+                # Strip scratchpad and calculate totals
+                clean_reply = strip_scratchpad(raw_reply)
+                
+                def add_total_row_to_markdown_table(text):
+                    import re
+                    lines = text.split('\n')
+                    table_start = -1
+                    table_end = -1
+                    for i, line in enumerate(lines):
+                        line_s = line.strip()
+                        if line_s.startswith('|') and line_s.endswith('|'):
+                            if 'Meal Time' in line or 'Exact Food' in line or 'Portion' in line:
+                                if table_start == -1:
+                                    table_start = i
+                            if table_start != -1:
+                                table_end = i
+                    if table_start == -1 or table_end == -1:
+                        return text
+                    total_cal, total_pro, total_carb, total_fat = 0.0, 0.0, 0.0, 0.0
+                    for idx in range(table_start + 2, table_end + 1):
+                        line = lines[idx].strip()
+                        if not (line.startswith('|') and line.endswith('|')):
+                            continue
+                        cols = [c.strip() for c in line.strip('|').split('|')]
+                        if len(cols) < 7:
+                            continue
+                        if any(w in cols[0].lower() or w in cols[1].lower() for w in ['total', 'summation', 'global']):
+                            continue
+                        def clean_num(val):
+                            m = re.search(r'([0-9]+(?:\.[0-9]+)?)', val)
+                            return float(m.group(1)) if m else 0.0
+                        total_cal += clean_num(cols[3])
+                        total_pro += clean_num(cols[4])
+                        total_carb += clean_num(cols[5])
+                        total_fat += clean_num(cols[6])
+                    total_row = f"| **Total Summary** | **All Meals** | **-** | **{total_cal:.1f} kcal** | **{total_pro:.1f}g** | **{total_carb:.1f}g** | **{total_fat:.1f}g** |"
+                    lines.insert(table_end + 1, total_row)
+                    return "\n".join(lines)
+
+                final_reply = add_total_row_to_markdown_table(clean_reply)
+                placeholder.markdown(final_reply)
                 st.caption(f"⏱️ Execution Trace: Module=Ollama, MySQL | Time={time.time() - start_llm:.2f} seconds")
                 st.caption(f"⏱️ Execution Trace: Module=Ollama, MySQL | Time={time.time() - start_llm:.2f} seconds")
                 
                 
                 # PDF Generation
                 # PDF Generation
@@ -1304,7 +1350,7 @@ with tab_planner:
                     import re
                     import re
                     # Aggressive sanitization: if a table row has 4 columns and the last contains a comma or space before 'g', split it
                     # Aggressive sanitization: if a table row has 4 columns and the last contains a comma or space before 'g', split it
                     sanitized_lines = []
                     sanitized_lines = []
-                    for line in text.split('\\n'):
+                    for line in text.split('\n'):
                         line = line.strip()
                         line = line.strip()
                         if line.startswith('|') and line.endswith('|') and '---' not in line:
                         if line.startswith('|') and line.endswith('|') and '---' not in line:
                             cols = [c.strip() for c in line.strip('|').split('|')]
                             cols = [c.strip() for c in line.strip('|').split('|')]
@@ -1321,7 +1367,7 @@ with tab_planner:
                             sanitized_lines.append('| ' + ' | '.join(cols) + ' |')
                             sanitized_lines.append('| ' + ' | '.join(cols) + ' |')
                         else:
                         else:
                             sanitized_lines.append(line)
                             sanitized_lines.append(line)
-                    text = '\\n'.join(sanitized_lines)
+                    text = '\n'.join(sanitized_lines)
 
 
                     pdf = FPDF()
                     pdf = FPDF()
                     pdf.add_page()
                     pdf.add_page()
@@ -1383,7 +1429,7 @@ with tab_planner:
                 
                 
                 st.download_button(
                 st.download_button(
                     label="📄 Download PDF Export",
                     label="📄 Download PDF Export",
-                    data=generate_pdf(strip_scratchpad(raw_reply)),
+                    data=generate_pdf(final_reply),
                     file_name="Clinical_Meal_Plan.pdf",
                     file_name="Clinical_Meal_Plan.pdf",
                     mime="application/pdf",
                     mime="application/pdf",
                     type="primary"
                     type="primary"