import pandas as pd import gradio as gr from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler def train_model(): df = pd.read_csv("mixed_linkedin_profiles.csv") df.fillna("", inplace=True) features = ['Skills', 'Education', 'Job Title', 'Summary', 'Connections', 'Experience (Years)'] X = df[features] y = df['Label'].astype(int) preprocessor = ColumnTransformer([ ('skills_vec', CountVectorizer(max_features=30), 'Skills'), ('education_vec', CountVectorizer(max_features=10), 'Education'), ('jobtitle_vec', CountVectorizer(max_features=10), 'Job Title'), ('summary_tfidf', TfidfVectorizer(max_features=40), 'Summary'), ('num_features', StandardScaler(), ['Connections', 'Experience (Years)']) ]) pipeline = Pipeline([ ('preprocessor', preprocessor), ('classifier', RandomForestClassifier(n_estimators=120, random_state=42)) ]) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.18, random_state=42) pipeline.fit(X_train, y_train) return pipeline model = train_model() def predict_profile(skills, education, job_title, summary, connections, experience, company_name, job_posting): input_data = pd.DataFrame([{ 'Skills': skills, 'Education': education, 'Job Title': job_title, 'Summary': summary, 'Connections': int(connections), 'Experience (Years)': int(experience) }]) pred = model.predict(input_data)[0] prob = model.predict_proba(input_data)[0][1] is_fake_company = False is_fake_job = False company_warnings = [] job_warnings = [] if len(company_name) < 3: company_warnings.append("⚠️ Company name is too short or generic") is_fake_company = True if not any(c.isupper() for c in company_name): company_warnings.append("⚠️ Company name lacks proper capitalization") is_fake_company = True if len(job_posting) < 30: job_warnings.append("⚠️ Job description is too short or generic") is_fake_job = True if len(job_posting.split()) < 10: job_warnings.append("⚠️ Job description is too brief") is_fake_job = True profile_result = f"⚠️ Likely FAKE profile ({prob*100:.1f}% confidence)" if pred == 1 else f"✅ Likely REAL profile ({(1-prob)*100:.1f}% confidence)" company_result = "⚠️ Likely FAKE company" if is_fake_company else "✅ Likely REAL company" job_result = "⚠️ Likely FAKE job posting" if is_fake_job else "✅ Likely REAL job posting" confidence = float(prob) if pred == 1 else float(1-prob) tips = """

How to Spot a Fake LinkedIn Profile or Company/Job Posting

""" company_warnings_html = "
".join(company_warnings) if company_warnings else "No warnings detected." job_warnings_html = "
".join(job_warnings) if job_warnings else "No warnings detected." return (profile_result, confidence, company_result, company_warnings_html, job_result, job_warnings_html, tips) # Cleaned custom CSS with responsive media queries custom_css = """ body, .gradio-container { background: url('https://tse3.mm.bing.net/th?id=OIP.DwukLU73pXKo7c68jGhN1AHaEo&pid=Api&P=0&h=220') no-repeat center center fixed !important; background-size: cover !important; } .gradio-container { min-height: 100vh; } .gradio-block, .gradio-row, .gradio-column { background: rgba(255,255,255,0.12) !important; border-radius: 32px !important; box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37) !important; backdrop-filter: blur(20px) !important; border: 1px solid rgba(255,255,255,0.18) !important; margin-bottom: 24px !important; animation: fadeInUp 1.2s cubic-bezier(.39,.575,.565,1.000) both; } @keyframes fadeInUp { 0% {opacity:0;transform:translateY(40px);} 100% {opacity:1;transform:translateY(0);} } .gradio-markdown, .gradio-html, .gradio-textbox, .gradio-number, .gradio-slider { background: rgba(255,255,255,0.85) !important; border-radius: 18px !important; box-shadow: 0 2px 12px rgba(0,0,0,0.08) !important; margin-bottom: 12px !important; font-size: 1.09em !important; animation: fadeIn 1.2s; } @keyframes fadeIn { from {opacity:0;} to {opacity:1;} } .gradio-button { background: linear-gradient(90deg, #4a6baf 0%, #6dd5ed 100%) !important; color: white !important; border: none !important; border-radius: 18px !important; padding: 16px 36px !important; font-size: 1.15em !important; font-weight: bold !important; transition: background 0.5s, transform 0.2s, box-shadow 0.3s; box-shadow: 0 0 16px 4px #6dd5ed66, 0 6px 30px 0 rgba(76, 201, 240, 0.19); animation: pulseGlow 2s infinite alternate; } @keyframes pulseGlow { 0% {box-shadow: 0 0 16px 4px #6dd5ed66, 0 6px 30px 0 rgba(76, 201, 240, 0.19);} 100% {box-shadow: 0 0 32px 8px #4a6baf66, 0 12px 40px 0 rgba(76, 201, 240, 0.25);} } .gradio-button:hover { background: linear-gradient(90deg, #4776e6 0%, #43cea2 100%) !important; transform: scale(1.07) rotate(-2deg); } .tips-card { background: rgb(128, 128, 128); border-radius: 16px; padding: 20px 24px; margin-top: 22px; box-shadow: 0 2px 12px rgba(0,0,0,0.10); animation: fadeIn 1.6s; } .title-card { background: #fffacd !important; border-radius: 24px; padding: 24px 32px; margin: 0 auto 32px; max-width: 800px; box-shadow: 0 8px 24px rgba(0,0,0,0.12); border: 1px solid rgba(255,255,255,0.3); display: flex; flex-direction: column; align-items: center; animation: textPop 1.3s cubic-bezier(.23,1.01,.32,1) both; } .title-card h1 { color: #e63946; margin: 0; font-size: 2.2em; font-weight: 700; text-align: center; text-shadow: 0 2px 12px #ff999977; letter-spacing: 0.05em; } .features-list { background: #fffacd !important; border-radius: 24px; padding: 24px 28px; margin: 0 auto 24px; max-width: 800px; color: #000 !important; font-size: 1.1em; line-height: 1.7; box-shadow: 0 2px 12px rgba(0,0,0,0.10); } .features-list h2, .features-list ul, .features-list li, .features-list strong { color: #000 !important; } .tips-card h4 { color: #e63946; margin-bottom: 12px; letter-spacing: 0.03em; animation: slideInLeft 1.2s; } @keyframes slideInLeft { from {opacity:0;transform:translateX(-40px);} to {opacity:1;transform:translateX(0);} } .tips-card ul { padding-left: 20px; color: #222; } .gradio-textbox[readonly], .gradio-html { font-weight: bold; letter-spacing: 0.01em; color: #222 !important; border: 2px solid #4a6baf !important; background: rgba(255,255,255,0.93) !important; animation: fadeInUp 1s; } h1, h2, h3, h4, h5 { animation: textPop 1.3s cubic-bezier(.23,1.01,.32,1) both; } @keyframes textPop { 0% {opacity:0;transform:scale(0.7);} 100% {opacity:1;transform:scale(1);} } .gradio-slider .noUi-base { background: linear-gradient(90deg, #4a6baf 0%, #6dd5ed 100%) !important; } .credits-footnote { text-align: center; margin-top: 24px; font-size: 0.95em; color: #555; font-weight: 500; padding-bottom: 16px; } footer {visibility: hidden;} /* Responsive adjustments for mobile */ @media (max-width: 768px) { .gradio-container { padding: 8px !important; } .gradio-block, .gradio-row, .gradio-column { width: 100% !important; margin-bottom: 12px !important; } .gradio-textbox, .gradio-number, .gradio-slider, .gradio-button { width: 100% !important; font-size: 1em !important; } .gradio-button { padding: 12px 24px !important; } .title-card h1 { font-size: 1.6em !important; } .features-list { font-size: 0.9em !important; padding: 12px !important; } } """ features_html = """

Our App Features

""" with gr.Blocks(css=custom_css, title="LinkShield | LinkedIn Fake Profile & Company Detector", fill_width=True) as demo: gr.HTML("""

LinkShield (LinkedIn Fake Profile and Company Detector)

""") gr.HTML(features_html) gr.Markdown( "
Enter LinkedIn profile or company/job posting details.
The model will predict if they are likely Fake or Real.
" ) with gr.Row(): with gr.Column(min_width=300): skills = gr.Textbox(label="Skills (comma-separated)", value="Python, SQL, Marketing") education = gr.Textbox(label="Education", value="MBA in Marketing") job_title = gr.Textbox(label="Job Title", value="Marketing Specialist") summary = gr.Textbox(label="Profile Summary", lines=3, value="Experienced professional with proven track record...") connections = gr.Number(label="Connections", value=500, precision=0) experience = gr.Number(label="Years of Experience", value=5, precision=0) company_name = gr.Textbox(label="Company Name", value="TechCorp Inc.") job_posting = gr.Textbox(label="Job Posting Description", lines=3, value="Seeking a motivated individual to join our team...") submit_btn = gr.Button("✨ Check Profile & Company") with gr.Column(min_width=300): result = gr.Textbox(label="Profile Prediction", interactive=False) confidence = gr.Slider(label="Confidence", minimum=0, maximum=1, step=0.01, interactive=False) company_result = gr.Textbox(label="Company Prediction", interactive=False) company_warnings = gr.HTML(label="Company Warnings") job_result = gr.Textbox(label="Job Posting Prediction", interactive=False) job_warnings = gr.HTML(label="Job Posting Warnings") tips = gr.HTML(label="Tips for Spotting Fakes") submit_btn.click( predict_profile, inputs=[skills, education, job_title, summary, connections, experience, company_name, job_posting], outputs=[result, confidence, company_result, company_warnings, job_result, job_warnings, tips] ) gr.Markdown("---") gr.Markdown("
The model uses profile features (skills, education, job title, summary, connections, experience) and text analysis to estimate the likelihood of a profile or company being fake.
For best results, provide as much detail as possible.
") gr.HTML("
Created by Sreelekha Putta
") if __name__ == "__main__": demo.launch() # Only for local testing