import streamlit as st import pandas as pd import os import json import shutil import time from datetime import datetime import plotly.express as px import plotly.graph_objects as go from dotenv import load_dotenv import base64 from io import BytesIO # Load environment variables load_dotenv() # Ensure data directory exists os.makedirs('data', exist_ok=True) # Set page config for wide layout st.set_page_config(page_title="YourCarbonEmissions by GXS", page_icon="🌍", layout="wide") # Initialize session state variables if they don't exist # Set default language to Vietnamese if 'language' not in st.session_state: st.session_state.language = 'Vietnamese' if 'emissions_data' not in st.session_state: # Load data if exists, otherwise create empty dataframe if os.path.exists('data/emissions.json'): try: with open('data/emissions.json', 'r') as f: data = f.read().strip() if data: # Check if file is not empty try: st.session_state.emissions_data = pd.DataFrame(json.loads(data)) except json.JSONDecodeError: # Create a backup of the corrupted file backup_file = f'data/emissions_backup_{int(time.time())}.json' shutil.copy('data/emissions.json', backup_file) st.warning(f"Corrupted emissions data file found. A backup has been created at {backup_file}") # Create empty dataframe st.session_state.emissions_data = pd.DataFrame(columns=[ 'date', 'scope', 'category', 'activity', 'quantity', 'unit', 'emission_factor', 'emissions_kgCO2e', 'notes' ]) else: # Empty file, create new DataFrame st.session_state.emissions_data = pd.DataFrame(columns=[ 'date', 'scope', 'category', 'activity', 'quantity', 'unit', 'emission_factor', 'emissions_kgCO2e', 'notes' ]) except Exception as e: st.error(f"Error loading emissions data: {str(e)}") # Create empty dataframe if loading fails st.session_state.emissions_data = pd.DataFrame(columns=[ 'date', 'scope', 'category', 'activity', 'quantity', 'unit', 'emission_factor', 'emissions_kgCO2e', 'notes' ]) # Make sure data directory exists os.makedirs('data', exist_ok=True) else: st.session_state.emissions_data = pd.DataFrame(columns=[ 'date', 'scope', 'category', 'activity', 'quantity', 'unit', 'emission_factor', 'emissions_kgCO2e', 'notes' ]) # Make sure data directory exists os.makedirs('data', exist_ok=True) if 'theme' not in st.session_state: st.session_state.theme = 'dark' if 'active_page' not in st.session_state: st.session_state.active_page = "AI Insights" # Translation dictionary translations = { 'English': { 'title': 'YourCarbonEmissions by GXS', 'subtitle': 'Carbon Accounting & Reporting Tool for SMEs', 'dashboard': 'Dashboard', 'data_entry': 'Data Entry', 'reports': 'Reports', 'settings': 'Settings', 'about': 'About', 'scope1': 'Scope 1 (Direct Emissions)', 'scope2': 'Scope 2 (Indirect Emissions - Purchased Energy)', 'scope3': 'Scope 3 (Other Indirect Emissions)', 'date': 'Date', 'scope': 'Scope', 'category': 'Category', 'activity': 'Activity', 'quantity': 'Quantity', 'unit': 'Unit', 'emission_factor': 'Emission Factor', 'emissions': 'Emissions (kgCO2e)', 'notes': 'Notes', 'add_entry': 'Add Entry', 'upload_csv': 'Upload CSV', 'download_report': 'Download Report', 'total_emissions': 'Total Emissions', 'emissions_by_scope': 'Emissions by Scope', 'emissions_by_category': 'Emissions by Category', 'emissions_over_time': 'Emissions Over Time', 'language': 'Language', 'welcome_message': 'Welcome! Please add your first emission entry to get started.', 'csv_uploaded': 'CSV uploaded successfully!', 'entry_added': 'Entry added successfully!', 'entry_failed': 'Failed to add entry.', 'clear_form': 'Clear Form', 'delete_entry': 'Delete Entry', 'select_entry_to_delete': 'Select entry number to delete', 'delete_success': 'Entry deleted successfully!', 'delete_failed': 'Failed to delete entry', 'existing_data': 'Existing Emissions Data', 'manual_entry': 'Manual Entry', 'csv_upload': 'CSV Upload', 'download_sample_csv': 'Download Sample CSV', 'company_info': 'Company Information', 'company_name': 'Company Name', 'industry': 'Industry', 'location': 'Location', 'contact_person': 'Contact Person', 'email': 'Email', 'phone': 'Phone', 'export_markets': 'Export Markets', 'save_settings': 'Save Settings', 'settings_saved': 'Settings saved successfully!', 'ai_insights': 'AI Insights', 'data_assistant': 'Data Entry Assistant', 'report_summary': 'Report Summary Generator', 'offset_advisor': 'Carbon Offset Advisor', 'regulation_radar': 'Regulation Radar', 'emission_optimizer': 'Emission Optimizer', 'describe_emission': 'Describe your emission activity', 'get_assistance': 'Get Assistance', 'generate_summary': 'Generate Summary', 'get_offset_recommendations': 'Get Offset Recommendations', 'check_regulations': 'Check Regulations', 'generate_optimization': 'Generate Optimization Recommendations', 'not_enough_data': 'No emissions data available. Please add data first.', 'please_enter_location': 'Please enter your location.', 'please_describe_emission': 'Please describe your emission activity first.', 'custom_category': 'Custom Category', 'custom_unit': 'Custom Unit', 'business_unit': 'Business Unit', 'project': 'Project', 'country': 'Country', 'facility': 'Facility/Location', 'responsible_person': 'Responsible Person', 'data_quality': 'Data Quality', 'verification_status': 'Verification Status', 'cost_optional': 'Cost (Optional)', 'currency': 'Currency', 'custom_activity': 'Custom Activity', 'custom_country': 'Custom Country', 'total_entries': 'Total Entries', 'latest_entry': 'Latest Entry', 'no_date_data': 'No date data', 'no_emissions_scope': 'No emissions data available for scope breakdown.', 'no_emissions_category': 'No emissions data available for category breakdown.', 'not_enough_time_data': 'Not enough time data to show emissions over time.', 'no_valid_date': 'No valid date data available for time series chart.', 'no_emissions_time': 'No emissions data available for time series chart.', 'ai_suggestion': 'AI Suggestion', 'ai_suggestion_text': 'Based on your selections, a typical emission factor for this category and country is shown.', 'go_to': 'Go to' }, 'Vietnamese': { 'title': 'YourCarbonEmissions by GXS', 'subtitle': 'Công cụ Kiểm kê Khí nhà kính & Lập Báo cáo Phát thải cho Doanh nghiệp vừa và nhỏ Việt Nam', 'dashboard': 'Bảng điều khiển', 'data_entry': 'Nhập dữ liệu', 'reports': 'Báo cáo', 'settings': 'Cài đặt', 'about': 'Giới thiệu', 'scope1': 'Phạm vi 1 (Phát thải trực tiếp)', 'scope2': 'Phạm vi 2 (Phát thải gián tiếp - Năng lượng mua vào)', 'scope3': 'Phạm vi 3 (Phát thải gián tiếp khác)', 'date': 'Ngày', 'scope': 'Phạm vi', 'category': 'Danh mục', 'activity': 'Hoạt động', 'quantity': 'Số lượng', 'unit': 'Đơn vị', 'emission_factor': 'Hệ số phát thải', 'emissions': 'Phát thải (kgCO2e)', 'notes': 'Ghi chú', 'add_entry': 'Thêm mục', 'upload_csv': 'Tải lên CSV', 'download_report': 'Tải báo cáo', 'total_emissions': 'Tổng phát thải', 'emissions_by_scope': 'Phát thải theo phạm vi', 'emissions_by_category': 'Phát thải theo danh mục', 'emissions_over_time': 'Phát thải theo thời gian', 'language': 'Ngôn ngữ', 'welcome_message': 'Chào mừng! Vui lòng thêm mục phát thải đầu tiên để bắt đầu.', 'csv_uploaded': 'Tải lên CSV thành công!', 'entry_added': 'Thêm mục thành công!', 'entry_failed': 'Thêm mục thất bại.', 'clear_form': 'Xóa biểu mẫu', 'delete_entry': 'Xóa mục', 'select_entry_to_delete': 'Chọn số thứ tự mục để xóa', 'delete_success': 'Xóa mục thành công!', 'delete_failed': 'Xóa mục thất bại', 'existing_data': 'Dữ liệu phát thải hiện có', 'manual_entry': 'Nhập thủ công', 'csv_upload': 'Tải lên CSV', 'download_sample_csv': 'Tải CSV mẫu', 'company_info': 'Thông tin công ty', 'company_name': 'Tên công ty', 'industry': 'Ngành nghề', 'location': 'Địa điểm', 'contact_person': 'Người liên hệ', 'email': 'Email', 'phone': 'Số điện thoại', 'export_markets': 'Thị trường xuất khẩu', 'save_settings': 'Lưu cài đặt', 'settings_saved': 'Lưu cài đặt thành công!', 'ai_insights': 'Chuyên gia AI tư vấn phát thải khí nhà kính và giải pháp', 'data_assistant': 'Trợ lý nhập liệu', 'report_summary': 'Tạo tóm tắt báo cáo', 'offset_advisor': 'Tư vấn bù trừ carbon', 'regulation_radar': 'Kiểm tra Tuân thủ Quy định', 'emission_optimizer': 'Tối ưu hóa phát thải', 'describe_emission': 'Mô tả hoạt động phát thải của bạn', 'get_assistance': 'Nhận trợ giúp', 'generate_summary': 'Tạo tóm tắt', 'get_offset_recommendations': 'Nhận khuyến nghị bù trừ', 'check_regulations': 'Kiểm tra quy định', 'generate_optimization': 'Tạo khuyến nghị tối ưu hóa', 'not_enough_data': 'Không có dữ liệu phát thải. Vui lòng thêm dữ liệu trước.', 'please_enter_location': 'Vui lòng nhập địa điểm.', 'please_describe_emission': 'Vui lòng mô tả hoạt động phát thải trước.', 'custom_category': 'Danh mục tùy chỉnh', 'custom_unit': 'Đơn vị tùy chỉnh', 'business_unit': 'Đơn vị kinh doanh', 'project': 'Dự án', 'country': 'Quốc gia', 'facility': 'Cơ sở/Địa điểm', 'responsible_person': 'Người chịu trách nhiệm', 'data_quality': 'Chất lượng dữ liệu', 'verification_status': 'Trạng thái xác minh', 'cost_optional': 'Chi phí (Tùy chọn)', 'currency': 'Tiền tệ', 'custom_activity': 'Hoạt động tùy chỉnh', 'custom_country': 'Quốc gia tùy chỉnh', 'total_entries': 'Tổng số mục', 'latest_entry': 'Mục mới nhất', 'no_date_data': 'Không có dữ liệu ngày', 'no_emissions_scope': 'Không có dữ liệu phát thải cho phân tích phạm vi.', 'no_emissions_category': 'Không có dữ liệu phát thải cho phân tích danh mục.', 'not_enough_time_data': 'Không đủ dữ liệu thời gian để hiển thị phát thải theo thời gian.', 'no_valid_date': 'Không có dữ liệu ngày hợp lệ cho biểu đồ chuỗi thời gian.', 'no_emissions_time': 'Không có dữ liệu phát thải cho biểu đồ chuỗi thời gian.', 'ai_suggestion': 'Gợi ý AI', 'ai_suggestion_text': 'Dựa trên lựa chọn của bạn, hệ số phát thải điển hình cho danh mục và quốc gia này được hiển thị.', 'go_to': 'Đi tới' } } def t(key): lang = st.session_state.get('language', 'English') return translations.get(lang, translations['English']).get(key, key) # Add this dictionary for AI input notice ai_input_notice = { "English": "⚠️ Please note: AI features are experimental and may require a valid API key. Data is not sent to third parties except for AI processing.", "Vietnamese": "⚠️ Lưu ý: Các tính năng AI đang thử nghiệm. Dữ liệu chỉ được gửi cho AI để xử lý, không chia sẻ với bên thứ ba. Hãy lựa chọn ngôn ngữ phù hợp để sử dụng Ứng dụng và tính năng AI. (Các chi phí đã được GXS hỗ trợ)" } # Function to save emissions data def save_emissions_data(): try: # Create data directory if it doesn't exist os.makedirs('data', exist_ok=True) # Create a backup of the existing file if it exists if os.path.exists('data/emissions.json'): backup_path = 'data/emissions_backup.json' try: with open('data/emissions.json', 'r') as src, open(backup_path, 'w') as dst: dst.write(src.read()) except Exception: # Continue even if backup fails pass # Save data to JSON file with proper formatting with open('data/emissions.json', 'w') as f: if len(st.session_state.emissions_data) > 0: json.dump(st.session_state.emissions_data.to_dict('records'), f, indent=2) else: # Write empty array if no data f.write('[]') return True except Exception as e: st.error(f"Error saving data: {str(e)}") return False # Function to add new emission entry def add_emission_entry(date, business_unit, project, scope, category, activity, country, facility, responsible_person, quantity, unit, emission_factor, data_quality, verification_status, notes): """Add a new emission entry to the emissions data.""" try: # Calculate emissions emissions_kgCO2e = float(quantity) * float(emission_factor) # Create new entry new_entry = pd.DataFrame([{ 'date': date.strftime('%Y-%m-%d'), 'business_unit': business_unit, 'project': project, 'scope': scope, 'category': category, 'activity': activity, 'country': country, 'facility': facility, 'responsible_person': responsible_person, 'quantity': float(quantity), 'unit': unit, 'emission_factor': float(emission_factor), 'emissions_kgCO2e': emissions_kgCO2e, 'data_quality': data_quality, 'verification_status': verification_status, 'notes': notes }]) # Add to existing data st.session_state.emissions_data = pd.concat([st.session_state.emissions_data, new_entry], ignore_index=True) # Save data and return success/failure return save_emissions_data() except Exception as e: st.error(f"Error adding entry: {str(e)}") return False def delete_emission_entry(index): try: # Make a copy of the current data if len(st.session_state.emissions_data) > index: # Drop the row at the specified index st.session_state.emissions_data = st.session_state.emissions_data.drop(index).reset_index(drop=True) # Save data and return success/failure return save_emissions_data() else: st.error("Invalid index for deletion") return False except Exception as e: st.error(f"Error deleting entry: {str(e)}") return False # Function to process uploaded CSV def process_csv(uploaded_file): """Process uploaded CSV file and add to emissions data.""" try: # Read CSV file df = pd.read_csv(uploaded_file) required_columns = ['date', 'scope', 'category', 'activity', 'quantity', 'unit', 'emission_factor'] # Check if all required columns exist if not all(col in df.columns for col in required_columns): st.error(f"CSV must contain all required columns: {', '.join(required_columns)}") return False # Validate data types try: # Convert quantity and emission_factor to float df['quantity'] = df['quantity'].astype(float) df['emission_factor'] = df['emission_factor'].astype(float) # Validate dates df['date'] = pd.to_datetime(df['date']).dt.strftime('%Y-%m-%d') except Exception as e: st.error(f"Data validation error: {str(e)}") return False # Calculate emissions if not provided if 'emissions_kgCO2e' not in df.columns: df['emissions_kgCO2e'] = df['quantity'] * df['emission_factor'] # Add enterprise fields if not present enterprise_fields = { 'business_unit': 'Corporate', 'project': 'Not Applicable', 'country': 'Vietnam', 'facility': '', 'responsible_person': '', 'data_quality': 'Medium', 'verification_status': 'Unverified', 'notes': '' } # Add missing columns with default values for field, default_value in enterprise_fields.items(): if field not in df.columns: df[field] = default_value # Append to existing data st.session_state.emissions_data = pd.concat([st.session_state.emissions_data, df], ignore_index=True) # Save data if save_emissions_data(): st.success(f"Successfully added {len(df)} entries") return True else: st.error("Failed to save data") return False except Exception as e: st.error(f"Error processing CSV: {str(e)}") return False # Function to generate PDF report def generate_report(): # Create a BytesIO object buffer = BytesIO() # Create a simple CSV report for now st.session_state.emissions_data.to_csv(buffer, index=False) buffer.seek(0) return buffer # Custom CSS def local_css(): st.markdown(''' ''', unsafe_allow_html=True) # Navigation component def render_navigation(): nav_items = [ {"icon": "🤖", "label": t("ai_insights"), "id": "AI Insights"}, {"icon": "📝", "label": t("data_entry"), "id": "Data Entry"}, {"icon": "📊", "label": t("dashboard"), "id": "Dashboard"}, {"icon": "⚙️", "label": t("settings"), "id": "Settings"} ] st.markdown("### " + t("about")) for item in nav_items: active_class = "active" if st.session_state.active_page == item["id"] else "" if st.sidebar.button( f"{item['icon']} {item['label']}", key=f"nav_{item['id']}", help=f"{t('go_to')} {item['label']}" if 'go_to' in translations[st.session_state.language] else f"Go to {item['label']}", use_container_width=True ): st.session_state.active_page = item["id"] st.rerun() # Metric card component def metric_card(title, value, description=None, icon=None, prefix="", suffix=""): st.markdown(f'''
{f'
{icon}
' if icon else ''}
{title}
{prefix}{value}{suffix}
{f'
{description}
' if description else ''}
''', unsafe_allow_html=True) # Card component def card(content, title=None): if title: st.markdown(f"

{title}

{content}
", unsafe_allow_html=True) else: st.markdown(f"
{content}
", unsafe_allow_html=True) # Define unit_options globally so it's always available unit_options = [ 'kg', 'ton', 'liter', 'kWh', 'm3', 'mile', 'km', 'MJ', 'GJ', 'BTU', 'Other' ] # Apply custom CSS local_css() # Sidebar with st.sidebar: st.markdown(f"

{t('title')}

", unsafe_allow_html=True) st.markdown(f"

{t('subtitle')}

", unsafe_allow_html=True) st.divider() language = st.selectbox(t('language'), ['Vietnamese', 'English'], index=0 if st.session_state.language == 'Vietnamese' else 1) if language != st.session_state.language: st.session_state.language = language st.rerun() st.divider() render_navigation() st.divider() st.markdown( "", unsafe_allow_html=True ) # Main content if st.session_state.active_page == "Dashboard": st.markdown(f"

{t('dashboard')}

", unsafe_allow_html=True) if len(st.session_state.emissions_data) == 0: st.markdown(f"
{t('welcome_message')}
", unsafe_allow_html=True) else: # Calculate metrics # Ensure emissions_kgCO2e is numeric st.session_state.emissions_data['emissions_kgCO2e'] = pd.to_numeric(st.session_state.emissions_data['emissions_kgCO2e'], errors='coerce') # Replace NaN with 0 st.session_state.emissions_data['emissions_kgCO2e'].fillna(0, inplace=True) total_emissions = st.session_state.emissions_data['emissions_kgCO2e'].sum() # Display metrics col1, col2, col3 = st.columns(3) with col1: metric_card( title=t('total_emissions'), value=f"{total_emissions:.2f}", suffix=" kgCO2e", icon="🌍" ) with col2: if 'date' in st.session_state.emissions_data.columns: st.session_state.emissions_data['date'] = pd.to_datetime(st.session_state.emissions_data['date'], errors='coerce') if not st.session_state.emissions_data['date'].isnull().all(): latest_date = st.session_state.emissions_data['date'].max().strftime('%Y-%m-%d') else: latest_date = t("no_date_data") if "no_date_data" in translations[st.session_state.language] else "No date data" metric_card( title=t("latest_entry") if "latest_entry" in translations[st.session_state.language] else "Latest Entry", value=latest_date, icon="📅" ) with col3: entry_count = len(st.session_state.emissions_data) metric_card( title=t("total_entries") if "total_entries" in translations[st.session_state.language] else "Total Entries", value=str(entry_count), icon="📊" ) # Charts st.markdown(f"

{t('emissions_by_scope')}

", unsafe_allow_html=True) # Check if there are any non-zero emissions before creating charts if total_emissions > 0: # Create scope data for pie chart scope_data = st.session_state.emissions_data.groupby('scope')['emissions_kgCO2e'].sum().reset_index() # Only create chart if we have data with emissions if not scope_data.empty and scope_data['emissions_kgCO2e'].sum() > 0: fig1 = px.pie( scope_data, values='emissions_kgCO2e', names='scope', color='scope', color_discrete_map={'Scope 1': '#4CAF50', 'Scope 2': '#2196F3', 'Scope 3': '#FFC107'}, hole=0.4 ) fig1.update_layout( margin=dict(t=0, b=0, l=0, r=0), legend=dict(orientation="h", yanchor="bottom", y=-0.2, xanchor="center", x=0.5), height=400 ) st.plotly_chart(fig1, use_container_width=True, config={'displayModeBar': False}) else: st.info(t("no_emissions_scope") if "no_emissions_scope" in translations[st.session_state.language] else "No emissions data available for scope breakdown.") else: st.info(t("no_emissions_scope") if "no_emissions_scope" in translations[st.session_state.language] else "No emissions data available for scope breakdown.") col1, col2 = st.columns(2) with col1: st.markdown(f"

{t('emissions_by_category')}

", unsafe_allow_html=True) if total_emissions > 0: # Create category data for bar chart category_data = st.session_state.emissions_data.groupby('category')['emissions_kgCO2e'].sum().reset_index() category_data = category_data.sort_values('emissions_kgCO2e', ascending=False) # Only create chart if we have data with emissions if not category_data.empty and category_data['emissions_kgCO2e'].sum() > 0: fig2 = px.bar( category_data, x='category', y='emissions_kgCO2e', color='category', labels={'emissions_kgCO2e': 'Emissions (kgCO2e)', 'category': 'Category'} ) fig2.update_layout( showlegend=False, margin=dict(t=0, b=0, l=0, r=0), height=400 ) st.plotly_chart(fig2, use_container_width=True, config={'displayModeBar': False}) else: st.info(t("no_emissions_category") if "no_emissions_category" in translations[st.session_state.language] else "No emissions data available for category breakdown.") else: st.info(t("no_emissions_category") if "no_emissions_category" in translations[st.session_state.language] else "No emissions data available for category breakdown.") with col2: st.markdown(f"

{t('emissions_over_time')}

", unsafe_allow_html=True) if total_emissions > 0 and 'date' in st.session_state.emissions_data.columns: # Convert date column to datetime time_data = st.session_state.emissions_data.copy() time_data['date'] = pd.to_datetime(time_data['date'], errors='coerce') # Filter out rows with invalid dates time_data = time_data.dropna(subset=['date']) if not time_data.empty: # Create month column for aggregation time_data['month'] = time_data['date'].dt.strftime('%Y-%m') # Group by month and scope time_data = time_data.groupby(['month', 'scope'])['emissions_kgCO2e'].sum().reset_index() if len(time_data['month'].unique()) > 0: # Create line chart fig3 = px.line( time_data, x='month', y='emissions_kgCO2e', color='scope', markers=True, color_discrete_map={'Scope 1': '#4CAF50', 'Scope 2': '#2196F3', 'Scope 3': '#FFC107'}, labels={'emissions_kgCO2e': 'Emissions (kgCO2e)', 'month': 'Month', 'scope': 'Scope'} ) fig3.update_layout( margin=dict(t=0, b=0, l=0, r=0), xaxis_title="", yaxis_title="kgCO2e", legend_title="", height=400 ) st.plotly_chart(fig3, use_container_width=True, config={'displayModeBar': False}) else: st.info(t("not_enough_time_data") if "not_enough_time_data" in translations[st.session_state.language] else "Not enough time data to show emissions over time.") else: st.info(t("no_valid_date") if "no_valid_date" in translations[st.session_state.language] else "No valid date data available for time series chart.") else: st.info(t("no_emissions_time") if "no_emissions_time" in translations[st.session_state.language] else "No emissions data available for time series chart.") elif st.session_state.active_page == "Data Entry": st.markdown(f"

{t('data_entry')}

", unsafe_allow_html=True) tabs = st.tabs([f" {t('manual_entry')}", f" {t('csv_upload')}"]) with tabs[0]: st.markdown(f"

{t('add_entry')}

", unsafe_allow_html=True) with st.form("emission_form", border=False): col1, col2 = st.columns(2) with col1: date = st.date_input(t('date'), datetime.now(), help=t("date") + " " + ("when the emission occurred" if st.session_state.language == "English" else "khi phát thải xảy ra")) business_unit = st.selectbox( t("business_unit") if "business_unit" in translations[st.session_state.language] else "Business Unit", ["Corporate", "Manufacturing", "Sales", "R&D", "Logistics", "IT", "Other"], help=t("business_unit") + " " + ( "responsible for this emission" if st.session_state.language == "English" else "chịu trách nhiệm cho phát thải này") ) if business_unit == "Other": business_unit = st.text_input("Custom Business Unit", placeholder="Enter business unit name") project = st.selectbox( t("project") if "project" in translations[st.session_state.language] else "Project", ["Not Applicable", "Carbon Reduction Initiative", "Sustainability Program", "Operational", "Other"], help=t("project") + " " + ( "associated with this emission" if st.session_state.language == "English" else "liên quan đến phát thải này") ) if project == "Other": project = st.text_input("Custom Project", placeholder="Enter project name") scope = st.selectbox( t('scope'), ['Scope 1', 'Scope 2', 'Scope 3'], help=t('scope') + ": " + ( "Scope 1: Direct emissions from owned sources\nScope 2: Indirect emissions from purchased energy\nScope 3: All other indirect emissions in value chain" if st.session_state.language == "English" else "Phạm vi 1: Phát thải trực tiếp\nPhạm vi 2: Phát thải gián tiếp từ năng lượng mua vào\nPhạm vi 3: Phát thải gián tiếp khác trong chuỗi giá trị") ) category_options = { 'Scope 1': ['Stationary Combustion', 'Mobile Combustion', 'Fugitive Emissions', 'Process Emissions', 'Other'], 'Scope 2': ['Electricity', 'Steam', 'Heating', 'Cooling', 'Other'], 'Scope 3': ['Purchased Goods and Services', 'Capital Goods', 'Fuel- and Energy-Related Activities', 'Upstream Transportation and Distribution', 'Waste Generated in Operations', 'Business Travel', 'Employee Commuting', 'Upstream Leased Assets', 'Downstream Transportation and Distribution', 'Processing of Sold Products', 'Use of Sold Products', 'End-of-Life Treatment of Sold Products', 'Downstream Leased Assets', 'Franchises', 'Investments', 'Other'] } category = st.selectbox( t('category'), category_options[scope], help=t('category') + " " + ( "of emission source" if st.session_state.language == "English" else "nguồn phát thải") ) if category == 'Other': category = st.text_input(t('custom_category'), placeholder=t("custom_category")) country_options = ['Vietnam', 'India', 'United States', 'United Kingdom', 'Japan', 'Indonesia', 'Other'] country = st.selectbox( t("country") if "country" in translations[st.session_state.language] else "Country", country_options, help=t("country") + " " + ( "where the emission occurred" if st.session_state.language == "English" else "nơi phát thải xảy ra") ) if country == 'Other': country = st.text_input(t("custom_country") if "custom_country" in translations[st.session_state.language] else "Custom Country", placeholder=t("custom_country") if "custom_country" in translations[st.session_state.language] else "Enter country name") facility = st.text_input( t("facility") if "facility" in translations[st.session_state.language] else "Facility/Location", placeholder=t("facility") if "facility" in translations[st.session_state.language] else "e.g., Ho Chi Minh City HQ, Binh Duong Plant 2, etc.", help=t("facility") + " " + ( "where the emission occurred" if st.session_state.language == "English" else "nơi phát thải xảy ra") ) responsible_person = st.text_input( t("responsible_person") if "responsible_person" in translations[st.session_state.language] else "Responsible Person", placeholder=t("responsible_person") if "responsible_person" in translations[st.session_state.language] else "Person responsible for this emission source", help=t("responsible_person") + " " + ( "accountable for managing this emission source" if st.session_state.language == "English" else "chịu trách nhiệm quản lý nguồn phát thải này") ) with col2: activity_options = { 'Stationary Combustion': ['Boiler', 'Furnace', 'Generator', 'Other'], 'Mobile Combustion': ['Company Vehicle', 'Fleet Vehicle', 'Machinery', 'Other'], 'Fugitive Emissions': ['Refrigerant Leak', 'SF6 Emissions', 'Other'], 'Process Emissions': ['Cement Production', 'Chemical Production', 'Other'], 'Electricity': ['Office Electricity', 'Manufacturing Electricity', 'Other'], 'Steam': ['Industrial Steam', 'Heating Steam', 'Other'], 'Heating': ['Office Heating', 'Industrial Heating', 'Other'], 'Cooling': ['Office Cooling', 'Industrial Cooling', 'Other'], 'Purchased Goods and Services': ['Raw Materials', 'Office Supplies', 'Other'], 'Capital Goods': ['Equipment Purchase', 'Vehicle Purchase', 'Other'], 'Fuel- and Energy-Related Activities': ['Upstream Fuel Production', 'Transmission Losses', 'Other'], 'Upstream Transportation and Distribution': ['Supplier Transport', 'Inbound Logistics', 'Other'], 'Waste Generated in Operations': ['Solid Waste', 'Wastewater', 'Other'], 'Business Travel': ['Air Travel', 'Ground Travel', 'Hotel Stays', 'Other'], 'Employee Commuting': ['Private Vehicle', 'Public Transport', 'Other'], 'Upstream Leased Assets': ['Leased Equipment', 'Leased Vehicles', 'Other'], 'Downstream Transportation and Distribution': ['Outbound Logistics', 'Customer Transport', 'Other'], 'Processing of Sold Products': ['Intermediate Processing', 'Final Assembly', 'Other'], 'Use of Sold Products': ['Product Operation', 'Energy Consumption', 'Other'], 'End-of-Life Treatment of Sold Products': ['Recycling', 'Landfill', 'Other'], 'Downstream Leased Assets': ['Leased Equipment', 'Leased Property', 'Other'], 'Franchises': ['Franchise Operations', 'Franchise Energy Use', 'Other'], 'Investments': ['Investment Emissions', 'Financed Emissions', 'Other'], 'Other': ['Custom Activity', 'Other'] } activity_key = category if category != 'Other' else 'Other' activity_list = activity_options.get(activity_key, ['Custom Activity', 'Other']) activity = st.selectbox( t("activity"), activity_options.get(category, ['Other']), help=t("activity") + " " + ( "that generated the emissions" if st.session_state.language == "English" else "tạo ra phát thải") ) if activity == 'Other': activity = st.text_input(t("custom_activity") if "custom_activity" in translations[st.session_state.language] else "Custom Activity", placeholder=t("custom_activity") if "custom_activity" in translations[st.session_state.language] else "Enter custom activity") quantity = st.number_input( t('quantity'), min_value=0.0, format="%.2f", help=t("quantity") + " " + ( "(e.g., kWh used, liters consumed, etc.)" if st.session_state.language == "English" else "(ví dụ: kWh sử dụng, lít tiêu thụ, v.v.)") ) unit = st.selectbox( t('unit'), unit_options, help=t("unit") + " " + ( "of measurement for the quantity" if st.session_state.language == "English" else "đơn vị đo lường cho số lượng") ) if unit == 'Other': unit = st.text_input(t('custom_unit'), placeholder=t('custom_unit')) emission_factors = { 'India': { 'Electricity': 0.82, 'Mobile Combustion': 2.31, 'Stationary Combustion': 1.85, 'Other': 0.0 }, 'United States': { 'Electricity': 0.42, 'Mobile Combustion': 2.32, 'Stationary Combustion': 2.01, 'Business Travel': 0.12, 'Employee Commuting': 0.15 } } default_factor = emission_factors.get(country, {}).get(category, 0.0) if country != 'Other' else 0.0 st.info(f"💡 {t('ai_suggestion') if 'ai_suggestion' in translations[st.session_state.language] else 'AI Suggestion'}: {t('ai_suggestion_text') if 'ai_suggestion_text' in translations[st.session_state.language] else f'Based on your selections, a typical emission factor for {category} in {country} would be around {default_factor:.4f} kgCO2e per unit.'}") emission_factor = st.number_input( t('emission_factor'), min_value=0.0, value=default_factor, format="%.4f", help=t('emission_factor') + " " + (f"kgCO2e per unit. Typical range: {max(0.1, default_factor*0.8):.4f} to {default_factor*1.2:.4f}" if st.session_state.language == "English" else f"kgCO2e trên mỗi đơn vị. Khoảng điển hình: {max(0.1, default_factor*0.8):.4f} đến {default_factor*1.2:.4f}") ) data_quality = st.select_slider( t("data_quality") if "data_quality" in translations[st.session_state.language] else "Data Quality", options=["Low", "Medium", "High"], value="Medium", help=t("data_quality") + " " + ( "🔴 Low: Estimated or proxy data\n🟡 Medium: Calculated from bills or invoices\n🟢 High: Directly measured or metered data" if st.session_state.language == "English" else "🔴 Thấp: Dữ liệu ước tính hoặc thay thế\n🟡 Trung bình: Tính toán từ hóa đơn\n🟢 Cao: Đo trực tiếp") ) verification_status = st.selectbox( t("verification_status") if "verification_status" in translations[st.session_state.language] else "Verification Status", ["Unverified", "Internally Verified", "Third-Party Verified"], help=t("verification_status") + " " + ( "Unverified: No verification process applied\nInternally Verified: Checked by internal team\nThird-Party Verified: Validated by external auditor" if st.session_state.language == "English" else "Chưa xác minh: Không áp dụng quy trình xác minh\nXác minh nội bộ: Kiểm tra bởi đội ngũ nội bộ\nXác minh bên thứ ba: Được kiểm tra bởi kiểm toán viên bên ngoài") ) notes = st.text_area( t('notes'), placeholder=t('notes'), help=t('notes') + " " + ( "Include information about data sources, calculation methodology, assumptions made, and any other relevant context" if st.session_state.language == "English" else "Bao gồm thông tin về nguồn dữ liệu, phương pháp tính toán, giả định và các thông tin liên quan khác") ) cost = st.number_input( t("cost_optional") if "cost_optional" in translations[st.session_state.language] else "Cost (Optional)", min_value=0.0, value=0.0, format="%.2f", help=t("cost_optional") + " " + ( "Associated cost in your local currency" if st.session_state.language == "English" else "Chi phí liên quan bằng đơn vị tiền tệ của bạn") ) if cost > 0: currency = st.selectbox( t("currency") if "currency" in translations[st.session_state.language] else "Currency", ["USD", "EUR", "INR", "GBP", "JPY", "Other"], help=t("currency") + " " + ( "for the entered cost" if st.session_state.language == "English" else "cho chi phí đã nhập") ) # Form submission buttons (always inside the form) col1, col2 = st.columns([1, 1]) with col1: submitted = st.form_submit_button(t('add_entry'), type="primary", use_container_width=True) with col2: clear = st.form_submit_button(t('clear_form'), type="secondary", use_container_width=True) if submitted: # Basic validation if quantity <= 0: st.error("Quantity must be greater than zero.") elif not facility.strip(): st.warning("Facility/Location is recommended for enterprise tracking.") else: try: # Include cost in the entry if provided cost_value = cost if 'cost' in locals() and cost > 0 else 0.0 currency_value = currency if 'currency' in locals() and cost > 0 else "" add_emission_entry( date, business_unit, project, scope, category, activity, country, facility, responsible_person, quantity, unit, emission_factor, data_quality, verification_status, notes ) st.success(t('entry_added')) # Redirect to Dashboard after successful entry st.session_state.active_page = "Dashboard" st.rerun() except Exception as e: st.error(f"{t('entry_failed')} {str(e)}") # Show existing data table if len(st.session_state.emissions_data) > 0: st.markdown(f"

{t('existing_data')}

", unsafe_allow_html=True) # Create a copy of the dataframe with an action column display_df = st.session_state.emissions_data.copy() # Add a column for the delete action col1, col2 = st.columns([3, 1]) with col1: # Display the dataframe st.dataframe( display_df, column_config={ "date": st.column_config.DateColumn("Date"), "business_unit": st.column_config.TextColumn("Business Unit"), "project": st.column_config.TextColumn("Project"), "scope": st.column_config.TextColumn("Scope"), "category": st.column_config.TextColumn("Category"), "activity": st.column_config.TextColumn("Activity"), "country": st.column_config.TextColumn("Country"), "facility": st.column_config.TextColumn("Facility"), "responsible_person": st.column_config.TextColumn("Responsible Person"), "quantity": st.column_config.NumberColumn("Quantity", format="%.2f"), "unit": st.column_config.TextColumn("Unit"), "emission_factor": st.column_config.NumberColumn("Emission Factor", format="%.4f"), "emissions_kgCO2e": st.column_config.NumberColumn("Emissions (kgCO2e)", format="%.2f"), "data_quality": st.column_config.TextColumn("Data Quality"), "verification_status": st.column_config.TextColumn("Verification"), "notes": st.column_config.TextColumn("Notes"), }, use_container_width=True, hide_index=False ) with col2: # Add delete functionality st.markdown(f"### {t('delete_entry')}", unsafe_allow_html=True) entry_to_delete = st.number_input(t('select_entry_to_delete'), min_value=0, max_value=len(display_df)-1 if len(display_df) > 0 else 0, step=1, help=t('select_entry_to_delete')) if st.button("🗑️ " + t('delete_entry'), type="primary"): if delete_emission_entry(entry_to_delete): st.success(f"{t('delete_success')} {entry_to_delete}!") st.rerun() else: st.error(f"{t('delete_failed')} {entry_to_delete}") with tabs[1]: st.markdown(f"

{t('upload_csv')}

", unsafe_allow_html=True) uploaded_file = st.file_uploader(t('upload_csv'), type='csv') if uploaded_file is not None: if process_csv(uploaded_file): st.success(t('csv_uploaded')) st.session_state.active_page = "Dashboard" st.rerun() else: st.error(t("entry_failed")) # Sample CSV download with enterprise-grade fields sample_data = { 'date': ['2025-01-15', '2025-01-20'], 'business_unit': ['Corporate', 'Logistics'], 'project': ['Carbon Reduction Initiative', 'Operational'], 'scope': ['Scope 2', 'Scope 1'], 'category': ['Electricity', 'Mobile Combustion'], 'activity': ['Office Electricity', 'Company Vehicle'], 'country': ['Vietnam', 'United States'], 'facility': ['Hanoi HQ', 'Soc Son Distribution Center'], 'responsible_person': ['Nguyen Thuy Trang', 'Tran Quoc Hung'], 'quantity': [1000, 50], 'unit': ['kWh', 'liter'], # This uses unit_options 'emission_factor': [0.82, 2.31495], 'data_quality': ['High', 'Medium'], 'verification_status': ['Internally Verified', 'Unverified'], 'notes': ['Monthly electricity bill', 'Fleet vehicle fuel consumption'] } sample_df = pd.DataFrame(sample_data) csv = sample_df.to_csv(index=False).encode('utf-8') st.download_button( label=t("download_sample_csv"), data=csv, file_name="sample_emissions.csv", mime="text/csv", ) elif st.session_state.active_page == "Settings": st.markdown(f"

{t('settings')}

", unsafe_allow_html=True) st.markdown(f"

{t('company_info')}

", unsafe_allow_html=True) with st.form("company_info_form"): col1, col2 = st.columns(2) with col1: company_name = st.text_input(t("company_name")) industry = st.text_input(t("industry")) location = st.text_input(t("location")) with col2: contact_person = st.text_input(t("contact_person")) email = st.text_input(t("email")) phone = st.text_input(t("phone")) st.markdown(f"

{t('export_markets')}

", unsafe_allow_html=True) col1, col2, col3 = st.columns(3) with col1: eu_market = st.checkbox("European Union") with col2: japan_market = st.checkbox("Japan") with col3: unitedstates_market = st.checkbox("United States") submitted = st.form_submit_button(t("save_settings")) if submitted: st.success(t("settings_saved")) elif st.session_state.active_page == "AI Insights": st.markdown(f"

🤖 {t('ai_insights')}

", unsafe_allow_html=True) st.info(ai_input_notice[st.session_state.language]) from ai_agents import CarbonFootprintAgents if 'ai_agents' not in st.session_state: st.session_state.ai_agents = CarbonFootprintAgents() ai_tabs = st.tabs([ t('data_assistant'), t('report_summary'), t('offset_advisor'), t('regulation_radar'), t('emission_optimizer') ]) with ai_tabs[0]: st.markdown(f"

{t('data_assistant')}

", unsafe_allow_html=True) st.markdown(t('describe_emission')) data_description = st.text_area(t('describe_emission')) if st.button(t('get_assistance'), key="data_assistant_btn"): if data_description: with st.spinner("AI assistant is analyzing your request..." if st.session_state.language == "English" else "Trợ lý AI đang phân tích yêu cầu của bạn..."): try: result = st.session_state.ai_agents.run_data_entry_crew(data_description, st.session_state.language) result_str = str(result) st.markdown(f"
{result_str}
", unsafe_allow_html=True) except Exception as e: st.error(f"Error: {str(e)}. Please check your API key and try again." if st.session_state.language == "English" else f"Lỗi: {str(e)}. Vui lòng kiểm tra API key và thử lại.") else: st.warning(t('please_describe_emission')) with ai_tabs[1]: st.markdown(f"

{t('report_summary')}

", unsafe_allow_html=True) st.markdown(t('generate_summary')) if len(st.session_state.emissions_data) == 0: st.warning(t('not_enough_data')) else: if st.button(t('generate_summary'), key="report_summary_btn"): with st.spinner("Generating report summary..." if st.session_state.language == "English" else "Đang tạo tóm tắt báo cáo..."): try: emissions_str = st.session_state.emissions_data.to_string() result = st.session_state.ai_agents.run_report_summary_crew(emissions_str, st.session_state.language) result_str = str(result) st.markdown(f"
{result_str}
", unsafe_allow_html=True) except Exception as e: st.error(f"Error: {str(e)}. Please check your API key and try again." if st.session_state.language == "English" else f"Lỗi: {str(e)}. Vui lòng kiểm tra API key và thử lại.") with ai_tabs[2]: st.markdown(f"

{t('offset_advisor')}

", unsafe_allow_html=True) st.markdown(t('get_offset_recommendations')) col1, col2 = st.columns(2) with col1: location = st.text_input(t('location')) industry = st.selectbox(t('industry'), ["Manufacturing", "Technology", "Agriculture", "Transportation", "Energy", "Services", "Other"]) if len(st.session_state.emissions_data) == 0: st.warning(t('not_enough_data')) else: total_emissions = st.session_state.emissions_data['emissions_kgCO2e'].sum() st.markdown(f"

{t('total_emissions')}: {total_emissions:.2f} kgCO2e

", unsafe_allow_html=True) if st.button(t('get_offset_recommendations'), key="offset_advisor_btn"): if location: with st.spinner("Finding offset options..." if st.session_state.language == "English" else "Đang tìm các lựa chọn bù trừ..."): try: result = st.session_state.ai_agents.run_offset_advice_crew(total_emissions, location, industry, st.session_state.language) result_str = str(result) st.markdown(f"
{result_str}
", unsafe_allow_html=True) except Exception as e: st.error(f"Error: {str(e)}. Please check your API key and try again." if st.session_state.language == "English" else f"Lỗi: {str(e)}. Vui lòng kiểm tra API key và thử lại.") else: st.warning(t('please_enter_location')) with ai_tabs[3]: st.markdown(f"

{t('regulation_radar')}

", unsafe_allow_html=True) st.markdown(t('check_regulations')) col1, col2 = st.columns(2) with col1: location = st.text_input(t('location'), key="reg_location") industry = st.selectbox(t('industry'), ["Manufacturing", "Technology", "Agriculture", "Transportation", "Energy", "Services", "Other"], key="reg_industry") with col2: export_markets = st.multiselect(t('export_markets'), ["European Union", "Japan", "United States", "China", "Middle East", "India", "Other"]) if st.button(t('check_regulations'), key="regulation_radar_btn"): if location and len(export_markets) > 0: with st.spinner("Analyzing regulatory requirements..." if st.session_state.language == "English" else "Đang phân tích yêu cầu pháp lý..."): try: result = st.session_state.ai_agents.run_regulation_check_crew(location, industry, ", ".join(export_markets), st.session_state.language) result_str = str(result) st.markdown(f"
{result_str}
", unsafe_allow_html=True) except Exception as e: st.error(f"Error: {str(e)}. Please check your API key and try again." if st.session_state.language == "English" else f"Lỗi: {str(e)}. Vui lòng kiểm tra API key và thử lại.") else: st.warning(f"{t('please_enter_location')} {t('export_markets')}") with ai_tabs[4]: st.markdown(f"

{t('emission_optimizer')}

", unsafe_allow_html=True) st.markdown(t('generate_optimization')) if len(st.session_state.emissions_data) == 0: st.warning(t('not_enough_data')) else: if st.button(t('generate_optimization'), key="emission_optimizer_btn"): with st.spinner("Analyzing your emissions data..." if st.session_state.language == "English" else "Đang phân tích dữ liệu phát thải của bạn..."): try: emissions_str = st.session_state.emissions_data.to_string() result = st.session_state.ai_agents.run_optimization_crew(emissions_str, st.session_state.language) result_str = str(result) st.markdown(f"
{result_str}
", unsafe_allow_html=True) except Exception as e: st.error(f"Error: {str(e)}. Please check your API key and try again." if st.session_state.language == "English" else f"Lỗi: {str(e)}. Vui lòng kiểm tra API key và thử lại.")