#!/usr/bin/env python3 """CV Matching Engine""" import os,re,json,warnings,tempfile import fitz,spacy,gradio as gr import numpy as np,pandas as pd import plotly.graph_objects as go from collections import defaultdict from typing import Dict,List from sklearn.metrics.pairwise import cosine_similarity from sentence_transformers import SentenceTransformer warnings.filterwarnings("ignore") _nlp=None;_embed_model=None def get_nlp(): global _nlp if _nlp is None: _nlp=spacy.load("en_core_web_sm") return _nlp def get_embed_model(): global _embed_model if _embed_model is None: _embed_model=SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") return _embed_model print("[INFO] App baslatildi.") SKILL_ONTOLOGY: Dict[str, List[str]] = { # ── 1. PROGRAMLAMA DİLLERİ ─────────────────────────────────────────────── "python" : ["python3", "python 3", "py", "cpython"], "java" : ["java se", "java ee", "java 8", "java 11", "java 17", "jvm", "openjdk"], "c#" : ["csharp", "c sharp", ".net c#", "dotnet csharp"], "c++" : ["cpp", "c plus plus", "c/c++", "iso c++"], "c" : ["c language", "c programming", "ansi c", "embedded c"], "javascript" : ["js", "ecmascript", "es6", "es2015", "vanilla js"], "typescript" : ["ts", "typed javascript"], "kotlin" : ["kotlin jvm", "kotlin android"], "swift" : ["swift ios", "swift ui", "apple swift"], "go" : ["golang", "go language"], "rust" : ["rust lang", "rust programming"], "php" : ["php7", "php8", "hypertext preprocessor"], "ruby" : ["ruby on rails", "ror", "rails"], "scala" : ["scala jvm", "akka"], "r" : ["rstudio", "r programming", "r language", "r stats"], "matlab" : ["matlab/simulink", "simulink", "mathworks"], "python" : ["python3", "py"], "dart" : ["dart flutter", "dart language"], "perl" : ["perl scripting"], "bash" : ["shell scripting", "bash scripting", "shell script", "sh", "zsh"], "powershell" : ["ps1", "windows powershell", "pwsh"], "groovy" : ["groovy jvm", "grails"], "lua" : ["lua scripting"], "haskell" : ["functional programming haskell"], "elixir" : ["elixir phoenix", "phoenix framework"], "clojure" : ["clojure jvm"], "assembly" : ["asm", "x86 assembly", "arm assembly"], "vba" : ["visual basic for applications", "excel vba", "office vba"], "visual basic" : ["vb.net", "vb6", "visual basic .net"], "cobol" : ["cobol mainframe"], "fortran" : ["fortran 90", "fortran 95"], # ── 2. WEB FRONTEND ───────────────────────────────────────────────────── "react" : ["reactjs", "react.js", "react native", "react hooks", "next.js", "nextjs"], "angular" : ["angularjs", "angular 2+", "angular cli", "ng"], "vue" : ["vuejs", "vue.js", "vue 3", "nuxt", "nuxt.js"], "svelte" : ["sveltekit"], "html" : ["html5", "hypertext markup language"], "css" : ["css3", "cascading style sheets", "scss", "sass", "less"], "tailwind" : ["tailwindcss", "tailwind css"], "bootstrap" : ["bootstrap 5", "bootstrap css"], "jquery" : ["jquery js"], "webpack" : ["webpack bundler", "vite", "rollup", "parcel"], "graphql" : ["graphql api", "apollo graphql"], # ── 3. WEB BACKEND / API ───────────────────────────────────────────────── "node.js" : ["nodejs", "node js", "express", "express.js", "expressjs"], "django" : ["django rest", "drf", "django framework"], "flask" : ["flask api", "flask python"], "fastapi" : ["fast api", "fastapi python"], "spring boot" : ["spring", "spring framework", "spring mvc", "spring security", "spring data"], "asp.net" : ["asp net", "asp.net core", ".net core", "dotnet core", "aspnet", "mvc .net"], "laravel" : ["laravel php", "laravel framework"], "nestjs" : ["nest.js", "nest js"], "rest api" : ["restful api", "rest", "restful", "rest services", "web api", "http api"], "microservices" : ["micro services", "service oriented architecture", "soa", "distributed systems"], "grpc" : ["grpc api", "protocol buffers", "protobuf"], "websocket" : ["websockets", "socket.io", "real-time communication"], "oauth" : ["oauth2", "openid connect", "jwt", "json web token", "oidc"], # ── 4. MOBİL GELİŞTİRME ───────────────────────────────────────────────── "android" : ["android development", "android studio", "android sdk", "android kotlin"], "ios" : ["ios development", "xcode", "swift ios", "objective-c", "ios sdk"], "flutter" : ["flutter dart", "flutter mobile", "flutter sdk"], "react native" : ["react-native", "rn mobile"], "xamarin" : ["xamarin forms", "xamarin android", "xamarin ios"], "ionic" : ["ionic framework", "ionic angular"], # ── 5. VERİTABANI — İLİŞKİSEL ──────────────────────────────────────────── "sql" : ["structured query language", "ansi sql", "sql dili"], "mysql" : ["mysql database", "mysql server", "mysql 8"], "postgresql" : ["postgres", "psql", "postgresql database"], "mssql" : ["sql server", "microsoft sql server", "t-sql", "tsql", "ms sql"], "oracle" : ["oracle database", "oracle db", "oracle sql", "pl/sql", "plsql"], "sqlite" : ["sqlite3", "sqlite database"], "mariadb" : ["maria db", "mariadb server"], "db2" : ["ibm db2", "db2 database"], # ── 6. VERİTABANI — NoSQL ──────────────────────────────────────────────── "mongodb" : ["mongo", "mongo db", "mongodb atlas", "nosql mongodb"], "redis" : ["redis cache", "redis server", "in-memory database"], "cassandra" : ["apache cassandra", "cql"], "elasticsearch" : ["elastic search", "elk stack", "opensearch"], "dynamodb" : ["aws dynamodb", "amazon dynamodb"], "neo4j" : ["graph database", "neo4j db", "cypher query"], "couchdb" : ["apache couchdb"], "firebase" : ["firebase realtime", "firestore", "firebase db"], "influxdb" : ["time series database", "influx db"], # ── 7. BULUT PLATFORMLARI ───────────────────────────────────────────────── "aws" : ["amazon web services", "amazon aws", "aws cloud", "ec2", "s3", "lambda", "rds", "eks", "ecs", "sagemaker", "cloudwatch", "cloudformation"], "azure" : ["microsoft azure", "azure cloud", "azure devops", "azure functions", "azure kubernetes", "aks", "azure blob", "azure sql", "azure ad"], "google cloud" : ["gcp", "google cloud platform", "gke", "bigquery", "cloud run", "cloud functions", "vertex ai", "firebase"], "alibaba cloud" : ["aliyun", "alibaba cloud computing"], "oracle cloud" : ["oci", "oracle cloud infrastructure"], # ── 8. DevOps / SRE / CI-CD ────────────────────────────────────────────── "docker" : ["containerization", "container", "dockerfile", "docker-compose", "docker swarm", "container runtime"], "kubernetes" : ["k8s", "k8", "container orchestration", "helm", "kubectl", "kube", "openshift"], "git" : ["github", "gitlab", "bitbucket", "version control", "git flow", "source control", "git branching"], "jenkins" : ["jenkins ci", "jenkins pipeline", "jenkins ci/cd"], "github actions" : ["gh actions", "github ci", "github workflow"], "gitlab ci" : ["gitlab cicd", "gitlab pipeline"], "terraform" : ["terraform iac", "hashicorp terraform", "infrastructure as code", "iac"], "ansible" : ["ansible playbook", "ansible automation", "configuration management"], "prometheus" : ["prometheus monitoring", "grafana prometheus"], "grafana" : ["grafana dashboard", "grafana monitoring"], "nginx" : ["nginx server", "nginx reverse proxy", "nginx load balancer"], "apache" : ["apache http", "apache server", "httpd"], "linux" : ["ubuntu", "centos", "debian", "red hat", "rhel", "fedora", "linux server", "unix", "linux administration"], "vagrant" : ["vagrant vm", "hashicorp vagrant"], "puppet" : ["puppet configuration"], "chef" : ["chef infra", "chef automation"], # ── 9. VERİ MÜHENDİSLİĞİ / BIG DATA ───────────────────────────────────── "apache spark" : ["spark", "pyspark", "spark streaming", "apache spark sql"], "hadoop" : ["apache hadoop", "hdfs", "mapreduce", "hive", "hbase"], "kafka" : ["apache kafka", "kafka streaming", "kafka broker", "event streaming"], "airflow" : ["apache airflow", "airflow dag", "workflow orchestration"], "dbt" : ["data build tool", "dbt core", "dbt cloud"], "etl" : ["extract transform load", "data pipeline", "data integration", "data ingestion", "elt"], "data warehouse" : ["veri ambarı", "snowflake", "redshift", "bigquery", "azure synapse", "data lake", "lakehouse", "delta lake"], "data analysis" : ["veri analizi", "data analytics", "exploratory data analysis", "eda", "veri madenciliği", "data mining"], "pandas" : ["pandas dataframe", "pandas python"], "numpy" : ["numpy array", "numerical python"], "tableau" : ["tableau desktop", "tableau server", "tableau public"], "power bi" : ["powerbi", "microsoft power bi", "power bi desktop"], "qlik" : ["qliksense", "qlik sense", "qlikview"], "looker" : ["looker studio", "google looker", "data studio"], # ── 10. YAPAY ZEKA / MAKİNE ÖĞRENMESİ ─────────────────────────────────── "machine learning": ["ml", "makine öğrenmesi", "makine öğrenimi", "supervised learning", "unsupervised learning", "classification", "regression", "clustering", "makine öğrenimi"], "deep learning" : ["dl", "derin öğrenme", "neural network", "ann", "dnn", "convolutional neural network", "cnn", "rnn", "lstm", "gru"], "natural language processing": ["nlp", "doğal dil işleme", "text mining", "transformers", "text classification", "named entity recognition", "ner", "sentiment analysis", "text generation"], "computer vision" : ["cv", "image processing", "görüntü işleme", "opencv", "object detection", "image classification", "yolo", "segmentation"], "reinforcement learning": ["rl", "pekiştirmeli öğrenme", "dqn", "ppo", "a3c"], "large language model": ["llm", "gpt", "bert", "llama", "fine-tuning", "rag", "retrieval augmented generation", "prompt engineering", "chatgpt", "claude", "gemini", "mistral"], "tensorflow" : ["tf", "keras", "tensorflow 2"], "pytorch" : ["torch", "pytorch lightning"], "scikit-learn" : ["sklearn", "scikit learn"], "hugging face" : ["huggingface", "transformers library", "hf hub"], "langchain" : ["lang chain", "langchain python", "langchain agents"], "mlflow" : ["ml flow", "mlflow tracking", "model registry"], "feature engineering": ["özellik mühendisliği", "feature selection", "feature extraction"], "xgboost" : ["extreme gradient boosting", "xgb", "lightgbm", "lgbm", "catboost"], "time series" : ["zaman serisi", "forecasting", "talep tahmini", "arima", "sarima", "prophet", "lstm forecasting"], # ── 11. SİBER GÜVENLİK ─────────────────────────────────────────────────── "cybersecurity" : ["siber güvenlik", "information security", "bilgi güvenliği", "security engineering", "application security"], "penetration testing": ["pentest", "ethical hacking", "offensive security", "vulnerability assessment", "metasploit", "burp suite"], "soc" : ["security operations center", "siem", "soc analyst", "splunk", "qradar", "incident response"], "network security": ["firewall", "ids", "ips", "vpn", "zero trust", "ddos protection", "network monitoring"], "cryptography" : ["encryption", "ssl/tls", "ssl", "tls", "pki", "certificate management", "hsm"], "devsecops" : ["dev sec ops", "security in devops", "shift left security", "sast", "dast", "dependency scanning"], "identity management": ["iam", "active directory", "ldap", "sso", "azure ad", "okta", "privileged access management", "pam"], # ── 12. YAZILIM MÜHENDİSLİĞİ / MİMARİ ─────────────────────────────────── "software architecture": ["yazılım mimarisi", "solution architecture", "system design", "design patterns", "solid principles"], "microservices" : ["micro services", "distributed systems", "service mesh", "event driven architecture", "eda architecture", "cqrs", "ddd"], "unit testing" : ["test driven development", "tdd", "junit", "pytest", "nunit", "xunit", "mocha", "jasmine", "jest"], "code review" : ["peer review", "pull request", "code quality", "static analysis", "sonarqube"], "agile" : ["scrum", "kanban", "safe", "extreme programming", "xp", "sprint", "backlog", "çevik"], "uml" : ["unified modeling language", "class diagram", "sequence diagram"], "clean code" : ["refactoring", "solid", "dry principle", "kiss principle"], # ── 13. ERP / CRM / KURUMSAL YAZILIMLAR ────────────────────────────────── "sap" : ["sap erp", "sap s/4hana", "sap hana", "sap abap", "sap bw", "sap fi", "sap mm", "sap sd", "sap pp", "sap hr"], "oracle erp" : ["oracle ebs", "oracle e-business suite", "oracle fusion", "oracle hcm", "oracle scm"], "microsoft dynamics": ["dynamics 365", "dynamics crm", "dynamics ax", "dynamics nav", "business central"], "salesforce" : ["salesforce crm", "salesforce cloud", "apex salesforce", "soql"], "servicenow" : ["service now", "itsm servicenow"], "jira" : ["jira software", "jira service", "atlassian jira", "confluence"], # ── 14. TESTİNG / QA ───────────────────────────────────────────────────── "selenium" : ["selenium webdriver", "selenium grid", "selenium automation"], "cypress" : ["cypress testing", "cypress e2e"], "playwright" : ["playwright testing", "playwright e2e"], "appium" : ["appium mobile testing"], "postman" : ["postman api testing", "api testing"], "load testing" : ["jmeter", "locust", "k6", "gatling", "performance testing", "stress testing"], "qa" : ["quality assurance", "kalite güvencesi", "test automation", "manual testing", "test engineer"], # ── 15. AĞLAR / SİSTEM / ALTYAPI ───────────────────────────────────────── "networking" : ["tcp/ip", "dns", "dhcp", "routing", "switching", "cisco", "vlan", "bgp", "ospf", "network administration"], "windows server" : ["windows server 2019", "windows server 2022", "active directory", "group policy", "iis"], "virtualization" : ["vmware", "vsphere", "hyper-v", "virtualbox", "proxmox"], "storage" : ["san", "nas", "object storage", "backup solutions"], # ── 16. IOT / GÖMÜLÜ SİSTEMLER ─────────────────────────────────────────── "iot" : ["internet of things", "nesnelerin interneti", "mqtt", "iot platform", "azure iot", "aws iot"], "embedded systems": ["gömülü sistemler", "rtos", "freertos", "bare metal", "embedded linux", "microcontroller"], "arduino" : ["arduino uno", "arduino ide", "arduino programming"], "raspberry pi" : ["rpi", "raspberry pi os"], "fpga" : ["vhdl", "verilog", "xilinx", "intel fpga"], # ── 17. ROBOTİK / KONTROL SİSTEMLERİ ───────────────────────────────────── "ros" : ["robot operating system", "ros2", "roslaunch", "rosnode"], "control systems" : ["kontrol sistemleri", "pid controller", "pid", "control theory", "feedback control", "state space"], "robotics" : ["robot programlama", "robot kinematics", "motion planning"], "plc" : ["programmable logic controller", "siemens plc", "allen bradley", "ladder logic", "structured text", "scada"], "solidworks" : ["cad", "catia", "autocad", "fusion 360", "inventor"], "matlab" : ["matlab/simulink", "simulink", "mathworks"], # ── 18. LOJİSTİK / OPERASYON ────────────────────────────────────────────── "supply chain" : ["tedarik zinciri", "logistics", "lojistik", "supply chain management", "scm"], "vehicle routing" : ["vrp", "route optimization", "güzergah optimizasyonu"], "demand forecasting": ["talep tahmini", "demand planning", "inventory forecasting"], "warehouse management": ["wms", "depo yönetimi", "warehouse operations"], "operations research": ["yöneylem araştırması", "linear programming", "integer programming", "optimization", "or tools", "mathematical optimization"], # ── 19. PROJE YÖNETİMİ / SOFT SKILLS ───────────────────────────────────── "project management": ["proje yönetimi", "agile", "scrum", "kanban", "pmp", "prince2", "waterfall"], "team leadership" : ["takım liderliği", "people management", "tech lead", "team lead", "mentoring"], "communication" : ["presentation skills", "technical writing", "documentation", "iletişim becerileri"], "problem solving" : ["analytical thinking", "critical thinking", "problem çözme"], # ── 20. İŞ ANALİZİ & YÖNETİM ────────────────────────────────────── "business analysis" : ["iş analizi", "ba", "business analyst", "iş analisti", "requirements analysis", "gereksinim analizi"], "requirements engineering": ["gereksinim mühendisliği", "requirements gathering", "requirements management", "brd", "frd", "srs"], "process modeling" : ["süreç modelleme", "bpmn", "business process", "iş süreçleri", "workflow modeling", "process mapping"], "stakeholder management": ["paydaş yönetimi", "stakeholder analysis", "stakeholder engagement"], "user story" : ["kullanıcı hikayesi", "user stories", "acceptance criteria", "kabul kriterleri", "epic", "product backlog"], "use case" : ["kullanım senaryosu", "use case diagram", "use case modeling", "uml use case"], "business intelligence": ["bi", "iş zekası", "bi tools", "bi reporting", "business analytics"], "data visualization" : ["veri görselleştirme", "data viz", "dashboard design", "reporting", "raporlama"], "kpi" : ["key performance indicator", "performans göstergesi", "kpi tracking", "metrics", "okr"], "excel" : ["microsoft excel", "excel advanced", "pivot table", "vlookup", "excel macros", "spreadsheet"], "financial analysis" : ["finansal analiz", "financial modeling", "financial reporting", "mali analiz", "bütçe analizi"], "risk management" : ["risk yönetimi", "risk analysis", "risk assessment", "risk mitigation"], "change management" : ["değişiklik yönetimi", "organizational change", "change control"], "product management" : ["ürün yönetimi", "product owner", "po", "product strategy", "product roadmap", "ürün yol haritası"], "ux research" : ["kullanıcı araştırması", "user research", "usability testing", "user interview", "persona"], "erp consulting" : ["erp danışmanlığı", "erp implementation", "erp customization", "system integration"], "crm" : ["customer relationship management", "müşteri ilişkileri yönetimi", "crm tools", "hubspot", "zoho crm"], "digital marketing" : ["dijital pazarlama", "seo", "sem", "google analytics", "social media marketing", "content marketing"], "six sigma" : ["lean", "lean six sigma", "kaizen", "continuous improvement", "sürekli iyileştirme", "dmaic"], # ── 21. MEKANİK MÜHENDİSLİK ─────────────────────────────────────── "mechanical design" : ["mekanik tasarım", "machine design", "mechanical engineering", "mekanik mühendislik", "design engineer"], "cad" : ["autocad", "solidworks cad", "catia", "inventor", "creo", "bilgisayar destekli tasarım", "computer aided design"], "cam" : ["computer aided manufacturing", "bilgisayar destekli üretim", "cnc programming", "cam software"], "fea" : ["finite element analysis", "sonlu elemanlar analizi", "ansys", "abaqus", "nastran", "comsol", "fem"], "cfd" : ["computational fluid dynamics", "hesaplamalı akışkanlar", "fluent", "openfoam", "flow simulation"], "thermodynamics" : ["termodinamik", "heat transfer", "ısı transferi", "thermal analysis", "termal analiz"], "materials science" : ["malzeme bilimi", "material selection", "malzeme seçimi", "metallurgy", "metalürji", "composites", "kompozitler"], "manufacturing" : ["üretim", "production engineering", "üretim mühendisliği", "injection molding", "sheet metal", "casting", "döküm", "welding", "kaynak"], "quality control" : ["kalite kontrol", "qc", "inspection", "muayene", "quality assurance", "kalite güvence"], "tolerance analysis" : ["tolerans analizi", "gd&t", "geometric dimensioning", "tolerancing"], "cnc" : ["cnc machining", "cnc tezgah", "cnc torna", "cnc freze", "g-code", "lathe", "milling"], # ── 22. ELEKTRİK & ELEKTRONİK ───────────────────────────────────── "electrical engineering": ["elektrik mühendisliği", "electrical design", "elektrik tasarım", "power systems", "güç sistemleri"], "pcb design" : ["baskı devre tasarımı", "pcb layout", "altium", "eagle", "kicad", "orcad"], "power electronics" : ["güç elektroniği", "inverter", "converter", "motor drive", "motor sürücü"], "signal processing" : ["sinyal işleme", "dsp", "digital signal processing", "analog design", "analog tasarım"], "scada" : ["scada systems", "hmi", "industrial automation", "endüstriyel otomasyon", "dcs"], # ── 23. OTOMOTİV ────────────────────────────────────────────────── "automotive engineering": ["otomotiv mühendisliği", "vehicle engineering", "araç mühendisliği"], "adas" : ["advanced driver assistance", "ileri sürücü destek", "autonomous driving", "otonom sürüş", "lidar", "radar"], "vehicle dynamics" : ["araç dinamiği", "suspension", "süspansiyon", "braking systems", "fren sistemleri"], "powertrain" : ["güç aktarma", "engine design", "motor tasarım", "transmission", "şanzıman", "electric vehicle", "ev"], "iso 26262" : ["functional safety", "fonksiyonel güvenlik", "asil", "automotive safety"], # ── 24. ENDÜSTRİ MÜHENDİSLİĞİ ──────────────────────────────────── "industrial engineering": ["endüstri mühendisliği", "ie", "work study", "iş etüdü", "ergonomi", "ergonomics"], "production planning" : ["üretim planlama", "mrp", "mrp ii", "erp planning", "capacity planning", "kapasite planlama"], "inventory management" : ["stok yönetimi", "inventory control", "stok kontrol", "warehouse optimization", "depo optimizasyonu"], "process optimization" : ["süreç optimizasyonu", "operational excellence", "time study", "zaman etüdü", "line balancing", "hat dengeleme", "value stream mapping"], "iso 9001" : ["quality management system", "kalite yönetim sistemi", "qms", "iso 14001", "iso 45001", "iatf 16949"], # ── 25. ROBOTİK & OTOMASYON (genişletilmiş) ─────────────────────── "robot programming" : ["robot programlama", "teach pendant", "offline programming", "abb robot", "fanuc robot", "kuka robot", "ur robot"], "machine vision" : ["makine görüşü", "industrial vision", "endüstriyel görüş", "opencv industrial", "vision inspection"], "motion control" : ["hareket kontrol", "servo motor", "stepper motor", "motion planning", "trajectory planning"], "industrial robot" : ["endüstriyel robot", "cobot", "collaborative robot", "pick and place", "palletizing", "robot cell"], "automation design" : ["otomasyon tasarım", "factory automation", "fabrika otomasyonu", "industry 4.0", "endüstri 4.0"], # ── 26. İNŞAAT & YAPI ───────────────────────────────────────────── "structural engineering": ["yapı mühendisliği", "structural analysis", "yapı analizi", "steel structure", "çelik yapı", "reinforced concrete", "betonarme"], "bim" : ["building information modeling", "yapı bilgi modellemesi", "revit", "navisworks", "archicad"], "hvac" : ["heating ventilation", "iklimlendirme", "air conditioning", "soğutma", "ısıtma"], "geotechnical" : ["geoteknik", "soil mechanics", "zemin mekaniği", "foundation design", "temel tasarımı"], } # ── Ters index: eş anlamlı → canonical form ───────────────────────────────── SYNONYM_TO_CANONICAL: Dict[str, str] = {} for canonical, synonyms in SKILL_ONTOLOGY.items(): SYNONYM_TO_CANONICAL[canonical] = canonical for syn in synonyms: SYNONYM_TO_CANONICAL[syn.lower()] = canonical # ── Kategoriler: canonical skill → kategori ────────────────────────────────── SKILL_CATEGORIES: Dict[str, str] = { # Programlama Dilleri "python": "Prog. Dili", "java": "Prog. Dili", "c#": "Prog. Dili", "c++": "Prog. Dili", "c": "Prog. Dili", "javascript": "Prog. Dili", "typescript": "Prog. Dili", "kotlin": "Prog. Dili", "swift": "Prog. Dili", "go": "Prog. Dili", "rust": "Prog. Dili", "php": "Prog. Dili", "ruby": "Prog. Dili", "scala": "Prog. Dili", "r": "Prog. Dili", "matlab": "Prog. Dili", "dart": "Prog. Dili", "perl": "Prog. Dili", "bash": "Prog. Dili", "powershell": "Prog. Dili", "groovy": "Prog. Dili", "lua": "Prog. Dili", "haskell": "Prog. Dili", "elixir": "Prog. Dili", "clojure": "Prog. Dili", "assembly": "Prog. Dili", "vba": "Prog. Dili", "visual basic": "Prog. Dili", "cobol": "Prog. Dili", "fortran": "Prog. Dili", # Web Frontend "react": "Web Frontend", "angular": "Web Frontend", "vue": "Web Frontend", "svelte": "Web Frontend", "html": "Web Frontend", "css": "Web Frontend", "tailwind": "Web Frontend", "bootstrap": "Web Frontend", "jquery": "Web Frontend", "webpack": "Web Frontend", "graphql": "Web Frontend", # Web Backend "node.js": "Backend", "django": "Backend", "flask": "Backend", "fastapi": "Backend", "spring boot": "Backend", "asp.net": "Backend", "laravel": "Backend", "nestjs": "Backend", "rest api": "Backend", "microservices": "Backend", "grpc": "Backend", "websocket": "Backend", "oauth": "Backend", # Mobil "android": "Mobil", "ios": "Mobil", "flutter": "Mobil", "react native": "Mobil", "xamarin": "Mobil", "ionic": "Mobil", # Veritabanı "sql": "Veritabanı", "mysql": "Veritabanı", "postgresql": "Veritabanı", "mssql": "Veritabanı", "oracle": "Veritabanı", "sqlite": "Veritabanı", "mariadb": "Veritabanı", "db2": "Veritabanı", "mongodb": "Veritabanı", "redis": "Veritabanı", "cassandra": "Veritabanı", "elasticsearch": "Veritabanı", "dynamodb": "Veritabanı", "neo4j": "Veritabanı", "couchdb": "Veritabanı", "firebase": "Veritabanı", "influxdb": "Veritabanı", # Bulut "aws": "Bulut", "azure": "Bulut", "google cloud": "Bulut", "alibaba cloud": "Bulut", "oracle cloud": "Bulut", # DevOps "docker": "DevOps", "kubernetes": "DevOps", "git": "DevOps", "jenkins": "DevOps", "github actions": "DevOps", "gitlab ci": "DevOps", "terraform": "DevOps", "ansible": "DevOps", "prometheus": "DevOps", "grafana": "DevOps", "nginx": "DevOps", "apache": "DevOps", "linux": "DevOps", "vagrant": "DevOps", "puppet": "DevOps", "chef": "DevOps", # Veri Mühendisliği "apache spark": "Veri Mühendisliği", "hadoop": "Veri Mühendisliği", "kafka": "Veri Mühendisliği", "airflow": "Veri Mühendisliği", "dbt": "Veri Mühendisliği", "etl": "Veri Mühendisliği", "data warehouse": "Veri Mühendisliği", "data analysis": "Veri Mühendisliği", "pandas": "Veri Mühendisliği", "numpy": "Veri Mühendisliği", "tableau": "Veri Mühendisliği", "power bi": "Veri Mühendisliği", "qlik": "Veri Mühendisliği", "looker": "Veri Mühendisliği", # AI/ML "machine learning": "Yapay Zeka", "deep learning": "Yapay Zeka", "natural language processing": "Yapay Zeka", "computer vision": "Yapay Zeka", "reinforcement learning": "Yapay Zeka", "large language model": "Yapay Zeka", "tensorflow": "Yapay Zeka", "pytorch": "Yapay Zeka", "scikit-learn": "Yapay Zeka", "hugging face": "Yapay Zeka", "langchain": "Yapay Zeka", "mlflow": "Yapay Zeka", "feature engineering": "Yapay Zeka", "xgboost": "Yapay Zeka", "time series": "Yapay Zeka", # Siber Güvenlik "cybersecurity": "Siber Güvenlik", "penetration testing": "Siber Güvenlik", "soc": "Siber Güvenlik", "network security": "Siber Güvenlik", "cryptography": "Siber Güvenlik", "devsecops": "Siber Güvenlik", "identity management": "Siber Güvenlik", # Yazılım Mimarisi "software architecture": "Yazılım Mimarisi", "microservices": "Yazılım Mimarisi", "unit testing": "Yazılım Mimarisi", "code review": "Yazılım Mimarisi", "agile": "Yazılım Mimarisi", "uml": "Yazılım Mimarisi", "clean code": "Yazılım Mimarisi", # ERP/CRM "sap": "ERP/CRM", "oracle erp": "ERP/CRM", "microsoft dynamics": "ERP/CRM", "salesforce": "ERP/CRM", "servicenow": "ERP/CRM", "jira": "ERP/CRM", # Testing "selenium": "Testing/QA", "cypress": "Testing/QA", "playwright": "Testing/QA", "appium": "Testing/QA", "postman": "Testing/QA", "load testing": "Testing/QA", "qa": "Testing/QA", # Altyapı "networking": "Sistem/Altyapı", "windows server": "Sistem/Altyapı", "virtualization": "Sistem/Altyapı", "storage": "Sistem/Altyapı", # IoT / Gömülü "iot": "IoT/Gömülü", "embedded systems": "IoT/Gömülü", "arduino": "IoT/Gömülü", "raspberry pi": "IoT/Gömülü", "fpga": "IoT/Gömülü", # Robotik "ros": "Robotik", "control systems": "Robotik", "robotics": "Robotik", "plc": "Robotik", "solidworks": "Robotik", # Lojistik "supply chain": "Lojistik", "vehicle routing": "Lojistik", "demand forecasting": "Lojistik", "warehouse management": "Lojistik", "operations research": "Lojistik", # Soft Skills "project management": "Soft Skill", "team leadership": "Soft Skill", "communication": "Soft Skill", "problem solving": "Soft Skill", # İş Analizi & Yönetim "business analysis": "İş Analizi", "requirements engineering": "İş Analizi", "process modeling": "İş Analizi", "stakeholder management": "İş Analizi", "user story": "İş Analizi", "use case": "İş Analizi", "business intelligence": "İş Analizi", "data visualization": "İş Analizi", "kpi": "İş Analizi", "excel": "İş Analizi", "financial analysis": "İş Analizi", "risk management": "İş Analizi", "change management": "İş Analizi", "product management": "İş Analizi", "ux research": "İş Analizi", "erp consulting": "İş Analizi", "crm": "İş Analizi", "digital marketing": "İş Analizi", "six sigma": "İş Analizi", # Mekanik Mühendislik "mechanical design": "Mekanik Müh.", "cad": "Mekanik Müh.", "cam": "Mekanik Müh.", "fea": "Mekanik Müh.", "cfd": "Mekanik Müh.", "thermodynamics": "Mekanik Müh.", "materials science": "Mekanik Müh.", "manufacturing": "Mekanik Müh.", "quality control": "Mekanik Müh.", "tolerance analysis": "Mekanik Müh.", "cnc": "Mekanik Müh.", # Elektrik & Elektronik "electrical engineering": "Elektrik/Elektronik", "pcb design": "Elektrik/Elektronik", "power electronics": "Elektrik/Elektronik", "signal processing": "Elektrik/Elektronik", "scada": "Elektrik/Elektronik", # Otomotiv "automotive engineering": "Otomotiv", "adas": "Otomotiv", "vehicle dynamics": "Otomotiv", "powertrain": "Otomotiv", "iso 26262": "Otomotiv", # Endüstri Mühendisliği "industrial engineering": "Endüstri Müh.", "production planning": "Endüstri Müh.", "inventory management": "Endüstri Müh.", "process optimization": "Endüstri Müh.", "iso 9001": "Endüstri Müh.", # Robotik & Otomasyon (genişletilmiş) "robot programming": "Robotik", "machine vision": "Robotik", "motion control": "Robotik", "industrial robot": "Robotik", "automation design": "Robotik", # İnşaat & Yapı "structural engineering": "İnşaat/Yapı", "bim": "İnşaat/Yapı", "hvac": "İnşaat/Yapı", "geotechnical": "İnşaat/Yapı", } # ── Özet istatistik ────────────────────────────────────────────────────────── def extract_text_from_pdf(pdf_path: str, dual_column: bool = False) -> str: """ PDF'den metin çıkarır. dual_column=True → iki sütunlu CV formatlarını düzgün ayrıştırır """ doc = fitz.open(pdf_path) full_text = "" for page in doc: if dual_column: # Dual-list CV sorununun çözümü: clip ile sol/sağ sütun ayrı okunur width = page.rect.width left_rect = fitz.Rect(0, 0, width / 2, page.rect.height) right_rect = fitz.Rect(width / 2, 0, width, page.rect.height) left_text = page.get_text("text", clip=left_rect) right_text = page.get_text("text", clip=right_rect) full_text += left_text + "\n" + right_text else: full_text += page.get_text("text") doc.close() return full_text.strip() def clean_text(text: str) -> str: """Metni normalize eder: fazla boşlukları temizler, lowercase yapar""" text = re.sub(r'\s+', ' ', text) text = text.strip() return text # ════════════════════════════════════════════════════════════════════════════ # FAZ 1 — ENTITY EXTRACTION: Konum, Üniversite, Firma, Dil # # Literatür Boşlukları: # 1. "Shallow Parsing" → Mevcut sistemler sadece skill çıkarır, yapısal # bilgileri (konum, eğitim, firma) ihmal eder. # 2. "Geographic Bias" → Hard location filter aday havuzunu daraltır. # Bu sistem soft scoring kullanır, konum uyarır ama elemez. # 3. "Single-Entity NER" → spaCy NER'i GPE+ORG+NORP ile çoklu entity # çıkarma yaparak zengin aday profili oluşturur. # 4. "Title Mismatch" → İş tanımı bazlı eşleştirme, title uyumsuzluğunu # ortadan kaldırır (iş ilanı CV ile doğrudan karşılaştırılır). # ════════════════════════════════════════════════════════════════════════════ # ── Türkiye şehir listesi (konum çıkarma için) ───────────────────────────── TR_CITIES = { "istanbul","ankara","izmir","bursa","antalya","adana","konya","gaziantep", "mersin","diyarbakır","kayseri","eskişehir","trabzon","samsun","denizli", "malatya","erzurum","van","batman","şanlıurfa","sakarya","kocaeli", "tekirdağ","manisa","balıkesir","elazığ","muğla","bolu","düzce", "çanakkale","aydın","hatay","kahramanmaraş","afyon","aksaray","sivas", "tokat","yozgat","çorum","kastamonu","rize","artvin","giresun","ordu", } # ── Üniversite anahtar kelimeleri ───────────────────────────────────────── UNIVERSITY_KEYWORDS = [ "university", "üniversitesi", "üniversite", "institute of technology", "polytechnic", "college", "école", "hochschule", "universität", "fakülte", "faculty", "mühendislik fakültesi", "engineering faculty", "yüksek lisans", "master", "bachelor", "lisans", "doktora", "phd", "mba", "msc", "bsc", "bs", "ms", ] # ── Dil anahtar kelimeleri ───────────────────────────────────────────────── LANGUAGE_KEYWORDS = { "turkish": "Türkçe", "türkçe": "Türkçe", "english": "İngilizce", "ingilizce": "İngilizce", "german": "Almanca", "almanca": "Almanca", "deutsch": "Almanca", "french": "Fransızca", "fransızca": "Fransızca", "français": "Fransızca", "spanish": "İspanyolca", "ispanyolca": "İspanyolca", "español": "İspanyolca", "arabic": "Arapça", "arapça": "Arapça", "russian": "Rusça", "rusça": "Rusça", "chinese": "Çince", "çince": "Çince", "mandarin": "Çince", "japanese": "Japonca", "japonca": "Japonca", "korean": "Korece", "korece": "Korece", "italian": "İtalyanca", "italyanca": "İtalyanca", "portuguese": "Portekizce", "portekizce": "Portekizce", "dutch": "Hollandaca", "hollandaca": "Hollandaca", } def extract_location(text: str) -> Dict: """ CV veya iş ilanından konum bilgisi çıkarır. Yöntem: spaCy NER (GPE) + Türkiye şehir listesi + pattern matching. Literatür katkısı: Çoğu sistem binary konum filtresi kullanır. Bu fonksiyon soft bilgi çıkarır, eşleştirme aşamasında ağırlıklandırılır. Returns: {"city": str|None, "country": str|None, "remote": bool, "raw_locations": list} """ nlp_model = get_nlp() # spaCy modeli doc = nlp_model(text[:5000]) # İlk 5000 karakter yeterli raw_locations = [] for ent in doc.ents: if ent.label_ == "GPE": raw_locations.append(ent.text.strip()) # Türkiye şehir eşleştirmesi text_lower = text.lower() city = None country = None for c in TR_CITIES: if re.search(r'\b' + re.escape(c) + r'\b', text_lower): city = c.title() country = "Türkiye" break # NER'den çıkan konumları da değerlendir if not city and raw_locations: city = raw_locations[0] # Ülke tespiti (yaygın ülke adları) country_map = { "turkey": "Türkiye", "türkiye": "Türkiye", "germany": "Almanya", "united states": "ABD", "usa": "ABD", "uk": "İngiltere", "united kingdom": "İngiltere", "netherlands": "Hollanda", "france": "Fransa", "canada": "Kanada", "australia": "Avustralya", "uae": "BAE", "dubai": "BAE", "singapore": "Singapur", } if not country: for key, val in country_map.items(): if re.search(r'\b' + re.escape(key) + r'\b', text_lower): country = val break # Remote/uzaktan çalışma tespiti remote = bool(re.search( r'\b(remote|uzaktan|home.?office|hybrid|hibrit|work from home|evden çalışma)\b', text_lower )) return { "city": city, "country": country, "remote": remote, "raw_locations": list(set(raw_locations)), } # ── Türkiye Üniversiteleri (dropdown + matching için) ────────────────────── TR_UNIVERSITIES = [ "Abdullah Gül Üniversitesi", "Acıbadem Üniversitesi", "Adana Alparslan Türkeş Bilim ve Teknoloji Üniversitesi", "Adıyaman Üniversitesi", "Afyon Kocatepe Üniversitesi", "Afyonkarahisar Sağlık Bilimleri Üniversitesi", "Ağrı İbrahim Çeçen Üniversitesi", "Akdeniz Üniversitesi", "Aksaray Üniversitesi", "Alanya Alaaddin Keykubat Üniversitesi", "Alanya Üniversitesi", "Altınbaş Üniversitesi", "Amasya Üniversitesi", "Anadolu Üniversitesi", "Anka Teknoloji Üniversitesi", "Ankara Bilim Üniversitesi", "Ankara Hacı Bayram Veli Üniversitesi", "Ankara Medipol Üniversitesi", "Ankara Müzik ve Güzel Sanatlar Üniversitesi", "Ankara Sosyal Bilimler Üniversitesi", "Ankara Üniversitesi", "Antalya Belek Üniversitesi", "Antalya Bilim Üniversitesi", "Ardahan Üniversitesi", "Artvin Çoruh Üniversitesi", "Atatürk Üniversitesi", "Atılım Üniversitesi", "Avrasya Üniversitesi", "Aydın Adnan Menderes Üniversitesi", "Bahçeşehir Üniversitesi", "Balıkesir Üniversitesi", "Bandırma Onyedi Eylül Üniversitesi", "Bartın Üniversitesi", "Başkent Üniversitesi", "Batman Üniversitesi", "Bayburt Üniversitesi", "Beykent Üniversitesi", "Beykoz Üniversitesi", "Bezmiâlem Vakıf Üniversitesi", "Bilecik Şeyh Edebali Üniversitesi", "Bingöl Üniversitesi", "Biruni Üniversitesi", "Bitlis Eren Üniversitesi", "Bizim Tepe Üniversitesi", "Boğaziçi Üniversitesi", "Bolu Abant İzzet Baysal Üniversitesi", "Burdur Mehmet Akif Ersoy Üniversitesi", "Bursa Teknik Üniversitesi", "Bursa Uludağ Üniversitesi", "Çağ Üniversitesi", "Çanakkale Onsekiz Mart Üniversitesi", "Çankaya Üniversitesi", "Çankırı Karatekin Üniversitesi", "Çukurova Üniversitesi", "Demiroğlu Bilim Üniversitesi", "Dicle Üniversitesi", "Doğuş Üniversitesi", "Dokuz Eylül Üniversitesi", "Dumlupınar Üniversitesi", "Düzce Üniversitesi", "Ege Üniversitesi", "Erciyes Üniversitesi", "Erzincan Binali Yıldırım Üniversitesi", "Erzurum Teknik Üniversitesi", "Eskişehir Osmangazi Üniversitesi", "Eskişehir Teknik Üniversitesi", "Fatih Sultan Mehmet Vakıf Üniversitesi", "Fenerbahçe Üniversitesi", "Fırat Üniversitesi", "Galatasaray Üniversitesi", "Gazi Üniversitesi", "Gaziantep İslam Bilim ve Teknoloji Üniversitesi", "Gaziantep Üniversitesi", "Gebze Teknik Üniversitesi", "Giresun Üniversitesi", "Gümüşhane Üniversitesi", "Hacettepe Üniversitesi", "Hakkari Üniversitesi", "Haliç Üniversitesi", "Harran Üniversitesi", "Hasan Kalyoncu Üniversitesi", "Hitit Üniversitesi", "Iğdır Üniversitesi", "Işık Üniversitesi", "İbn Haldun Üniversitesi", "İhsan Doğramacı Bilkent Üniversitesi", "İnönü Üniversitesi", "İskenderun Teknik Üniversitesi", "İstanbul Atlas Üniversitesi", "İstanbul Arel Üniversitesi", "İstanbul Aydın Üniversitesi", "İstanbul Bilgi Üniversitesi", "İstanbul Esenyurt Üniversitesi", "İstanbul Galata Üniversitesi", "İstanbul Gedik Üniversitesi", "İstanbul Gelişim Üniversitesi", "İstanbul Kent Üniversitesi", "İstanbul Kültür Üniversitesi", "İstanbul Medeniyet Üniversitesi", "İstanbul Medipol Üniversitesi", "İstanbul Okan Üniversitesi", "İstanbul Rumeli Üniversitesi", "İstanbul Sabahattin Zaim Üniversitesi", "İstanbul Sağlık ve Teknoloji Üniversitesi", "İstanbul Teknik Üniversitesi", "İstanbul Ticaret Üniversitesi", "İstanbul Üniversitesi", "İstanbul Üniversitesi-Cerrahpaşa", "İstanbul Yeni Yüzyıl Üniversitesi", "İstanbul 29 Mayıs Üniversitesi", "İstinye Üniversitesi", "İzmir Bakırçay Üniversitesi", "İzmir Demokrasi Üniversitesi", "İzmir Ekonomi Üniversitesi", "İzmir Katip Çelebi Üniversitesi", "İzmir Tınaztepe Üniversitesi", "İzmir Yüksek Teknoloji Enstitüsü", "Kadir Has Üniversitesi", "Kafkas Üniversitesi", "Kahramanmaraş Sütçü İmam Üniversitesi", "Kahramanmaraş İstiklal Üniversitesi", "Karabük Üniversitesi", "Karadeniz Teknik Üniversitesi", "Karamanoğlu Mehmetbey Üniversitesi", "Kastamonu Üniversitesi", "Kayseri Üniversitesi", "Kırıkkale Üniversitesi", "Kırklareli Üniversitesi", "Kırşehir Ahi Evran Üniversitesi", "Kilis 7 Aralık Üniversitesi", "Kocaeli Sağlık ve Teknoloji Üniversitesi", "Kocaeli Üniversitesi", "Koç Üniversitesi", "Konya Gıda ve Tarım Üniversitesi", "Konya Teknik Üniversitesi", "KTO Karatay Üniversitesi", "Kütahya Dumlupınar Üniversitesi", "Kütahya Sağlık Bilimleri Üniversitesi", "Lokman Hekim Üniversitesi", "Malatya Turgut Özal Üniversitesi", "Maltepe Üniversitesi", "Manisa Celal Bayar Üniversitesi", "Mardin Artuklu Üniversitesi", "Marmara Üniversitesi", "MEF Üniversitesi", "Mersin Üniversitesi", "Mimar Sinan Güzel Sanatlar Üniversitesi", "Mudanya Üniversitesi", "Muğla Sıtkı Koçman Üniversitesi", "Munzur Üniversitesi", "Muş Alparslan Üniversitesi", "Necmettin Erbakan Üniversitesi", "Nevşehir Hacı Bektaş Veli Üniversitesi", "Niğde Ömer Halisdemir Üniversitesi", "Nişantaşı Üniversitesi", "Nuh Naci Yazgan Üniversitesi", "Ondokuz Mayıs Üniversitesi", "Ordu Üniversitesi", "Orta Doğu Teknik Üniversitesi", "Osmaniye Korkut Ata Üniversitesi", "Ostim Teknik Üniversitesi", "Özyeğin Üniversitesi", "Pamukkale Üniversitesi", "Piri Reis Üniversitesi", "Recep Tayyip Erdoğan Üniversitesi", "Sabancı Üniversitesi", "Sağlık Bilimleri Üniversitesi", "Sakarya Uygulamalı Bilimler Üniversitesi", "Sakarya Üniversitesi", "Samsun Üniversitesi", "Sanko Üniversitesi", "Selçuk Üniversitesi", "Siirt Üniversitesi", "Sinop Üniversitesi", "Sivas Bilim ve Teknoloji Üniversitesi", "Sivas Cumhuriyet Üniversitesi", "Süleyman Demirel Üniversitesi", "Şırnak Üniversitesi", "Tarsus Üniversitesi", "TED Üniversitesi", "Tekirdağ Namık Kemal Üniversitesi", "TOBB Ekonomi ve Teknoloji Üniversitesi", "Tokat Gaziosmanpaşa Üniversitesi", "Toros Üniversitesi", "Trabzon Üniversitesi", "Trakya Üniversitesi", "Türk-Alman Üniversitesi", "Türk Hava Kurumu Üniversitesi", "Türkiye Uluslararası İslam, Bilim ve Teknoloji Üniversitesi", "Ufuk Üniversitesi", "Uşak Üniversitesi", "Üsküdar Üniversitesi", "Yalova Üniversitesi", "Yaşar Üniversitesi", "Yeditepe Üniversitesi", "Yıldız Teknik Üniversitesi", "Yozgat Bozok Üniversitesi", "Yüksek İhtisas Üniversitesi", "Zonguldak Bülent Ecevit Üniversitesi" ] # İngilizce karşılıklar, kısaltmalar ve karakter normalizasyonu TR_UNI_EN_MAP = { # Popüler Kısaltmalar ve İngilizce Karşılıklar "metu": "Orta Doğu Teknik Üniversitesi", "odtü": "Orta Doğu Teknik Üniversitesi", "odtu": "Orta Doğu Teknik Üniversitesi", "middle east technical university": "Orta Doğu Teknik Üniversitesi", "itu": "İstanbul Teknik Üniversitesi", "itü": "İstanbul Teknik Üniversitesi", "istanbul technical university": "İstanbul Teknik Üniversitesi", "ytu": "Yıldız Teknik Üniversitesi", "ytü": "Yıldız Teknik Üniversitesi", "yildiz technical university": "Yıldız Teknik Üniversitesi", "boun": "Boğaziçi Üniversitesi", "bogazici university": "Boğaziçi Üniversitesi", "boğaziçi university": "Boğaziçi Üniversitesi", "hacettepe university": "Hacettepe Üniversitesi", "bilkent": "İhsan Doğramacı Bilkent Üniversitesi", "bilkent university": "İhsan Doğramacı Bilkent Üniversitesi", "sabanci university": "Sabancı Üniversitesi", "koc university": "Koç Üniversitesi", "ozyegin university": "Özyeğin Üniversitesi", "marmara university": "Marmara Üniversitesi", "istanbul university": "İstanbul Üniversitesi", "ankara university": "Ankara Üniversitesi", "ege university": "Ege Üniversitesi", "dokuz eylul university": "Dokuz Eylül Üniversitesi", "gazi university": "Gazi Üniversitesi", "gebze technical university": "Gebze Teknik Üniversitesi", "gtu": "Gebze Teknik Üniversitesi", "iyte": "İzmir Yüksek Teknoloji Enstitüsü", "izmir institute of technology": "İzmir Yüksek Teknoloji Enstitüsü", # Liste Geneli (İngilizce Karakter ve 'University' Varyasyonları) "abdullah gul university": "Abdullah Gül Üniversitesi", "agu": "Abdullah Gül Üniversitesi", "acibadem university": "Acıbadem Üniversitesi", "adana alparslan turkes science and technology university": "Adana Alparslan Türkeş Bilim ve Teknoloji Üniversitesi", "adiyaman university": "Adıyaman Üniversitesi", "afyon kocatepe university": "Afyon Kocatepe Üniversitesi", "agri ibrahim cecen university": "Ağrı İbrahim Çeçen Üniversitesi", "akdeniz university": "Akdeniz Üniversitesi", "aksaray university": "Aksaray Üniversitesi", "alanya alaaddin keykubat university": "Alanya Alaaddin Keykubat Üniversitesi", "altinbas university": "Altınbaş Üniversitesi", "amasya university": "Amasya Üniversitesi", "anadolu university": "Anadolu Üniversitesi", "atilim university": "Atılım Üniversitesi", "bahcesehir university": "Bahçeşehir Üniversitesi", "bau": "Bahçeşehir Üniversitesi", "balikesir university": "Balıkesir Üniversitesi", "baskent university": "Başkent Üniversitesi", "beykent university": "Beykent Üniversitesi", "bezmialem vakif university": "Bezmiâlem Vakıf Üniversitesi", "bilecik seyh edebali university": "Bilecik Şeyh Edebali Üniversitesi", "bingol university": "Bingöl Üniversitesi", "biruni university": "Biruni Üniversitesi", "bolu abant izzet baysal university": "Bolu Abant İzzet Baysal Üniversitesi", "bursa uludag university": "Bursa Uludağ Üniversitesi", "canakkale onsekiz mart university": "Çanakkale Onsekiz Mart Üniversitesi", "comu": "Çanakkale Onsekiz Mart Üniversitesi", "cankaya university": "Çankaya Üniversitesi", "cukurova university": "Çukurova Üniversitesi", "dicle university": "Dicle Üniversitesi", "dogus university": "Doğuş Üniversitesi", "dumlupinar university": "Kütahya Dumlupınar Üniversitesi", "duzce university": "Düzce Üniversitesi", "erciyes university": "Erciyes Üniversitesi", "erzincan binali yildirim university": "Erzincan Binali Yıldırım Üniversitesi", "eskisehir osmangazi university": "Eskişehir Osmangazi Üniversitesi", "ogü": "Eskişehir Osmangazi Üniversitesi", "eskisehir technical university": "Eskişehir Teknik Üniversitesi", "estü": "Eskişehir Teknik Üniversitesi", "fatih sultan mehmet vakif university": "Fatih Sultan Mehmet Vakıf Üniversitesi", "fırat university": "Fırat Üniversitesi", "galatasaray university": "Galatasaray Üniversitesi", "gaziantep university": "Gaziantep Üniversitesi", "haliç university": "Haliç Üniversitesi", "inonu university": "İnönü Üniversitesi", "iskenderun technical university": "İskenderun Teknik Üniversitesi", "iste": "İskenderun Teknik Üniversitesi", "istanbul aydın university": "İstanbul Aydın Üniversitesi", "istanbul bilgi university": "İstanbul Bilgi Üniversitesi", "istanbul gelisim university": "İstanbul Gelişim Üniversitesi", "istanbul medipol university": "İstanbul Medipol Üniversitesi", "istinye university": "İstinye Üniversitesi", "izmir economy university": "İzmir Ekonomi Üniversitesi", "kadir has university": "Kadir Has Üniversitesi", "karabuk university": "Karabük Üniversitesi", "karadeniz technical university": "Karadeniz Teknik Üniversitesi", "ktu": "Karadeniz Teknik Üniversitesi", "kastamonu university": "Kastamonu Üniversitesi", "kirikkale university": "Kırıkkale Üniversitesi", "kocaeli university": "Kocaeli Üniversitesi", "maltepe university": "Maltepe Üniversitesi", "manisa celal bayar university": "Manisa Celal Bayar Üniversitesi", "mef university": "MEF Üniversitesi", "mersin university": "Mersin Üniversitesi", "mugla sitki kocman university": "Muğla Sıtkı Koçman Üniversitesi", "namik kemal university": "Tekirdağ Namık Kemal Üniversitesi", "nisantasi university": "Nişantaşı Üniversitesi", "ondokuz mayis university": "Ondokuz Mayıs Üniversitesi", "omu": "Ondokuz Mayıs Üniversitesi", "ordu university": "Ordu Üniversitesi", "pamukkale university": "Pamukkale Üniversitesi", "paü": "Pamukkale Üniversitesi", "recep tayyip erdogan university": "Recep Tayyip Erdoğan Üniversitesi", "sakarya university": "Sakarya Üniversitesi", "sau": "Sakarya Üniversitesi", "selcuk university": "Selçuk Üniversitesi", "suleyman demirel university": "Süleyman Demirel Üniversitesi", "sdü": "Süleyman Demirel Üniversitesi", "tobb etu": "TOBB Ekonomi ve Teknoloji Üniversitesi", "tobb university of economics and technology": "TOBB Ekonomi ve Teknoloji Üniversitesi", "trakya university": "Trakya Üniversitesi", "turkish-german university": "Türk-Alman Üniversitesi", "tau": "Türk-Alman Üniversitesi", "uskudar university": "Üsküdar Üniversitesi", "yasar university": "Yaşar Üniversitesi", "yeditepe university": "Yeditepe Üniversitesi", "zonguldak bulent ecevit university": "Zonguldak Bülent Ecevit Üniversitesi" } def extract_universities(text: str) -> List[str]: """ CV'den üniversite adlarını çıkarır. 3 katmanlı yaklaşım: 1. TR üniversite listesinden doğrudan eşleştirme (EN + TR) 2. "University/Üniversite" keyword'ü içeren satırları yakala 3. Eğitim bölümü context'inde arama Returns: Bulunan üniversite adlarının listesi. """ text_lower = text.lower() found = [] # Katman 1: TR üniversite listesinden doğrudan eşleştirme for en_name, tr_name in TR_UNI_EN_MAP.items(): if en_name in text_lower: found.append(tr_name) # Katman 2: Genel "university/üniversite" pattern'i (uluslararası üniversiteler için) uni_patterns = [ r"(?:university of [\w\s]+|[\w\s]+ university)", r"(?:[\w\s]+ üniversitesi)", r"(?:institute of technology|polytechnic|école|hochschule)", ] for pattern in uni_patterns: matches = re.findall(pattern, text_lower) for m in matches: clean = m.strip().title() if len(clean) > 5 and clean not in found: # TR listesinde zaten varsa tekrar ekleme if not any(clean.lower() in f.lower() for f in found): found.append(clean) # Katman 3: Derece bilgisi ile satır bazlı arama (Bachelor, Master, PhD) degree_keywords = ["bachelor", "master", "phd", "lisans", "yüksek lisans", "doktora", "mba"] for line in text.split("\n"): line_clean = line.strip() line_lower = line_clean.lower() if any(dk in line_lower for dk in degree_keywords): # Bu satırda veya bir sonraki satırda üniversite adı olabilir for kw in ["university", "üniversite", "institute", "college"]: if kw in line_lower and len(line_clean) > 10: clean = re.sub(r"\s+", " ", line_clean) if len(clean) < 120 and not any(clean.lower()[:30] in f.lower() for f in found): found.append(clean[:80]) break # Tekrarları kaldır seen = set() unique = [] for f in found: key = f.lower()[:30] if key not in seen: seen.add(key) unique.append(f) return unique[:5] def extract_companies(text: str) -> List[str]: """ CV'den firma/şirket adlarını çıkarır. İş deneyimi bölümüne odaklanan context-aware yaklaşım. Yöntem: 1. İş deneyimi bölümünü tespit et (EXPERIENCE, WORK, İŞ DENEYİMİ vb.) 2. Firma-konum pattern'i ara: "FirmaAdı | Şehir" veya "FirmaAdı, Şehir" 3. spaCy NER ORG'u sadece bu bölümde çalıştır 4. Geniş skip listesi ile tool/skill/üniversite adlarını filtrele Returns: Bulunan firma adlarının listesi. """ companies = [] # Yöntem 1: "Firma | Şehir" veya "Firma, Şehir, Country" pattern'i (en güvenilir) # Örnek: "Horoz Lojistik | Istanbul, Turkey" # Örnek: "DataWorks Consultancy Austin, TX" company_patterns = [ r"^\s*([A-ZÇĞİÖŞÜa-zçğıöşü][\w\s&.,-]+?)\s*[|·•]\s*[A-ZÇĞİÖŞÜ][\w\s,]+$", r"^\s*([A-ZÇĞİÖŞÜ][\w\s&.]+?)\s+(?:Istanbul|Ankara|İstanbul|Izmir|İzmir|Bursa|San\s+\w+|New\s+\w+|Los\s+\w+|Dallas|Austin|Seattle|Chicago|London|Berlin|Munich|Dubai)", ] lines = text.split("\n") in_experience = False for line in lines: line_clean = line.strip() line_lower = line_clean.lower() # İş deneyimi bölümünü tespit et if any(kw in line_lower for kw in ["experience", "work experience", "iş deneyimi", "deneyim"]): in_experience = True continue # Eğitim bölümüne gelince çık if any(kw in line_lower for kw in ["education", "eğitim", "certif", "sertifika", "project"]): in_experience = False continue if in_experience: for pattern in company_patterns: m = re.search(pattern, line_clean, re.MULTILINE) if m: name = m.group(1).strip() name = re.sub(r"\s+", " ", name) if 3 < len(name) < 50: companies.append(name) # Yöntem 2: spaCy NER (yedek, sadece iş deneyimi bölümü bulunamazsa) if not companies: nlp_model = get_nlp() doc = nlp_model(text[:5000]) # Geniş skip listesi — tool, skill, dil, üniversite, kişi adı parçaları skip_terms = { "python", "java", "sql", "html", "css", "react", "angular", "vue", "docker", "kubernetes", "aws", "azure", "gcp", "linux", "git", "github", "gitlab", "jira", "confluence", "slack", "trello", "tensorflow", "pytorch", "scikit-learn", "pandas", "numpy", "scipy", "power bi", "tableau", "excel", "spss", "knime", "r", "flask", "django", "fastapi", "node.js", "spring", "postgresql", "mysql", "mongodb", "redis", "kafka", "bachelor", "master", "phd", "mba", "university", "üniversite", "enstitü", "fakülte", "academy", "english", "turkish", "german", "french", "spanish", "native", "intermediate", "advanced", "fluent", "data scientist", "senior", "junior", "intern", "engineer", "manager", "analyst", "developer", "designer", "consultant", "stored procedures", "query optimization", "tidyverse", "beautifulsoup", "selenium", "opencv", "tesseract", "datacamp", "coursera", "udemy", "qwiklabs", "california", "texas", "new york", } for ent in doc.ents: if ent.label_ == "ORG": name = ent.text.strip() name_lower = name.lower() # Filtreler if len(name) <= 2: continue if name_lower in skip_terms: continue if any(skip in name_lower for skip in [ "university", "üniversite", "fakülte", "college", "institute", "academy", "school", "certification", "certificate", ]): continue # Tek kelime ve çok kısa = muhtemelen tool adı if len(name.split()) == 1 and len(name) < 6: continue companies.append(name) # Tekrarları kaldır seen = set() unique = [] for c in companies: key = c.lower().strip() if key not in seen and len(key) > 2: seen.add(key) unique.append(c) return unique[:10] def extract_languages(text: str) -> List[str]: """ CV'den bilinen dilleri çıkarır. Returns: Normalize edilmiş dil adları listesi. """ text_lower = text.lower() found = set() for keyword, lang_name in LANGUAGE_KEYWORDS.items(): if re.search(r'\b' + re.escape(keyword) + r'\b', text_lower): found.add(lang_name) return sorted(found) def location_score(cv_loc: Dict, job_loc: Dict) -> float: """ Konum uyumu skoru (0-1). Skorlama mantığı (HR feedback'e göre): - Aynı şehir → 1.0 - Aynı ülke, farklı şehir → 0.7 - CV veya iş ilanı remote → 0.8 - Farklı ülke → 0.4 - Konum bilgisi bulunamadı → 0.5 (nötr, cezalandırma yok) Kritik tasarım kararı: HR: "illaki konum uzakta diye CV'si uyuyorsa elenmez" Bu yüzden minimum skor 0.4 — hiçbir zaman 0 olmaz. """ cv_city = (cv_loc.get("city") or "").lower() cv_country = (cv_loc.get("country") or "").lower() job_city = (job_loc.get("city") or "").lower() job_country= (job_loc.get("country") or "").lower() # Bilgi yoksa nötr skor if not cv_city and not cv_country: return 0.5 if not job_city and not job_country: return 0.5 # Remote = yüksek skor if cv_loc.get("remote") or job_loc.get("remote"): return 0.8 # Aynı şehir if cv_city and job_city and cv_city == job_city: return 1.0 # Aynı ülke if cv_country and job_country and cv_country == job_country: return 0.7 # Farklı ülke — düşük ama SIFIR DEĞİL return 0.4 def keyword_search(text: str, keywords: str) -> Dict[str, bool]: """ CV metninde belirli anahtar kelimeleri arar. HR: "bazı diller - kelimeler aranabilmeli" Args: text: CV metni keywords: Virgülle ayrılmış anahtar kelimeler Returns: {keyword: bulundu_mu} """ if not keywords.strip(): return {} text_lower = text.lower() results = {} for kw in keywords.split(","): kw = kw.strip() if kw: results[kw] = bool(re.search(r'\b' + re.escape(kw.lower()) + r'\b', text_lower)) return results def extract_skills(text: str) -> Dict[str, List[str]]: """ Ontoloji tabanlı skill extraction. Hem canonical hem eş anlamlıları tanır. Returns: { 'canonical_skills': [...], # normalize edilmiş beceriler 'raw_matches': [...], # ham eşleşmeler 'by_category': {...} # kategori → beceriler } """ text_lower = text.lower() canonical_found = set() raw_matches = [] for term, canonical in SYNONYM_TO_CANONICAL.items(): # Kelime sınırlarına dikkat ederek eşleştir pattern = r'\b' + re.escape(term) + r'\b' if re.search(pattern, text_lower): canonical_found.add(canonical) raw_matches.append(term) # Kategorilere göre grupla by_category: Dict[str, List[str]] = defaultdict(list) for skill in canonical_found: cat = SKILL_CATEGORIES.get(skill, "Diğer") by_category[cat].append(skill) return { "canonical_skills": sorted(canonical_found), "raw_matches" : sorted(set(raw_matches)), "by_category" : dict(by_category) } def skill_overlap_score(cv_skills: List[str], job_skills: List[str]) -> float: """ Jaccard Similarity ile skill örtüşme skoru (0-1) """ if not job_skills: return 0.0 cv_set = set(cv_skills) job_set = set(job_skills) intersection = cv_set & job_set union = cv_set | job_set return len(intersection) / len(union) if union else 0.0 def get_embedding(text: str) -> np.ndarray: """Metni vektöre dönüştürür""" return get_embed_model().encode(text, show_progress_bar=False) def compute_semantic_similarity(text1: str, text2: str) -> float: """İki metin arasındaki cosine similarity (0-1)""" emb1 = get_embedding(text1) emb2 = get_embedding(text2) score = cosine_similarity([emb1], [emb2])[0][0] return float(score) # ════════════════════════════════════════════════════════════════════════════ # HİBRİT SKORLAMA — 4 BİLEŞENLİ (Literatür Katkısı: Multi-Signal Scoring) # # Mevcut sistemler genellikle 1-2 sinyal (keyword match + TF-IDF) kullanır. # Bu sistem 4 bileşenli ağırlıklı hibrit skor ile daha robust eşleştirme sağlar. # # Önemli: İş tanımı bazlı eşleştirme yapılır, title değil. # HR: "kişinin CV'si iş tanımıyla uyumlu olabilir ama title uymayabilir" # Bu yüzden sistem iş tanımını (description) temel alır, title opsiyoneldir. # ════════════════════════════════════════════════════════════════════════════ SCORE_WEIGHTS = { "semantic" : 0.50, # Semantik anlam benzerliği (Sentence-BERT) "skill_overlap": 0.30, # Skill örtüşmesi (Jaccard) "experience" : 0.10, # Deneyim yılı bonusu "location" : 0.10, # Konum uyumu (soft signal) } assert abs(sum(SCORE_WEIGHTS.values()) - 1.0) < 1e-6, "Ağırlıklar toplamı 1 olmalı!" def extract_experience_years(text: str) -> float: """ Metinden deneyim yılını çıkarır (basit regex). Örn: '5 years of experience' → 5.0 """ patterns = [ r'(\d+)\+?\s*(?:years?|yıl)\s*(?:of\s+)?(?:experience|deneyim)', r'(?:experience|deneyim)\s*:?\s*(\d+)\+?\s*(?:years?|yıl)', ] for pattern in patterns: match = re.search(pattern, text.lower()) if match: return float(match.group(1)) return 0.0 def experience_score(cv_years: float, min_years: float = 0.0, max_years: float = 15.0) -> float: """ Deneyim skoru (0-1) — aralık bazlı. min_years-max_years aralığında tam skor, dışında oransal düşüş. Örnekler (3-5 yıl aralığı): 0 yıl → 0.0 2 yıl → 0.67 3 yıl → 1.0 (aralık içi) 5 yıl → 1.0 (aralık içi) 7 yıl → 0.8 (fazla deneyim hafif düşüş — overqualified sinyali) """ if max_years <= 0 and min_years <= 0: return 1.0 # Deneyim filtresi kapalı # Aralık içinde → tam skor if min_years <= cv_years <= max_years: return 1.0 # Aralığın altında → oransal if cv_years < min_years: if min_years <= 0: return 1.0 return max(cv_years / min_years, 0.0) # Aralığın üstünde → hafif düşüş (overqualified ama cezalandırma yok) if cv_years > max_years and max_years > 0: excess = cv_years - max_years return max(1.0 - (excess * 0.05), 0.6) # Min 0.6, çok deneyimli aday elenmez return 0.5 def hybrid_score( semantic: float, skill_jac: float, exp_score: float, loc_score: float = 0.5, weights: Dict[str, float] = SCORE_WEIGHTS ) -> float: """ 4-bileşenli ağırlıklı hibrit skor. Literatür Katkısı: - Konum soft signal olarak dahil edilir, hard filter değil. - Ağırlıklar UI'dan ayarlanabilir (transparanlık). """ return ( weights.get("semantic", 0) * semantic + weights.get("skill_overlap", 0) * skill_jac + weights.get("experience", 0) * exp_score + weights.get("location", 0) * loc_score ) def explain_match( cv_skills : List[str], job_skills : List[str], semantic_score: float, skill_score : float, exp_score : float, loc_score : float, final_score : float, weights : Dict[str, float] = SCORE_WEIGHTS ) -> Dict: """ XAI: Skorun neden bu değer olduğunu açıklar. 4-bileşenli (semantic + skill + experience + location). """ matched_skills = sorted(set(cv_skills) & set(job_skills)) missing_skills = sorted(set(job_skills) - set(cv_skills)) extra_skills = sorted(set(cv_skills) - set(job_skills)) contributions = { "Semantik Benzerlik" : round(weights.get("semantic", 0) * semantic_score, 4), "Skill Örtüşmesi" : round(weights.get("skill_overlap", 0) * skill_score, 4), "Deneyim Bonusu" : round(weights.get("experience", 0) * exp_score, 4), "Konum Uyumu" : round(weights.get("location", 0) * loc_score, 4), } if final_score >= 0.75: verdict = "🟢 Güçlü Eşleşme — Mülakat önerilir" elif final_score >= 0.55: verdict = "🟡 Orta Eşleşme — Değerlendirmeye alınabilir" else: verdict = "🔴 Zayıf Eşleşme — Pozisyona uygun değil" return { "verdict" : verdict, "final_score" : round(final_score, 4), "contributions" : contributions, "matched_skills" : matched_skills, "missing_skills" : missing_skills, "extra_skills" : extra_skills, "skill_gap_count" : len(missing_skills), } def print_explanation(name: str, explanation: Dict): """Konsola güzel formatlı açıklama yazdırır""" print(f"\n{'='*55}") print(f" 📋 Aday: {name}") print(f" 🏆 Nihai Skor : {explanation['final_score']:.4f}") print(f" 📌 Değerlendirme: {explanation['verdict']}") print(f"\n 📊 Skor Bileşenleri:") for component, contrib in explanation['contributions'].items(): bar = '█' * int(contrib * 40) print(f" {component:<22} {contrib:.4f} {bar}") print(f"\n ✅ Eşleşen Beceriler ({len(explanation['matched_skills'])}): {', '.join(explanation['matched_skills']) or '-'}") print(f" ❌ Eksik Beceriler ({len(explanation['missing_skills'])}): {', '.join(explanation['missing_skills']) or '-'}") print(f" ➕ Ek Beceriler ({len(explanation['extra_skills'])}): {', '.join(explanation['extra_skills']) or '-'}") print(f"{'='*55}") def evaluate_candidate( cv_text : str, job_description : str, job_skills : List[str], job_location : Dict = None, min_years : float = 0.0, max_years : float = 15.0, name : str = "Bilinmeyen", weights : Dict[str, float] = SCORE_WEIGHTS, verbose : bool = True ) -> Dict: """ Tek bir CV için tam değerlendirme — zenginleştirilmiş profil. Literatür Katkısı: - Multi-entity extraction (skill + konum + üniversite + firma + dil) - İş tanımı bazlı eşleştirme (title değil, description) - Soft location scoring (eleme yok, uyarı var) """ # 1. Skill extraction cv_skill_result = extract_skills(cv_text) cv_skills = cv_skill_result["canonical_skills"] # 2. Semantic similarity (iş TANIMI bazlı — title değil) sem_score = compute_semantic_similarity(cv_text, job_description) # 3. Skill overlap (Jaccard) jac_score = skill_overlap_score(cv_skills, job_skills) # 4. Experience score cv_years = extract_experience_years(cv_text) exp_sc = experience_score(cv_years, min_years, max_years) # 5. Entity extraction — Faz 1 cv_location = extract_location(cv_text) cv_universities = extract_universities(cv_text) cv_companies = extract_companies(cv_text) cv_languages = extract_languages(cv_text) # 6. Location score (soft signal) if job_location is None: job_location = {} loc_sc = location_score(cv_location, job_location) # 7. Hybrid score (4 bileşenli) final = hybrid_score(sem_score, jac_score, exp_sc, loc_sc, weights) # 8. XAI explanation explanation = explain_match( cv_skills, job_skills, sem_score, jac_score, exp_sc, loc_sc, final, weights ) if verbose: print_explanation(name, explanation) return { "name" : name, "cv_skills" : cv_skills, "skill_by_category": cv_skill_result["by_category"], "semantic_score" : round(sem_score, 4), "skill_score" : round(jac_score, 4), "experience_years" : cv_years, "experience_score" : round(exp_sc, 4), "location_score" : round(loc_sc, 4), "final_score" : round(final, 4), "explanation" : explanation, # Yeni entity bilgileri "cv_location" : cv_location, "universities" : cv_universities, "companies" : cv_companies, "languages" : cv_languages, } import plotly.express as px from plotly.subplots import make_subplots import tempfile, shutil, os, json import pandas as pd # ──────────────────────────────────────────────────────────────────────────── # CUSTOM CSS — Precision Dark / Industrial Analytics Teması # ──────────────────────────────────────────────────────────────────────────── CUSTOM_CSS = """ @import url('https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Inter:wght@300;400;500&display=swap'); :root { --bg-primary: #060d1a; --bg-card: #0d1829; --bg-input: #111e33; --bg-hover: #162440; --border: #1e3050; --border-glow: #00d4aa40; --teal: #00d4aa; --teal-dim: #00a886; --amber: #f59e0b; --amber-dim: #d97706; --red: #ef4444; --green: #22c55e; --text-primary: #e2eaf6; --text-muted: #6b8ab0; --text-dim: #3d5878; --font-display: 'Rajdhani', sans-serif; --font-mono: 'JetBrains Mono', monospace; --font-body: 'Inter', sans-serif; --radius: 8px; --shadow: 0 4px 24px rgba(0,0,0,0.6); --glow: 0 0 20px rgba(0,212,170,0.15); } /* ── Global ── */ body, .gradio-container { background: var(--bg-primary) !important; color: var(--text-primary) !important; font-family: var(--font-body) !important; } .gradio-container { max-width: 100% !important; width: 100% !important; padding: 0 16px !important; margin: 0 auto !important; } /* ── Header Banner ── */ #hero-banner { background: linear-gradient(135deg, #0d1829 0%, #061224 50%, #0a1f10 100%); border: 1px solid var(--border); border-bottom: 2px solid var(--teal); border-radius: var(--radius); padding: 28px 36px; margin-bottom: 20px; position: relative; overflow: hidden; box-shadow: var(--shadow), var(--glow); } #hero-banner::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: repeating-linear-gradient(90deg, transparent, transparent 40px, rgba(0,212,170,0.02) 40px, rgba(0,212,170,0.02) 41px), repeating-linear-gradient(0deg, transparent, transparent 40px, rgba(0,212,170,0.02) 40px, rgba(0,212,170,0.02) 41px); pointer-events: none; } #hero-banner h1 { font-family: var(--font-display) !important; font-size: 2.4rem !important; font-weight: 700 !important; color: var(--teal) !important; letter-spacing: 2px !important; margin: 0 0 6px 0 !important; text-shadow: 0 0 30px rgba(0,212,170,0.4); } #hero-banner p { font-family: var(--font-body) !important; color: var(--text-muted) !important; font-size: 0.9rem !important; margin: 0 !important; letter-spacing: 0.5px; } .hero-badge { display: inline-block; background: rgba(0,212,170,0.1); border: 1px solid var(--teal); color: var(--teal); font-family: var(--font-mono); font-size: 0.7rem; padding: 2px 10px; border-radius: 20px; margin-right: 8px; letter-spacing: 1px; } /* ── Tabs ── */ .tab-nav { background: var(--bg-card) !important; border: 1px solid var(--border) !important; border-radius: var(--radius) !important; padding: 4px !important; margin-bottom: 16px !important; } .tab-nav button { font-family: var(--font-display) !important; font-size: 0.95rem !important; font-weight: 600 !important; letter-spacing: 1px !important; color: var(--text-muted) !important; background: transparent !important; border: none !important; border-radius: 6px !important; padding: 10px 20px !important; transition: all 0.2s !important; } .tab-nav button:hover { color: var(--teal) !important; background: rgba(0,212,170,0.08) !important; } .tab-nav button.selected { color: var(--bg-primary) !important; background: var(--teal) !important; box-shadow: 0 0 15px rgba(0,212,170,0.3) !important; } /* ── Cards / Panels ── */ .panel-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px 24px; box-shadow: var(--shadow); } .panel-title { font-family: var(--font-display); font-size: 1.1rem; font-weight: 700; letter-spacing: 1.5px; color: var(--teal); text-transform: uppercase; border-bottom: 1px solid var(--border); padding-bottom: 10px; margin-bottom: 14px; } /* ── Inputs ── */ textarea, input[type='text'] { background: var(--bg-input) !important; border: 1px solid var(--border) !important; color: var(--text-primary) !important; font-family: var(--font-body) !important; border-radius: 6px !important; transition: border-color 0.2s, box-shadow 0.2s !important; } textarea:focus, input[type='text']:focus { border-color: var(--teal) !important; box-shadow: 0 0 0 2px rgba(0,212,170,0.15) !important; outline: none !important; } label, .label-wrap span { font-family: var(--font-display) !important; font-weight: 600 !important; letter-spacing: 0.8px !important; color: var(--text-muted) !important; font-size: 0.85rem !important; } /* ── Buttons ── */ button.primary-btn, #analyze-btn { background: linear-gradient(135deg, var(--teal) 0%, var(--teal-dim) 100%) !important; color: var(--bg-primary) !important; font-family: var(--font-display) !important; font-size: 1rem !important; font-weight: 700 !important; letter-spacing: 2px !important; border: none !important; border-radius: var(--radius) !important; padding: 14px 32px !important; cursor: pointer !important; transition: all 0.2s !important; box-shadow: 0 4px 20px rgba(0,212,170,0.3) !important; width: 100% !important; } button.primary-btn:hover, #analyze-btn:hover { transform: translateY(-2px) !important; box-shadow: 0 8px 30px rgba(0,212,170,0.45) !important; } .secondary-btn { background: transparent !important; color: var(--teal) !important; border: 1px solid var(--teal) !important; font-family: var(--font-display) !important; font-weight: 600 !important; letter-spacing: 1px !important; border-radius: 6px !important; padding: 8px 20px !important; transition: all 0.2s !important; } .secondary-btn:hover { background: rgba(0,212,170,0.1) !important; } /* ── File Upload ── */ .upload-zone { background: var(--bg-input) !important; border: 2px dashed var(--border) !important; border-radius: var(--radius) !important; transition: border-color 0.2s, background 0.2s !important; } .upload-zone:hover { border-color: var(--teal) !important; background: rgba(0,212,170,0.05) !important; } .upload-zone .icon { color: var(--teal) !important; } /* ── Slider ── */ input[type='range'] { accent-color: var(--teal) !important; } /* ── Score Cards (HTML içinde) ── */ .score-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin: 12px 0; } .score-card { background: var(--bg-input); border: 1px solid var(--border); border-radius: 8px; padding: 14px 16px; text-align: center; position: relative; overflow: hidden; } .score-card::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 3px; background: linear-gradient(90deg, var(--teal), var(--amber)); } .score-card .val { font-family: var(--font-mono); font-size: 1.8rem; font-weight: 500; color: var(--teal); } .score-card .lbl { font-family: var(--font-display); font-size: 0.7rem; letter-spacing: 1px; color: var(--text-muted); text-transform: uppercase; margin-top: 4px; } .verdict-strong { background: rgba(34,197,94,0.12); border: 1px solid rgba(34,197,94,0.4); color: #22c55e; } .verdict-mid { background: rgba(245,158,11,0.12); border: 1px solid rgba(245,158,11,0.4); color: #f59e0b; } .verdict-weak { background: rgba(239,68,68,0.12); border: 1px solid rgba(239,68,68,0.4); color: #ef4444; } /* ── Skill Tags ── */ .skill-tag { display: inline-block; font-family: var(--font-mono); font-size: 0.72rem; padding: 3px 10px; border-radius: 20px; margin: 3px; } .skill-match { background: rgba(34,197,94,0.15); border: 1px solid rgba(34,197,94,0.5); color: #22c55e; } .skill-missing { background: rgba(239,68,68,0.12); border: 1px solid rgba(239,68,68,0.4); color: #ef4444; } .skill-extra { background: rgba(0,212,170,0.1); border: 1px solid rgba(0,212,170,0.35); color: #00d4aa; } /* ── Progress Bar ── */ .prog-bar-wrap { margin: 8px 0; } .prog-bar-label { font-family: var(--font-display); font-size: 0.78rem; letter-spacing: 0.5px; color: var(--text-muted); display: flex; justify-content: space-between; margin-bottom: 4px; } .prog-bar-track { background: var(--bg-input); border-radius: 4px; height: 8px; overflow: hidden; } .prog-bar-fill { height: 100%; border-radius: 4px; transition: width 0.6s ease; } /* ── Ranking Table ── */ .rank-table { width: 100%; border-collapse: collapse; font-family: var(--font-body); font-size: 0.88rem; } .rank-table thead tr { background: rgba(0,212,170,0.08); border-bottom: 2px solid var(--teal); } .rank-table th { font-family: var(--font-display); font-weight: 700; letter-spacing: 1px; color: var(--teal); padding: 12px 14px; text-align: left; font-size: 0.8rem; text-transform: uppercase; } .rank-table td { padding: 12px 14px; border-bottom: 1px solid var(--border); color: var(--text-primary); vertical-align: middle; } .rank-table tr:hover td { background: var(--bg-hover); } .rank-num { font-family: var(--font-mono); font-size: 1.1rem; color: var(--teal); font-weight: 500; } .score-pill { font-family: var(--font-mono); font-size: 0.85rem; font-weight: 500; padding: 4px 12px; border-radius: 20px; } .score-high { background: rgba(34,197,94,0.15); color: #22c55e; } .score-mid { background: rgba(245,158,11,0.15); color: #f59e0b; } .score-low { background: rgba(239,68,68,0.12); color: #ef4444; } /* ── Status / Log ── */ .log-box { background: var(--bg-input); border: 1px solid var(--border); border-radius: 6px; padding: 14px 18px; font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-muted); line-height: 1.8; max-height: 120px; overflow-y: auto; } .log-ok { color: var(--green); } .log-warn { color: var(--amber); } .log-info { color: var(--teal); } /* ── Dividers ── */ .section-divider { border: none; border-top: 1px solid var(--border); margin: 18px 0; } /* ── Scrollbar ── */ ::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar-track { background: var(--bg-card); } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { background: var(--teal-dim); } """ # ──────────────────────────────────────────────────────────────────────────── # YARDIMCI FONKSİYONLAR — Gradio callback'leri (Zenginleştirilmiş) # ──────────────────────────────────────────────────────────────────────────── def make_score_pill(score: float) -> str: cls = 'score-high' if score >= 0.75 else 'score-mid' if score >= 0.55 else 'score-low' return f'{score:.3f}' def make_skill_tags(skills: list, kind: str) -> str: if not skills: return '—' return ' '.join(f'{s}' for s in skills) def make_progress_bar(label: str, value: float, color: str = '#00d4aa') -> str: pct = int(value * 100) return f"""
""" def make_location_badge(loc_data: dict) -> str: """Konum badge'i oluşturur (şehir + remote etiketi)""" city = loc_data.get("city", "") country = loc_data.get("country", "") remote = loc_data.get("remote", False) parts = [] if city: parts.append(city) elif country: parts.append(country) else: parts.append("—") loc_text = ", ".join(parts) remote_tag = ' REMOTE' if remote else '' return f'{loc_text}{remote_tag}' def build_ranking_table(df: pd.DataFrame) -> str: """Zenginleştirilmiş sıralama tablosu — konum, üniversite, firma, dil""" rows = "" for _, r in df.iterrows(): verdict = r['Değerlendirme'] badge_cls = 'verdict-strong' if '🟢' in verdict else 'verdict-mid' if '🟡' in verdict else 'verdict-weak' badge_txt = 'Güçlü' if '🟢' in verdict else 'Orta' if '🟡' in verdict else 'Zayıf' # Konum badge raw_data = r.get('_raw', {}) loc_data = raw_data.get('cv_location', {}) loc_html = make_location_badge(loc_data) # Üniversite (kısa gösterim) uni = str(r.get('Üniversite', '—'))[:40] rows += f"""| Sıra | Aday | Final Skor | Semantik | Skill | Deneyim | Konum | Üniversite | Eşleşen/Eksik | Durum |
|---|