Update app.py
Browse files
app.py
CHANGED
|
@@ -115,10 +115,76 @@ st.markdown("""
|
|
| 115 |
color: #E53E3E;
|
| 116 |
font-weight: bold;
|
| 117 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
</style>
|
| 119 |
""", unsafe_allow_html=True)
|
| 120 |
|
| 121 |
-
# Function to
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
def safe_get(row, key, default="N/A"):
|
| 123 |
try:
|
| 124 |
# Special handling for Name field
|
|
@@ -263,11 +329,14 @@ def create_sample_data():
|
|
| 263 |
}
|
| 264 |
|
| 265 |
# Header
|
| 266 |
-
col1, col2 = st.columns([6, 1])
|
| 267 |
with col1:
|
| 268 |
st.markdown("# π‘οΈ Terror Finance & Maritime Watch")
|
|
|
|
| 269 |
with col2:
|
| 270 |
theme_toggle = st.checkbox("π", key="theme")
|
|
|
|
|
|
|
| 271 |
|
| 272 |
# Load data
|
| 273 |
try:
|
|
@@ -536,7 +605,7 @@ with tab3:
|
|
| 536 |
)
|
| 537 |
|
| 538 |
# Filters for vessels
|
| 539 |
-
col1, col2, col3 = st.columns([3, 2, 2])
|
| 540 |
|
| 541 |
with col1:
|
| 542 |
name_filter = st.text_input("Search vessels", placeholder="Enter vessel name or IMO...", key="search_vessels")
|
|
@@ -555,6 +624,13 @@ with tab3:
|
|
| 555 |
status_options += sorted([s for s in statuses if s and s.lower() != 'nan'])
|
| 556 |
status_filter = st.selectbox("Status", status_options, key="status_vessels")
|
| 557 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 558 |
# Apply filters
|
| 559 |
filtered_vessels = data['vessels'].copy()
|
| 560 |
|
|
@@ -563,7 +639,8 @@ with tab3:
|
|
| 563 |
mask = (
|
| 564 |
filtered_vessels['Name'].str.contains(name_filter, case=False, na=False) |
|
| 565 |
filtered_vessels.get('IMO', pd.Series()).astype(str).str.contains(name_filter, case=False, na=False) |
|
| 566 |
-
filtered_vessels.get('Owner', pd.Series()).astype(str).str.contains(name_filter, case=False, na=False)
|
|
|
|
| 567 |
)
|
| 568 |
filtered_vessels = filtered_vessels[mask]
|
| 569 |
|
|
@@ -576,6 +653,11 @@ with tab3:
|
|
| 576 |
filtered_vessels = filtered_vessels[
|
| 577 |
filtered_vessels['Status'] == status_filter
|
| 578 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 579 |
|
| 580 |
# Display vessels
|
| 581 |
for _, vessel in filtered_vessels.iterrows():
|
|
@@ -699,6 +781,12 @@ with tab5:
|
|
| 699 |
|
| 700 |
# Footer
|
| 701 |
st.markdown("---")
|
| 702 |
-
st.
|
| 703 |
-
|
| 704 |
-
st.markdown(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
color: #E53E3E;
|
| 116 |
font-weight: bold;
|
| 117 |
}
|
| 118 |
+
|
| 119 |
+
/* Pariente AI Branding */
|
| 120 |
+
.pariente-ai {
|
| 121 |
+
color: #4A5568;
|
| 122 |
+
font-size: 0.875rem;
|
| 123 |
+
font-style: italic;
|
| 124 |
+
text-align: right;
|
| 125 |
+
}
|
| 126 |
+
.pariente-ai-footer {
|
| 127 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 128 |
+
-webkit-background-clip: text;
|
| 129 |
+
-webkit-text-fill-color: transparent;
|
| 130 |
+
font-weight: bold;
|
| 131 |
+
}
|
| 132 |
</style>
|
| 133 |
""", unsafe_allow_html=True)
|
| 134 |
|
| 135 |
+
# Function to extract IMO from Information field
|
| 136 |
+
def extract_imo_from_info(info_text):
|
| 137 |
+
"""Extract IMO number from Information field"""
|
| 138 |
+
import re
|
| 139 |
+
if not info_text or pd.isna(info_text):
|
| 140 |
+
return "N/A"
|
| 141 |
+
|
| 142 |
+
# Look for IMO pattern: IMO- followed by 7 digits
|
| 143 |
+
imo_match = re.search(r'IMO-?\s*(\d{7})', str(info_text), re.IGNORECASE)
|
| 144 |
+
if imo_match:
|
| 145 |
+
return imo_match.group(1)
|
| 146 |
+
return "N/A"
|
| 147 |
+
|
| 148 |
+
def extract_mmsi_from_info(info_text):
|
| 149 |
+
"""Extract MMSI from Information field"""
|
| 150 |
+
import re
|
| 151 |
+
if not info_text or pd.isna(info_text):
|
| 152 |
+
return "N/A"
|
| 153 |
+
|
| 154 |
+
# Look for MMSI pattern
|
| 155 |
+
mmsi_match = re.search(r'MMSI-?\s*(\d{9})', str(info_text), re.IGNORECASE)
|
| 156 |
+
if mmsi_match:
|
| 157 |
+
return mmsi_match.group(1)
|
| 158 |
+
return "N/A"
|
| 159 |
+
|
| 160 |
+
def extract_call_sign_from_info(info_text):
|
| 161 |
+
"""Extract Call Sign from Information field"""
|
| 162 |
+
import re
|
| 163 |
+
if not info_text or pd.isna(info_text):
|
| 164 |
+
return "N/A"
|
| 165 |
+
|
| 166 |
+
# Look for Call Sign pattern
|
| 167 |
+
call_match = re.search(r'Call Sign-?\s*([A-Z0-9]+)', str(info_text), re.IGNORECASE)
|
| 168 |
+
if call_match:
|
| 169 |
+
return call_match.group(1)
|
| 170 |
+
return "N/A"
|
| 171 |
+
|
| 172 |
+
def extract_owner_from_info(info_text):
|
| 173 |
+
"""Extract owner information from Information field"""
|
| 174 |
+
import re
|
| 175 |
+
if not info_text or pd.isna(info_text):
|
| 176 |
+
return "N/A"
|
| 177 |
+
|
| 178 |
+
# Look for Registered owner or Commercial manager
|
| 179 |
+
owner_match = re.search(r'Registered owner-?\s*([^,]+)', str(info_text), re.IGNORECASE)
|
| 180 |
+
if owner_match:
|
| 181 |
+
return owner_match.group(1).strip()
|
| 182 |
+
|
| 183 |
+
manager_match = re.search(r'Commercial manager-?\s*([^,]+)', str(info_text), re.IGNORECASE)
|
| 184 |
+
if manager_match:
|
| 185 |
+
return manager_match.group(1).strip()
|
| 186 |
+
|
| 187 |
+
return "N/A"
|
| 188 |
def safe_get(row, key, default="N/A"):
|
| 189 |
try:
|
| 190 |
# Special handling for Name field
|
|
|
|
| 329 |
}
|
| 330 |
|
| 331 |
# Header
|
| 332 |
+
col1, col2, col3 = st.columns([6, 1, 1])
|
| 333 |
with col1:
|
| 334 |
st.markdown("# π‘οΈ Terror Finance & Maritime Watch")
|
| 335 |
+
st.markdown("*Powered by Pariente AI - Advanced Intelligence Analytics*")
|
| 336 |
with col2:
|
| 337 |
theme_toggle = st.checkbox("π", key="theme")
|
| 338 |
+
with col3:
|
| 339 |
+
st.markdown('<div class="pariente-ai">π€ <span class="pariente-ai-footer">Pariente AI</span><br><em>Intelligence Platform</em></div>', unsafe_allow_html=True)
|
| 340 |
|
| 341 |
# Load data
|
| 342 |
try:
|
|
|
|
| 605 |
)
|
| 606 |
|
| 607 |
# Filters for vessels
|
| 608 |
+
col1, col2, col3, col4 = st.columns([3, 2, 2, 2])
|
| 609 |
|
| 610 |
with col1:
|
| 611 |
name_filter = st.text_input("Search vessels", placeholder="Enter vessel name or IMO...", key="search_vessels")
|
|
|
|
| 624 |
status_options += sorted([s for s in statuses if s and s.lower() != 'nan'])
|
| 625 |
status_filter = st.selectbox("Status", status_options, key="status_vessels")
|
| 626 |
|
| 627 |
+
with col4:
|
| 628 |
+
org_options = ["All Organizations"]
|
| 629 |
+
if len(data['vessels']) > 0 and 'Linked To' in data['vessels'].columns:
|
| 630 |
+
orgs = data['vessels']['Linked To'].dropna().astype(str).unique()
|
| 631 |
+
org_options += sorted([o for o in orgs if o and o.lower() != 'nan'])
|
| 632 |
+
org_filter = st.selectbox("Organization", org_options, key="org_vessels")
|
| 633 |
+
|
| 634 |
# Apply filters
|
| 635 |
filtered_vessels = data['vessels'].copy()
|
| 636 |
|
|
|
|
| 639 |
mask = (
|
| 640 |
filtered_vessels['Name'].str.contains(name_filter, case=False, na=False) |
|
| 641 |
filtered_vessels.get('IMO', pd.Series()).astype(str).str.contains(name_filter, case=False, na=False) |
|
| 642 |
+
filtered_vessels.get('Owner', pd.Series()).astype(str).str.contains(name_filter, case=False, na=False) |
|
| 643 |
+
filtered_vessels.get('Information', pd.Series()).astype(str).str.contains(name_filter, case=False, na=False)
|
| 644 |
)
|
| 645 |
filtered_vessels = filtered_vessels[mask]
|
| 646 |
|
|
|
|
| 653 |
filtered_vessels = filtered_vessels[
|
| 654 |
filtered_vessels['Status'] == status_filter
|
| 655 |
]
|
| 656 |
+
|
| 657 |
+
if org_filter != "All Organizations":
|
| 658 |
+
filtered_vessels = filtered_vessels[
|
| 659 |
+
filtered_vessels['Linked To'] == org_filter
|
| 660 |
+
]
|
| 661 |
|
| 662 |
# Display vessels
|
| 663 |
for _, vessel in filtered_vessels.iterrows():
|
|
|
|
| 781 |
|
| 782 |
# Footer
|
| 783 |
st.markdown("---")
|
| 784 |
+
col1, col2 = st.columns([3, 1])
|
| 785 |
+
with col1:
|
| 786 |
+
st.markdown("π‘οΈ **Terror Finance & Maritime Watch** - Monitoring entities involved in terror financing and maritime sanctions evasion")
|
| 787 |
+
st.markdown(f"π Data processed: {len(data['individuals'])} individuals, {len(data['companies'])} companies, {len(data['vessels'])} vessels")
|
| 788 |
+
st.markdown(f"π Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
| 789 |
+
with col2:
|
| 790 |
+
st.markdown("**Powered by**")
|
| 791 |
+
st.markdown("π€ **Pariente AI**")
|
| 792 |
+
st.markdown("*Advanced Intelligence Analytics*")
|