diff --git a/.gitignore b/.gitignore index 904ea51..a9ace74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,62 @@ +# Byte-compiled / optimized / DLL files __pycache__/ -*.logs/ -.Python -*.txt +*.py[cod] +*$py.class + +# Database locks / journals / temp backups (do NOT ignore faker_pk/faker_pk.db itself) +*.db-journal +*.db-wal +*.db-shm +*.db.bak + +# Distribution / packaging +build/ +develop-eggs/ dist/ -*.egg-info +downloads/ +eggs/ .eggs/ -build/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +# Virtual environments venv/ -.env +.venv/ +.venv +ENV/ +env/ +active_env/ + +# Testing / coverage +.pytest_cache/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.run/ + +# Editors and IDEs +.vscode/ +.idea/ +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? -.vscode/ \ No newline at end of file +# Miscellaneous +.DS_Store +*.logs \ No newline at end of file diff --git a/faker_pk/__init__.py b/faker_pk/__init__.py index 454da21..a932ffa 100644 --- a/faker_pk/__init__.py +++ b/faker_pk/__init__.py @@ -2,16 +2,18 @@ from .address import city, province, full_address from .company import company_name, industry_name, iban, bank_name, salary, job_title_with_industry, job_title from .provider import FakerPKProvider - - +from .education import institution, student_dob, student_profile + + + class FakerPK: """Generate fake Pakistani names, addresses, CNICs, phone numbers, and more.""" - - def _generate_multiple(self, func, count): + + def _generate_multiple(self, func, count, **kwargs): """Generate one or many values based on count.""" if count == 1: - return func() - return [func() for _ in range(count)] + return func(**kwargs) + return [func(**kwargs) for _ in range(count)] # -------------------- # Personal Info @@ -22,11 +24,11 @@ def male_name(self, count=1): def female_name(self, count=1): return self._generate_multiple(female_name, count) - def cnic(self, count=1): - return self._generate_multiple(cnic, count) + def cnic(self, count=1, gender=None): + return self._generate_multiple(cnic, count, gender=gender) - def phone_number(self, count=1): - return self._generate_multiple(phone_number, count) + def phone_number(self, count=1, provider=None): + return self._generate_multiple(phone_number, count, provider=provider) def sim_provider(self, count=1): return self._generate_multiple(sim_provider, count) @@ -43,20 +45,20 @@ def dob(self, count=1): # -------------------- # Address Info # -------------------- - def city(self, count=1): - return self._generate_multiple(city, count) + def city(self, count=1, province=None): + return self._generate_multiple(city, count, province=province) - def province(self, count=1): - return self._generate_multiple(province, count) + def province(self, count=1, city=None): + return self._generate_multiple(province, count, city=city) - def full_address(self, count=1): - return self._generate_multiple(full_address, count) + def full_address(self, count=1, city=None, province=None): + return self._generate_multiple(full_address, count, city=city, province=province) # -------------------- # Company Info # -------------------- - def company_name(self, count=1): - return self._generate_multiple(company_name, count) + def company_name(self, count=1, industry=None): + return self._generate_multiple(company_name, count, industry=industry) def industry_name(self, count=1): return self._generate_multiple(industry_name, count) @@ -64,23 +66,28 @@ def industry_name(self, count=1): def bank_name(self, count=1): return self._generate_multiple(bank_name, count) - def iban(self, count=1): - return self._generate_multiple(iban, count) + def iban(self, count=1, bank=None): + return self._generate_multiple(iban, count, bank=bank) # -------------------- # Job Info # -------------------- def job_title(self, count=1, industry=None): - if count == 1: - return job_title(industry=industry) - return [job_title(industry=industry) for _ in range(count)] - + return self._generate_multiple(job_title, count, industry=industry) + def job_title_with_industry(self, count=1): return self._generate_multiple(job_title_with_industry, count) def salary(self, count=1, industry=None): - """Generate random salary from company.py.""" - if count == 1: - return salary(industry=industry) - return [salary(industry=industry) for _ in range(count)] + return self._generate_multiple(salary, count, industry=industry) + + # -------------------- + # Institution Info + # -------------------- + def institution(self, count=1, level=None, city=None, province=None): + return self._generate_multiple(institution, count, level=level, city=city, province=province) + + def student_profile(self, count=1, level=None, province=None): + return self._generate_multiple(student_profile, count, level=level, province=province) + __all__ = ["FakerPK", "FakerPKProvider"] \ No newline at end of file diff --git a/faker_pk/address.py b/faker_pk/address.py index 463716e..e2febb5 100644 --- a/faker_pk/address.py +++ b/faker_pk/address.py @@ -1,52 +1,44 @@ -import random - -# Mapping of provinces to their major cities/towns -PROVINCE_CITIES = { - "Punjab": [ - "Lahore", "Faisalabad", "Rawalpindi", "Multan", "Gujranwala", - "Sialkot", "Bahawalpur", "Sargodha", "Sahiwal", "Dera Ghazi Khan" - ], - "Sindh": [ - "Karachi", "Hyderabad", "Sukkur", "Larkana", "Mirpur Khas", - "Nawabshah", "Shikarpur", "Khairpur", "Jacobabad", "Thatta" - ], - "Khyber Pakhtunkhwa": [ - "Peshawar", "Mardan", "Abbottabad", "Swat", "Charsadda", - "Bannu", "Kohat", "Dera Ismail Khan", "Haripur", "Mansehra" - ], - "Balochistan": [ - "Quetta", "Gwadar", "Sibi", "Khuzdar", "Turbat", - "Chaman", "Zhob", "Bela", "Makran", "Pasni" - ], - "Gilgit Baltistan": [ - "Gilgit", "Skardu", "Hunza", "Ghizer", "Diamer", - "Astore", "Shigar", "Kharmang" - ], - "Islamabad Capital Territory": ["Islamabad"] -} - - -def city_and_province(): - """Return a tuple of (city, province) based on accurate mapping.""" - province_name = random.choice(list(PROVINCE_CITIES.keys())) - city_name = random.choice(PROVINCE_CITIES[province_name]) - return city_name, province_name - - -def city(): - """Return a random Pakistani city.""" - return city_and_province()[0] - - -def province(): - """Return the province corresponding to the random city.""" - return city_and_province()[1] - - -def full_address(): - """Generate a realistic full Pakistani address.""" +import random +from .utils import query_value, query_row + + +def city(province=None): + """Return a random Pakistani city. Optionally filter by province.""" + if province: + return query_value( + "SELECT city FROM locations WHERE province = ? ORDER BY RANDOM() LIMIT 1", + (province,) + ) + return query_value("SELECT city FROM locations ORDER BY RANDOM() LIMIT 1") + + +def province(city=None): + """Return a random province. If city is given, return its matching province.""" + if city: + return query_value( + "SELECT province FROM locations WHERE city = ? LIMIT 1", (city,) + ) + return query_value("SELECT province FROM locations ORDER BY RANDOM() LIMIT 1") + + +def full_address(city=None, province=None): + """Generate a realistic full Pakistani address with real postal code.""" + if city: + row = query_row( + "SELECT city, province, postal_code FROM locations WHERE city = ? LIMIT 1", + (city,) + ) + elif province: + row = query_row( + "SELECT city, province, postal_code FROM locations WHERE province = ? ORDER BY RANDOM() LIMIT 1", + (province,) + ) + else: + row = query_row( + "SELECT city, province, postal_code FROM locations ORDER BY RANDOM() LIMIT 1" + ) + + city_name, province_name, postal_code = row house_no = f"House No. {random.randint(1, 999)}" street = f"Street No. {random.randint(1, 30)}" - city_name, province_name = city_and_province() - postal_code = random.randint(10000, 99999) return f"{house_no}, {street}, {city_name}, {province_name}, {postal_code}" \ No newline at end of file diff --git a/faker_pk/company.py b/faker_pk/company.py index 3ccb795..f1a3743 100644 --- a/faker_pk/company.py +++ b/faker_pk/company.py @@ -1,144 +1,87 @@ -import random - -COMPANIES = [ - "Tech Solutions", "Global Enterprises", "NexGen Software", "Bright Future Ltd", - "Innovatech", "Pak Logistics", "Star Industries", "FutureTech Labs", "NextEra Solutions", - "Digital Horizons", "GreenField Enterprises", "Alpha Systems", "Skyline Technologies", - "Visionary Solutions", "Prime Consulting", "BlueWave Software", "Omega Solutions", - "Sunrise Industries", "Quantum Tech", "Everest Solutions", "Peak Dynamics", - "Galaxy Enterprises", "Crescent Innovations", "Summit Tech", "Aurora Systems", - "Infinity Solutions", "Pioneer Technologies", "Vertex Labs", "NextGen Analytics", - "Momentum Solutions", "Nova Systems", "Titan Tech", "Elite Software", "GlobalTech", - "Fusion Enterprises", "Legacy Solutions", "Vortex Innovations", "Ascend Tech", - "CoreLogic", "Hyperion Labs", "Luminous Software", "EverTech", "Matrix Solutions", - "Sapphire Systems", "Digital Minds", "Vertex Solutions", "Zenith Tech", "Precision Labs", - "Altair Enterprises", "Phoenix Systems" -] - -BANKS = [ - "Habib Bank", "MCB Bank", "UBL", "Bank Alfalah", "Standard Chartered", "Allied Bank", "Meezan Bank", "Bank of Punjab", "HBL" -] - +import random +from .utils import query_value, query_row + + +def _normalize_industry(industry): + """Map a human-readable industry name to its internal code. + Accepts both full names ('Information Technology') and codes ('IT'). + """ + # Check if it's already a valid code + code = query_value("SELECT code FROM industries WHERE code = ?", (industry,)) + if code: + return code + # Try mapping from full name + code = query_value("SELECT code FROM industries WHERE name = ?", (industry,)) + if code: + return code + raise ValueError(f"Industry '{industry}' not found.") + + def bank_name(): - return random.choice(BANKS) - -def iban(): - # Basic mock IBAN: PK + 2-digit checksum + 4-digit bank code + random 16 digits - return f"PK{random.randint(10,99)}{random.randint(1000,9999)}{random.randint(10**15,10**16-1)}" - -def company_name(): - return random.choice(COMPANIES) - - -# Industries -INDUSTRIES = { - "Information Technology": "IT", - "Finance": "Finance", - "Healthcare": "Healthcare", - "Education": "Education", - "Marketing & Media": "Marketing", - "Government / Public Sector": "Government", - "Engineering / Manufacturing": "Engineering", - "Hospitality / Retail": "Retail", - "Entrepreneur / Startup": "Entrepreneur", - "Legal / Consulting": "Consulting" -} -# Job titles mapped to industries - -JOB_TITLE_MAPPING = { - "IT": [ - "Software Engineer", "Frontend Developer", "Backend Developer", - "Full Stack Developer", "DevOps Engineer", "Data Scientist", - "Machine Learning Engineer", "AI Researcher", "Cybersecurity Engineer", - "Cloud Solutions Architect", "Mobile App Developer", "Blockchain Developer", - "QA / Test Engineer", "UI/UX Designer", "Network Engineer", "Database Administrator", - "IT Support Specialist", "Systems Analyst" - ], - "Finance": [ - "Accountant", "Auditor", "Financial Analyst", "Investment Analyst", - "Tax Consultant", "Risk Manager", "Credit Analyst", "Loan Officer", - "Treasury Manager", "Financial Controller" - ], - "Healthcare": [ - "Doctor", "Nurse", "Pharmacist", "Lab Technician", "Radiologist", - "Medical Researcher", "Physiotherapist", "Dietitian", "Surgeon", "Psychologist" - ], - "Education": [ - "Teacher", "Lecturer", "Professor", "Research Associate", - "Academic Coordinator", "Curriculum Designer", "Educational Consultant" - ], - "Marketing": [ - "Graphic Designer", "Content Writer", "Copywriter", "Video Editor", - "Animator", "Photographer", "Digital Marketing Specialist", - "Social Media Manager", "Art Director", "Marketing Manager", "Sales Executive" - ], - "Government": [ - "Civil Servant", "Policy Analyst", "Administrative Officer", - "Diplomat", "Law Enforcement Officer", "Urban Planner" - ], - "Engineering": [ - "Civil Engineer", "Mechanical Engineer", "Electrical Engineer", - "Chemical Engineer", "Industrial Engineer", "Production Manager", - "Quality Assurance Engineer" - ], - "Retail": [ - "Hotel Manager", "Chef", "Waiter", "Waitress", "Store Manager", - "Sales Associate", "Customer Service Representative" - ], - "Entrepreneur": [ - "Entrepreneur", "Startup Founder", "Business Development Manager", - "Operations Manager", "Strategy Analyst", "Consultant" - ], - "Consulting": [ - "Legal Advisor", "Lawyer", "Advocate", "Compliance Officer", "Consultant" - ] -} - -# Functions + """Return a random Pakistani bank name.""" + return query_value("SELECT name FROM banks ORDER BY RANDOM() LIMIT 1") + + +def iban(bank=None): + """Generate a realistic Pakistani IBAN. + If bank is specified, use that bank's actual IBAN code. + """ + if bank: + code = query_value("SELECT iban_code FROM banks WHERE name = ?", (bank,)) + if not code: + raise ValueError(f"Bank '{bank}' not found.") + else: + code = query_value("SELECT iban_code FROM banks ORDER BY RANDOM() LIMIT 1") + + checksum = str(random.randint(10, 99)) + account = str(random.randint(10**15, 10**16 - 1)) + return f"PK{checksum}{code}{account}" + + +def company_name(industry=None): + """Return a random company name. Optionally filter by industry.""" + if industry: + code = _normalize_industry(industry) + return query_value( + "SELECT name FROM companies WHERE industry_code = ? ORDER BY RANDOM() LIMIT 1", + (code,) + ) + return query_value("SELECT name FROM companies ORDER BY RANDOM() LIMIT 1") + + +def industry_name(): + """Return a random industry name (human-readable).""" + return query_value("SELECT name FROM industries ORDER BY RANDOM() LIMIT 1") + def job_title(industry=None): + """Return a random job title. Optionally filter by industry.""" if industry: - # Map human-readable input to internal keys (e.g. "Information Technology" -> "IT") - normalized = INDUSTRIES.get(industry, industry) - if normalized not in JOB_TITLE_MAPPING: - raise ValueError(f"Industry '{industry}' not found.") + code = _normalize_industry(industry) + return query_value( + "SELECT title FROM job_titles WHERE industry_code = ? ORDER BY RANDOM() LIMIT 1", + (code,) + ) + return query_value("SELECT title FROM job_titles ORDER BY RANDOM() LIMIT 1") - return random.choice(JOB_TITLE_MAPPING[normalized]) - # Randomly pick an industry first - selected_industry = random.choice(list(JOB_TITLE_MAPPING.keys())) - return random.choice(JOB_TITLE_MAPPING[selected_industry]) def job_title_with_industry(): - selected_industry = random.choice(list(JOB_TITLE_MAPPING.keys())) - title = random.choice(JOB_TITLE_MAPPING[selected_industry]) - return f"{title} - {selected_industry}" + """Return a string of 'job_title - industry_code'.""" + row = query_row("SELECT title, industry_code FROM job_titles ORDER BY RANDOM() LIMIT 1") + return f"{row[0]} - {row[1]}" -def industry_name(): - return random.choice(list(INDUSTRIES.keys())) def salary(industry=None): - """ - Generate a random salary in PKR based on industry rough ranges. - """ - # Rough salary ranges (monthly PKR) - ranges = { - "IT": (50000, 250000), - "Finance": (40000, 200000), - "Healthcare": (30000, 180000), - "Education": (25000, 120000), - "Marketing": (30000, 150000), - "Government": (25000, 120000), - "Engineering": (35000, 180000), - "Retail": (20000, 100000), - "Entrepreneur": (50000, 300000), - "Consulting": (40000, 200000) - } + """Generate a random salary in PKR based on industry salary ranges.""" if industry: - normalized = INDUSTRIES.get(industry, industry) - if normalized in ranges: - low, high = ranges[normalized] - else: - low, high = random.choice(list(ranges.values())) - else: - low, high = random.choice(list(ranges.values())) - return random.randint(low, high) + code = _normalize_industry(industry) + row = query_row( + "SELECT min_salary, max_salary FROM industries WHERE code = ?", (code,) + ) + if row: + return random.randint(row[0], row[1]) + + row = query_row( + "SELECT min_salary, max_salary FROM industries ORDER BY RANDOM() LIMIT 1" + ) + return random.randint(row[0], row[1]) \ No newline at end of file diff --git a/faker_pk/education.py b/faker_pk/education.py new file mode 100644 index 0000000..2bb496f --- /dev/null +++ b/faker_pk/education.py @@ -0,0 +1,95 @@ +import random +from datetime import date, timedelta +from .utils import query_value, query_row + + +# Age ranges per institution level +AGE_RANGES = { + "school": (5, 14), + "college": (14, 18), + "university": (18, 25) +} + + +def institution(level=None, city=None, province=None): + """Return a random institution name, optionally filtered by level, city, or province.""" + conditions = [] + params = [] + + if level: + conditions.append("i.type = ?") + params.append(level) + if city: + conditions.append("i.city = ?") + params.append(city) + if province: + conditions.append("l.province = ?") + params.append(province) + + where = f"WHERE {' AND '.join(conditions)}" if conditions else "" + + query = f""" + SELECT i.name FROM institutions i + JOIN locations l ON i.city = l.city + {where} + ORDER BY RANDOM() LIMIT 1 + """ + return query_value(query, tuple(params)) + + +def student_dob(level="university"): + """Generate an age-appropriate DOB for a student at the given level.""" + min_age, max_age = AGE_RANGES.get(level, (18, 25)) + today = date.today() + start = today.replace(year=today.year - max_age) + end = today.replace(year=today.year - min_age) + delta = (end - start).days + return start + timedelta(days=random.randint(0, delta)) + + +def student_profile(level=None, province=None): + """Generate a complete, coherent student profile. + + Returns a dict with name, gender, cnic, institution, city, province, dob. + All fields are consistent (institution matches city/province, dob matches level). + """ + from .personal import male_name, female_name, cnic + + # Default to random level if not specified + if not level: + level = random.choice(["school", "college", "university"]) + + # Pick a random institution, optionally in the given province + conditions = ["i.type = ?"] + params = [level] + if province: + conditions.append("l.province = ?") + params.append(province) + + where = f"WHERE {' AND '.join(conditions)}" + row = query_row(f""" + SELECT i.name, i.city, l.province FROM institutions i + JOIN locations l ON i.city = l.city + {where} + ORDER BY RANDOM() LIMIT 1 + """, tuple(params)) + + if not row: + raise ValueError(f"No institution found for level='{level}', province='{province}'") + + inst_name, city_name, province_name = row + + # Generate gender-consistent personal data + gender = random.choice(["male", "female"]) + name = male_name() if gender == "male" else female_name() + + return { + "name": name, + "gender": gender, + "cnic": cnic(gender=gender), + "institution": inst_name, + "level": level, + "city": city_name, + "province": province_name, + "dob": student_dob(level) + } \ No newline at end of file diff --git a/faker_pk/faker_pk.db b/faker_pk/faker_pk.db new file mode 100644 index 0000000..7a847e9 Binary files /dev/null and b/faker_pk/faker_pk.db differ diff --git a/faker_pk/initialize_db.py b/faker_pk/initialize_db.py new file mode 100644 index 0000000..11214e8 --- /dev/null +++ b/faker_pk/initialize_db.py @@ -0,0 +1,615 @@ +import sqlite3 +import os + +MALE_NAMES = [ + "Ahmed", "Muhammad", "Ali", "Hassan", "Hussain", "Bilal", "Hamza", "Umar", "Usman", "Abdullah", + "Abdul Rehman", "Abdul Basit", "Abdul Hadi", "Abdul Wahab", "Abdul Samad", "Abdul Qadir", "Abdul Majeed", + "Abdul Rauf", "Abdul Aziz","Abdul Kareem", "Abdul Aleem", "Abdul Ghaffar", "Abdul Ghani", "Abdul Haq", + "Abdul Shakoor", "Abdul Sattar", "Abdul Wasi", "Ahmad","Zeeshan", "Danish", "Faizan", "Fahad", + "Waleed", "Zain", "Saad", "Ahsan", "Adeel", "Asad","Arsalan", "Shahzaib", "Shehryar", "Salman", + "Noman", "Omer", "Tahir", "Talha", "Kashif", "Kamran","Shahid", "Naveed", "Imran", "Junaid", "Farhan", + "Faisal", "Khalid", "Raza", "Rizwan", "Adnan","Arif", "Yasir", "Irfan", "Zubair", "Shayan", "Sameer", "Umair", + "Huzaifa", "Ayaan", "Rayyan","Azaan", "Areeb", "Raheel", "Sufyan", "Haris", "Anas", "Arham", "Asim", "Moiz", + "Ibtisam", "Saif", "Ilyas", "Ismail", "Ibrahim", "Eesa", "Musa", "Yousuf", "Dawood", "Yunus","Hashir", + "Nuh", "Luqman", "Taimoor", "Murtaza", "Baqir", "Shahmeer", "Shaheer", "Daniyal", "Abdul Malik", + "Zarar", "Zaryab", "Aafaq", "Abrar", "Adil", "Amaan", "Amjad", "Anees", "Anwar", "Aqeel", + "Arqam", "Arsal", "Asghar", "Ashar", "Atif", "Awais", "Ayaz", "Azhar", "Azlan", "Barkat", + "Basim", "Babar", "Burhan", "Ehtisham", "Ehsan", "Faraz", "Farid", "Fawad", "Feroz", "Ghazanfar", + "Haider", "Hammad", "Hameed", "Hasan", "Haseeb", "Hashim", "Hisham","Adeel", "Huzaifah", "Ijaz", "Imad", + "Inam", "Javed", "Kamal", "Khalil", "Khizar", "Mahad", "Mahir", "Mansoor", "Maaz", "Mazhar","Abdul Rahman", + "Mehdi", "Muneeb", "Mustafa", "Naeem", "Nouman", "Qasim", "Rameez", "Rehan", "Sadiq", "Safeer", + "Saifullah", "Sarfaraz", "Shahbaz", "Shafqat", "Shafiq", "Sharjeel", "Shehzad", "Sohail", "Subhan", "Sultan", + "Tabish", "Talal", "Tauseef", "Tufail", "Ubaid", "Umer", "Usama", "Wajid", "Waqas", "Wasif", + "Yasir", "Yawar", "Yameen", "Yasin", "Zakariya", "Zaman", "Zawwar", "Zia", "Zohaib", "Zubair", + "Zain ul Abidin","Taha", "Irtaza", "Raza", "Ehtesham", "Mirza", "Azeem", "Saqib", "Shabbir", + "Tahseen", "Salman", "Fahim", "Jawad", "Sarmad", "Nabeel", "Faiq", "Rashid", "Rahim", "Habib", + "Munir", "Zameer", "Akram", "Zafar", "Wasim", "Nauman", "Nasir", "Khalil", "Jibran", "Kashan", + "Adi", "Fakhir", "Sabtain", "Farooq", "Faiz", "Nisar", "Salman","Gohar","ghazangfar", + "Rifat", "Tahmid", "Zohair", "Zaeem", "Sarmal", "Arsal", "Areez", "Sarim","Aoun", + "Zayyan", "Razaq", "Asfar", "Affan", "Hanzala", "Hammad", "Ziyad", "Adeel", + "Karim", "Qadeer", "Hanan", "Rameen", "Taha", "Shahroz", "Sameer", "Yasrab", "Ammar", + "Shakir", "Rauf", "Danish", "Hamdaan", "Maher", "Uzair", "Shareef", + "Zarrar", "Faris", "Azmat", "Riaz", "Munawwar", "Kaleem", "Sufyan", "Zameel", "Sajid", "Sarim", + "Tabriz", "Yasin", "Attiq", "Zaid", "Murtaza", "Aneeb", "Moazzam", + "Fida", "Najam", "Tauqeer", "Shakeel", "Najeeb", "Basit", "Faizan","Farhan", "Haris", "Ahsan", "Taimoor", "Fahad", "Waris", +] + +FEMALE_NAMES = [ + "Aliya", "Amna", "Anaya", "Anisa", "Asia", "Aasma", "Abida", "Adeela", "Adeelah", + "Afifa", "Afsheen", "Hifza", "Afreen", "Aiman", "Aina", "Aiza", "Aleena", + "Aleesha", "Aleeza", "Ayla", "Alishba", "Amal", "Ammara", "Amber", "Ameena", "Amira", "Anabia", + "Anila", "Aniqa", "Anisa", "Anum", "Anusha", "Anzela", "Aqsa", "Arfa", "Arisha", + "Urwa", "Asfa", "Asma", "Asmara", "Atiya", "Ayesha", "Ayra", "Azka", + "Azra", "Bisma", "Batool", "Benish", "Bushra", "Dur-e-Fatima","Dur e Fishan", "Dua", "Ayman", "Esha", + "Eiman","Erum", "Faiza", "Fakhra", "Falaq", "Falak", + "Fariha", "Farwah", "Farzana", "Fatima", "Fauzia", "Fiza", "Ghazal", "Ghazala", "Gulnaz", + "Mahrukh", "Habiba", "Hafsa", "Haleema", "Hania", "Hadia", "Hareem", "Haseena", "Hiba", "Hifza", + "Hina", "Hira", "Humaira", "Humna", "Iffat", "Ifra", "Iqra", + "Irum", "Isha", "Ishaal", "Eshwa", "Isra", "Jameela", "Javeria", "Jannat", "Yasmeen", + "Jiya", "Kainat", "Khadija", "Khansa", "Kiran", "Komal", "Laiba", "Laila", "Laraib", + "Lehna", "Lubna", "Mahira", "Mahjabeen", "Mahnoor", "Maha", "Maliha", "Maria", + "Mariam", "Marwa","Maheen", "Mehak", "Mehr", "Mehwish", "Minal", "Mishal", "Misbah", + "Mona", "Mubashira", "Muqaddas", "Meesha", "Nabeela", "Nadia", "Nafisa", "Naila", "Najma", "Natasha", + "Naureen", "Nayyab", "Neha", "Nida", "Nimra", "Nishat", "Noreen", "Nosheen", "Nusrat","Faqiha", + "Naima", "Zubaidah", "Parveen", "Qandeel", "Qurat tul Ain", "Rabiya", "Rabia", "Rafia", "Rafiya", "Rameen", + "Rania", "Rozena", "Rashida", "Rida", "Rimsha", "Rizwana", "Soha", "Romana", "Roohi", "Ruba", + "Rubina", "Rukhsar", "Rumaisa","Rumaisha","Rimsha", "Ruqayya", "Saba", "Sabeen", "Sabahat", "Sadia", "Sadra", "Sania", + "Sahar", "Saira", "Saria","Sajida", "Sakina", "Salma", "Samina", "Samiya", "Sana", "Sanober","Sarah", "Sasha", + "Subha", "Subhana", "Suhana", "Sumaira", "Sumaiya", "Sundus", "Tabinda", "Tabassum", "Taha", "Tahira", + "Tahreem", "Tahirah", "Tania", "Tanisha", "Tanzeela", "Tayyaba", "Tehreem", "Tooba", "Ujala", "Umama","Umaima","Ajwa", + "Umm e Habiba", "Umm e Hani", "Umm e Kulsoom", "Umm e Hani", "Umm e Rubab","Rutab","Rahaf", "Umm e Salma", "Urooj", + "Urwa", "Uzma", "Wajiha","Wardah", "Yashma", "Yasmeen", "Yumna", "Zainab", "Zakia","Haram", + "Zeenia", "Zahra", "Zaib", "Zakia", "Zeba","Zareen", "Zarish", "Zarmeen","Atfa","Tooba","Anshara","Zeba","Naina","Namal", + "Zarqa", "Zartaj", "Zaryab", "Zehra", "Zeenat", "Zimal", "Zobia", "Zohra", "Farwa", "Farheen", "Gulshan", + "Zonaira", "Zoya","Zoha", "Zubaida", "Zulekha", "Zunaisha","Adeeba", "Areeba","Fakhira", "Fariha", "Faryal", + "Maheen", "Arooba","Bareera","Bushra", "Dania", "Dua", "Eshaal","Urwa","Uswa","Mawra","Shibrah","Haya", + "Hajra", "Haleemah", "Hamna", "Huriya","Hurain","Nayarra", "Insha","Ishaal","Noor","Saffa","Minahil", + "Jannat", "Kinza","Khizra","Saima","Madeeha","Maleeha","Komal","Shanza","Shanzay","Faiqa","Hareem","Gohar", + "Laiba", "Laila", "Mehwish", "Maida", "Memoona", "Momina","Marjan", "Mehreen", "MehruNisa", "Minal", + "Maira", "Naheed","Naveed", "Naila","Noshaba","Parveen", "Rabi", "Rafia", "Ramsha","Iraj","Rija","Reshma", + "Aruba", "Rukhsana","Rehanna" , "Saba", "Sabiha","Nabeeha","Saeeda", "Sahar","Sameera", "Samra", "Samia", +] + +LAST_NAMES = [ + "Abbasi", "Abbas", "Abid", "Afzal", "Ahmad","Akbar", "Akhter", "Alam", "Ali","Sethi","Anwar", + "Amjad", "Anjum", "Ansari", "Arif", "Asad", "Ashfaq", "Asghar", "Aslam", "Atif", "Awan", + "Azam", "Azhar", "Babar", "Baig", "Bajwa", "Bakht", "Baloch", "Bangash", "Basit", "Batool", + "Bhatti", "Bukhari", "Butt", "Chaudhry", "Cheema", "Chishti", "Dar", "Danish", "Daud", "Deen", + "Durrani", "Ejaz", "Fahim", "Faheem", "Farid", "Farooq", "Farrukh", "Fazal", "Feroz", "Ghafoor", + "Ghani", "Ghazanfar", "Ghaznavi", "Ghauri", "Gohar", "Habib", "Hafeez", "Hafiz", + "Haider", "Hameed", "Hamid", "Hanif", "Hashim", "Hasnain", "Hassan", "Hayat", "Hussain", "Hyder", + "Iftikhar", "Ijaz", "Ilyas", "Imam", "Imran", "Inam", "Iqbal", "Irshad", "Ismail", "Ishaq", + "Jahangir", "Jamal", "Jamali", "Jamshed", "Javed", "Jawad", "Kabir", "Qadir", "Kaleem", + "Kamran", "Kamil", "Karim", "Kashif", "Kazmi", "Khalid", "Khalil", "Khan", "Khizar", "Khurram", + "Latif", "Mahmood", "Malik", "Manzoor", "Masood", "Mazhar", "Mehmood", "Mir", "Mirza", "Moin", + "Mohsin", "Moinuddin", "Monis", "Mubashir", "Mujeeb", "Mukhtar", "Munir", "Murad", "Mustafa", "Murtaza", + "Nadeem", "Naeem", "Naseem", "Nasir", "Nawaz", "Niaz", "Noor", "Noman", "Numan", "Obaid", + "Qadir", "Qaiser", "Qamar", "Qasim", "Qayyum", "Qureshi", "Rafiq", "Rafique", "Rahim", "Raja", + "Rameez", "Rana", "Rasheed", "Rauf", "Raza", "Razzaq", "Rehman", "Riaz", "Rizwan", + "Sabir", "Sadiq", "Safeer", "Shafi", "Saeed", "Shafiullah", "Sajid", "Saleem", "Salman", + "Sami", "Sarfaraz","Shafi", "Shafique", "Shahid", "Shakeel", "Sharif", "Shaukat", "Sheikh", + "Shehzad", "Sheraz", "Shoukat", "Siddiq","Sadiq", "Siddique", "Sohail", "Suleman", "Sultan", "Tahir", "Talib", + "Tariq", "Tufail", "Ubaid", "Umar", "Usman", "Waheed", "Wali", "Waseem", "Yaseen", "Yasin", + "Yousaf", "Younas", "Zafar", "Zahid", "Zakir", "Zaman", "Zameer", + "Abbass", "Aftab", "Akram", "Alvi", "Ashraf", "Aziz", "Badar", "Bari", "Bashir", + "Basharat", "Burhan", "Chughtai", "Zawar", "Ejaz", "Ehsan", "Faisal", "Farhan", "Fazal", + "Gul", "Gulzar", "Haqqani", "Hashmi", "Hussaini", "Jalil", "Jamil", "Junaid", "Kabir", + "Kamal", "Khawaja", "Khattak", "Kiani", "Khoso", "Khokhar", "Khosa", "Lodhi", "Mahmud", + "Malook", "Mandokhel", "Memon", "Mughal", "Naqvi", "Naseer", "Niazi", "Orakzai", "Pirzada", + "Qadri", "Qasmi", "Rashidi", "Saboor", "Sadaqat", "Sajjad", "Saleh", "Samiullah", "Sarwar", "Shafiullah", + "Shah", "Shahbaz", "Shahzad", "Shams", "Sharafat", "Sharifuddin", "Sikandar", "Sohaib", "Subhan", + "Tabassum", "Taha", "Talha", "Tanveer", "Tauqeer", "Tariq", "Wahid", "Wajid", "Waqas", + "Yaseer", "Zaheer", "Zaman", "Zarrar", "Zeeshan", "Agha","Yousafzai", "Asmat", "Atta", "Baber", "Baloch", "Chohan", + "Dasti", "Dogar", "Gandapur", "Gill", "Gulshan", "Khatri", + "Hingoro", "Hoti", "Jakhrani", "Jatoi", "Junejo", "Kalhoro", "Kakar", "Kashmiri", "Khoso", + "Langah", "Laghari", "Liaqat", "Lodhi", "Mahsud", "Magsi", "Marwat", "Mashwani", + "Mengal", "Mirani", "Naseeruddin", "Niazi","Qasoori","Warind", + "Rajput", "Rind", "Samar", "Samad", "Sandhu", "Shahwani", "Sheerazi", "Sial", + "Soomro", "Soomra", "Talpur", "Tanoli", "Tarin", "Tiwana", "Toor", + "Turk", "Wazir", "Yusufzai", "Zehri", "Zuberi", "Kiyani", "Khakwani", "Taj", "Meer", + "Mirwani", "Bhutta", "Randhawa", "Ghuman", "Gillani", "Naqash", "Abbassi", "Bohra", "Rajpoot", + "Siddiqui", "Qaim", "Shuja", "Irfan" +] + +BANKS_WITH_CODES = [ + ("Habib Bank", "HABB"), + ("MCB Bank", "MUCB"), + ("UBL", "UNIL"), + ("Bank Alfalah", "ALFH"), + ("Standard Chartered", "SCBL"), + ("Allied Bank", "ABPA"), + ("Meezan Bank", "MEZN"), + ("Bank of Punjab", "BPUN") +] + +SIM_PREFIXES = { + "Jazz": ["300", "301", "302", "303", "304", "305", "306", "307", "308", "309", "320", "321", "322", "323", "324", "325"], + "Zong": ["310", "311", "312", "313", "314", "315", "316", "317", "318", "319"], + "Ufone": ["330", "331", "332", "333", "334", "335", "336", "337"], + "Telenor": ["340", "341", "342", "343", "344", "345", "346", "347", "348", "349"] +} + +CASTES = ["Sheikh", "Ansari", "Raja", "Malik", "Qureshi", "Chaudhry", "Jutt", "Butt", "Rajput", "Rana","Mahar"] +SECTS = ["Sunni", "Shia"] + +# Province with City postcard +PROVINCE_CITIES = { + "Punjab": [ + ("Lahore", 54000), ("Faisalabad", 38000), ("Rawalpindi", 46000), + ("Multan", 60000), ("Gujranwala", 52250), ("Sialkot", 51310), + ("Bahawalpur", 63100), ("Sargodha", 40100), ("Sahiwal", 57000), + ("Dera Ghazi Khan", 32200),("Rahim yar khan",64200) + ], + "Sindh": [ + ("Karachi", 74000), ("Hyderabad", 71000), ("Sukkur", 65200), + ("Larkana", 77150), ("Mirpur Khas", 69000), ("Nawabshah", 67450), + ("Shikarpur", 64200), ("Khairpur", 66020), ("Jacobabad", 79000), + ("Thatta", 73130) + ], + "Khyber Pakhtunkhwa": [ + ("Peshawar", 25000), ("Mardan", 23200), ("Abbottabad", 22010), + ("Swat", 19130), ("Charsadda", 24420), ("Bannu", 28100), + ("Kohat", 26000), ("Dera Ismail Khan", 29050), ("Haripur", 22620), + ("Mansehra", 21300) + ], + "Balochistan": [ + ("Quetta", 87300), ("Gwadar", 91200), ("Sibi", 82000), + ("Khuzdar", 89100), ("Turbat", 92600), ("Chaman", 86000), + ("Zhob", 85200), ("Bela", 90150), ("Makran", 92500), + ("Pasni", 91300) + ], + "Gilgit Baltistan": [ + ("Gilgit", 15100), ("Skardu", 16100), ("Hunza", 15700), + ("Ghizer", 15200), ("Diamer", 14100), ("Astore", 14200), + ("Shigar", 16300), ("Kharmang", 16200) + ], + "Islamabad Capital Territory": [ + ("Islamabad", 44000) + ] +} + +INDUSTRIES_RANGES = [ + ("Information Technology", "IT", 50000, 250000), + ("Finance", "Finance", 40000, 200000), + ("Healthcare", "Healthcare", 30000, 180000), + ("Education", "Education", 25000, 120000), + ("Marketing & Media", "Marketing", 30000, 150000), + ("Government / Public Sector", "Government", 25000, 120000), + ("Engineering / Manufacturing", "Engineering", 35000, 180000), + ("Hospitality / Retail", "Retail", 20000, 100000), + ("Entrepreneur / Startup", "Entrepreneur", 50000, 300000), + ("Legal / Consulting", "Consulting", 40000, 200000), + ("Art & Entertainment", "Art", 25000, 200000), + ("Politics", "Politics", 50000, 300000), + ("Agriculture", "Agriculture", 15000, 80000), + ("Domestic & Personal Services", "Services", 12000, 60000), + ("Defense & Public Safety", "Defense", 30000, 150000) +] + +JOB_TITLE_MAPPING = { + "IT": [ + "Software Engineer", "Frontend Developer", "Backend Developer", + "Full Stack Developer", "DevOps Engineer", "Data Scientist","Game Developer","AI Engineer","IOT Engineer", + "Machine Learning Engineer", "AI Researcher", "Cybersecurity Engineer", + "Cloud Solutions Architect", "Mobile App Developer", "Blockchain Developer", + "QA / Test Engineer", "UI/UX Designer", "Network Engineer", "Database Administrator", + "IT Support Specialist", "Systems Analyst","Cloud Engineer","Data Analyst","Data Engineer" + ], + "Finance": [ + "Accountant", "Auditor", "Financial Analyst", "Investment Analyst", + "Tax Consultant", "Risk Manager", "Credit Analyst", "Loan Officer", + "Treasury Manager", "Financial Controller","CA","CFO" + ], + "Healthcare": [ + "Doctor", "Nurse", "Pharmacist", "Lab Technician", "Radiologist","Cardiologist","Gynecologist","Dermatologist","Cosmetologist", + "Medical Researcher", "Physiotherapist", "Dietitian", "Surgeon", "Psychologist","Oncologist","Pediatrician","Orthopedic Surgeon" + ], + "Education": [ + "Teacher", "Lecturer", "Associate Professor","Assistant Professor","Teaching Assistant(TA)", "Research Associate", + "Academic Coordinator", "Curriculum Designer", "Educational Consultant" + ], + "Marketing": [ + "Graphic Designer", "Content Writer", "Copywriter", "Video Editor", + "Animator", "Photographer", "Digital Marketing Specialist","Social Media Influencer", "SEO Specialist", + "Social Media Manager", "Art Director", "Marketing Manager", "Sales Executive" + ], + "Government": [ + "Civil Servant", "Policy Analyst", "Administrative Officer","Public Relations Officer", "Intelligence Analyst", + "Diplomat", "Law Enforcement Officer", "Urban Planner","Public Health Administrator","Emergency Management Specialist" + ], + "Engineering": [ + "Civil Engineer", "Mechanical Engineer", "Electrical Engineer", + "Chemical Engineer", "Industrial Engineer", "Production Manager", + "Quality Assurance Engineer","Environmental Engineer","Aerospace Engineer" + ], + "Retail": [ + "Hotel Manager", "Chef", "Waiter", "Waitress", "Store Manager","Product Manager", + "Sales Associate", "Customer Service Representative","Event Manager", "Tour Guide", "Travel Agent", + ], + "Entrepreneur": [ + "Entrepreneur", "Startup Founder", "Business Development Manager", + "Operations Manager", "Strategy Analyst", "Consultant","Investor Relations Manager", "Venture Capitalist" + ], + "Consulting": [ + "Legal Advisor", "Lawyer", "Advocate", "Compliance Officer", "Consultant","Business Analyst", + "Management Consultant", "Strategy Consultant" + ], + "Art": [ + "Painter", "Dancer", "Singer", "Actor", "Interior Designer","Event Planner", "Makeup Artist","Sketcher","Home Decorator", + "Sculptor", "Musician", "Choreographer", "Director", "Photographer","Fashion Designer","Cinematographer","Illustrator","UI/UX Designer" + ], + "Politics": [ + "Politician", "Member of National Assembly", "Senator","Governor", + "Minister", "Mayor", "Councillor", "Campaign Manager", "Political Consultant" + ], + "Agriculture": [ + "Farmer", "Farm Manager", "Tractor Driver", "Agronomist", + "Dairy Farmer", "Harvesting Worker", "Irrigation Specialist","Agricultural Engineer", "Livestock Specialist" + ], + "Services": [ + "Housekeeper", "Maid", "Private Driver", "Gardener", "Plumber", "Electrician","Carpenter","Miner","Welder","Mechanic", + "Security Guard", "Cook", "Nanny / Babysitter", "Caregiver", "Personal Assistant", "Laundry Worker","Driver" + ], + "Defense": [ + "Soldier", "Army Captain", "Police Officer", "Sub-Inspector", + "Firefighter", "Security Officer", "Rescue Warden", "Paramedic", + "Civil Defense Officer", "Emergency Medical Technician (EMT)", + ] +} + +COMPANY_INDUSTRIES = { + # 1. Information Technology (IT) + "Systems Limited": "IT", + "NetSol Technologies": "IT", + "Folio3 Software": "IT", + "Contour Software": "IT", + "NexGen Digital Solutions": "IT", + + # 2. Finance + "Habib Metropolitan Financials": "Finance", + "Pakistan Stock Exchange": "Finance", + "Lakson Investments": "Finance", + "JS Bank Limited": "Finance", + "Alfalah Capital": "Finance", + + # 3. Healthcare + "Shaukat Khanum Hospital": "Healthcare", + "Aga Khan University Hospital": "Healthcare", + "Chughtai Lab": "Healthcare", + "Indus Hospital Network": "Healthcare", + "Getz Pharma": "Healthcare", + + # 4. Education + "Beaconhouse School System": "Education", + "The City School": "Education", + "Roots Millennium Schools": "Education", + "KIPS Academy": "Education", + "NUST University": "Education", + + # 5. Marketing & Media + "Symmetry Group": "Marketing", + "Brainchild Communications": "Marketing", + "Adcom Leo Burnett": "Marketing", + "Interflow Communications": "Marketing", + "Red Communication Arts": "Marketing", + + # 6. Government / Public Sector + "Federal Board of Revenue (FBR)": "Government", + "NADRA Pakistan": "Government", + "Pakistan Post": "Government", + "WAPDA Pakistan": "Government", + "Capital Development Authority (CDA)": "Government", + + # 7. Engineering & Manufacturing + "Indus Motor Company (Toyota)": "Engineering", + "Pak Suzuki Motor Company": "Engineering", + "Descon Engineering": "Engineering", + "Lucky Cement Limited": "Engineering", + "Pak Arab Refinery (PARCO)": "Engineering", + + # 8. Hospitality / Retail + "Imtiaz Super Market": "Retail", + "Metro Cash & Carry Pakistan": "Retail", + "Al-Fatah Department Store": "Retail", + "Khaadi Retail": "Retail", + "Pearl Continental Hotels": "Retail", + + # 9. Entrepreneur / Startup + "Bazaar Technologies": "Entrepreneur", + "Dastgyr Technologies": "Entrepreneur", + "Jugnu Startup Labs": "Entrepreneur", + "Retailo Pakistan": "Entrepreneur", + "SastaTicket.pk": "Entrepreneur", + + # 10. Legal / Consulting + "A.F. Ferguson & Co. (PwC)": "Consulting", + "Abacus Consulting": "Consulting", + "KPMG Taseer Hadi": "Consulting", + "EY Ford Rhodes": "Consulting", + "BDO Ebrahim & Co.": "Consulting", + + # 11. Art & Entertainment + "Canvas Art Gallery": "Art", + "National College of Arts (NCA)": "Art", + "Coke Studio Pakistan": "Art", + "Ajoka Theatre Group": "Art", + "Ghazal Harmonies Studio": "Art", + + # 12. Politics + "Senate Secretariat": "Politics", + "National Assembly of Pakistan": "Politics", + "Policy Research Institute (PRIP)": "Politics", + "Citizens Coalition for Reforms": "Politics", + "Election Commission of Pakistan": "Politics", + + # 13. Agriculture + "Fauji Fertilizer Company (FFC)": "Agriculture", + "Engro Fertilizers": "Agriculture", + "Green Punjab Agri Farms": "Agriculture", + "Sindh Seed Corporation": "Agriculture", + "Zarai Taraqiati Bank (ZTBL)": "Agriculture", + + # 14. Domestic & Personal Services + "Domestic Ease Services": "Services", + "Lahore Security Services": "Services", + "Pak Plumbers & Electricians Network": "Services", + "Care & Cleaning Pakistan": "Services", + "Safe Hands Nanny Agency": "Services", + + # 15. Defense & Public Safety + "Pakistan Army": "Defense", + "Punjab Police Department": "Defense", + "Sindh Police Department": "Defense", + "Rescue 1122": "Defense", + "Civil Defense Department": "Defense", + "Fauji Security Services": "Defense" +} + +COMPANIES = list(COMPANY_INDUSTRIES.keys()) + +INSTITUTIONS = [ + # Universities — Punjab + ("University of the Punjab", "university", "Lahore"), + ("UCP - University of Central Punjab", "university", "Lahore"), + ("LUMS", "university", "Lahore"), + ("UET Lahore", "university", "Lahore"), + ("COMSATS Lahore", "university", "Lahore"), + ("GCU Lahore", "university", "Lahore"), + ("BZU Multan", "university", "Multan"), + ("NTU Faisalabad", "university", "Faisalabad"), + + # Universities — Sindh + ("University of Karachi", "university", "Karachi"), + ("NED University", "university", "Karachi"), + ("IBA Karachi", "university", "Karachi"), + ("SZABIST Karachi", "university", "Karachi"), + ("University of Sindh", "university", "Hyderabad"), + + # Universities — KPK + ("AWKUM - Abdul Wali Khan University", "university", "Peshawar"), + ("University of Peshawar", "university", "Peshawar"), + ("COMSATS Abbottabad", "university", "Abbottabad"), + ("Islamia College University", "university", "Peshawar"), + + # Universities — Islamabad + ("NUST", "university", "Islamabad"), + ("COMSATS Islamabad", "university", "Islamabad"), + ("IIUI - International Islamic University", "university", "Islamabad"), + ("Quaid-i-Azam University", "university", "Islamabad"), + ("Air University", "university", "Islamabad"), + ("FAST-NUCES Islamabad", "university", "Islamabad"), + + # Universities — Balochistan + ("University of Balochistan", "university", "Quetta"), + ("BUITEMS", "university", "Quetta"), + + # Colleges — Punjab + ("Government College Lahore", "college", "Lahore"), + ("Forman Christian College", "college", "Lahore"), + ("Punjab College Faisalabad", "college", "Faisalabad"), + ("Superior College Multan", "college", "Multan"), + + # Colleges — Sindh + ("DJ Sindh Government Science College", "college", "Karachi"), + ("Adamjee Government Science College", "college", "Karachi"), + + # Colleges — KPK + ("Edwardes College Peshawar", "college", "Peshawar"), + ("Islamia College Peshawar", "college", "Peshawar"), + + # Colleges — Islamabad + ("F.G. Sir Syed College", "college", "Islamabad"), + ("Islamabad Model College", "college", "Islamabad"), + + # Schools — spread across cities + ("Beaconhouse School", "school", "Lahore"), + ("The City School", "school", "Karachi"), + ("Roots Millennium", "school", "Islamabad"), + ("Froebel's International", "school", "Islamabad"), + ("Aitchison College", "school", "Lahore"), + ("Karachi Grammar School", "school", "Karachi"), + ("Peshawar Model School", "school", "Peshawar"), + ("Army Public School Rawalpindi", "school", "Rawalpindi"), +] + + +def initialize_database(db_path: str) -> None: + """Creates the schema and populates the SQLite database with all fake data records.""" + conn = sqlite3.connect(db_path) + try: + cursor = conn.cursor() + + # Create Schema + cursor.execute("DROP TABLE IF EXISTS industries") + cursor.execute(""" + CREATE TABLE industries ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + code TEXT NOT NULL UNIQUE, + min_salary INTEGER NOT NULL, + max_salary INTEGER NOT NULL + ) + """) + + cursor.execute("DROP TABLE IF EXISTS job_titles") + cursor.execute(""" + CREATE TABLE job_titles ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + industry_code TEXT NOT NULL, + FOREIGN KEY (industry_code) REFERENCES industries(code) + ) + """) + + cursor.execute("DROP TABLE IF EXISTS companies") + cursor.execute(""" + CREATE TABLE companies ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + industry_code TEXT, + FOREIGN KEY (industry_code) REFERENCES industries(code) + ) + """) + + cursor.execute("DROP TABLE IF EXISTS locations") + cursor.execute(""" + CREATE TABLE locations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + city TEXT NOT NULL UNIQUE, + province TEXT NOT NULL, + postal_code INTEGER NOT NULL + ) + """) + + cursor.execute("DROP TABLE IF EXISTS names") + cursor.execute(""" + CREATE TABLE names ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + type TEXT NOT NULL + ) + """) + + cursor.execute("DROP TABLE IF EXISTS sim_providers") + cursor.execute(""" + CREATE TABLE sim_providers ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE + ) + """) + + cursor.execute("DROP TABLE IF EXISTS sim_prefixes") + cursor.execute(""" + CREATE TABLE sim_prefixes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + provider_name TEXT NOT NULL, + prefix TEXT NOT NULL UNIQUE, + FOREIGN KEY (provider_name) REFERENCES sim_providers(name) + ) + """) + + cursor.execute("DROP TABLE IF EXISTS castes") + cursor.execute(""" + CREATE TABLE castes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE + ) + """) + + cursor.execute("DROP TABLE IF EXISTS sects") + cursor.execute(""" + CREATE TABLE sects ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE + ) + """) + + cursor.execute("DROP TABLE IF EXISTS banks") + cursor.execute(""" + CREATE TABLE banks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + iban_code TEXT NOT NULL UNIQUE + ) + """) + + cursor.execute("DROP TABLE IF EXISTS institutions") + cursor.execute(""" + CREATE TABLE institutions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + type TEXT NOT NULL, + city TEXT NOT NULL, + FOREIGN KEY (city) REFERENCES locations(city) + ) + """) + + # Insert Data + # 1. Industries + cursor.executemany( + "INSERT INTO industries (name, code, min_salary, max_salary) VALUES (?, ?, ?, ?)", + INDUSTRIES_RANGES + ) + + # 2. Job Titles + job_titles_data = [] + for code, titles in JOB_TITLE_MAPPING.items(): + for title in titles: + job_titles_data.append((title, code)) + cursor.executemany("INSERT INTO job_titles (title, industry_code) VALUES (?, ?)", job_titles_data) + + # 3. Companies + companies_data = [(name, code) for name, code in COMPANY_INDUSTRIES.items()] + cursor.executemany("INSERT INTO companies (name, industry_code) VALUES (?, ?)", companies_data) + + # 4. Locations + locations_data = [] + for province, cities in PROVINCE_CITIES.items(): + for city, code in cities: + locations_data.append((city, province, code)) + cursor.executemany("INSERT INTO locations (city, province, postal_code) VALUES (?, ?, ?)", locations_data) + + names_data = ( + [(name, 'male') for name in MALE_NAMES] + + [(name, 'female') for name in FEMALE_NAMES] + + [(name, 'last') for name in LAST_NAMES] + ) + cursor.executemany("INSERT INTO names (name, type) VALUES (?, ?)", names_data) + + # 6. SIM Providers and Prefixes + providers_data = [(provider,) for provider in SIM_PREFIXES.keys()] + cursor.executemany("INSERT INTO sim_providers (name) VALUES (?)", providers_data) + + prefixes_data = [] + for provider, prefixes in SIM_PREFIXES.items(): + for prefix in prefixes: + prefixes_data.append((provider, prefix)) + cursor.executemany("INSERT INTO sim_prefixes (provider_name, prefix) VALUES (?, ?)", prefixes_data) + + # 7. Castes + castes_data = [(caste,) for caste in CASTES] + cursor.executemany("INSERT INTO castes (name) VALUES (?)", castes_data) + + # 8. Sects + sects_data = [(sect,) for sect in SECTS] + cursor.executemany("INSERT INTO sects (name) VALUES (?)", sects_data) + + # 9. Banks + cursor.executemany("INSERT INTO banks (name, iban_code) VALUES (?, ?)", BANKS_WITH_CODES) + + #10. Institutions + cursor.executemany( + "INSERT INTO institutions (name, type, city) VALUES (?, ?, ?)", + INSTITUTIONS + ) + + + conn.commit() + finally: + conn.close() + + +if __name__ == "__main__": + db_file = os.path.join(os.path.dirname(__file__), "faker_pk.db") + initialize_database(db_file) + print("Database successfully initialized!") \ No newline at end of file diff --git a/faker_pk/personal.py b/faker_pk/personal.py index 19d52bb..0c27090 100644 --- a/faker_pk/personal.py +++ b/faker_pk/personal.py @@ -1,164 +1,77 @@ import random from datetime import date, timedelta - -MALE_NAMES = [ - "Ahmed", "Muhammad", "Ali", "Hassan", "Hussain", "Bilal", "Hamza", "Umar", "Usman", "Abdullah", - "Abdul Rahman", "Abdul Rehman", "Abdul Basit", "Abdul Hadi", "Abdul Wahab", "Abdul Samad", "Abdul Qadir", "Abdul Majeed", "Abdul Rauf", "Abdul Aziz", - "Abdul Kareem", "Abdul Aleem", "Abdul Ghaffar", "Abdul Ghani", "Abdul Haq", "Abdul Malik", "Abdul Shakoor", "Abdul Sattar", "Abdul Wasi", "Ahmad", - "Zeeshan", "Danish", "Faizan", "Fahad", "Waleed", "Zain", "Saad", "Ahsan", "Adeel", "Asad", - "Arsalan", "Shahzaib", "Shehryar", "Salman", "Noman", "Omer", "Tahir", "Talha", "Kashif", "Kamran", - "Shahid", "Naveed", "Imran", "Junaid", "Farhan", "Faisal", "Khalid", "Raza", "Rizwan", "Adnan", - "Arif", "Yasir", "Irfan", "Zubair", "Shayan", "Sameer", "Umair", "Huzaifa", "Ayaan", "Rayyan", - "Azaan", "Areeb", "Raheel", "Sufyan", "Haris", "Anas", "Arham", "Asim", "Moiz", "Hashir", - "Ibtisam", "Saif", "Ilyas", "Ismail", "Ibrahim", "Eesa", "Musa", "Yousuf", "Dawood", "Yunus", - "Nuh", "Luqman", "Taimoor", "Murtaza", "Baqir", "Rayan", "Shahmeer", "Shaheer", "Daniyal", "Arsab", - "Zarar", "Zaryab", "Aafaq", "Abrar", "Adil", "Amaan", "Amjad", "Anees", "Anwar", "Aqeel", - "Arqam", "Arsal", "Asghar", "Ashar", "Atif", "Awais", "Ayaz", "Azhar", "Azlan", "Barkat", - "Basim", "Babar", "Burhan", "Ehtisham", "Ehsan", "Faraz", "Farid", "Fawad", "Feroz", "Ghazanfar", - "Haider", "Hammad", "Hamid", "Hasan", "Haseeb", "Hashim", "Hisham", "Huzaifah", "Ijaz", "Imad", - "Inam", "Javed", "Kamal", "Khalil", "Khizar", "Mahad", "Mahir", "Mansoor", "Maaz", "Mazhar", - "Mehdi", "Muneeb", "Mustafa", "Naeem", "Nouman", "Qasim", "Rameez", "Rehan", "Sadiq", "Safeer", - "Saifullah", "Sarfaraz", "Shahbaz", "Shafqat", "Shafiq", "Sharjeel", "Shehzad", "Sohail", "Subhan", "Sultan", - "Tabish", "Talal", "Tauseef", "Tufail", "Ubaid", "Umer", "Usama", "Wajid", "Waqas", "Wasif", - "Yasir", "Yawar", "Yameen", "Yasin", "Zakariya", "Zaman", "Zawwar", "Zia", "Zohaib", "Zubair", - "Zainul Abidin", "Irham", "Taha", "Irtaza", "Reza", "Ehtesham", "Mirza", "Azim", "Saqib", "Shabbir", - "Tahseen", "Salman", "Fahim", "Jawad", "Sarmad", "Nabeel", "Faiq", "Rashid", "Rahim", "Habib", - "Munir", "Zameer", "Akram", "Zafar", "Wasim", "Nauman", "Nasir", "Khalil", "Jibran", "Kashan", - "Emaan", "Adi", "Fakhir", "Sibtain", "Farooq", "Saifur", "Faiz", "Nisar", "Salman", "Arqam", - "Rifat", "Tahmid", "Zohair", "Zaeem", "Jawwad", "Sarmal", "Arsal", "Areez", "Rizq", "Sarim", - "Zayyan", "Razaq", "Azfar", "Affan", "Hanzala", "Hammad", "Fuzail", "Ziyad", "Adeel", "Jameel", - "Karim", "Qadeer", "Hanan", "Rameen", "Tahal", "Shahroz", "Saamir", "Tahaib", "Yasrab", "Ammar", - "Shakir", "Rauf", "Sameel", "Tahseen", "Dani", "Azaib", "Hamdaan", "Maheer", "Uzair", "Sharif", - "Zarrar", "Firas", "Azmat", "Riaz", "Munawwar", "Kaleem", "Sufyan", "Zameel", "Sajid", "Sarim", - "Tabriz", "Yasin", "Aniq", "Rizal", "Zaid", "Irteza", "Aabid", "Zawar", "Aneeb", "Moazzam", - "Fida", "Najam", "Tauqeer", "Shakeel", "Rashad", "Rameel", "Najeeb", "Basit", "Fawzan" -] - -FEMALE_NAMES = [ - "Aaliya", "Aaminah", "Aamna", "Aaniya", "Aanisa", "Aasia", "Aasma", "Abida", "Adeela", "Adeelah", - "Afifa", "Afsheen", "Afza", "Afreen", "Aisha", "Aiman", "Aina", "Aini", "Aiza", "Aleena", - "Aleesha", "Aleya", "Aliya", "Alishba", "Amal", "Amara", "Amber", "Ameena", "Amira", "Anabia", - "Anaya", "Anila", "Aniqa", "Anisa", "Anum", "Anusha", "Anzela", "Aqsa", "Arfa", "Arisha", - "Arwa", "Asfa", "Asfiya", "Asma", "Asmara", "Atiya", "Ayesha", "Ayra", "Aysha", "Azka", - "Azra", "Basma", "Batool", "Benish", "Bisma", "Bushra", "Dur-e-Fatima", "Dua", "Eeman", "Eesha", - "Eiman", "Eliza", "Eman", "Emna", "Erum", "Esma", "Faiza", "Fakhra", "Falaq", "Falak", - "Fariha", "Farisha", "Farwah", "Farzana", "Fatima", "Fauzia", "Fiza", "Ghazal", "Ghazala", "Gulnaz", - "Gulrukh", "Habiba", "Hafsa", "Haleema", "Hania", "Hanin", "Hareem", "Haseena", "Hiba", "Hifza", - "Hina", "Hira", "Humaira", "Humna", "Iffat", "Ifra", "Imaan", "Inaya", "Iqra", "Iram", - "Irum", "Isha", "Ishaal", "Ishmal", "Isra", "Jameela", "Javeria", "Jannat", "Jasmin", "Jaweria", - "Jiya", "Kainat", "Khadija", "Khadijah", "Khansa", "Kiran", "Komal", "Laiba", "Laila", "Laraib", - "Lina", "Lubna", "Mahira", "Mahjabeen", "Mahnoor", "Mahrukh", "Maha", "Maliha", "Marium", "Maria", - "Mariam", "Marwa", "Maryam", "Maheen", "Mehak", "Mehr", "Mehwish", "Minal", "Mishal", "Misbah", - "Mona", "Mubashira", "Muqaddas", "Mysha", "Nabeela", "Nadia", "Nafisa", "Naila", "Najma", "Nashitah", - "Natasha", "Naureen", "Nayyab", "Neha", "Nida", "Nimra", "Nishat", "Noreen", "Nosheen", "Nusrat", - "Nuha", "Obaidah", "Parveen", "Qandeel", "Quratulain", "Rabiya", "Rabia", "Rafia", "Rafiya", "Rameen", - "Rania", "Raniah", "Rashida", "Rida", "Rimsha", "Rizwana", "Roha", "Romana", "Roohi", "Ruba", - "Rubina", "Rukhsar", "Rumaisa", "Ruqayya", "Saba", "Sabeen", "Sabahat", "Sadia", "Sadiaa", "Sadiya", - "Safia", "Sahar", "Saira", "Sajida", "Sakina", "Salma", "Sameena", "Samina", "Samiya", "Sana", - "Saniya", "Sanober", "Sara", "Sarah", "Sasha", "Shabana", "Shagufta", "Shaheen", "Shaista", "Shakila", - "Shamaila", "Shamim", "Shanza", "Shazia", "Sheeba", "Shehnaz", "Shifa", "Shiza", "Sobia", "Sonia", - "Subha", "Subhana", "Suhana", "Sumaira", "Sumaiya", "Sundus", "Tabinda", "Tabassum", "Taha", "Tahira", - "Tahreem", "Tahirah", "Tania", "Tanisha", "Tanzeela", "Tayyaba", "Tehreem", "Tooba", "Ujala", "Umama", - "Umayrah", "Ume Habiba", "Ume Hani", "Ume Kulsoom", "Umme Hani", "Umme Rubab", "Umme Salma", "Umaima", "Umairah", "Urooj", - "Urwa", "Uzma", "Wajiha", "Warda", "Wardah", "Yasira", "Yasmeen", "Yumna", "Zainab", "Zakia", - "Zainah", "Zahida", "Zahra", "Zaib", "Zakia", "Zakiaa", "Zameena", "Zareen", "Zarish", "Zarmeen", - "Zarqa", "Zartaj", "Zaryab", "Zehra", "Zeenat", "Zia", "Zimal", "Zinia", "Zobia", "Zohra", - "Zonaira", "Zoya", "Zubaida", "Zulekha", "Zunaira", "Zunisha", "Zunnoor", "Aamira", "Adeeba", "Areeba", - "Armeen", "Arooba", "Asiya", "Aqleema", "Aqra", "Armina", "Asra", "Ayisha", "Azima", "Bareera", - "Bushra", "Dania", "Daniah", "Dua", "Eimaan", "Elina", "Elaf", "Erum", "Esha", "Eshaal", - "Fakhira", "Faria", "Fariyal", "Fawzia", "Fareeha", "Farheen", "Fizza", "Gulshan", "Gulzar", "Hadia", - "Hajra", "Haleemah", "Hamna", "Haniyah", "Harema", "Huma", "Humera", "Huriya", "Inara", "Insha", - "Iqra", "Irsa", "Ishaqueen", "Ishrat", "Jannat", "Jawaria", "Javeriya", "Kanza", "Khadeeja", "Khizra", - "Komila", "Laiba", "Laila", "Lamees", "Mahgul", "Mahnoor", "Mahwish", "Maiza", "Mamoona", "Manahil", - "Mariam", "Marrium", "Marjan", "Maryam", "Mehreen", "Mehrunisa", "Minal", "Mishka", "Mishaal", "Mishkaat", - "Mona", "Muqaddas", "Myra", "Naheed", "Naila", "Nimrah", "Noreen", "Noshaba", "Nusrat", "Pari", - "Parveen", "Qurat", "Rabi", "Rafia", "Ramsha", "Rania", "Rija", "Rimsha", "Rishma", "Roohi", - "Romaisa", "Ruba", "Rukhsana", "Saba", "Sabiha", "Saeeda", "Sahar", "Sajida", "Sakina", "Salma", - "Sameera", "Samra", "Sania", "Saniya", "Saniah", "Saniaa", "Sania", "Saniaa", "Saniah", "Samiha" -] - -LAST_NAMES = [ - "Abbasi", "Abbas", "Abid", "Afzal", "Ahmad", "Ahmed", "Akbar", "Akhter", "Alam", "Ali", - "Amjad", "Anjum", "Ansari", "Arif", "Asad", "Ashfaq", "Asghar", "Aslam", "Atif", "Awan", - "Azam", "Azhar", "Babar", "Baig", "Bajwa", "Bakht", "Baloch", "Bangash", "Basit", "Batool", - "Bhatti", "Bukhari", "Butt", "Chaudhry", "Cheema", "Chishti", "Dar", "Danish", "Daud", "Deen", - "Durrani", "Ejaz", "Fahim", "Faheem", "Farid", "Farooq", "Farrukh", "Fazal", "Feroz", "Ghafoor", - "Ghani", "Ghazanfar", "Ghaznavi", "Ghauri", "Ghulam", "Gohar", "Habib", "Hadi", "Hafeez", "Hafiz", - "Haider", "Hameed", "Hamid", "Hanif", "Hashim", "Hasnain", "Hassan", "Hayat", "Hussain", "Hyder", - "Iftikhar", "Ijaz", "Ilyas", "Imam", "Imran", "Inam", "Iqbal", "Irshad", "Ismail", "Ishaq", - "Jadoon", "Jahangir", "Jamal", "Jamali", "Jamshed", "Javed", "Jawad", "Kabir", "Kadir", "Kaleem", - "Kamran", "Kamil", "Karim", "Kashif", "Kazmi", "Khalid", "Khalil", "Khan", "Khizar", "Khurram", - "Latif", "Mahmood", "Malik", "Manzoor", "Masood", "Mazhar", "Mehmood", "Mir", "Mirza", "Moin", - "Mohsin", "Moinuddin", "Monis", "Mubashir", "Mujeeb", "Mukhtar", "Munir", "Murad", "Mustafa", "Murtaza", - "Nadeem", "Naeem", "Naseem", "Nasir", "Nawaz", "Niaz", "Noor", "Noman", "Numan", "Obaid", - "Qadir", "Qaiser", "Qamar", "Qasim", "Qayyum", "Qureshi", "Rafiq", "Rafique", "Rahim", "Raja", - "Rameez", "Rana", "Rasheed", "Rashid", "Rauf", "Raza", "Razzaq", "Rehman", "Riaz", "Rizwan", - "Sabir", "Sadiq", "Safeer", "Safi", "Saeed", "Safiullah", "Sajid", "Salim", "Saleem", "Salman", - "Sami", "Sarfaraz", "Sarfraz", "Shafi", "Shafique", "Shahid", "Shakeel", "Sharif", "Shaukat", "Sheikh", - "Shehzad", "Sheraz", "Shoukat", "Siddiq", "Siddique", "Sohail", "Suleman", "Sultan", "Tahir", "Talib", - "Tariq", "Tufail", "Ubaid", "Umar", "Usman", "Waheed", "Wali", "Waseem", "Yaseen", "Yasin", - "Yousaf", "Younas", "Yousuf", "Zafar", "Zahid", "Zakir", "Zaman", "Zameer", "Zarif", "Zubair", - "Abbass", "Aftab", "Akram", "Alvi", "Anwar", "Ashraf", "Aziz", "Badar", "Bari", "Bashir", - "Basharat", "Bostan", "Burhan", "Chughtai", "Dawar", "Ejaz", "Ehsan", "Faisal", "Farhan", "Fazal", - "Gul", "Gulzar", "Haqqani", "Hashmi", "Hayee", "Hussaini", "Jalil", "Jamil", "Junaid", "Kabir", - "Kamal", "Khawaja", "Khattak", "Kiani", "Khoso", "Khokhar", "Khosa", "Langrial", "Lodhi", "Mahmud", - "Malook", "Mandokhel", "Memon", "Mughal", "Naqvi", "Naseer", "Niazi", "Orakzai", "Pathan", "Pirzada", - "Qadri", "Qasmi", "Rashidi", "Saboor", "Sadaqat", "Sajjad", "Saleh", "Samiullah", "Sarwar", "Shafiullah", - "Shah", "Shahbaz", "Shahzad", "Shams", "Sharafat", "Sharifuddin", "Shoaib", "Sikandar", "Sohaib", "Subhan", - "Tabassum", "Taha", "Talha", "Tanveer", "Tauqeer", "Tariq", "Uddin", "Wahid", "Wajid", "Waqas", - "Yar", "Yaseer", "Zaheer", "Zaman", "Zarrar", "Zeeshan", "Zia", "Zubairi", "Agha", "Aliani", - "Ansari", "Arain", "Asmat", "Atta", "Baber", "Balochi", "Bangulzai", "Bhinder", "Chohan", "Chughtai", - "Dasti", "Daudpota", "Dogar", "Domki", "Fani", "Gabol", "Gandapur", "Gill", "Goraya", "Gulshan", - "Hingoro", "Hoti", "Jakhrani", "Jatoi", "Junejo", "Kalhoro", "Kakar", "Kashmiri", "Khoso", "Khatri", - "Kundi", "Laghari", "Langah", "Leghari", "Liaqat", "Lodhi", "Mahsud", "Magsi", "Marwat", "Mashwani", - "Mengal", "Mirani", "Mitha", "Mugheri", "Mundokhel", "Naseeruddin", "Niazi", "Orakzai", "Qaimkhani", "Qasoori", - "Rajput", "Rind", "Samar", "Samad", "Sandhu", "Sarai", "Sayyid", "Shahwani", "Sheerazi", "Sial", - "Sindhu", "Soomro", "Soomra", "Suddiqi", "Syed", "Talpur", "Tanoli", "Tarin", "Tiwana", "Toor", - "Turk", "Wazir", "Yusufzai", "Zehri", "Zuberi", "Awan", "Kiyani", "Khakwani", "Taj", "Meer", - "Mirwani", "Chohan", "Bhutta", "Randhawa", "Sandhu", "Ghuman", "Raja", "Gillani", "Kazmi", "Naqash", - "Abbassi", "Bohra", "Hanjra", "Rajpoot", "Siddiqui", "Qaim", "Shuja", "Qamar", "Irfan", "Talpur" -] - -SIM_PROVIDERS = ["Jazz", "Zong", "Ufone", "Telenor", "Warid", "Onic"] - -def sim_provider(): - return random.choice(SIM_PROVIDERS) - - -CASTES = ["Sheikh", "Ansari", "Raja", "Malik", "Qureshi", "Chaudhry", "Jutt", "Butt", "Rajput", "Rana"] -SECTS = ["Sunni", "Shia"] +from .utils import query_value, query_list + + +def sim_provider(): + """Return a random SIM provider name.""" + return query_value("SELECT name FROM sim_providers ORDER BY RANDOM() LIMIT 1") + def caste(): - return random.choice(CASTES) + """Return a random caste name.""" + return query_value("SELECT name FROM castes ORDER BY RANDOM() LIMIT 1") + def sect(): - return random.choice(SECTS) - + """Return a random sect name.""" + return query_value("SELECT name FROM sects ORDER BY RANDOM() LIMIT 1") + + def dob(start_year=1950, end_year=2006): - """Generate a random date of birth between start_year and end_year""" + """Generate a random date of birth between start_year and end_year.""" start = date(start_year, 1, 1) - end = date(end_year, 12, 31) + end = date(end_year, 12, 31) delta = end - start random_days = random.randint(0, delta.days) return start + timedelta(days=random_days) -def male_name(): + +def male_name(): """Generate a random male full name.""" - return random.choice(MALE_NAMES) + " " + random.choice(LAST_NAMES) - + first = query_value("SELECT name FROM names WHERE type = 'male' ORDER BY RANDOM() LIMIT 1") + last = query_value("SELECT name FROM names WHERE type = 'last' ORDER BY RANDOM() LIMIT 1") + return f"{first} {last}" + + def female_name(): """Generate a random female full name.""" - return random.choice(FEMALE_NAMES) + " " + random.choice(LAST_NAMES) + first = query_value("SELECT name FROM names WHERE type = 'female' ORDER BY RANDOM() LIMIT 1") + last = query_value("SELECT name FROM names WHERE type = 'last' ORDER BY RANDOM() LIMIT 1") + return f"{first} {last}" -def cnic(): + +def cnic(gender=None): + """Generate a random CNIC. + If gender is 'male' or 'm', the last digit is odd. + If gender is 'female' or 'f', the last digit is even. + """ part1 = str(random.randint(10000, 99999)) part2 = str(random.randint(1000000, 9999999)) - part3 = str(random.randint(0, 9)) - return f"{part1}-{part2}-{part3}" -def phone_number(): - prefixes = ["300", "301", "302", "303", "304", "305", "306", "307", "308", "309", - "310", "311", "312", "313", "314", "315", "316", "317", "318", "319"] + if gender and gender.lower() in ('male', 'm'): + part3 = str(random.choice([1, 3, 5, 7, 9])) + elif gender and gender.lower() in ('female', 'f'): + part3 = str(random.choice([0, 2, 4, 6, 8])) + else: + part3 = str(random.randint(0, 9)) + + return f"{part1}-{part2}-{part3}" + + +def phone_number(provider=None): + """Generate a random Pakistani phone number. + If provider is specified, use that provider's prefixes only. + """ + if provider: + prefixes = query_list( + "SELECT prefix FROM sim_prefixes WHERE provider_name = ?", (provider,) + ) + if not prefixes: + raise ValueError(f"SIM provider '{provider}' not found.") + else: + prefixes = query_list("SELECT prefix FROM sim_prefixes") + prefix = random.choice(prefixes) remaining = str(random.randint(1000000, 9999999)) return f"+92{prefix}{remaining}" \ No newline at end of file diff --git a/faker_pk/provider.py b/faker_pk/provider.py index 3bfbdfa..dfb5c4c 100644 --- a/faker_pk/provider.py +++ b/faker_pk/provider.py @@ -1,114 +1,89 @@ -from faker.providers import BaseProvider -import random -from .personal import ( - male_name, female_name, cnic, phone_number, sim_provider, caste, sect, dob -) +from faker.providers import BaseProvider +from .personal import male_name, female_name, cnic, phone_number, sim_provider, caste, sect, dob from .address import city, province, full_address -from .company import ( - company_name, industry_name, job_title, job_title_with_industry, - salary, bank_name, iban -) - +from .company import company_name, industry_name, job_title, job_title_with_industry, salary, bank_name, iban +from .education import institution, student_dob, student_profile class FakerPKProvider(BaseProvider): """Faker provider for Pakistani personal info, addresses, companies, jobs, and more.""" + def _generate_multiple(self, func, count, **kwargs): + """Generate one or many values based on count.""" + if count == 1: + return func(**kwargs) + return [func(**kwargs) for _ in range(count)] + # -------------------- # Personal Info # -------------------- def pk_male_name(self, count=1): - if count == 1: - return male_name() - return [male_name() for _ in range(count)] + return self._generate_multiple(male_name, count) def pk_female_name(self, count=1): - if count == 1: - return female_name() - return [female_name() for _ in range(count)] + return self._generate_multiple(female_name, count) - def pk_cnic(self, count=1): - if count == 1: - return cnic() - return [cnic() for _ in range(count)] + def pk_cnic(self, count=1, gender=None): + return self._generate_multiple(cnic, count, gender=gender) - def pk_phone_number(self, count=1): - if count == 1: - return phone_number() - return [phone_number() for _ in range(count)] + def pk_phone_number(self, count=1, provider=None): + return self._generate_multiple(phone_number, count, provider=provider) def pk_sim_provider(self, count=1): - if count == 1: - return sim_provider() - return [sim_provider() for _ in range(count)] + return self._generate_multiple(sim_provider, count) def pk_caste(self, count=1): - if count == 1: - return caste() - return [caste() for _ in range(count)] + return self._generate_multiple(caste, count) def pk_sect(self, count=1): - if count == 1: - return sect() - return [sect() for _ in range(count)] + return self._generate_multiple(sect, count) def pk_dob(self, count=1): - if count == 1: - return dob() - return [dob() for _ in range(count)] + return self._generate_multiple(dob, count) # -------------------- # Address Info # -------------------- - def pk_city(self, count=1): - if count == 1: - return city() - return [city() for _ in range(count)] + def pk_city(self, count=1, province=None): + return self._generate_multiple(city, count, province=province) - def pk_province(self, count=1): - if count == 1: - return province() - return [province() for _ in range(count)] + def pk_province(self, count=1, city=None): + return self._generate_multiple(province, count, city=city) - def pk_full_address(self, count=1): - if count == 1: - return full_address() - return [full_address() for _ in range(count)] + def pk_full_address(self, count=1, city=None, province=None): + return self._generate_multiple(full_address, count, city=city, province=province) # -------------------- # Company Info # -------------------- - def pk_company_name(self, count=1): - if count == 1: - return company_name() - return [company_name() for _ in range(count)] + def pk_company_name(self, count=1, industry=None): + return self._generate_multiple(company_name, count, industry=industry) def pk_industry_name(self, count=1): - if count == 1: - return industry_name() - return [industry_name() for _ in range(count)] + return self._generate_multiple(industry_name, count) + + def pk_bank_name(self, count=1): + return self._generate_multiple(bank_name, count) + + def pk_iban(self, count=1, bank=None): + return self._generate_multiple(iban, count, bank=bank) + # -------------------- + # Job Info + # -------------------- def pk_job_title(self, count=1, industry=None): - if count == 1: - return job_title(industry=industry) - return [job_title(industry=industry) for _ in range(count)] + return self._generate_multiple(job_title, count, industry=industry) def pk_job_title_with_industry(self, count=1): - if count == 1: - return job_title_with_industry() - return [job_title_with_industry() for _ in range(count)] + return self._generate_multiple(job_title_with_industry, count) def pk_salary(self, count=1, industry=None): - if count == 1: - return salary(industry=industry) - return [salary(industry=industry) for _ in range(count)] - - def pk_bank_name(self, count=1): - if count == 1: - return bank_name() - return [bank_name() for _ in range(count)] + return self._generate_multiple(salary, count, industry=industry) + # -------------------- + # Institution Info + # -------------------- + def institution(self, count=1, level=None, city=None, province=None): + return self._generate_multiple(institution, count, level=level, city=city, province=province) - def pk_iban(self, count=1): - if count == 1: - return iban() - return [iban() for _ in range(count)] + def student_profile(self, count=1, level=None, province=None): + return self._generate_multiple(student_profile, count, level=level, province=province) \ No newline at end of file diff --git a/faker_pk/utils.py b/faker_pk/utils.py index e69de29..e7dab92 100644 --- a/faker_pk/utils.py +++ b/faker_pk/utils.py @@ -0,0 +1,50 @@ +import sqlite3 +import os + +DB_PATH = os.path.join(os.path.dirname(__file__), "faker_pk.db") +_db_initialized = False + + +def ensure_db_exists(): + """Initialize the database if it does not exist yet.""" + global _db_initialized + if _db_initialized: + return + if not os.path.exists(DB_PATH): + try: + from .initialize_db import initialize_database + initialize_database(DB_PATH) + except Exception as e: + import warnings + warnings.warn(f"faker-pk: could not auto-initialize database: {e}") + _db_initialized = True + + +def _run(query, params, fetch): + """Internal helper: open connection, run query, return fetched result.""" + ensure_db_exists() + with sqlite3.connect(DB_PATH) as conn: + cursor = conn.cursor() + cursor.execute(query, params) + return fetch(cursor) + + +def query_value(query, params=()): + """Return the first column of the first matching row.""" + row = _run(query, params, lambda c: c.fetchone()) + return row[0] if row else None + + +def query_row(query, params=()): + """Return the first matching row as a tuple.""" + return _run(query, params, lambda c: c.fetchone()) + + +def query_list(query, params=()): + """Return a flat list of the first column from all matching rows.""" + return _run(query, params, lambda c: [r[0] for r in c.fetchall()]) + + +def query_rows(query, params=()): + """Return all matching rows as a list of tuples.""" + return _run(query, params, lambda c: c.fetchall()) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 4aeae90..3714935 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] - +[tool.setuptools.package-data] +faker_pk = ["*.db"] [project.urls] "Homepage" = "https://github.com/Khubaib8281/faker-pk" \ No newline at end of file diff --git a/tests/test_institution_filtered_by_city.py b/tests/test_institution_filtered_by_city.py new file mode 100644 index 0000000..c9d6592 --- /dev/null +++ b/tests/test_institution_filtered_by_city.py @@ -0,0 +1,8 @@ +from faker_pk import FakerPK +from faker_pk.utils import query_value + +def test_institution_filtered_by_city(faker_pk: FakerPK): + inst = faker_pk.institution(city="Lahore") + assert isinstance(inst, str) + inst_city = query_value("SELECT city FROM institutions WHERE name = ?", (inst,)) + assert inst_city == "Lahore" diff --git a/tests/test_institution_filtered_by_level.py b/tests/test_institution_filtered_by_level.py new file mode 100644 index 0000000..ef5406d --- /dev/null +++ b/tests/test_institution_filtered_by_level.py @@ -0,0 +1,8 @@ +from faker_pk import FakerPK +from faker_pk.utils import query_value + +def test_institution_filtered_by_level(faker_pk: FakerPK): + inst = faker_pk.institution(level="university") + assert isinstance(inst, str) + inst_type = query_value("SELECT type FROM institutions WHERE name = ?", (inst,)) + assert inst_type == "university" diff --git a/tests/test_institution_filtered_by_province.py b/tests/test_institution_filtered_by_province.py new file mode 100644 index 0000000..95ffd39 --- /dev/null +++ b/tests/test_institution_filtered_by_province.py @@ -0,0 +1,11 @@ +from faker_pk import FakerPK +from faker_pk.utils import query_value + +def test_institution_filtered_by_province(faker_pk: FakerPK): + inst = faker_pk.institution(province="Khyber Pakhtunkhwa") + assert isinstance(inst, str) + inst_province = query_value( + "SELECT l.province FROM institutions i JOIN locations l ON i.city = l.city WHERE i.name = ?", + (inst,) + ) + assert inst_province == "Khyber Pakhtunkhwa" diff --git a/tests/test_realistic_combinations.py b/tests/test_realistic_combinations.py new file mode 100644 index 0000000..3a90409 --- /dev/null +++ b/tests/test_realistic_combinations.py @@ -0,0 +1,149 @@ +import os +import pytest +from faker_pk.utils import query_value + + +class TestCNICGender: + """Verify CNIC last digit aligns with gender.""" + + def test_male_cnic_ends_odd(self, faker_pk): + for _ in range(50): + c = faker_pk.cnic(gender='male') + last_digit = int(c[-1]) + assert last_digit % 2 == 1, f"Male CNIC should end odd, got {c}" + + def test_female_cnic_ends_even(self, faker_pk): + for _ in range(50): + c = faker_pk.cnic(gender='female') + last_digit = int(c[-1]) + assert last_digit % 2 == 0, f"Female CNIC should end even, got {c}" + + def test_shorthand_gender_m(self, faker_pk): + for _ in range(20): + c = faker_pk.cnic(gender='m') + assert int(c[-1]) % 2 == 1 + + def test_shorthand_gender_f(self, faker_pk): + for _ in range(20): + c = faker_pk.cnic(gender='f') + assert int(c[-1]) % 2 == 0 + + def test_no_gender_still_valid(self, faker_pk): + c = faker_pk.cnic() + assert isinstance(c, str) + assert len(c) == 15 + + +class TestAddressConsistency: + """Verify city, province, and postal code combinations are realistic.""" + + def test_city_filtered_by_province(self, faker_pk): + city = faker_pk.city(province="Sindh") + # Verify this city actually belongs to Sindh in the DB + province = query_value( + "SELECT province FROM locations WHERE city = ?", (city,) + ) + assert province == "Sindh" + + def test_province_for_known_city(self, faker_pk): + province = faker_pk.province(city="Lahore") + assert province == "Punjab" + + def test_full_address_with_city(self, faker_pk): + addr = faker_pk.full_address(city="Karachi") + assert "Karachi" in addr + assert "Sindh" in addr + assert "74000" in addr + + def test_full_address_with_province(self, faker_pk): + addr = faker_pk.full_address(province="Balochistan") + assert "Balochistan" in addr + # Verify the city in the address actually belongs to Balochistan + balochistan_cities = query_value( + "SELECT city FROM locations WHERE province = 'Balochistan' AND ? LIKE '%' || city || '%'", + (addr,) + ) + assert balochistan_cities is not None + + +class TestBankIBAN: + """Verify IBAN contains the correct bank identifier code.""" + + def test_iban_with_specific_bank(self, faker_pk): + iban_code = query_value( + "SELECT iban_code FROM banks WHERE name = ?", ("Meezan Bank",) + ) + iban = faker_pk.iban(bank="Meezan Bank") + assert iban.startswith("PK") + assert iban_code in iban + + def test_iban_format(self, faker_pk): + iban = faker_pk.iban() + assert iban.startswith("PK") + assert len(iban) > 10 + + def test_invalid_bank_raises(self, faker_pk): + with pytest.raises(ValueError): + faker_pk.iban(bank="Nonexistent Bank") + + +class TestPhoneProvider: + """Verify phone numbers use correct provider prefixes.""" + + def test_phone_with_specific_provider(self, faker_pk): + from faker_pk.utils import query_list + zong_prefixes = query_list( + "SELECT prefix FROM sim_prefixes WHERE provider_name = ?", ("Zong",) + ) + phone = faker_pk.phone_number(provider="Zong") + # Strip +92, the next 3 digits should be a Zong prefix + prefix = phone[3:6] + assert prefix in zong_prefixes, f"Prefix {prefix} not in Zong prefixes" + + def test_invalid_provider_raises(self, faker_pk): + with pytest.raises(ValueError): + faker_pk.phone_number(provider="NonexistentProvider") + + +class TestCompanyIndustry: + """Verify company names filter correctly by industry.""" + + def test_company_filtered_by_industry(self, faker_pk): + company = faker_pk.company_name(industry="IT") + # Verify this company actually belongs to IT in the DB + industry_code = query_value( + "SELECT industry_code FROM companies WHERE name = ?", (company,) + ) + assert industry_code == "IT" + + def test_invalid_industry_raises(self, faker_pk): + with pytest.raises(ValueError): + faker_pk.company_name(industry="FakeIndustry") + + +class TestAutoInitialization: + """Verify the database auto-creates if the .db file is missing.""" + + def test_db_recreates_at_new_path(self, tmp_path): + import faker_pk.utils as utils_module + + test_db_path = str(tmp_path / "faker_pk.db") + original_path = utils_module.DB_PATH + + try: + # Point utils to a path where no DB exists + utils_module.DB_PATH = test_db_path + utils_module._db_initialized = False + + # Confirm no DB exists yet + assert not os.path.exists(test_db_path) + + # This query should trigger auto-initialization + result = utils_module.query_value("SELECT COUNT(*) FROM names") + assert result is not None + assert result > 0 + assert os.path.exists(test_db_path) + finally: + # Restore original path and reset flag + utils_module.DB_PATH = original_path + utils_module._db_initialized = False \ No newline at end of file diff --git a/tests/test_student_dob.py b/tests/test_student_dob.py new file mode 100644 index 0000000..991f2a5 --- /dev/null +++ b/tests/test_student_dob.py @@ -0,0 +1,20 @@ +from datetime import date +from faker_pk.education import student_dob + +def test_student_dob_age_ranges(): + today = date.today() + + for _ in range(20): + dob = student_dob(level="school") + age = (today - dob).days // 365 + assert 5 <= age <= 14 + + for _ in range(20): + dob = student_dob(level="college") + age = (today - dob).days // 365 + assert 14 <= age <= 18 + + for _ in range(20): + dob = student_dob(level="university") + age = (today - dob).days // 365 + assert 18 <= age <= 25 diff --git a/tests/test_student_profile_consistency.py b/tests/test_student_profile_consistency.py new file mode 100644 index 0000000..13387b6 --- /dev/null +++ b/tests/test_student_profile_consistency.py @@ -0,0 +1,14 @@ +from faker_pk import FakerPK +from faker_pk.utils import query_row + +def test_student_profile_consistency(faker_pk: FakerPK): + for _ in range(20): + profile = faker_pk.student_profile(province="Punjab") + assert profile["province"] == "Punjab" + row = query_row( + "SELECT i.city, l.province FROM institutions i JOIN locations l ON i.city = l.city WHERE i.name = ?", + (profile["institution"],) + ) + assert row is not None + assert row[0] == profile["city"] + assert row[1] == profile["province"] diff --git a/tests/test_student_profile_structure.py b/tests/test_student_profile_structure.py new file mode 100644 index 0000000..ac052b9 --- /dev/null +++ b/tests/test_student_profile_structure.py @@ -0,0 +1,9 @@ +from faker_pk import FakerPK + +def test_student_profile_structure(faker_pk: FakerPK): + profile = faker_pk.student_profile() + assert isinstance(profile, dict) + required_keys = ["name", "gender", "cnic", "institution", "level", "city", "province", "dob"] + for key in required_keys: + assert key in profile + assert profile[key] is not None