function smartstat_save_selective
Flask route handler that saves selected statistical analysis rounds and their associated plots from a SmartStat session to a data section, with options to replace or append to existing content.
/tf/active/vicechatdev/vice_ai/new_app.py
5625 - 5761
complex
Purpose
This endpoint allows users to selectively save specific analysis rounds from their SmartStat session to a persistent data section. Users can choose which analyses to include, whether to include interpretations, which plots to save, and whether to replace existing content or append to it. The function handles session recovery, ownership verification, content formatting, and redirects users to the workspace with the updated document.
Source Code
def smartstat_save_selective(session_id):
"""Save selected analysis rounds and plots to data section"""
user_email = get_current_user()
data = request.get_json()
# Get selections and mode from request
selections = data.get('selections', []) # List of {analysis_index, include_interpretation, selected_plots}
save_mode = data.get('mode', 'replace') # 'replace' or 'append'
# Verify session exists
session = smartstat_service.get_session(session_id)
if not session:
logger.warning(f"Session {session_id} not found - attempting to recover")
all_sections = data_section_service.get_user_data_sections(user_email)
data_section = next((ds for ds in all_sections if ds.analysis_session_id == session_id), None)
if data_section:
session = SmartStatSession(session_id, data_section.id, data_section.title)
smartstat_service.sessions[session_id] = session
else:
return jsonify({'error': 'Session not found'}), 404
# Verify data section ownership
data_section = data_section_service.get_data_section(session.data_section_id)
if not data_section or data_section.owner != user_email:
return jsonify({'error': 'Access denied'}), 403
try:
# Build content from selected analyses
body_content = f"**Analysis Completed:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
logger.info(f"Processing {len(selections)} selections from user")
logger.info(f"Available analyses in session: {len(session.analysis_history)}")
selected_plots = []
for selection in selections:
idx = selection.get('analysis_index')
include_interp = selection.get('include_interpretation', True)
plot_indices = selection.get('selected_plots', [])
logger.info(f"Selection {idx}: include_interp={include_interp}, plots={plot_indices}")
# Get the analysis from history
if 0 <= idx < len(session.analysis_history):
analysis = session.analysis_history[idx]
logger.info(f"Processing analysis {idx}: query='{analysis['query']}', has_plots={len(analysis.get('plots', []))}")
body_content += f"## Analysis {idx + 1}: {analysis['query']}\n\n"
# Add interpretation if selected
if include_interp and analysis.get('results'):
results = analysis['results']
if isinstance(results, str):
try:
results = json.loads(results)
except:
pass
interpretation = results.get('interpretation', '') if isinstance(results, dict) else str(results)
if interpretation:
logger.info(f"Adding interpretation for analysis {idx} ({len(interpretation)} chars)")
body_content += f"### Interpretation\n\n{interpretation}\n\n"
# Collect selected plots (don't add to body_content - plots shown separately)
if analysis.get('plots') and plot_indices:
logger.info(f"Collecting {len(plot_indices)} plots from analysis {idx}")
for plot_idx in plot_indices:
if 0 <= plot_idx < len(analysis['plots']):
plot_path = analysis['plots'][plot_idx]
selected_plots.append(plot_path)
logger.info(f" - Plot {plot_idx}: {plot_path}")
else:
logger.warning(f"Analysis index {idx} out of range (0-{len(session.analysis_history)-1})")
logger.info(f"Final content length: {len(body_content)} chars, total plots: {len(selected_plots)}")
# Handle append vs replace mode
if save_mode == 'append' and data_section.current_content:
# Append new content to existing content with separator
existing_content = data_section.current_content.strip()
body_content = f"{existing_content}\n\n---\n\n{body_content}"
logger.info(f"Appending to existing content (mode: append)")
else:
logger.info(f"Replacing existing content (mode: {save_mode})")
# Handle append vs replace for plots
if save_mode == 'append' and data_section.generated_plots:
# Append new plots to existing plots (avoiding duplicates)
existing_plots = set(data_section.generated_plots)
for plot in selected_plots:
if plot not in existing_plots:
existing_plots.add(plot)
selected_plots = list(existing_plots)
logger.info(f"Appending plots, total: {len(selected_plots)}")
# Store raw markdown - frontend will render with marked.js
# Update data section
data_section.current_content = body_content # Store markdown, not HTML
data_section.analysis_conclusions = body_content
data_section.generated_plots = selected_plots
# Store selections in metadata for future reference
if not data_section.metadata:
data_section.metadata = {}
data_section.metadata['analysis_selections'] = selections
data_section.metadata['last_saved'] = datetime.now().isoformat()
data_section_service.update_data_section(data_section)
# Find which document contains this data section
document_id = None
conn = db_manager._get_connection()
cursor = conn.execute(
"SELECT document_id FROM document_sections WHERE section_id = ? AND section_type = 'data'",
(session.data_section_id,)
)
row = cursor.fetchone()
if row:
document_id = row[0]
# Redirect to workspace with document parameter if found
if document_id:
redirect_url = f"{url_for('workspace')}?document={document_id}"
else:
redirect_url = url_for('workspace')
return jsonify({
'success': True,
'message': f'Saved {len(selections)} analysis rounds with {len(selected_plots)} plots',
'redirect_url': redirect_url
})
except Exception as e:
logger.error(f"Error saving selective SmartStat results: {e}")
return jsonify({'error': str(e)}), 500
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
session_id |
- | - | positional_or_keyword |
Parameter Details
session_id: String identifier for the SmartStat session. Used to retrieve the session from smartstat_service and verify it exists. Must correspond to an active or recoverable session.
Return Value
Returns a Flask JSON response. On success (200): {'success': True, 'message': str, 'redirect_url': str} containing count of saved analyses/plots and workspace URL. On error: {'error': str} with status 404 (session not found), 403 (access denied), or 500 (server error).
Dependencies
flaskloggingjsondatetime
Required Imports
from flask import request, jsonify, url_for
from datetime import datetime
import json
import logging
Usage Example
# Client-side POST request to save selective analysis results
import requests
session_id = 'abc123-session-id'
selections = [
{
'analysis_index': 0,
'include_interpretation': True,
'selected_plots': [0, 1]
},
{
'analysis_index': 2,
'include_interpretation': False,
'selected_plots': [0]
}
]
response = requests.post(
f'http://localhost:5000/api/smartstat/{session_id}/save-selective',
json={
'selections': selections,
'mode': 'append' # or 'replace'
},
headers={'Authorization': 'Bearer <token>'}
)
if response.status_code == 200:
result = response.json()
print(f"Success: {result['message']}")
print(f"Redirect to: {result['redirect_url']}")
else:
print(f"Error: {response.json()['error']}")
Best Practices
- Always verify session existence and attempt recovery from data sections if session is not found in memory
- Verify data section ownership before allowing modifications to prevent unauthorized access
- Use the 'mode' parameter ('replace' or 'append') to control whether content is replaced or appended
- Store raw markdown content rather than HTML to allow flexible rendering on the frontend
- Avoid duplicate plots when appending by using set operations
- Store metadata about selections and timestamps for audit trails and future reference
- Handle both string and dict formats for analysis results to accommodate different data structures
- Log detailed information about processing steps for debugging and monitoring
- Validate analysis_index and plot_index values are within valid ranges before accessing arrays
- Return appropriate HTTP status codes (404, 403, 500) for different error conditions
- Include redirect_url in success response to guide user navigation after save operation
- Use try-except blocks to catch and log errors during the save process
- Format timestamps consistently using ISO format or user-friendly strings as appropriate
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function smartstat_save_to_document 85.5% similar
-
function smartstat_workspace 76.4% similar
-
function save_data_section_analysis 74.4% similar
-
function smartstat_get_history 72.8% similar
-
function smartstat_upload_data 71.8% similar