| import streamlit as st |
| import pandas as pd |
| import plotly.express as px |
| import requests |
| from io import StringIO |
| from datetime import datetime |
|
|
| |
| st.set_page_config( |
| page_title="Terror Finance & Maritime Watch", |
| page_icon="π‘οΈ", |
| layout="wide", |
| initial_sidebar_state="collapsed" |
| ) |
|
|
| |
| st.markdown(""" |
| <style> |
| .main { |
| padding: 0rem 1rem; |
| } |
| .stTabs [data-baseweb="tab-list"] { |
| gap: 24px; |
| } |
| .stTabs [data-baseweb="tab"] { |
| padding-left: 20px; |
| padding-right: 20px; |
| background-color: transparent; |
| border: none; |
| color: #666; |
| font-weight: 500; |
| } |
| .stTabs [aria-selected="true"] { |
| background-color: transparent; |
| color: #E53E3E; |
| border-bottom: 2px solid #E53E3E; |
| } |
| |
| /* Metric Cards */ |
| .metric-card { |
| background: white; |
| padding: 24px; |
| border-radius: 12px; |
| border: 1px solid #E2E8F0; |
| text-align: center; |
| box-shadow: 0 1px 3px rgba(0,0,0,0.1); |
| } |
| .metric-number { |
| font-size: 2.5rem; |
| font-weight: 700; |
| margin: 8px 0; |
| color: #1A202C !important; |
| } |
| .metric-label { |
| color: #4A5568 !important; |
| font-size: 0.875rem; |
| text-transform: uppercase; |
| letter-spacing: 0.5px; |
| } |
| |
| /* Entity Cards */ |
| .entity-card { |
| background: white; |
| padding: 24px; |
| border-radius: 12px; |
| border: 1px solid #E2E8F0; |
| margin-bottom: 16px; |
| box-shadow: 0 1px 3px rgba(0,0,0,0.1); |
| } |
| .entity-card h3, |
| .entity-card p, |
| .entity-card span, |
| .entity-card div { |
| color: #1A202C !important; |
| } |
| |
| /* Tags */ |
| .tag { |
| display: inline-block; |
| padding: 4px 12px; |
| background-color: #EBF8FF; |
| color: #2B6CB0; |
| border-radius: 16px; |
| font-size: 0.875rem; |
| margin-right: 8px; |
| margin-bottom: 8px; |
| } |
| .tag-danger { |
| background-color: #FED7D7; |
| color: #C53030; |
| } |
| .tag-warning { |
| background-color: #FEFCBF; |
| color: #975A16; |
| } |
| |
| /* Headers */ |
| h1 { |
| color: #1A202C; |
| font-weight: 700; |
| } |
| h2 { |
| color: #2D3748; |
| font-weight: 600; |
| margin-top: 2rem; |
| margin-bottom: 1rem; |
| } |
| |
| /* Status indicators */ |
| .status-active { |
| color: #48BB78; |
| font-weight: bold; |
| } |
| .status-inactive { |
| color: #E53E3E; |
| font-weight: bold; |
| } |
| |
| /* Pariente AI Branding */ |
| .pariente-ai { |
| color: #4A5568; |
| font-size: 0.875rem; |
| font-style: italic; |
| text-align: right; |
| } |
| .pariente-ai-footer { |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| font-weight: bold; |
| } |
| </style> |
| """, unsafe_allow_html=True) |
|
|
| |
| def extract_imo_from_info(info_text): |
| """Extract IMO number from Information field""" |
| import re |
| if not info_text or pd.isna(info_text): |
| return "N/A" |
| |
| |
| imo_match = re.search(r'IMO-?\s*(\d{7})', str(info_text), re.IGNORECASE) |
| if imo_match: |
| return imo_match.group(1) |
| return "N/A" |
|
|
| def extract_mmsi_from_info(info_text): |
| """Extract MMSI from Information field""" |
| import re |
| if not info_text or pd.isna(info_text): |
| return "N/A" |
| |
| |
| mmsi_match = re.search(r'MMSI-?\s*(\d{9})', str(info_text), re.IGNORECASE) |
| if mmsi_match: |
| return mmsi_match.group(1) |
| return "N/A" |
|
|
| def extract_call_sign_from_info(info_text): |
| """Extract Call Sign from Information field""" |
| import re |
| if not info_text or pd.isna(info_text): |
| return "N/A" |
| |
| |
| call_match = re.search(r'Call Sign-?\s*([A-Z0-9]+)', str(info_text), re.IGNORECASE) |
| if call_match: |
| return call_match.group(1) |
| return "N/A" |
|
|
| def extract_owner_from_info(info_text): |
| """Extract owner information from Information field""" |
| import re |
| if not info_text or pd.isna(info_text): |
| return "N/A" |
| |
| |
| owner_match = re.search(r'Registered owner-?\s*([^,]+)', str(info_text), re.IGNORECASE) |
| if owner_match: |
| return owner_match.group(1).strip() |
| |
| manager_match = re.search(r'Commercial manager-?\s*([^,]+)', str(info_text), re.IGNORECASE) |
| if manager_match: |
| return manager_match.group(1).strip() |
| |
| return "N/A" |
| def safe_get(row, key, default="N/A"): |
| try: |
| |
| if key == 'Name': |
| |
| if 'Name' in row.index and pd.notna(row['Name']) and str(row['Name']).strip(): |
| return str(row['Name']).strip() |
| |
| |
| last_name = row.get('Last_Name', '') if 'Last_Name' in row.index else '' |
| first_name = row.get('First_Name', '') if 'First_Name' in row.index else '' |
| |
| |
| last_name = str(last_name).strip() if pd.notna(last_name) else '' |
| first_name = str(first_name).strip() if pd.notna(first_name) else '' |
| |
| |
| if last_name and first_name: |
| return f"{last_name} {first_name}" |
| elif first_name: |
| return first_name |
| elif last_name: |
| return last_name |
| else: |
| dc_id = row.get('DC_ID', 'Unknown') |
| return f"Entity {dc_id}" |
| |
| |
| if key in row.index: |
| val = row[key] |
| else: |
| |
| alternatives = { |
| 'DC_ID': ['ID', 'Entity_ID'], |
| 'Countries': ['Country', 'Location'], |
| 'Companies': ['Company', 'Business'], |
| 'Phone#': ['Phone', 'Telephone', 'Contact_Phone'], |
| 'Linked To': ['Organization', 'Terror_Organization', 'Group'], |
| 'IMO': ['IMO_Number', 'IMO_No', 'International_Maritime_Organization'], |
| 'Flag': ['Flag_State', 'Flag_Country'], |
| 'DWT': ['Deadweight', 'Dead_Weight_Tonnage'], |
| 'Built_Year': ['Year_Built', 'Construction_Year', 'DOB'], |
| 'Status': ['Vessel_Status', 'Ship_Status', 'AIS_Status'], |
| 'Insurance': ['Insurer', 'Insurance_Company'] |
| } |
| |
| val = None |
| if key in alternatives: |
| for alt_key in alternatives[key]: |
| if alt_key in row.index: |
| val = row[alt_key] |
| break |
| |
| if val is None: |
| return default |
| |
| |
| if pd.isna(val) or str(val).strip() == '' or str(val).lower() in ['nan', 'none', 'null']: |
| return default |
| |
| return str(val).strip() |
| except (KeyError, TypeError, AttributeError): |
| return default |
|
|
| |
| @st.cache_data |
| def load_clean_data(): |
| """Load pre-processed clean datasets""" |
| base_url = "https://huggingface.co/spaces/Malaji71/list/resolve/main/" |
| |
| datasets = {} |
| files_to_load = { |
| 'individuals': 'individuals_clean.csv', |
| 'companies': 'companies_clean.csv', |
| 'vessels': 'vessels_clean.csv' |
| } |
| |
| for category, filename in files_to_load.items(): |
| try: |
| url = base_url + filename |
| response = requests.get(url) |
| response.raise_for_status() |
| |
| |
| df = pd.read_csv(StringIO(response.text)) |
| |
| |
| for col in df.columns: |
| if df[col].dtype == 'object': |
| df[col] = df[col].fillna('').astype(str) |
| |
| datasets[category] = df |
| |
| except Exception as e: |
| st.error(f"Error loading {filename}: {str(e)}") |
| |
| datasets[category] = pd.DataFrame() |
| |
| return datasets |
|
|
| |
| def create_sample_data(): |
| """Create sample data if CSV files are not available""" |
| individuals = pd.DataFrame({ |
| 'Name': ['Sample Individual 1', 'Sample Individual 2'], |
| 'DC_ID': [1, 2], |
| 'Position': ['Financier', 'Operator'], |
| 'Countries': ['Lebanon', 'Syria'], |
| 'Email': ['sample1@email.com', 'sample2@email.com'], |
| 'Phone#': ['+961-xxx-xxxx', '+963-xxx-xxxx'], |
| 'Companies': ['Sample Corp', 'Test LLC'], |
| 'Linked To': ['Hamas', 'Hezbollah'], |
| 'Auto_Category': ['Individual', 'Individual'] |
| }) |
| |
| companies = pd.DataFrame({ |
| 'Name': ['Sample Company 1', 'Sample Company 2'], |
| 'DC_ID': [101, 102], |
| 'Sub_Category': ['Shell Company', 'Front Company'], |
| 'Countries': ['Panama', 'Cyprus'], |
| 'Owner': ['Unknown', 'Sample Person'], |
| 'Key_Individuals': ['Person A', 'Person B'], |
| 'Linked To': ['Hamas', 'Hezbollah'], |
| 'Auto_Category': ['Company', 'Company'] |
| }) |
| |
| vessels = pd.DataFrame({ |
| 'Name': ['Sample Vessel 1', 'Sample Vessel 2'], |
| 'DC_ID': [201, 202], |
| 'IMO': ['1234567', '7654321'], |
| 'Flag': ['Panama', 'Liberia'], |
| 'Owner': ['Sample Maritime LLC', 'Ocean Holdings'], |
| 'Status': ['AIS Off', 'Active'], |
| 'Insurance': ['Unknown', 'Lloyd\'s'], |
| 'Auto_Category': ['Vessel', 'Vessel'] |
| }) |
| |
| return { |
| 'individuals': individuals, |
| 'companies': companies, |
| 'vessels': vessels |
| } |
|
|
| |
| col1, col2, col3 = st.columns([6, 1, 1]) |
| with col1: |
| st.markdown("# π‘οΈ Terror Finance & Maritime Watch") |
| st.markdown("*Powered by Pariente AI - Advanced Intelligence Analytics*") |
| with col2: |
| theme_toggle = st.checkbox("π", key="theme") |
| with col3: |
| st.markdown('<div class="pariente-ai">π€ <span class="pariente-ai-footer">Pariente AI</span><br><em>Intelligence Platform</em></div>', unsafe_allow_html=True) |
|
|
| |
| try: |
| data = load_clean_data() |
| |
| if all(len(df) == 0 for df in data.values()): |
| st.warning("β οΈ Using sample data - CSV files not found. Please upload the processed CSV files.") |
| data = create_sample_data() |
| except Exception as e: |
| st.error(f"Error loading data: {str(e)}") |
| data = create_sample_data() |
|
|
| |
| tab1, tab2, tab3, tab4, tab5 = st.tabs(["π₯ Individuals", "π’ Companies", "π’ Vessels", "π Summary", "π Data Reports"]) |
|
|
| |
| with tab4: |
| st.markdown("## Key Statistics") |
| col1, col2, col3, col4 = st.columns(4) |
|
|
| with col1: |
| st.markdown(f""" |
| <div class="metric-card"> |
| <div class="metric-number" style="color: #E53E3E;">{len(data['individuals'])}</div> |
| <div class="metric-label">Individuals Tracked</div> |
| </div> |
| """, unsafe_allow_html=True) |
| |
| with col2: |
| st.markdown(f""" |
| <div class="metric-card"> |
| <div class="metric-number" style="color: #3182CE;">{len(data['companies'])}</div> |
| <div class="metric-label">Companies Monitored</div> |
| </div> |
| """, unsafe_allow_html=True) |
| |
| with col3: |
| st.markdown(f""" |
| <div class="metric-card"> |
| <div class="metric-number" style="color: #F6AD55;">{len(data['vessels'])}</div> |
| <div class="metric-label">Vessels Documented</div> |
| </div> |
| """, unsafe_allow_html=True) |
| |
| with col4: |
| |
| try: |
| all_countries = [] |
| for df in data.values(): |
| if 'Countries' in df.columns: |
| countries = df['Countries'].dropna().astype(str) |
| for country_list in countries: |
| if ',' in country_list: |
| all_countries.extend([c.strip() for c in country_list.split(',') if c.strip()]) |
| else: |
| all_countries.append(country_list.strip()) |
| unique_countries = len(set([c for c in all_countries if c and c.lower() != 'nan'])) |
| except: |
| unique_countries = 0 |
| |
| st.markdown(f""" |
| <div class="metric-card"> |
| <div class="metric-number" style="color: #48BB78;">{unique_countries}</div> |
| <div class="metric-label">Countries Involved</div> |
| </div> |
| """, unsafe_allow_html=True) |
|
|
| |
| st.markdown("## Data Visualization") |
| col1, col2, col3 = st.columns(3) |
| |
| with col1: |
| st.markdown("### Organizations") |
| try: |
| if len(data['individuals']) > 0 and 'Linked To' in data['individuals'].columns: |
| org_data = data['individuals']['Linked To'].value_counts().reset_index() |
| org_data.columns = ['Organization', 'Count'] |
| if not org_data.empty: |
| fig = px.pie(org_data, names='Organization', values='Count') |
| fig.update_layout(showlegend=True, height=300) |
| st.plotly_chart(fig, use_container_width=True) |
| else: |
| st.write("No organization data available") |
| else: |
| st.write("No organization data available") |
| except Exception as e: |
| st.write("Error creating organization chart") |
| |
| with col2: |
| st.markdown("### Entity Types") |
| type_data = pd.DataFrame({ |
| 'Type': ['Individuals', 'Companies', 'Vessels'], |
| 'Count': [len(data['individuals']), len(data['companies']), len(data['vessels'])] |
| }) |
| fig = px.bar(type_data, x='Type', y='Count', color_discrete_sequence=['#3182CE']) |
| fig.update_layout(showlegend=False, height=300) |
| st.plotly_chart(fig, use_container_width=True) |
| |
| with col3: |
| st.markdown("### Top Countries") |
| try: |
| if unique_countries > 0: |
| country_counts = {} |
| for df in data.values(): |
| if 'Countries' in df.columns: |
| countries = df['Countries'].dropna().astype(str) |
| for country_list in countries: |
| if ',' in country_list: |
| for country in country_list.split(','): |
| country = country.strip() |
| if country and country.lower() != 'nan': |
| country_counts[country] = country_counts.get(country, 0) + 1 |
| else: |
| country = country_list.strip() |
| if country and country.lower() != 'nan': |
| country_counts[country] = country_counts.get(country, 0) + 1 |
| |
| if country_counts: |
| country_data = pd.DataFrame(list(country_counts.items()), columns=['Country', 'Count']) |
| country_data = country_data.sort_values('Count', ascending=False).head(10) |
| fig = px.pie(country_data, names='Country', values='Count') |
| fig.update_layout(showlegend=True, height=300) |
| st.plotly_chart(fig, use_container_width=True) |
| else: |
| st.write("No country data available") |
| else: |
| st.write("No country data available") |
| except Exception as e: |
| st.write("Error creating country chart") |
|
|
| |
| with tab1: |
| st.markdown("## Individuals Database") |
| |
| |
| col1, col2 = st.columns([6, 1]) |
| with col2: |
| if st.button("π₯ Export", key="export_individuals"): |
| csv_data = data['individuals'].to_csv(index=False) |
| st.download_button( |
| label="Download CSV", |
| data=csv_data, |
| file_name=f"individuals_{datetime.now().strftime('%Y%m%d')}.csv", |
| mime="text/csv" |
| ) |
| |
| |
| col1, col2, col3 = st.columns([3, 2, 2]) |
| |
| with col1: |
| name_filter = st.text_input("Search by name", placeholder="Enter name...", key="search_individuals") |
| |
| with col2: |
| country_options = ["All Countries"] |
| if len(data['individuals']) > 0 and 'Countries' in data['individuals'].columns: |
| all_countries = set() |
| for country_list in data['individuals']['Countries'].dropna().astype(str): |
| if ',' in country_list: |
| all_countries.update([c.strip() for c in country_list.split(',') if c.strip() and c.lower() != 'nan']) |
| else: |
| if country_list.strip() and country_list.lower() != 'nan': |
| all_countries.add(country_list.strip()) |
| country_options += sorted(list(all_countries)) |
| country_filter = st.selectbox("Country", country_options, key="country_individuals") |
| |
| with col3: |
| org_options = ["All Organizations"] |
| if len(data['individuals']) > 0 and 'Linked To' in data['individuals'].columns: |
| orgs = data['individuals']['Linked To'].dropna().astype(str) |
| org_options += sorted(orgs.unique().tolist()) |
| org_filter = st.selectbox("Organization", org_options, key="org_individuals") |
|
|
| |
| filtered_individuals = data['individuals'].copy() |
| |
| if name_filter: |
| filtered_individuals = filtered_individuals[ |
| filtered_individuals['Name'].str.contains(name_filter, case=False, na=False) |
| ] |
| |
| if country_filter != "All Countries": |
| filtered_individuals = filtered_individuals[ |
| filtered_individuals['Countries'].str.contains(country_filter, case=False, na=False) |
| ] |
| |
| if org_filter != "All Organizations": |
| filtered_individuals = filtered_individuals[ |
| filtered_individuals['Linked To'] == org_filter |
| ] |
|
|
| |
| for _, person in filtered_individuals.iterrows(): |
| org_value = safe_get(person, 'Linked To') |
| org_color = "tag-danger" if "Hamas" in org_value else "tag-warning" |
| |
| st.markdown(f""" |
| <div class="entity-card"> |
| <h3>{safe_get(person, 'Name')}</h3> |
| <p><strong>ID:</strong> {safe_get(person, 'DC_ID')}</p> |
| <p><strong>Position:</strong> {safe_get(person, 'Position')}</p> |
| <p><strong>Countries:</strong> {safe_get(person, 'Countries')}</p> |
| <p><strong>Email:</strong> {safe_get(person, 'Email')}</p> |
| <p><strong>Phone:</strong> {safe_get(person, 'Phone#')}</p> |
| <p><strong>Companies:</strong> {safe_get(person, 'Companies')}</p> |
| <span class="tag {org_color}">{org_value}</span> |
| </div> |
| """, unsafe_allow_html=True) |
|
|
| |
| with tab2: |
| st.markdown("## Companies Database") |
| |
| |
| col1, col2 = st.columns([6, 1]) |
| with col2: |
| if st.button("π₯ Export", key="export_companies"): |
| csv_data = data['companies'].to_csv(index=False) |
| st.download_button( |
| label="Download CSV", |
| data=csv_data, |
| file_name=f"companies_{datetime.now().strftime('%Y%m%d')}.csv", |
| mime="text/csv" |
| ) |
| |
| |
| name_filter = st.text_input("Search companies", placeholder="Enter company name...", key="search_companies") |
| |
| |
| filtered_companies = data['companies'].copy() |
| if name_filter: |
| filtered_companies = filtered_companies[ |
| filtered_companies['Name'].str.contains(name_filter, case=False, na=False) |
| ] |
|
|
| |
| for _, company in filtered_companies.iterrows(): |
| org_value = safe_get(company, 'Linked To') |
| org_color = "tag-danger" if "Hamas" in org_value else "tag-warning" |
| |
| st.markdown(f""" |
| <div class="entity-card"> |
| <h3>{safe_get(company, 'Name')}</h3> |
| <p><strong>ID:</strong> {safe_get(company, 'DC_ID')}</p> |
| <p><strong>Type:</strong> {safe_get(company, 'Sub_Category')}</p> |
| <p><strong>Countries:</strong> {safe_get(company, 'Countries')}</p> |
| <p><strong>Owner:</strong> {safe_get(company, 'Owner')}</p> |
| <p><strong>Key Individuals:</strong> {safe_get(company, 'Key_Individuals')}</p> |
| <span class="tag {org_color}">{org_value}</span> |
| </div> |
| """, unsafe_allow_html=True) |
|
|
| |
| with tab3: |
| st.markdown("## Maritime Vessels Database") |
| |
| |
| col1, col2 = st.columns([6, 1]) |
| with col2: |
| if st.button("π₯ Export", key="export_vessels"): |
| csv_data = data['vessels'].to_csv(index=False) |
| st.download_button( |
| label="Download CSV", |
| data=csv_data, |
| file_name=f"vessels_{datetime.now().strftime('%Y%m%d')}.csv", |
| mime="text/csv" |
| ) |
| |
| |
| col1, col2, col3, col4 = st.columns([3, 2, 2, 2]) |
| |
| with col1: |
| name_filter = st.text_input("Search vessels", placeholder="Enter vessel name or IMO...", key="search_vessels") |
| |
| with col2: |
| flag_options = ["All Flags"] |
| if len(data['vessels']) > 0 and 'Flag' in data['vessels'].columns: |
| flags = data['vessels']['Flag'].dropna().astype(str).unique() |
| flag_options += sorted([f for f in flags if f and f.lower() != 'nan']) |
| flag_filter = st.selectbox("Flag State", flag_options, key="flag_vessels") |
| |
| with col3: |
| status_options = ["All Status"] |
| if len(data['vessels']) > 0 and 'Status' in data['vessels'].columns: |
| statuses = data['vessels']['Status'].dropna().astype(str).unique() |
| status_options += sorted([s for s in statuses if s and s.lower() != 'nan']) |
| status_filter = st.selectbox("Status", status_options, key="status_vessels") |
| |
| with col4: |
| org_options = ["All Organizations"] |
| if len(data['vessels']) > 0 and 'Linked To' in data['vessels'].columns: |
| orgs = data['vessels']['Linked To'].dropna().astype(str).unique() |
| org_options += sorted([o for o in orgs if o and o.lower() != 'nan']) |
| org_filter = st.selectbox("Organization", org_options, key="org_vessels") |
| |
| |
| filtered_vessels = data['vessels'].copy() |
| |
| if name_filter: |
| |
| mask = ( |
| filtered_vessels['Name'].str.contains(name_filter, case=False, na=False) | |
| filtered_vessels.get('IMO', pd.Series()).astype(str).str.contains(name_filter, case=False, na=False) | |
| filtered_vessels.get('Owner', pd.Series()).astype(str).str.contains(name_filter, case=False, na=False) | |
| filtered_vessels.get('Information', pd.Series()).astype(str).str.contains(name_filter, case=False, na=False) |
| ) |
| filtered_vessels = filtered_vessels[mask] |
| |
| if flag_filter != "All Flags": |
| filtered_vessels = filtered_vessels[ |
| filtered_vessels['Flag'].str.contains(flag_filter, case=False, na=False) |
| ] |
| |
| if status_filter != "All Status": |
| filtered_vessels = filtered_vessels[ |
| filtered_vessels['Status'] == status_filter |
| ] |
| |
| if org_filter != "All Organizations": |
| filtered_vessels = filtered_vessels[ |
| filtered_vessels['Linked To'] == org_filter |
| ] |
|
|
| |
| for _, vessel in filtered_vessels.iterrows(): |
| status_value = safe_get(vessel, 'Status') |
| status_class = "status-inactive" if status_value == 'AIS Off' else "status-active" |
| |
| |
| imo = safe_get(vessel, 'IMO') |
| flag = safe_get(vessel, 'Flag') |
| vessel_type = safe_get(vessel, 'Sub_Category') |
| owner = safe_get(vessel, 'Owner') |
| built_year = safe_get(vessel, 'Built_Year', safe_get(vessel, 'DOB')) |
| dwt = safe_get(vessel, 'DWT') |
| |
| st.markdown(f""" |
| <div class="entity-card"> |
| <h3>{safe_get(vessel, 'Name')}</h3> |
| <p><strong>DC_ID:</strong> {safe_get(vessel, 'DC_ID')}</p> |
| <p><strong>IMO Number:</strong> <span style="color: #2B6CB0; font-weight: bold;">{imo}</span></p> |
| <p><strong>Vessel Type:</strong> {vessel_type}</p> |
| <p><strong>Flag State:</strong> {flag}</p> |
| <p><strong>Owner:</strong> {owner}</p> |
| <p><strong>Built Year:</strong> {built_year}</p> |
| <p><strong>DWT:</strong> {dwt}</p> |
| <p><strong>Status:</strong> <span class="{status_class}">{status_value}</span></p> |
| <p><strong>Insurance:</strong> {safe_get(vessel, 'Insurance')}</p> |
| <p><strong>Countries:</strong> {safe_get(vessel, 'Countries')}</p> |
| <p><strong>Information:</strong> {safe_get(vessel, 'Information')}</p> |
| </div> |
| """, unsafe_allow_html=True) |
|
|
| |
| with tab5: |
| st.markdown("## π Data Analysis Reports") |
| |
| |
| try: |
| |
| analysis_url = "https://huggingface.co/spaces/Malaji71/list/resolve/main/complete_analysis.json" |
| analysis_response = requests.get(analysis_url) |
| if analysis_response.status_code == 200: |
| analysis_data = analysis_response.json() |
| |
| col1, col2 = st.columns(2) |
| |
| with col1: |
| st.markdown("### π Data Quality Metrics") |
| if 'complete_analysis' in analysis_data: |
| quality_metrics = analysis_data['complete_analysis'].get('quality_metrics', {}) |
| if 'completeness' in quality_metrics: |
| completeness_df = pd.DataFrame( |
| list(quality_metrics['completeness'].items()), |
| columns=['Column', 'Completeness %'] |
| ).sort_values('Completeness %', ascending=False) |
| st.dataframe(completeness_df, use_container_width=True) |
| |
| if 'issues' in quality_metrics and quality_metrics['issues']: |
| st.markdown("### β οΈ Data Issues") |
| for issue in quality_metrics['issues']: |
| st.warning(issue) |
| |
| with col2: |
| st.markdown("### π― Categorization Analysis") |
| if 'complete_analysis' in analysis_data: |
| cat_analysis = analysis_data['complete_analysis'].get('categorization_analysis', {}) |
| if 'content_based_categorization' in cat_analysis: |
| cat_counts = cat_analysis['content_based_categorization'] |
| |
| for category, count in cat_counts.items(): |
| st.metric(category.title(), count) |
| |
| st.markdown("### π Processing Statistics") |
| processing_stats = { |
| 'Total Rows Processed': analysis_data.get('complete_analysis', {}).get('total_rows', 0), |
| 'Total Columns Analyzed': analysis_data.get('complete_analysis', {}).get('total_columns', 0), |
| 'Analysis Timestamp': analysis_data.get('complete_analysis', {}).get('timestamp', 'Unknown') |
| } |
| |
| for stat, value in processing_stats.items(): |
| st.write(f"**{stat}**: {value}") |
| |
| else: |
| st.info("π Complete analysis data not available. Upload complete_analysis.json to see detailed reports.") |
| |
| except Exception as e: |
| st.info("π Analysis reports will be available when you upload the JSON files.") |
| |
| |
| try: |
| |
| summary_url = "https://huggingface.co/spaces/Malaji71/list/resolve/main/executive_summary.txt" |
| summary_response = requests.get(summary_url) |
| if summary_response.status_code == 200: |
| st.markdown("### π Executive Summary") |
| st.text(summary_response.text) |
| except: |
| pass |
| |
| try: |
| |
| report_url = "https://huggingface.co/spaces/Malaji71/list/resolve/main/analysis_report.txt" |
| report_response = requests.get(report_url) |
| if report_response.status_code == 200: |
| st.markdown("### π Full Analysis Report") |
| with st.expander("View Complete Report"): |
| st.text(report_response.text) |
| except: |
| pass |
| |
| |
| st.markdown("### π₯ Download Analysis Files") |
| st.markdown(""" |
| **Available Analysis Files:** |
| - `complete_analysis.json` - Complete analysis data in JSON format |
| - `analysis_report.txt` - Detailed human-readable report |
| - `executive_summary.txt` - Executive summary |
| - `uncertain_entities.csv` - Entities requiring manual review |
| |
| Upload these files to your Hugging Face Space to enable full analysis viewing. |
| """) |
|
|
| |
| st.markdown("---") |
| col1, col2 = st.columns([3, 1]) |
| with col1: |
| st.markdown("π‘οΈ **Terror Finance & Maritime Watch** - Monitoring entities involved in terror financing and maritime sanctions evasion") |
| st.markdown(f"π Data processed: {len(data['individuals'])} individuals, {len(data['companies'])} companies, {len(data['vessels'])} vessels") |
| st.markdown(f"π Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") |
| with col2: |
| st.markdown("**Powered by**") |
| st.markdown("π€ **Pariente AI**") |
| st.markdown("*Advanced Intelligence Analytics*") |