import streamlit as st import pandas as pd import numpy as np import json import base64 import io from datetime import datetime import os # Page configuration st.set_page_config( page_title="Collaborative Features", page_icon="👥", layout="wide" ) # Page title st.title("👥 Collaborative Features") st.markdown(""" Share your analysis, save projects for later, and collaborate with others by importing and exporting complete analysis workflows and results. """) # Create tabs for different collaborative features tab1, tab2, tab3 = st.tabs([ "Project Management", "Share Analysis", "Import/Export Workflows" ]) # Initialize project state if not exists if 'projects' not in st.session_state: st.session_state.projects = [] # Project Management Tab with tab1: st.subheader("Project Management") # Project saving section st.markdown(""" Save your current dataset and analysis settings as a project to continue working on it later. Projects are stored in your browser and will be available when you return. """) # Form for saving a project with st.form("save_project_form"): project_name = st.text_input("Project Name", placeholder="My Data Analysis Project") project_description = st.text_area("Description (optional)", placeholder="Brief description of this analysis project") col1, col2, col3 = st.columns([2, 1, 1]) with col1: project_tags = st.text_input("Tags (comma separated)", placeholder="finance, stocks, analysis") with col2: save_data = st.checkbox("Include Dataset", value=True) with col3: overwrite = st.checkbox("Overwrite if exists", value=False) save_button = st.form_submit_button("Save Project") if save_button: if not project_name: st.error("Please provide a project name.") else: try: # Check if project exists existing_project = False for i, project in enumerate(st.session_state.projects): if project['name'] == project_name: existing_project = True if overwrite: # Update existing project project_index = i break else: st.error(f"Project '{project_name}' already exists. Use 'Overwrite if exists' to replace it.") break # If project doesn't exist or we're overwriting if not existing_project or (existing_project and overwrite): # Create project data structure project_data = { 'name': project_name, 'description': project_description, 'tags': [tag.strip() for tag in project_tags.split(',')] if project_tags else [], 'date_created': datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 'dataset_name': st.session_state.get('dataset_name', 'Unknown'), 'settings': {} } # Include dataset if selected if save_data and 'df' in st.session_state and st.session_state.df is not None: # Convert dataframe to CSV string csv_buffer = io.StringIO() st.session_state.df.to_csv(csv_buffer, index=False) csv_str = csv_buffer.getvalue() # Base64 encode to reduce storage size csv_bytes = csv_str.encode() b64_csv = base64.b64encode(csv_bytes).decode() # Add to project data project_data['dataset'] = b64_csv project_data['row_count'] = len(st.session_state.df) project_data['column_count'] = len(st.session_state.df.columns) else: project_data['dataset'] = None # Save the project if existing_project and overwrite: st.session_state.projects[project_index] = project_data st.success(f"Project '{project_name}' updated successfully!") else: st.session_state.projects.append(project_data) st.success(f"Project '{project_name}' saved successfully!") except Exception as e: st.error(f"Error saving project: {e}") # Project list section st.subheader("Your Saved Projects") if not st.session_state.projects: st.info("You don't have any saved projects yet.") else: # Filter and search options col1, col2 = st.columns([3, 1]) with col1: search_term = st.text_input("Search Projects", placeholder="Enter project name or tag") with col2: sort_by = st.selectbox("Sort By", ["Date (newest)", "Date (oldest)", "Name (A-Z)", "Name (Z-A)"]) # Filter projects based on search term filtered_projects = st.session_state.projects if search_term: filtered_projects = [ p for p in filtered_projects if ( search_term.lower() in p['name'].lower() or search_term.lower() in p['description'].lower() or any(search_term.lower() in tag.lower() for tag in p['tags']) ) ] # Sort projects if sort_by == "Date (newest)": filtered_projects = sorted(filtered_projects, key=lambda p: p['date_created'], reverse=True) elif sort_by == "Date (oldest)": filtered_projects = sorted(filtered_projects, key=lambda p: p['date_created']) elif sort_by == "Name (A-Z)": filtered_projects = sorted(filtered_projects, key=lambda p: p['name']) elif sort_by == "Name (Z-A)": filtered_projects = sorted(filtered_projects, key=lambda p: p['name'], reverse=True) # Display projects in a grid if filtered_projects: # Create 3 columns for the grid cols = st.columns(3) # Display each project in the grid for i, project in enumerate(filtered_projects): col_index = i % 3 with cols[col_index]: st.markdown(f"""

{project['name']}

{project['date_created']}

{project['description']}

{'Dataset: ' + (str(project.get('row_count', 'N/A')) + ' rows × ' + str(project.get('column_count', 'N/A')) + ' columns' if project.get('dataset') else 'No dataset')}

{', '.join(['#' + tag for tag in project['tags']]) if project['tags'] else 'No tags'}

""", unsafe_allow_html=True) c1, c2, c3 = st.columns([1, 1, 1]) with c1: if st.button("Load", key=f"load_{i}"): # Load the project if project.get('dataset'): try: # Decode base64 to CSV b64_csv = project['dataset'] csv_bytes = base64.b64decode(b64_csv) csv_str = csv_bytes.decode() # Load CSV into dataframe csv_buffer = io.StringIO(csv_str) loaded_df = pd.read_csv(csv_buffer) # Update session state st.session_state.df = loaded_df st.session_state.dataset_name = project['name'] st.success(f"Project '{project['name']}' loaded successfully!") st.rerun() except Exception as e: st.error(f"Error loading project data: {e}") else: st.warning("This project does not include dataset. Only project metadata was loaded.") with c2: if st.button("Delete", key=f"delete_{i}"): # Ask for confirmation st.session_state.delete_index = i st.info(f"Are you sure you want to delete project '{project['name']}'?") st.button("Yes, Delete", key=f"confirm_delete_{i}", on_click=lambda: delete_project(i)) with c3: if st.button("Export", key=f"export_{i}"): # Export the project to JSON try: project_json = json.dumps(project, indent=2) b64 = base64.b64encode(project_json.encode()).decode() href = f'Download Project JSON' st.markdown(href, unsafe_allow_html=True) except Exception as e: st.error(f"Error exporting project: {e}") else: st.info("No projects match your search criteria.") # Function to delete a project def delete_project(index): if 0 <= index < len(st.session_state.projects): project_name = st.session_state.projects[index]['name'] del st.session_state.projects[index] st.success(f"Project '{project_name}' deleted successfully!") st.rerun() # Share Analysis Tab with tab2: st.subheader("Share Your Analysis") # Analysis report generation st.markdown(""" Generate a sharable report of your current analysis, including visualizations and insights. You can download the report in various formats or get a shareable link. """) # Check if data is loaded if 'df' not in st.session_state or st.session_state.df is None: st.warning("Please load a dataset first to generate a report.") else: # Report options st.subheader("Report Configuration") col1, col2 = st.columns(2) with col1: report_title = st.text_input("Report Title", value=f"Analysis of {st.session_state.dataset_name}") report_author = st.text_input("Author/Organization") with col2: include_options = st.multiselect( "Include in Report", ["Dataset Info", "Data Preview", "Summary Statistics", "Visualizations", "Insights"], default=["Dataset Info", "Data Preview", "Summary Statistics"] ) report_format = st.selectbox( "Report Format", ["HTML", "PDF", "JSON"] ) if st.button("Generate Report"): with st.spinner("Generating report..."): try: # Create basic report data report_data = { "title": report_title, "author": report_author, "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "dataset_name": st.session_state.dataset_name, "sections": [] } # Include selected sections if "Dataset Info" in include_options: dataset_info = { "section_title": "Dataset Information", "content": { "rows": len(st.session_state.df), "columns": len(st.session_state.df.columns), "column_names": st.session_state.df.columns.tolist(), "data_types": {col: str(dtype) for col, dtype in st.session_state.df.dtypes.items()} } } report_data["sections"].append(dataset_info) if "Data Preview" in include_options: data_preview = { "section_title": "Data Preview", "content": { "preview": st.session_state.df.head(10).to_dict(orient="records") } } report_data["sections"].append(data_preview) if "Summary Statistics" in include_options: # Get numeric columns for summary stats numeric_cols = st.session_state.df.select_dtypes(include=['number']).columns.tolist() if numeric_cols: summary_stats = { "section_title": "Summary Statistics", "content": { "stats": st.session_state.df[numeric_cols].describe().to_dict() } } report_data["sections"].append(summary_stats) # Generate report based on format if report_format == "JSON": # Convert report data to JSON report_json = json.dumps(report_data, indent=2) # Create download link b64 = base64.b64encode(report_json.encode()).decode() filename = f"{report_title.replace(' ', '_')}_report.json" href = f'Download Report JSON' st.success("Report generated successfully!") st.markdown(href, unsafe_allow_html=True) elif report_format == "HTML": # Create a simple HTML report html_report = f""" {report_title}

{report_title}

Author: {report_author if report_author else 'Anonymous'}

Date: {report_data['date']}

Dataset: {report_data['dataset_name']}

""" # Add sections for section in report_data["sections"]: section_title = section["section_title"] content = section["content"] html_report += f"

{section_title}

" if section_title == "Dataset Information": html_report += f"""

Rows: {content['rows']}

Columns: {content['columns']}

Columns:

""" for col in content['column_names']: html_report += f""" """ html_report += "
Column Name Data Type
{col} {content['data_types'][col]}
" elif section_title == "Data Preview": preview = content['preview'] if preview: # Create table headers html_report += """ """ for col in preview[0].keys(): html_report += f"" html_report += "" # Add data rows for row in preview: html_report += "" for col, val in row.items(): html_report += f"" html_report += "" html_report += "
{col}
{val}
" elif section_title == "Summary Statistics": stats = content['stats'] if stats: # Create table headers html_report += """ """ for col in stats.keys(): html_report += f"" html_report += "" # Add stats rows for stat in ['count', 'mean', 'std', 'min', '25%', '50%', '75%', 'max']: html_report += f"" for col, values in stats.items(): value = values.get(stat, "") if isinstance(value, (int, float)): value = f"{value:.4f}" if stat != 'count' else f"{int(value)}" html_report += f"" html_report += "" html_report += "
Statistic{col}
{stat}{value}
" # Close HTML html_report += """ """ # Create download link b64 = base64.b64encode(html_report.encode()).decode() filename = f"{report_title.replace(' ', '_')}_report.html" href = f'Download HTML Report' st.success("Report generated successfully!") st.markdown(href, unsafe_allow_html=True) # Preview with st.expander("Preview HTML Report", expanded=False): st.components.v1.html(html_report, height=500, scrolling=True) elif report_format == "PDF": st.info("PDF export requires additional libraries. Please use HTML or JSON format instead.") except Exception as e: st.error(f"Error generating report: {e}") # Import/Export Workflows Tab with tab3: st.subheader("Import and Export Workflows") st.markdown(""" Save your entire analysis workflow, including dataset, transformations, and visualizations. Export workflows to share with others or import workflows created by your colleagues. """) # Export workflow section st.subheader("Export Current Workflow") # Check if data is loaded if 'df' not in st.session_state or st.session_state.df is None: st.warning("Please load a dataset first to export a workflow.") else: # Workflow export options include_data = st.checkbox("Include Dataset", value=True) if st.button("Export Workflow"): with st.spinner("Preparing workflow export..."): try: # Create workflow data structure workflow_data = { 'name': st.session_state.get('dataset_name', 'Unnamed Workflow'), 'date_exported': datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 'settings': {} } # Include dataset if selected if include_data: # Convert dataframe to CSV string csv_buffer = io.StringIO() st.session_state.df.to_csv(csv_buffer, index=False) csv_str = csv_buffer.getvalue() # Base64 encode to reduce storage size csv_bytes = csv_str.encode() b64_csv = base64.b64encode(csv_bytes).decode() # Add to workflow data workflow_data['dataset'] = b64_csv workflow_data['row_count'] = len(st.session_state.df) workflow_data['column_count'] = len(st.session_state.df.columns) else: workflow_data['dataset'] = None # Convert workflow to JSON workflow_json = json.dumps(workflow_data, indent=2) # Create download link b64 = base64.b64encode(workflow_json.encode()).decode() filename = f"{workflow_data['name'].replace(' ', '_')}_workflow.json" href = f'Download Workflow' st.success("Workflow exported successfully!") st.markdown(href, unsafe_allow_html=True) except Exception as e: st.error(f"Error exporting workflow: {e}") # Import workflow section st.subheader("Import Workflow") uploaded_file = st.file_uploader("Upload Workflow JSON", type=['json']) if uploaded_file is not None: try: # Load JSON from uploaded file workflow_json = uploaded_file.read() workflow_data = json.loads(workflow_json) # Display workflow info st.write(f"**Workflow Name:** {workflow_data.get('name', 'Unnamed Workflow')}") st.write(f"**Date Exported:** {workflow_data.get('date_exported', 'Unknown')}") if workflow_data.get('dataset') is not None: st.write(f"**Includes Dataset:** Yes ({workflow_data.get('row_count', 'N/A')} rows × {workflow_data.get('column_count', 'N/A')} columns)") else: st.write("**Includes Dataset:** No") # Import button if st.button("Import Workflow"): with st.spinner("Importing workflow..."): if workflow_data.get('dataset') is not None: try: # Decode base64 to CSV b64_csv = workflow_data['dataset'] csv_bytes = base64.b64decode(b64_csv) csv_str = csv_bytes.decode() # Load CSV into dataframe csv_buffer = io.StringIO(csv_str) loaded_df = pd.read_csv(csv_buffer) # Update session state st.session_state.df = loaded_df st.session_state.dataset_name = workflow_data['name'] st.success(f"Workflow '{workflow_data['name']}' imported successfully with dataset!") st.rerun() except Exception as e: st.error(f"Error importing workflow dataset: {e}") else: st.warning("This workflow does not include a dataset. Only workflow metadata was imported.") except Exception as e: st.error(f"Error parsing workflow file: {e}") # Sidebar with educational content with st.sidebar: st.title("📚 Collaborative Data Science") st.markdown(""" ### Benefits of Collaboration in Data Science Collaborative data science leads to: - **Diverse perspectives** that enhance analysis - **Knowledge sharing** across team members - **Faster problem solving** through combined expertise - **Increased reproducibility** with shared workflows - **Better documentation** for future reference ### Best Practices for Collaboration 1. **Document your analysis** - Include clear descriptions of what you did and why - Note any assumptions made - Explain preprocessing steps 2. **Use version control** - Save your work regularly - Track changes over time - Create branches for experimental analyses 3. **Standardize workflows** - Use consistent naming conventions - Organize projects with clear structure - Define standard preprocessing steps 4. **Share meaningful insights** - Focus on key findings rather than all results - Use visualizations effectively - Relate analysis to business questions 5. **Request specific feedback** - Ask targeted questions when sharing work - Be open to constructive criticism - Incorporate feedback into iterations ### Data Privacy Considerations - Always consider data sensitivity when sharing - Remove or anonymize personally identifiable information - Follow relevant regulations (GDPR, HIPAA, etc.) - Use secure methods for sharing sensitive data """)