File size: 35,774 Bytes
ccfdce4 32f6d1b 7e7a349 334102f ccfdce4 7e7a349 ccfdce4 1a20e64 ccfdce4 1a20e64 ccfdce4 334102f ccfdce4 1a20e64 ccfdce4 7e7a349 ccfdce4 334102f ccfdce4 334102f ccfdce4 1a20e64 ccfdce4 334102f ccfdce4 1a20e64 ccfdce4 1a20e64 ccfdce4 1a20e64 ccfdce4 334102f ccfdce4 1a20e64 ccfdce4 1a20e64 ccfdce4 334102f 7e7a349 ccfdce4 24e24ac ccfdce4 24e24ac 334102f d8d4b8b 1256f85 d8d4b8b 334102f d8d4b8b 7e7a349 043ed58 334102f ccfdce4 7e7a349 ccfdce4 7e7a349 043ed58 7e7a349 043ed58 7e7a349 05efe4b 7e7a349 467b6f8 7e7a349 467b6f8 7e7a349 043ed58 7e7a349 043ed58 7e7a349 043ed58 7e7a349 043ed58 7e7a349 043ed58 7e7a349 043ed58 334102f 32f6d1b 7e7a349 ccfdce4 24e24ac ccfdce4 24e24ac ccfdce4 24e24ac ccfdce4 7e7a349 ccfdce4 7e7a349 d8d4b8b 51bf6f3 ccfdce4 51bf6f3 ccfdce4 129ac9f ccfdce4 129ac9f ccfdce4 129ac9f 7e7a349 ccfdce4 129ac9f ccfdce4 129ac9f ccfdce4 129ac9f 7e7a349 ccfdce4 129ac9f ccfdce4 7e7a349 ccfdce4 129ac9f 7e7a349 ccfdce4 7e7a349 d6f8eab 7e7a349 d6f8eab 7e7a349 d6f8eab 129ac9f ccfdce4 7e7a349 ccfdce4 129ac9f ccfdce4 7e7a349 ccfdce4 7e7a349 ccfdce4 7e7a349 d6f8eab 7e7a349 d6f8eab 7e7a349 d6f8eab ccfdce4 7e7a349 ccfdce4 7e7a349 ccfdce4 7e7a349 51bf6f3 d6f8eab ccfdce4 7e7a349 d6f8eab 7e7a349 d6f8eab 7e7a349 129ac9f ccfdce4 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f ccfdce4 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f ccfdce4 7e7a349 ccfdce4 7e7a349 334102f 7e7a349 334102f 7e7a349 334102f 7e7a349 1256f85 24e24ac 1256f85 7e7a349 24e24ac 7e7a349 1256f85 334102f 1256f85 24e24ac 1256f85 7e7a349 1256f85 7e7a349 24e24ac 7e7a349 1256f85 334102f 7e7a349 1256f85 7e7a349 1256f85 334102f d8d4b8b 3a591f7 c5dceed 3a591f7 c5dceed 3a591f7 c5dceed 3a591f7 c5dceed d8d4b8b 3a591f7 d8d4b8b 7e7a349 24e24ac | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 | import streamlit as st
import pandas as pd
import plotly.express as px
import requests
from io import StringIO
from datetime import datetime
# Page configuration
st.set_page_config(
page_title="Terror Finance & Maritime Watch",
page_icon="π‘οΈ",
layout="wide",
initial_sidebar_state="collapsed"
)
# Custom CSS for styling
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)
# Function to extract IMO from Information field
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"
# Look for IMO pattern: IMO- followed by 7 digits
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"
# Look for MMSI pattern
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"
# Look for Call Sign pattern
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"
# Look for Registered owner or Commercial manager
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:
# Special handling for Name field
if key == 'Name':
# Try to get Name column first
if 'Name' in row.index and pd.notna(row['Name']) and str(row['Name']).strip():
return str(row['Name']).strip()
# Fallback: combine Last_Name and First_Name
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 ''
# Clean up the names
last_name = str(last_name).strip() if pd.notna(last_name) else ''
first_name = str(first_name).strip() if pd.notna(first_name) else ''
# Combine names
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}"
# Try exact key first
if key in row.index:
val = row[key]
else:
# Try alternative column names for common fields
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
# Handle None, NaN, empty strings
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
# Load clean datasets from Hugging Face
@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()
# Load CSV from response content
df = pd.read_csv(StringIO(response.text))
# Clean up any remaining data issues
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)}")
# Create empty dataframe as fallback
datasets[category] = pd.DataFrame()
return datasets
# Create fallback data if CSV loading fails
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
}
# Header
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)
# Load data
try:
data = load_clean_data()
# Check if data was loaded successfully
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()
# Navigation tabs
tab1, tab2, tab3, tab4, tab5 = st.tabs(["π₯ Individuals", "π’ Companies", "π’ Vessels", "π Summary", "π Data Reports"])
# Summary Tab
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:
# Calculate unique countries safely
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)
# Charts
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")
# Individuals Tab
with tab1:
st.markdown("## Individuals Database")
# Export and filters
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"
)
# Filters
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")
# Apply filters
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
]
# Display individuals
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)
# Companies Tab
with tab2:
st.markdown("## Companies Database")
# Export
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"
)
# Search
name_filter = st.text_input("Search companies", placeholder="Enter company name...", key="search_companies")
# Apply filters
filtered_companies = data['companies'].copy()
if name_filter:
filtered_companies = filtered_companies[
filtered_companies['Name'].str.contains(name_filter, case=False, na=False)
]
# Display companies
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)
# Vessels Tab
with tab3:
st.markdown("## Maritime Vessels Database")
# Export
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"
)
# Filters for vessels
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")
# Apply filters
filtered_vessels = data['vessels'].copy()
if name_filter:
# Search in Name, IMO, and other relevant fields
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
]
# Display vessels
for _, vessel in filtered_vessels.iterrows():
status_value = safe_get(vessel, 'Status')
status_class = "status-inactive" if status_value == 'AIS Off' else "status-active"
# Get vessel-specific information
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')) # Sometimes vessel age is in 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)
# Data Reports Tab
with tab5:
st.markdown("## π Data Analysis Reports")
# Load additional analysis files
try:
# Load analysis JSON
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.")
# Load and display text reports
try:
# Executive Summary
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:
# Analysis Report excerpt
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
# Download section
st.markdown("### π₯ Download Analysis Files")
col1, col2, col3, col4 = st.columns(4)
with col1:
try:
# Download complete analysis JSON
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:
st.download_button(
label="π Analysis JSON",
data=analysis_response.content,
file_name=f"complete_analysis_{datetime.now().strftime('%Y%m%d')}.json",
mime="application/json",
help="Complete analysis data in JSON format"
)
else:
st.button("π Analysis JSON", disabled=True, help="File not available")
except:
st.button("π Analysis JSON", disabled=True, help="File not available")
with col2:
try:
# Download analysis report
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.download_button(
label="π Full Report",
data=report_response.text.encode('utf-8'),
file_name=f"analysis_report_{datetime.now().strftime('%Y%m%d')}.txt",
mime="text/plain",
help="Detailed human-readable report"
)
else:
st.button("π Full Report", disabled=True, help="File not available")
except:
st.button("π Full Report", disabled=True, help="File not available")
with col3:
try:
# Download executive summary
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.download_button(
label="π Executive Summary",
data=summary_response.text.encode('utf-8'),
file_name=f"executive_summary_{datetime.now().strftime('%Y%m%d')}.txt",
mime="text/plain",
help="Executive summary"
)
else:
st.button("π Executive Summary", disabled=True, help="File not available")
except:
st.button("π Executive Summary", disabled=True, help="File not available")
with col4:
try:
# Download uncertain entities
uncertain_url = "https://huggingface.co/spaces/Malaji71/list/resolve/main/uncertain_entities.csv"
uncertain_response = requests.get(uncertain_url)
if uncertain_response.status_code == 200:
st.download_button(
label="β Uncertain Entities",
data=uncertain_response.content,
file_name=f"uncertain_entities_{datetime.now().strftime('%Y%m%d')}.csv",
mime="text/csv",
help="Entities requiring manual review"
)
else:
st.button("β Uncertain Entities", disabled=True, help="File not available")
except:
st.button("β Uncertain Entities", disabled=True, help="File not available")
# Additional download options
st.markdown("---")
st.markdown("### π¦ Bulk Download")
if st.button("π₯ Download All Analysis Files", use_container_width=True):
try:
# Create a ZIP file with all analysis files
import zipfile
from io import BytesIO
zip_buffer = BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
# Try to add each file
files_to_add = [
("complete_analysis.json", "application/json"),
("analysis_report.txt", "text/plain"),
("executive_summary.txt", "text/plain"),
("uncertain_entities.csv", "text/csv")
]
for filename, mime_type in files_to_add:
try:
file_url = f"https://huggingface.co/spaces/Malaji71/list/resolve/main/{filename}"
response = requests.get(file_url)
if response.status_code == 200:
zip_file.writestr(filename, response.content)
except:
# Add placeholder if file not available
zip_file.writestr(f"{filename}.missing", f"File {filename} not available")
st.download_button(
label="π¦ Download ZIP Package",
data=zip_buffer.getvalue(),
file_name=f"terror_finance_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip",
mime="application/zip",
help="Download all analysis files in a single ZIP package"
)
except Exception as e:
st.error(f"Error creating ZIP file: {str(e)}")
st.markdown("---")
st.markdown("**π Analysis Package Contents:**")
st.markdown("""
- `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
**π€ Generated by Pariente AI - Advanced Intelligence Analytics**
""")
# Footer
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*") |