File size: 7,491 Bytes
c674f01 |
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 |
import streamlit as st
import pandas as pd
import bisect
import requests
from io import BytesIO
from bs4 import BeautifulSoup
# ------------------------------------------------------------------------------------
# Step 1: Load Data (Fetch and Prepare the DataFrame)
# ------------------------------------------------------------------------------------
@st.cache_data(ttl=3600)
def fetch_ods_file():
"""
Fetches the .ods file from the visa decisions website and returns its binary content.
Returns:
- A BytesIO object containing the file content if successful.
- None if the file could not be fetched.
"""
url = "https://www.ireland.ie/en/india/newdelhi/services/visas/processing-times-and-decisions/"
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
)
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
soup = BeautifulSoup(response.content, 'html.parser')
links = soup.find_all('a')
# Find the link containing the specific text
file_url = None
for link in links:
if "Visa decisions made from 1 January 2024 to" in link.get_text(strip=True):
file_url = link.get('href')
break
if file_url:
# Resolve relative URLs to absolute
if not file_url.startswith("http"):
file_url = requests.compat.urljoin(url, file_url)
file_response = requests.get(file_url, headers=headers)
if file_response.status_code == 200:
return BytesIO(file_response.content)
return None
@st.cache_data
def prepare_dataframe(file):
"""
Prepares and cleans the DataFrame from the fetched .ods file.
Args:
file: The .ods file content as BytesIO.
Returns:
A cleaned and sorted DataFrame ready for searching.
"""
df = pd.read_excel(file, engine='odf')
df.drop(columns=["Unnamed: 0", "Unnamed: 1"], inplace=True, errors="ignore")
df.dropna(how="all", inplace=True)
df.reset_index(drop=True, inplace=True)
# Identify the header row
for idx, row in df.iterrows():
if row["Unnamed: 2"] == "Application Number" and row["Unnamed: 3"] == "Decision":
df.columns = ["Application Number", "Decision"]
df = df.iloc[idx + 1:] # Skip the header row
break
# Process application numbers and sort the DataFrame
df["Application Number"] = df["Application Number"].astype(str).str.strip().astype(int)
df.sort_values(by="Application Number", inplace=True)
df.reset_index(drop=True, inplace=True)
return df
# ------------------------------------------------------------------------------------
# Step 2: Binary Search Utility for Finding Nearest Application Numbers
# ------------------------------------------------------------------------------------
def binary_search_nearest(df, target):
"""
Uses binary search to find the nearest application numbers in the DataFrame.
Args:
df: The DataFrame containing the application numbers.
target: The target application number to search for.
Returns:
Two nearest application numbers (before and after the target).
"""
application_numbers = df["Application Number"].tolist()
pos = bisect.bisect_left(application_numbers, target)
before = application_numbers[pos - 1] if pos > 0 else None
after = application_numbers[pos] if pos < len(application_numbers) else None
return before, after
# ------------------------------------------------------------------------------------
# Step 3: Search Application Status
# ------------------------------------------------------------------------------------
def search_application(df):
"""
Handles the user input and searches for the application number in the DataFrame.
Args:
df: The DataFrame containing application numbers and decisions.
"""
user_input = st.text_input("Enter your Application Number (including IRL if applicable):")
if user_input:
# Validate user input
if "irl" in user_input.lower():
try:
application_number = int("".join(filter(str.isdigit, user_input.lower().split("irl")[-1])))
if len(str(application_number)) < 8:
st.warning("Please enter a valid application number with at least 8 digits after IRL.")
return
except ValueError:
st.error("Invalid input after IRL. Please enter only digits.")
return
else:
if not user_input.isdigit() or len(user_input) < 8:
st.warning("Please enter at least 8 digits for your VISA application number.")
return
elif len(user_input) > 8:
st.warning("The application number cannot exceed 8 digits. Please correct your input.")
return
application_number = int(user_input)
# Search for the application number in the DataFrame
result = df[df["Application Number"] == application_number]
if not result.empty:
decision = result.iloc[0]["Decision"]
if decision.lower() == "refused":
st.error(f"Application Number: {application_number}\n\nDecision: **Refused**")
elif decision.lower() == "approved":
st.success(f"Application Number: {application_number}\n\nDecision: **Approved**")
else:
st.info(f"Application Number: {application_number}\n\nDecision: **{decision}**")
else:
st.warning(f"No record found for Application Number: {application_number}.")
# Find nearest application numbers using binary search
before, after = binary_search_nearest(df, application_number)
nearest_records = pd.DataFrame({
"Nearest Application": ["Before", "After"],
"Application Number": [before, after],
"Decision": [
df[df["Application Number"] == before]["Decision"].values[0] if before else None,
df[df["Application Number"] == after]["Decision"].values[0] if after else None
],
"Difference": [
application_number - before if before else None,
after - application_number if after else None
]
}).dropna()
if not nearest_records.empty:
st.subheader("Nearest Application Numbers")
st.table(nearest_records.reset_index(drop=True))
else:
st.info("No nearest application numbers found.")
# ------------------------------------------------------------------------------------
# Main Streamlit Application Logic
# ------------------------------------------------------------------------------------
def main():
st.title("Visa Application Status Checker")
# Fetch and prepare the data
ods_file = fetch_ods_file()
if ods_file:
df = prepare_dataframe(ods_file)
if df is not None:
search_application(df)
else:
st.error("Failed to prepare the data.")
else:
st.error("Failed to fetch the .ods file.")
if __name__ == "__main__":
main()
|