🔍 Code Extractor

function smartstat_save_selective

Maturity: 57

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.

File:
/tf/active/vicechatdev/vice_ai/new_app.py
Lines:
5625 - 5761
Complexity:
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

  • flask
  • logging
  • json
  • datetime

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

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function smartstat_save_to_document 85.5% similar

    Flask route handler that saves SmartStat statistical analysis results back to a data section document, generating a final report with queries, results, and plots.

    From: /tf/active/vicechatdev/vice_ai/new_app.py
  • function smartstat_workspace 76.4% similar

    Flask route handler that opens a SmartStat statistical analysis workspace for a specific data section, managing session creation, data restoration, and access control.

    From: /tf/active/vicechatdev/vice_ai/new_app.py
  • function save_data_section_analysis 74.4% similar

    Flask API endpoint that saves analysis results (plots, conclusions, and analysis data) from a data analysis session to a data section in the database.

    From: /tf/active/vicechatdev/vice_ai/new_app.py
  • function smartstat_get_history 72.8% similar

    Flask API endpoint that retrieves analysis history for a SmartStat session, with automatic session recovery from saved data if the session is not found in memory.

    From: /tf/active/vicechatdev/vice_ai/new_app.py
  • function smartstat_upload_data 71.8% similar

    Flask route handler that uploads CSV or Excel data files to a SmartStat analysis session, with support for multi-sheet Excel files and session recovery.

    From: /tf/active/vicechatdev/vice_ai/new_app.py
← Back to Browse