#!/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"""
{label}{value:.4f}
""" 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""" #{r['Sıra']} {r['Aday']} {make_score_pill(r['Final Skor'])} {r['Semantic']:.3f} {r['Skill Overlap']:.3f} {int(r['Deneyim (Yıl)'])} yıl {loc_html} {uni} {int(r['Eşleşen Skill'])} / {int(r['Eksik Skill'])} {badge_txt} """ return f"""
{rows}
SıraAdayFinal Skor SemantikSkillDeneyim KonumÜniversite Eşleşen/EksikDurum
""" def build_xai_panel(result: dict) -> str: """Zenginleştirilmiş XAI paneli — konum, üniversite, firma, dil bilgileri""" exp = result['explanation'] verdict = exp['verdict'] badge_cls = 'verdict-strong' if '🟢' in verdict else 'verdict-mid' if '🟡' in verdict else 'verdict-weak' sem = result['semantic_score'] skill = result['skill_score'] expsc = result['experience_score'] locsc = result.get('location_score', 0.5) final = result['final_score'] w = SCORE_WEIGHTS bars = ( make_progress_bar(f'Semantik Benzerlik (×{w.get("semantic",0)})', sem * w.get('semantic',0), '#00d4aa') + make_progress_bar(f'Skill Örtüşmesi (×{w.get("skill_overlap",0)})', skill * w.get('skill_overlap',0), '#a78bfa') + make_progress_bar(f'Deneyim Bonusu (×{w.get("experience",0)})', expsc * w.get('experience',0), '#f59e0b') + make_progress_bar(f'Konum Uyumu (×{w.get("location",0)})', locsc * w.get('location',0), '#3b82f6') ) # Profil bilgileri (konum, üniversite, firma, dil) loc_data = result.get('cv_location', {}) loc_html = make_location_badge(loc_data) unis = result.get('universities', []) companies = result.get('companies', []) languages = result.get('languages', []) profile_html = f"""
👤 Aday Profili
📍 Konum: {loc_html}
🌐 Diller: {', '.join(languages) or '—'}
🎓 Eğitim: {'; '.join(unis[:2]) or '—'}
🏢 Firmalar: {', '.join(companies[:5]) or '—'}
""" return f"""
📊 Skor Analizi
{final:.3f}
Final Skor
{sem:.3f}
Semantik
{skill:.3f}
Skill
{result['experience_years']:.0f} yıl
Deneyim
{locsc:.2f}
Konum
⚖️ Bileşen Katkıları
{bars}
{verdict}
{profile_html}
🔍 Skill Gap Analizi
✅ EŞLEŞEN BECERİLER ({len(exp['matched_skills'])})
{make_skill_tags(exp['matched_skills'], 'match')}
❌ EKSİK BECERİLER ({len(exp['missing_skills'])})
{make_skill_tags(exp['missing_skills'], 'missing')}
""" # ──────────────────────────────────────────────────────────────────────────── # PLOTLy GRAFİK FONKSİYONLARI # ──────────────────────────────────────────────────────────────────────────── PLOTLY_LAYOUT = dict( paper_bgcolor='rgba(13,24,41,0)', plot_bgcolor='rgba(13,24,41,0)', font=dict(family='Rajdhani, Inter, sans-serif', color='#6b8ab0'), title_font=dict(family='Rajdhani, sans-serif', color='#00d4aa', size=16), margin=dict(l=20, r=20, t=50, b=20), colorway=['#00d4aa', '#f59e0b', '#a78bfa', '#ef4444', '#22c55e'], ) def plotly_ranking_bar(df: pd.DataFrame) -> go.Figure: colors = ['#22c55e' if s >= 0.75 else '#f59e0b' if s >= 0.55 else '#ef4444' for s in df['Final Skor']] fig = go.Figure(go.Bar( x=df['Final Skor'], y=df['Aday'], orientation='h', marker_color=colors, text=[f'{s:.3f}' for s in df['Final Skor']], textposition='outside', textfont=dict(family='JetBrains Mono', color='#e2eaf6', size=12), )) fig.add_vline(x=0.75, line_dash='dot', line_color='rgba(34,197,94,0.5)', annotation_text='Güçlü', annotation_font_color='#22c55e') fig.add_vline(x=0.55, line_dash='dot', line_color='rgba(245,158,11,0.5)', annotation_text='Orta', annotation_font_color='#f59e0b') fig.update_layout(**PLOTLY_LAYOUT, title='🏆 Nihai Aday Sıralaması', xaxis=dict(range=[0, 1.05], gridcolor='rgba(30,48,80,0.8)', title='Skor'), yaxis=dict(autorange='reversed', gridcolor='rgba(30,48,80,0.8)'), height=max(280, len(df) * 62)) return fig def plotly_component_stacked(df: pd.DataFrame) -> go.Figure: w = SCORE_WEIGHTS names = df['Aday'].tolist() fig = go.Figure() fig.add_trace(go.Bar(name='Semantik', x=names, y=(df['Semantic'] * w['semantic']).tolist(), marker_color='#00d4aa', opacity=0.9)) fig.add_trace(go.Bar(name='Skill Overlap', x=names, y=(df['Skill Overlap'] * w['skill_overlap']).tolist(), marker_color='#a78bfa', opacity=0.9)) fig.add_trace(go.Bar(name='Deneyim', x=names, y=(df['Deneyim (Yıl)'].apply(lambda x: min(x/3,1)) * w['experience']).tolist(), marker_color='#f59e0b', opacity=0.9)) fig.update_layout(**PLOTLY_LAYOUT, barmode='stack', title='📊 Skor Bileşeni Dağılımı', xaxis=dict(gridcolor='rgba(30,48,80,0.8)'), yaxis=dict(gridcolor='rgba(30,48,80,0.8)', title='Katkı'), legend=dict(bgcolor='rgba(13,24,41,0.8)', bordercolor='#1e3050', borderwidth=1), height=360) return fig def plotly_skill_gap(df: pd.DataFrame) -> go.Figure: names = df['Aday'].tolist() fig = go.Figure() fig.add_trace(go.Bar(name='Eşleşen ✅', x=names, y=df['Eşleşen Skill'].tolist(), marker_color='rgba(34,197,94,0.8)')) fig.add_trace(go.Bar(name='Eksik ❌', x=names, y=df['Eksik Skill'].tolist(), marker_color='rgba(239,68,68,0.7)')) fig.update_layout(**PLOTLY_LAYOUT, barmode='group', title='🔍 Skill Gap Analizi', xaxis=dict(gridcolor='rgba(30,48,80,0.8)'), yaxis=dict(gridcolor='rgba(30,48,80,0.8)', title='Skill Sayısı'), legend=dict(bgcolor='rgba(13,24,41,0.8)', bordercolor='#1e3050', borderwidth=1), height=340) return fig def plotly_scatter(df: pd.DataFrame) -> go.Figure: fig = go.Figure(go.Scatter( x=df['Semantic'], y=df['Skill Overlap'], mode='markers+text', text=df['Aday'].apply(lambda n: n.split()[0]), textposition='top center', textfont=dict(family='Rajdhani', color='#e2eaf6', size=12), marker=dict( size=18, color=df['Final Skor'], colorscale=[[0,'#ef4444'],[0.5,'#f59e0b'],[1,'#00d4aa']], showscale=True, colorbar=dict(title='Final Skor', tickfont=dict(color='#6b8ab0')), line=dict(width=1, color='#1e3050'), ), hovertemplate='%{text}
Semantic: %{x:.3f}
Skill: %{y:.3f}' )) fig.update_layout(**PLOTLY_LAYOUT, title='🔵 Semantic vs Skill Dağılımı', xaxis=dict(title='Semantic Similarity', gridcolor='rgba(30,48,80,0.8)', range=[0,1]), yaxis=dict(title='Skill Overlap (Jaccard)', gridcolor='rgba(30,48,80,0.8)', range=[0,1]), height=360) return fig # ──────────────────────────────────────────────────────────────────────────── # ANA CALLBACK (Zenginleştirilmiş — 4 bileşenli + entity extraction) # ──────────────────────────────────────────────────────────────────────────── _last_results: list = [] # Global cache — XAI tab için def run_analysis( pdf_files, job_desc: str, sem_weight: float, skill_weight: float, exp_weight: float, loc_weight: float, min_years: float, max_years: float, dual_col: bool, job_location_str: str, keyword_filter: str, uni_filter: list, ): global _last_results # ── Validasyon ────────────────────────────────────────────────────────── if not pdf_files: empty = '
⚠ Lütfen en az bir PDF CV yükleyin.
' return (empty, None, None, None, None, gr.update(choices=[], value=None), empty) if not job_desc.strip(): empty = '
⚠ İş ilanı açıklaması boş olamaz.
' return (empty, None, None, None, None, gr.update(choices=[], value=None), empty) # ── Ağırlık normalize (4 bileşen) ─────────────────────────────────── total = sem_weight + skill_weight + exp_weight + loc_weight if total == 0: total = 1 weights = { 'semantic' : round(sem_weight / total, 4), 'skill_overlap': round(skill_weight / total, 4), 'experience' : round(exp_weight / total, 4), 'location' : round(loc_weight / total, 4), } # ── İş ilanından konum bilgisi çıkar ──────────────────────────────── job_location = {"city": None, "country": None, "remote": False} if job_location_str.strip(): # Kullanıcı manuel konum girdiyse loc_parts = [p.strip() for p in job_location_str.split(",")] job_location["city"] = loc_parts[0] if loc_parts else None if len(loc_parts) > 1: job_location["country"] = loc_parts[1] if "remote" in job_location_str.lower() or "uzaktan" in job_location_str.lower(): job_location["remote"] = True else: # İş ilanı metninden otomatik çıkar job_location = extract_location(job_desc) # ── PDF okuma ─────────────────────────────────────────────────────────── candidates = [] log_lines = ['► Analiz başlatıldı...'] for f in pdf_files: path = f.name if hasattr(f, 'name') else f fname = os.path.basename(path) try: text = extract_text_from_pdf(path, dual_column=dual_col) text = clean_text(text) candidates.append({'name': fname.replace('.pdf',''), 'text': text}) log_lines.append(f'✔ Yüklendi: {fname}') except Exception as e: log_lines.append(f'✘ Hata ({fname}): {e}') if not candidates: log_html = '
' + '
'.join(log_lines) + '
' return log_html, None, None, None, None, gr.update(choices=[], value=None), log_html # ── Pipeline çalıştır ─────────────────────────────────────────────────── job_skills = extract_skills(job_desc)['canonical_skills'] log_lines.append(f'► İş ilanı skill sayısı: {len(job_skills)}') if job_location.get("city"): log_lines.append(f'► İş konumu: {job_location["city"]}') results = [] for cand in candidates: res = evaluate_candidate( cv_text=cand['text'], job_description=job_desc, job_skills=job_skills, job_location=job_location, min_years=min_years, max_years=max_years, name=cand['name'], weights=weights, verbose=False ) results.append(res) loc_info = res.get("cv_location", {}).get("city", "?") log_lines.append(f'✔ {cand["name"]} → {res["final_score"]:.3f} (📍{loc_info})') # ── Keyword filter (post-hoc) ─────────────────────────────────────── if keyword_filter.strip(): filtered_results = [] kw_list = [kw.strip().lower() for kw in keyword_filter.split(",") if kw.strip()] for res in results: cand_text = next((c['text'] for c in candidates if c['name'] == res['name']), "") text_lower = cand_text.lower() all_found = all(kw in text_lower for kw in kw_list) if all_found: filtered_results.append(res) else: missing_kws = [kw for kw in kw_list if kw not in text_lower] log_lines.append(f'⊘ {res["name"]} filtrelendi (eksik: {", ".join(missing_kws)})') results = filtered_results log_lines.append(f'► Keyword filtre sonrası: {len(results)} aday') # ── Üniversite filtresi (post-hoc) ────────────────────────────────── if uni_filter and len(uni_filter) > 0: uni_set = set(u.lower() for u in uni_filter) filtered_results = [] for res in results: unis = res.get("universities", []) uni_text = " ".join(unis).lower() if any(u in uni_text for u in uni_set): filtered_results.append(res) else: log_lines.append(f'⊘ {res["name"]} filtrelendi (üniversite eşleşmedi)') results = filtered_results log_lines.append(f'► Üniversite filtre sonrası: {len(results)} aday') _last_results = results if not results: log_lines.append('⚠ Keyword filtresine uyan aday bulunamadı.') log_html = '
' + '
'.join(log_lines) + '
' return ( '
Filtrelere uyan aday yok.
', None, None, None, None, gr.update(choices=[], value=None), log_html ) # ── DataFrame (zenginleştirilmiş) ─────────────────────────────────── df = pd.DataFrame([{ 'Sıra': 0, 'Aday': r['name'], 'Final Skor': r['final_score'], 'Semantic': r['semantic_score'], 'Skill Overlap': r['skill_score'], 'Konum Skoru': r.get('location_score', 0.5), 'Deneyim (Yıl)': r['experience_years'], 'Konum': r.get('cv_location', {}).get('city') or '—', 'Üniversite': (r.get('universities') or ['—'])[0], 'Firmalar': ', '.join((r.get('companies') or [])[:3]) or '—', 'Diller': ', '.join(r.get('languages') or []) or '—', 'CV Skill Sayısı': len(r['cv_skills']), 'Eşleşen Skill': len(r['explanation']['matched_skills']), 'Eksik Skill': len(r['explanation']['missing_skills']), 'Değerlendirme': r['explanation']['verdict'], '_raw': r } for r in results]) df = df.sort_values('Final Skor', ascending=False).reset_index(drop=True) df['Sıra'] = df.index + 1 # ── Çıktılar ───────────────────────────────────────────────────────── log_lines.append(f'✔ Analiz tamamlandı — {len(results)} aday değerlendirildi.') log_html = '
' + '
'.join(log_lines) + '
' table_html = build_ranking_table(df) fig_bar = plotly_ranking_bar(df) fig_stack = plotly_component_stacked(df) fig_gap = plotly_skill_gap(df) fig_scatter = plotly_scatter(df) names_list = [r['name'] for r in results] return ( table_html, fig_bar, fig_stack, fig_gap, fig_scatter, gr.update(choices=names_list, value=names_list[0] if names_list else None), log_html ) def show_xai(selected_name: str): if not selected_name or not _last_results: return '
Önce analiz çalıştırın.
' result = next((r for r in _last_results if r['name'] == selected_name), None) if not result: return '
Aday bulunamadı.
' return build_xai_panel(result) # ══════════════════════════════════════════════════════════════════════════════ # CV ASİSTANI — Kural Tabanlı (LLM bağımlılığı yok, hızlı + güvenilir) # ══════════════════════════════════════════════════════════════════════════════ def chat_with_cvs(user_message, history): """Kural tabanlı CV asistanı — yapısal veriden doğrudan yanıt üretir.""" if not _last_results: return "⚠️ Önce CV yükleyip analizi başlatın, sonra sorularınızı sorun." return answer_question(user_message) def answer_question(q): ql = q.lower().strip() R = _last_results best = max(R, key=lambda x: x['final_score']) # ── En iyi aday ── if any(w in ql for w in ['en iyi', 'best', 'birinci', 'en yüksek', 'hangisi daha iyi', 'öneri', 'tavsiye', 'kimi seçmeliyim', 'mülakat', 'interview', 'öner', 'recommend', 'top', 'winner']): exp = best.get('explanation', {}) matched = exp.get('matched_skills', []) missing = exp.get('missing_skills', []) loc = best.get('cv_location', {}) return f"""🏆 **En uygun aday: {best['name']}** 📊 Final Skor: **{best['final_score']:.3f}** 📅 Deneyim: {best.get('experience_years',0):.0f} yıl 📍 Konum: {loc.get('city','?')}{' (Remote)' if loc.get('remote') else ''} 🎓 Eğitim: {', '.join(best.get('universities',['—'])[:2])} 🏢 Firmalar: {', '.join(best.get('companies',[])[:4]) or '—'} ✅ Eşleşen beceriler ({len(matched)}): {', '.join(matched[:10]) or '—'} ❌ Eksik beceriler ({len(missing)}): {', '.join(missing[:10]) or '—'}""" # ── Tüm adayları sırala ── if any(w in ql for w in ['sırala', 'sıralama', 'ranking', 'rank', 'tüm adaylar', 'hepsini göster', 'liste', 'genel durum', 'özet']): sorted_r = sorted(R, key=lambda x: x['final_score'], reverse=True) lines = [] for i, r in enumerate(sorted_r, 1): medal = ['🥇','🥈','🥉'][i-1] if i <= 3 else f'#{i}' lines.append(f"{medal} **{r['name']}** — Skor: {r['final_score']:.3f} | {r.get('experience_years',0):.0f} yıl | {r.get('cv_location',{}).get('city','?')}") avg = sum(r['final_score'] for r in R) / len(R) return f"📊 **Aday Sıralaması** ({len(R)} aday, ort: {avg:.3f})\n\n" + '\n'.join(lines) # ── Skill arama ── skill_keywords = { 'python':'python', 'sql':'sql', 'java':'java', 'javascript':'javascript', 'react':'react', 'docker':'docker', 'kubernetes':'kubernetes', 'machine learning':'machine learning', 'ml':'machine learning', 'deep learning':'deep learning', 'nlp':'natural language processing', 'natural language':'natural language processing', 'pytorch':'pytorch', 'tensorflow':'tensorflow', 'power bi':'power bi', 'tableau':'data visualization', 'excel':'excel', 'aws':'aws', 'azure':'azure', 'gcp':'google cloud', 'git':'git', 'linux':'linux', 'flask':'flask', 'django':'django', 'solidworks':'cad', 'autocad':'cad', 'cad':'cad', 'plc':'plc', 'scada':'scada', 'ros':'ros', 'opencv':'computer vision', 'computer vision':'computer vision', 'pandas':'pandas', 'numpy':'numpy', 'scikit':'scikit-learn', 'r programlama':'r', 'r dili':'r', ' r ':'r', 'spss':'spss', 'matlab':'matlab', } for kw, skill in skill_keywords.items(): if kw in ql: matches = [] for r in R: cv_skills_lower = [s.lower() for s in r.get('cv_skills', [])] if skill.lower() in cv_skills_lower or kw in ' '.join(cv_skills_lower): matches.append(r) if matches: lines = [] for r in sorted(matches, key=lambda x: x['final_score'], reverse=True): lines.append(f" ✅ **{r['name']}** (Skor: {r['final_score']:.3f}, {r.get('experience_years',0):.0f} yıl)") return f"🔍 **{skill.title()}** bilen adaylar ({len(matches)}/{len(R)}):\n\n" + '\n'.join(lines) return f"❌ **{skill.title()}** becerisine sahip aday bulunamadı." # ── Konum ── if any(w in ql for w in ['konum', 'nerede', 'şehir', 'lokasyon', 'location', 'istanbul', 'ankara', 'izmir', 'remote', 'uzaktan']): # Spesifik şehir arama for city in ['istanbul', 'ankara', 'izmir', 'bursa', 'antalya']: if city in ql: matches = [r for r in R if city in (r.get('cv_location',{}).get('city','') or '').lower()] if matches: lines = [f" • **{r['name']}** (Skor: {r['final_score']:.3f})" for r in matches] return f"📍 **{city.title()}** konumundaki adaylar ({len(matches)}):\n\n" + '\n'.join(lines) return f"📍 {city.title()} konumunda aday bulunamadı." if 'remote' in ql or 'uzaktan' in ql: matches = [r for r in R if r.get('cv_location',{}).get('remote')] if matches: lines = [f" • **{r['name']}** ({r.get('cv_location',{}).get('city','?')})" for r in matches] return f"🏠 **Remote** çalışabilecek adaylar ({len(matches)}):\n\n" + '\n'.join(lines) return "🏠 Remote tercih belirten aday bulunamadı." lines = [] for r in R: loc = r.get('cv_location', {}) remote = ' 🟢 Remote' if loc.get('remote') else '' lines.append(f" 📍 **{r['name']}**: {loc.get('city','?')}{remote}") return "📍 **Tüm Konum Bilgileri**\n\n" + '\n'.join(lines) # ── Deneyim ── if any(w in ql for w in ['deneyim', 'tecrübe', 'experience', 'yıl', 'kıdemli', 'senior', 'junior', 'stajyer', 'yeni mezun', 'entry']): sorted_r = sorted(R, key=lambda x: x.get('experience_years',0), reverse=True) lines = [f" {'🟢' if r.get('experience_years',0)>=3 else '🟡' if r.get('experience_years',0)>=1 else '🔴'} **{r['name']}**: {r.get('experience_years',0):.0f} yıl" for r in sorted_r] avg = sum(r.get('experience_years',0) for r in R) / len(R) return f"📅 **Deneyim Sıralaması** (ort: {avg:.1f} yıl)\n\n" + '\n'.join(lines) # ── Üniversite ── if any(w in ql for w in ['üniversite', 'okul', 'eğitim', 'mezun', 'university', 'lisans', 'master', 'yüksek lisans', 'doktora', 'phd', 'marmara', 'boğaziçi', 'itü', 'odtü', 'bilkent']): # Spesifik üniversite arama for uni in ['marmara', 'boğaziçi', 'itü', 'istanbul teknik', 'odtü', 'bilkent', 'hacettepe', 'ege', 'ankara', 'sabancı', 'koç']: if uni in ql: matches = [r for r in R if uni in ' '.join(r.get('universities',[])).lower()] if matches: lines = [f" • **{r['name']}** (Skor: {r['final_score']:.3f})" for r in matches] return f"🎓 **{uni.title()}** mezunu adaylar:\n\n" + '\n'.join(lines) return f"🎓 {uni.title()} mezunu aday bulunamadı." lines = [f" 🎓 **{r['name']}**: {', '.join(r.get('universities',['—'])[:2])}" for r in R] return "🎓 **Eğitim Bilgileri**\n\n" + '\n'.join(lines) # ── Firma ── if any(w in ql for w in ['firma', 'şirket', 'company', 'çalıştığı', 'iş yeri', 'nereden gelmiş', 'geçmiş', 'work history']): lines = [f" 🏢 **{r['name']}**: {', '.join(r.get('companies',[])[:4]) or '—'}" for r in R] return "🏢 **Firma Bilgileri**\n\n" + '\n'.join(lines) # ── Dil ── if any(w in ql for w in ['dil', 'language', 'ingilizce', 'almanca', 'türkçe', 'fransızca', 'ispanyolca', 'bildiği diller']): for lang in ['ingilizce', 'almanca', 'fransızca', 'ispanyolca', 'arapça', 'çince']: if lang in ql: matches = [r for r in R if lang.title() in r.get('languages',[])] if matches: lines = [f" • **{r['name']}**" for r in matches] return f"🌐 **{lang.title()}** bilen adaylar:\n\n" + '\n'.join(lines) return f"🌐 {lang.title()} bilen aday bulunamadı." lines = [f" 🌐 **{r['name']}**: {', '.join(r.get('languages',[])) or '—'}" for r in R] return "🌐 **Dil Bilgileri**\n\n" + '\n'.join(lines) # ── Karşılaştırma ── if any(w in ql for w in ['karşılaştır', 'compare', 'fark', 'vs', 'arasında', 'hangisi daha', 'avantaj', 'dezavantaj']): # İsim geçiyorsa o ikisini karşılaştır mentioned = [r for r in R if r['name'].lower().split()[0] in ql or r['name'].lower() in ql] if len(mentioned) < 2: mentioned = sorted(R, key=lambda x: x['final_score'], reverse=True)[:2] if len(mentioned) >= 2: a, b = mentioned[0], mentioned[1] return f"""🔄 **Karşılaştırma** | | **{a['name']}** | **{b['name']}** | |---|---|---| | Final Skor | {a['final_score']:.3f} | {b['final_score']:.3f} | | Semantik | {a['semantic_score']:.3f} | {b['semantic_score']:.3f} | | Skill | {a['skill_score']:.3f} | {b['skill_score']:.3f} | | Deneyim | {a.get('experience_years',0):.0f} yıl | {b.get('experience_years',0):.0f} yıl | | Konum | {a.get('cv_location',{}).get('city','?')} | {b.get('cv_location',{}).get('city','?')} | | Eşleşen | {len(a.get('explanation',{}).get('matched_skills',[]))} skill | {len(b.get('explanation',{}).get('matched_skills',[]))} skill | | Eksik | {len(a.get('explanation',{}).get('missing_skills',[]))} skill | {len(b.get('explanation',{}).get('missing_skills',[]))} skill |""" # ── Eksik beceri ── if any(w in ql for w in ['eksik', 'missing', 'skill gap', 'ne eksik', 'hangi skill', 'geliştirmeli', 'öğrenmeli']): lines = [] for r in sorted(R, key=lambda x: x['final_score'], reverse=True): missing = r.get('explanation',{}).get('missing_skills',[]) if missing: lines.append(f" ❌ **{r['name']}** ({len(missing)} eksik): {', '.join(missing[:6])}") return "🔍 **Eksik Beceri Analizi**\n\n" + ('\n'.join(lines) if lines else "Tüm adaylarda eksik beceri bilgisi mevcut değil.") # ── Eşleşen beceri ── if any(w in ql for w in ['eşleşen', 'matched', 'ortak skill', 'uyumlu', 'hangi beceri']): lines = [] for r in sorted(R, key=lambda x: x['final_score'], reverse=True): matched = r.get('explanation',{}).get('matched_skills',[]) lines.append(f" ✅ **{r['name']}** ({len(matched)}): {', '.join(matched[:8]) or '—'}") return "✅ **Eşleşen Beceriler**\n\n" + '\n'.join(lines) # ── Spesifik aday hakkında ── for r in R: name_parts = r['name'].lower().replace('_',' ').replace('-',' ').split() if any(part in ql for part in name_parts if len(part) > 2): loc = r.get('cv_location', {}) exp = r.get('explanation', {}) return f"""👤 **{r['name']}** 📊 Final Skor: {r['final_score']:.3f} (Semantik: {r['semantic_score']:.3f}, Skill: {r['skill_score']:.3f}) 📅 Deneyim: {r.get('experience_years',0):.0f} yıl 📍 Konum: {loc.get('city','?')}{' (Remote)' if loc.get('remote') else ''} 🎓 Eğitim: {', '.join(r.get('universities',['—'])[:2])} 🏢 Firmalar: {', '.join(r.get('companies',[])[:5]) or '—'} 🌐 Diller: {', '.join(r.get('languages',[])) or '—'} ✅ Eşleşen: {', '.join(exp.get('matched_skills',[])[:8]) or '—'} ❌ Eksik: {', '.join(exp.get('missing_skills',[])[:8]) or '—'}""" # ── Genel / tanınmayan soru ── avg = sum(r['final_score'] for r in R) / len(R) return f"""📊 **Analiz Özeti** ({len(R)} aday) 🏆 En iyi: **{best['name']}** ({best['final_score']:.3f}) 📊 Ortalama skor: {avg:.3f} 📅 Deneyim aralığı: {min(r.get('experience_years',0) for r in R):.0f} - {max(r.get('experience_years',0) for r in R):.0f} yıl Aşağıdaki hazır sorulardan birini seçin veya şu formatlarda sorun: • Aday adı yazın → detay bilgi (ör: "Cihat") • Skill adı yazın → o skill'e sahip adaylar (ör: "Docker bilen") • Şehir yazın → o konumdakiler (ör: "İstanbul") • "Karşılaştır" → ilk 2 adayın detaylı karşılaştırması""" # ── İş Pozisyonu Şablonları ────────────────────────────────────────────────── JOB_TEMPLATES = { "-- Pozisyon Seçin (Opsiyonel) --": "", # ── DATA & AI ──────────────────────────────────────────────────────── "Senior Data Scientist": """We are looking for a Senior Data Scientist with 3+ years of experience in building and deploying machine learning models. Required: Python, machine learning, deep learning, NLP, SQL, data analysis, pandas, scikit-learn. Nice to have: PyTorch, Docker, Git, demand forecasting, time series, feature engineering, MLflow. The candidate will work on LLM fine-tuning, transformer-based models, and A/B testing for model evaluation. Strong statistical background and experience with large-scale datasets expected.""", "Machine Learning Engineer": """Seeking an ML Engineer to design, build, and productionize machine learning pipelines at scale. Required: Python, machine learning, deep learning, PyTorch or TensorFlow, Docker, REST API, Git. Nice to have: Kubernetes, MLflow, Apache Spark, feature engineering, cloud platforms (AWS/GCP), CI/CD. Experience with model serving (TorchServe, TFServing), monitoring, and A/B testing is essential. Must be comfortable working across research and engineering teams.""", "Data Engineer": """Hiring a Data Engineer to architect, build, and maintain robust data pipelines and infrastructure. Required: Python, SQL, ETL, Apache Spark, data warehouse, Git, data modeling. Nice to have: Kafka, Airflow, dbt, AWS or GCP, Docker, PostgreSQL, Snowflake, data quality frameworks. Experience with big data technologies, streaming data, and cloud-native architectures. The candidate will own the data platform end-to-end.""", "Data Analyst": """Looking for a Data Analyst to transform raw data into actionable business insights. Required: SQL, Excel, data analysis, data visualization, Power BI or Tableau, Python or R. Nice to have: pandas, statistics, A/B testing, Google Analytics, business intelligence, KPI tracking. Strong storytelling ability with data and experience creating executive dashboards. Must collaborate closely with product and business teams.""", "NLP Engineer": """Hiring an NLP Engineer to build and optimize natural language processing systems. Required: Python, natural language processing, deep learning, PyTorch or TensorFlow, large language model. Nice to have: Hugging Face, LangChain, RAG, fine-tuning, text classification, named entity recognition, Docker. Experience with multilingual models, prompt engineering, and deploying NLP services at scale. Research publication experience is a plus.""", "AI/ML Researcher": """Seeking an AI/ML Researcher to push the boundaries of applied machine learning. Required: Python, machine learning, deep learning, PyTorch, natural language processing or computer vision. Nice to have: reinforcement learning, large language model, scientific writing, feature engineering, Git. Must have strong mathematical foundations (linear algebra, probability, optimization). Publication track record in top venues (NeurIPS, ICML, ACL, CVPR) preferred.""", # ── BACKEND & FRONTEND ─────────────────────────────────────────────── "Backend Developer (Python)": """We are hiring a Backend Developer with strong Python and API design skills. Required: Python, Django or FastAPI, REST API, PostgreSQL, Git, unit testing. Nice to have: Docker, Kubernetes, Redis, microservices, CI/CD, message queues (RabbitMQ/Kafka). Experience with cloud platforms (AWS/GCP), database optimization, and API security. The candidate will design scalable, high-performance backend services.""", "Backend Developer (Java/Spring)": """Looking for a Java Backend Developer to build enterprise-grade applications. Required: Java, Spring Boot, REST API, SQL, Git, microservices, unit testing. Nice to have: Docker, Kubernetes, Kafka, Redis, PostgreSQL, CI/CD, cloud platforms. Experience with domain-driven design, event-driven architecture, and performance tuning. Must be comfortable with code reviews, TDD, and agile methodologies.""", "Backend Developer (C#/.NET)": """Hiring a C#/.NET Backend Developer for scalable enterprise solutions. Required: C#, ASP.NET Core, REST API, SQL Server, Git, Entity Framework, unit testing. Nice to have: Docker, Azure, microservices, RabbitMQ, Redis, CI/CD pipelines. Experience with clean architecture, CQRS pattern, and Azure DevOps. The candidate will maintain and evolve mission-critical backend systems.""", "Frontend Developer (React)": """Looking for a Frontend Developer experienced in modern web technologies. Required: JavaScript, TypeScript, React, HTML, CSS, Git, responsive design. Nice to have: Next.js, Tailwind, GraphQL, unit testing (Jest), Figma, Storybook. Strong understanding of web performance, accessibility (a11y), and state management. The candidate will collaborate with designers and backend engineers.""", "Frontend Developer (Angular)": """Seeking an Angular Frontend Developer for enterprise web applications. Required: JavaScript, TypeScript, Angular, HTML, CSS, Git, RxJS. Nice to have: NgRx, Tailwind or Bootstrap, unit testing (Jasmine/Karma), REST API integration. Experience with modular architecture, lazy loading, and enterprise design systems. Must be comfortable with agile workflows and cross-functional teams.""", "Full Stack Developer": """Seeking a Full Stack Developer with end-to-end development experience. Required: JavaScript, TypeScript, React, Node.js, SQL, REST API, Git. Nice to have: Docker, MongoDB, AWS, CI/CD pipelines, GraphQL, unit testing. The candidate will own features from database design to UI deployment. Must be comfortable context-switching between frontend and backend tasks.""", # ── MOBILE ─────────────────────────────────────────────────────────── "Mobile Developer (Flutter)": """Hiring a Mobile Developer for cross-platform app development. Required: Flutter, Dart, REST API, Git, state management, mobile UI/UX. Nice to have: Firebase, iOS, Android, unit testing, push notifications, app performance. Published apps on App Store or Google Play is a strong plus. Experience with CI/CD for mobile (Codemagic, Fastlane) preferred.""", "Mobile Developer (iOS)": """Looking for an iOS Developer to build high-quality native applications. Required: Swift, iOS, Xcode, UIKit or SwiftUI, REST API, Git. Nice to have: Core Data, Combine, unit testing (XCTest), CI/CD, push notifications, App Store deployment. Experience with MVVM/VIPER architecture and Apple Human Interface Guidelines. Must have published at least one app on the App Store.""", "Mobile Developer (Android)": """Seeking an Android Developer for native Android application development. Required: Kotlin, Android, Android Studio, REST API, Git, Jetpack Compose. Nice to have: Room, Coroutines, unit testing, Firebase, CI/CD, Google Play deployment. Experience with MVVM architecture, Material Design, and performance optimization. Must be familiar with Android lifecycle and background processing.""", # ── DEVOPS & CLOUD ─────────────────────────────────────────────────── "DevOps Engineer": """We need a DevOps Engineer to design and manage cloud infrastructure and CI/CD pipelines. Required: Linux, Docker, Kubernetes, Terraform, CI/CD, Git, Python or Bash. Nice to have: AWS or Azure, Prometheus, Grafana, Ansible, Helm, ArgoCD. Experience with Infrastructure as Code, monitoring/alerting, and incident response. The candidate will drive reliability, scalability, and deployment automation.""", "Cloud Architect (AWS)": """Hiring a Cloud Architect to design and govern AWS cloud infrastructure. Required: AWS, cloud architecture, networking, security, Terraform, Docker. Nice to have: Kubernetes, serverless (Lambda), cost optimization, multi-account strategy, CI/CD. AWS Solutions Architect certification strongly preferred. The candidate will define cloud standards and migration strategies.""", "Site Reliability Engineer (SRE)": """Looking for an SRE to ensure the reliability and performance of production systems. Required: Linux, Docker, Kubernetes, monitoring (Prometheus/Grafana), CI/CD, Python or Go. Nice to have: Terraform, incident management, SLO/SLI definition, chaos engineering, AWS or GCP. Experience with on-call rotations, postmortem culture, and capacity planning. Must balance reliability engineering with feature velocity.""", # ── SECURITY ───────────────────────────────────────────────────────── "Cybersecurity Analyst": """Looking for a Cybersecurity Analyst to protect our organization's infrastructure and data. Required: Cybersecurity, networking, Linux, SIEM, incident response, identity management. Nice to have: Penetration testing, SOC operations, Python scripting, cloud security, vulnerability scanning. Security certifications (CISSP, CEH, CompTIA Security+) are a plus. Experience with compliance frameworks (ISO 27001, SOC 2, GDPR) preferred.""", # ── İŞ ANALİZİ & YÖNETİM ──────────────────────────────────────────── "Business Analyst": """Looking for a Business Analyst to bridge business needs and technology solutions. Required: Business analysis, requirements engineering, process modeling, SQL, Excel, stakeholder management. Nice to have: Power BI or Tableau, BPMN, Jira, user story writing, data visualization, use case modeling. Strong communication, documentation, and presentation skills are essential. Experience with agile methodologies and cross-functional collaboration.""", "Senior Business Analyst": """Seeking a Senior Business Analyst to lead complex business transformation projects. Required: Business analysis, requirements engineering, process modeling, stakeholder management, SQL, Excel. Nice to have: Business intelligence, Power BI, data visualization, change management, risk management, ERP consulting. Must be able to facilitate workshops, manage conflicting requirements, and mentor junior analysts. CBAP or PMI-PBA certification is a plus.""", "Product Manager": """Seeking a Product Manager to own product strategy, roadmap, and execution. Required: Product management, stakeholder management, user story, KPI tracking, agile, data analysis. Nice to have: SQL, Jira, UX research, A/B testing, data visualization, business intelligence. Experience in SaaS, B2B, or platform products is preferred. Must be able to prioritize ruthlessly and communicate product vision clearly.""", "Product Owner": """Hiring a Product Owner to manage the product backlog and maximize value delivery. Required: Product management, user story, stakeholder management, agile, KPI tracking, Jira. Nice to have: SQL, data analysis, UX research, process modeling, business analysis. Must have strong prioritization skills and ability to translate business needs into technical requirements. Certified Scrum Product Owner (CSPO) is a plus.""", "Project Manager (IT)": """Looking for an IT Project Manager to deliver technology projects on time and within budget. Required: Project management, stakeholder management, risk management, agile, Excel, Jira. Nice to have: change management, process modeling, business analysis, KPI tracking, budgeting. PMP or PRINCE2 certification strongly preferred. Experience managing cross-functional teams of 5-20 members.""", "Scrum Master": """Seeking a Scrum Master to facilitate agile practices across development teams. Required: Agile, project management, stakeholder management, user story, communication. Nice to have: Jira, KPI tracking, change management, risk management, coaching skills. CSM or PSM certification required. Must understand Scrum, Kanban, and SAFe frameworks. Experience removing impediments and fostering continuous improvement culture.""", # ── ERP & CRM ──────────────────────────────────────────────────────── "ERP Consultant (SAP)": """Hiring an ERP Consultant specializing in SAP implementations and process optimization. Required: SAP, ERP consulting, process modeling, requirements engineering, SQL, stakeholder management. Nice to have: SAP S/4HANA, change management, Excel, data visualization, business analysis, ABAP basics. Functional expertise in FI/CO, MM/SD, or PP modules preferred. Experience with full-cycle SAP implementations (Blueprint to Go-Live).""", "CRM Specialist": """Looking for a CRM Specialist to optimize customer relationship management processes. Required: CRM, business analysis, data analysis, Excel, process modeling, stakeholder management. Nice to have: Salesforce, HubSpot, SQL, data visualization, KPI tracking, digital marketing. Experience with CRM configuration, workflow automation, and user training. Must understand sales/marketing funnels and customer journey mapping.""", # ── QA & TEST ──────────────────────────────────────────────────────── "QA / Test Automation Engineer": """Looking for a QA Engineer to design and implement automated test frameworks. Required: QA, Selenium or Cypress, Python or JavaScript, REST API testing, Git, unit testing. Nice to have: Playwright, CI/CD, Docker, Postman, load testing, agile, test planning. ISTQB certification is a plus. Experience with BDD (Cucumber) and performance testing (JMeter). Must advocate for quality throughout the software development lifecycle.""", # ── TASARIM ────────────────────────────────────────────────────────── "UI/UX Designer": """Seeking a UI/UX Designer to create intuitive and engaging digital experiences. Required: UX research, data visualization, process modeling, communication, Figma or Sketch. Nice to have: HTML, CSS, user story, stakeholder management, A/B testing, design systems. Portfolio demonstrating end-to-end design process (research → wireframe → prototype → test) required. Experience with accessibility standards and mobile-first design.""", # ── DİĞER ──────────────────────────────────────────────────────────── "Technical Writer": """Hiring a Technical Writer to create clear, accurate technical documentation. Required: Communication, process modeling, documentation tools (Confluence, GitBook), Git. Nice to have: HTML, REST API knowledge, UX research, user story, diagramming (draw.io, Mermaid). Must be able to translate complex technical concepts into user-friendly content. Experience with API documentation (Swagger/OpenAPI) is a plus.""", "Solutions Architect": """Seeking a Solutions Architect to design end-to-end technical solutions for enterprise clients. Required: Software architecture, cloud platforms (AWS/Azure/GCP), microservices, REST API, SQL, stakeholder management. Nice to have: Docker, Kubernetes, Terraform, process modeling, requirements engineering, security. Must bridge business requirements and technical implementation. Experience with pre-sales, RFP responses, and client-facing presentations.""", "Database Administrator (DBA)": """Looking for a DBA to manage, optimize, and secure database infrastructure. Required: SQL, PostgreSQL or MySQL or Oracle, database performance tuning, backup/recovery, Linux. Nice to have: MongoDB, Redis, cloud databases (RDS, Cloud SQL), automation scripting (Python/Bash). Experience with replication, sharding, and high-availability configurations. Must ensure data integrity, security, and compliance.""", # ── MEKANİK & ENDÜSTRİ MÜHENDİSLİĞİ ──────────────────────────────── "Mechanical Design Engineer": """Seeking a Mechanical Design Engineer for product development and detailed design. Required: Mechanical design, CAD (SolidWorks or CATIA), tolerance analysis, GD&T, materials science. Nice to have: FEA (ANSYS), CFD, manufacturing, 3D printing, PDM/PLM systems. Experience with design for manufacturability (DFM) and design reviews. Must be proficient in creating production-ready technical drawings.""", "Manufacturing Engineer": """Hiring a Manufacturing Engineer to optimize production processes and quality. Required: Manufacturing, CNC, quality control, CAD, process optimization, ISO 9001. Nice to have: Lean, Six Sigma, CAM, injection molding, sheet metal, welding, ERP planning. Experience with production planning, line balancing, and continuous improvement.""", "Robotics Engineer": """Looking for a Robotics Engineer to design and program industrial automation systems. Required: Robotics, ROS, PLC, robot programming, control systems, Python or C++. Nice to have: Machine vision, motion control, industrial robot, Arduino, embedded systems. Experience with ABB/FANUC/KUKA robot programming and sensor integration.""", "Automation Engineer": """Seeking an Automation Engineer for factory automation and Industry 4.0 solutions. Required: PLC, SCADA, automation design, industrial robot, electrical engineering, HMI. Nice to have: Robot programming, machine vision, IoT, Python, data analysis, OPC UA. Experience with Siemens/Allen-Bradley PLC programming and commissioning.""", "Automotive Engineer (R&D)": """Hiring an Automotive R&D Engineer for vehicle development projects. Required: Automotive engineering, CAD, FEA, vehicle dynamics, materials science. Nice to have: ADAS, powertrain, ISO 26262, CFD, MATLAB, Simulink. Experience with APQP/PPAP processes and automotive safety standards.""", "Electrical & Electronics Engineer": """Looking for an E&E Engineer for product development and system design. Required: Electrical engineering, PCB design, signal processing, circuit design, CAD. Nice to have: Power electronics, embedded systems, SCADA, MATLAB, testing/debugging. Experience with EMC testing, safety standards (CE/UL), and schematic review.""", "Industrial Engineer": """Seeking an Industrial Engineer to improve operational efficiency and productivity. Required: Industrial engineering, process optimization, production planning, Excel, data analysis. Nice to have: Lean, Six Sigma, inventory management, ERP, time study, simulation. Experience with capacity planning, ergonomics, and work measurement studies.""", "Quality Engineer": """Hiring a Quality Engineer to ensure product quality and compliance. Required: Quality control, ISO 9001, quality assurance, statistical analysis, root cause analysis. Nice to have: Six Sigma, FMEA, 8D, SPC, IATF 16949, measurement systems analysis. ASQ CQE certification is a plus.""", "Structural Engineer": """Looking for a Structural Engineer to design and analyze building structures. Required: Structural engineering, FEA, CAD, BIM (Revit), reinforced concrete, steel structure. Nice to have: Geotechnical, SAP2000, ETABS, seismic design, construction supervision. Must be licensed (Professional Engineer) or working towards licensure.""", "Mechatronics Engineer": """Hiring a Mechatronics Engineer to integrate mechanical, electronic, and software systems. Required: Mechanical design, electrical engineering, embedded systems, PLC, Python or C++. Nice to have: ROS, control systems, IoT, PCB design, CAD, machine vision. Experience with system integration, prototyping, and cross-disciplinary problem solving.""", } # ──────────────────────────────────────────────────────────────────────────── # GRADIO ARAYÜZÜ # ──────────────────────────────────────────────────────────────────────────── HERO_HTML = """

⚡ CV MATCHING ENGINE

PDF CV'leri yükle · İş ilanını gir · Semantik analiz & XAI ile sırala

SEMANTIC NLP SKILL ONTOLOGY HYBRID SCORING EXPLAINABLE AI
""" with gr.Blocks(css=CUSTOM_CSS, title="CV Matching Engine") as demo: # ── Hero Banner ──────────────────────────────────────────────────────── gr.HTML(HERO_HTML) # ── Ana Layout ──────────────────────────────────────────────────────── with gr.Row(): # ── SOL: Kontrol Paneli ────────────────────────────────────────── with gr.Column(scale=1, min_width=310): gr.HTML('
📂 CV YÜKLEME
') pdf_input = gr.File( label="PDF CV Dosyaları (Çoklu seçim desteklenir)", file_types=[".pdf"], file_count="multiple", elem_classes=["upload-zone"] ) gr.HTML('
📋 İŞ İLANI
') job_title_dd = gr.Dropdown( choices=list(JOB_TEMPLATES.keys()), value="-- Pozisyon Seçin (Opsiyonel) --", label="Pozisyon Şablonu", interactive=True, elem_classes=["job-title-dropdown"] ) job_input = gr.Textbox( label="İş Tanımı", placeholder="Yukarıdan şablon seçin veya kendi iş ilanınızı yazın...", lines=7, value="""We are looking for a Senior Data Scientist with 3+ years of experience. Required: Python, machine learning, deep learning, NLP, SQL. Nice to have: PyTorch, Docker, Git, demand forecasting, data analysis. The candidate will work on LLM fine-tuning and transformer-based models.""" ) gr.HTML('
⚙️ SKOR AĞIRLIKLARI
') gr.HTML('
Ağırlıklar otomatik normalize edilir (toplam = 1.0)
') sem_w = gr.Slider(0.0, 1.0, value=0.55, step=0.05, label="Semantik Benzerlik") skill_w = gr.Slider(0.0, 1.0, value=0.35, step=0.05, label="Skill Örtüşmesi") exp_w = gr.Slider(0.0, 1.0, value=0.10, step=0.05, label="Deneyim Bonusu") loc_w = gr.Slider(0.0, 1.0, value=0.10, step=0.05, label="Konum Uyumu") gr.HTML('
📍 KONUM & FİLTRE
') job_location_input = gr.Textbox( label="İş Konumu (opsiyonel)", placeholder="Örn: İstanbul, Türkiye veya Remote", lines=1, value="" ) keyword_input = gr.Textbox( label="Anahtar Kelime Filtresi (opsiyonel)", placeholder="Virgülle ayırın: SAP, Almanca, PMP", lines=1, value="" ) uni_dropdown = gr.Dropdown( choices=TR_UNIVERSITIES, value=[], label="Üniversite Filtresi (opsiyonel, çoklu seçim)", multiselect=True, interactive=True, ) gr.HTML('
🔧 SEÇENEKLER
') gr.HTML('
Deneyim aralığı (yıl)
') with gr.Row(): min_years = gr.Slider(0, 15, value=3, step=0.5, label="Min Deneyim") max_years = gr.Slider(0, 15, value=5, step=0.5, label="Max Deneyim") dual_col = gr.Checkbox(label="İki sütunlu CV formatı (dual-column)", value=False) analyze_btn = gr.Button("▶ ANALİZİ BAŞLAT", elem_id="analyze-btn", variant="primary") gr.HTML('
📟 DURUM
') status_out = gr.HTML('
Hazır. PDF yükleyip analizi başlatın.
') # ── SAĞ: Sonuç Paneli ──────────────────────────────────────────── with gr.Column(scale=3): with gr.Tabs(): # ── Tab 1: Sıralama Tablosu ─────────────────────────── with gr.Tab("🏆 Sıralama"): gr.HTML('
ADAY SIRALAMA TABLOSU
') table_out = gr.HTML( value='
' '' 'PDF CV yükleyip analizi başlatın
' ) gr.HTML('
💬 CV ASİSTANI
') chatbot = gr.Chatbot(height=300, show_label=False, type="tuples", elem_id="inline-chat") with gr.Row(): chat_input = gr.Textbox(placeholder="Adaylar hakkında sorun...", show_label=False, scale=6, lines=1) chat_send = gr.Button("Gönder", variant="primary", scale=1) gr.HTML('
HAZIR SORULAR
') quick_questions = [ "🏆 En iyi aday kim?", "📊 Tüm adayları sırala", "📅 Deneyim sıralaması", "📍 Konum bilgileri", "🎓 Üniversite bilgileri", "🏢 Firma geçmişleri", "🌐 Dil bilgileri", "🔄 İlk 2 adayı karşılaştır", "❌ Eksik beceri analizi", "✅ Eşleşen beceriler", "🐍 Python bilen adaylar", "🗄️ SQL bilen adaylar", "🤖 ML bilen adaylar", "🐳 Docker bilen adaylar", ] with gr.Row(elem_id="quick-q-row"): for qq in quick_questions[:7]: gr.Button(qq, size="sm", elem_classes=["quick-q-btn"]).click( fn=lambda q=qq: ([(q, chat_with_cvs(q, []))], ""), outputs=[chatbot, chat_input], api_name=False) with gr.Row(elem_id="quick-q-row2"): for qq in quick_questions[7:]: gr.Button(qq, size="sm", elem_classes=["quick-q-btn"]).click( fn=lambda q=qq: ([(q, chat_with_cvs(q, []))], ""), outputs=[chatbot, chat_input], api_name=False) # ── Tab 2: Görsel Dashboard ─────────────────────────── with gr.Tab("📊 Dashboard"): gr.HTML('
📊 SKOR GRAFİĞİ
') bar_plot = gr.Plot(label="") with gr.Row(): stack_plot = gr.Plot(label="") scatter_plot = gr.Plot(label="") with gr.Row(): gap_plot = gr.Plot(label="") # ── Tab 3: XAI Detay ────────────────────────────────── with gr.Tab("🔍 XAI Detay"): gr.HTML('
AÇIKLANABİLİR AI — ADAY DETAY ANALİZİ
') xai_selector = gr.Dropdown( choices=[], label="Aday Seçin", interactive=True ) xai_out = gr.HTML( value='
' '' 'Analiz tamamlandıktan sonra aday seçin
' ) # ── Event Handlers ─────────────────────────────────────────────────── def on_title_select(title): template = JOB_TEMPLATES.get(title, "") return gr.update(value=template) if template else gr.update() job_title_dd.change( fn=on_title_select, inputs=[job_title_dd], outputs=[job_input], api_name=False, # ← ekle ) analyze_btn.click( fn=run_analysis, inputs=[pdf_input, job_input, sem_w, skill_w, exp_w, loc_w, min_years, max_years, dual_col, job_location_input, keyword_input, uni_dropdown], outputs=[table_out, bar_plot, stack_plot, gap_plot, scatter_plot, xai_selector, status_out], api_name=False, # ← ekle ) xai_selector.change( fn=show_xai, inputs=[xai_selector], outputs=[xai_out], api_name=False, # ← ekle ) def chat_respond(msg, hist): if not msg.strip(): return hist, "" return hist + [(msg, chat_with_cvs(msg, hist))], "" chat_send.click(fn=chat_respond, inputs=[chat_input, chatbot], outputs=[chatbot, chat_input], api_name=False) chat_input.submit(fn=chat_respond, inputs=[chat_input, chatbot], outputs=[chatbot, chat_input], api_name=False) gr.close_all() # This shuts down any other apps running in the background # ── Uygulamayı başlat ──────────────────────────────────────────────────── demo.launch( server_name="0.0.0.0", server_port=7860, share=False, show_error=True, show_api=False, # ← bunu ekle height=900 )