| |
| """ |
| Gujarat Street-Grid Seed v3.0 |
| |
| Covers ALL Gujarat cities with a dense lat/lng grid (~800m spacing). |
| Every grid cell generates 3 workers + 1 store at random categories. |
| Result: from ANY point in any city, a 1km search finds workers nearby. |
| |
| Usage: |
| python scripts/scrape_seed.py # synthetic only |
| python scripts/scrape_seed.py --places-api # + Google Places enrichment |
| python scripts/scrape_seed.py --density 0.5 # override grid spacing (km) |
| python scripts/scrape_seed.py --no-clear # keep existing data |
| """ |
|
|
| import os, sys, json, random, math, hashlib, re |
| from datetime import datetime, timedelta |
| from dotenv import load_dotenv |
| from pymongo import MongoClient, InsertOne |
| from pymongo.errors import BulkWriteError |
|
|
| load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env')) |
| MONGODB_URI = os.getenv('MONGODB_URI') |
| GOOGLE_PLACES_API_KEY = os.getenv('GOOGLE_PLACES_API_KEY') |
| DB_NAME = "fixly" |
|
|
| BATCH_SIZE = 1000 |
| CLEAR_EXISTING = True |
| USE_PLACES_API = False |
| GRID_SPACING_KM = 0.8 |
|
|
| DICE_BEAR = "https://api.dicebear.com/7.x/initials/png?seed=%s&backgroundType=gradientLinear&backgroundRotation=0,360" |
|
|
| LANGUAGES = ["Gujarati", "Hindi", "English"] |
|
|
| |
| |
| |
| CITIES = { |
| "Ahmedabad": (23.0225, 72.5714, 32, 24, 1.0), |
| "Surat": (21.1702, 72.8311, 22, 18, 1.0), |
| "Vadodara": (22.3072, 73.1812, 16, 12, 0.8), |
| "Rajkot": (22.3039, 70.8022, 14, 10, 0.8), |
| "Bhavnagar": (21.7645, 72.1519, 12, 10, 0.7), |
| "Jamnagar": (22.4707, 70.0577, 11, 9, 0.7), |
| "Junagadh": (21.5222, 70.4579, 9, 8, 0.6), |
| "Gandhinagar": (23.2156, 72.6369, 8, 6, 0.6), |
| "Anand": (22.5645, 72.9289, 8, 6, 0.6), |
| "Nadiad": (22.6916, 72.8617, 7, 6, 0.6), |
| "Morbi": (22.8245, 70.8337, 7, 6, 0.6), |
| "Bharuch": (21.7051, 72.9958, 7, 6, 0.6), |
| "Navsari": (20.9467, 72.9520, 7, 6, 0.6), |
| "Valsad": (20.6060, 72.9343, 7, 6, 0.6), |
| "Mehsana": (23.5880, 72.3698, 8, 6, 0.6), |
| "Palanpur": (24.1710, 72.4381, 7, 5, 0.5), |
| "Porbandar": (21.6417, 69.6293, 7, 6, 0.5), |
| "Surendranagar":(22.7270, 71.6487, 7, 5, 0.5), |
| "Bhuj": (23.2420, 69.6669, 8, 6, 0.5), |
| "Gandhidham": (23.0853, 70.1194, 7, 5, 0.5), |
| "Godhra": (22.7780, 73.6148, 6, 5, 0.5), |
| "Dahod": (22.8450, 74.2600, 6, 5, 0.5), |
| "Patan": (23.8499, 72.1263, 6, 5, 0.5), |
| "Himmatnagar": (23.6000, 72.9500, 6, 5, 0.5), |
| "Veraval": (20.9085, 70.3623, 6, 5, 0.5), |
| "Amreli": (21.6111, 71.2421, 6, 5, 0.5), |
| "Botad": (22.1700, 71.6700, 5, 4, 0.5), |
| "Gondal": (21.9600, 70.8000, 5, 4, 0.5), |
| "Jetpur": (21.7500, 70.6200, 5, 4, 0.5), |
| "Kalol": (23.2430, 72.4960, 5, 4, 0.5), |
| "Kheda": (22.7520, 72.6850, 5, 4, 0.5), |
| "Sidhpur": (23.9160, 72.3720, 5, 4, 0.5), |
| "Visnagar": (23.6980, 72.5510, 5, 4, 0.5), |
| "Unjha": (23.8000, 72.4000, 5, 4, 0.5), |
| "Deesa": (24.2500, 72.1830, 5, 4, 0.5), |
| "Dhoraji": (21.7320, 70.4520, 4, 4, 0.5), |
| "Upleta": (21.7310, 70.2800, 4, 4, 0.5), |
| "Mahuva": (21.0830, 71.8000, 4, 4, 0.5), |
| "Palitana": (21.5250, 71.8230, 4, 4, 0.5), |
| "Modasa": (23.4670, 73.3000, 4, 4, 0.5), |
| "Vapi": (20.3710, 72.9040, 5, 4, 0.5), |
| "Vijapur": (23.5670, 72.7500, 4, 4, 0.5), |
| "Kapadvanj": (23.0200, 73.0700, 4, 4, 0.5), |
| "Tharad": (24.3900, 71.6200, 4, 4, 0.5), |
| "Lunavada": (23.1300, 73.6100, 4, 4, 0.5), |
| "Chhota Udepur":(22.3000, 74.0100, 4, 4, 0.5), |
| } |
|
|
| |
| CITY_AREAS = { |
| "Ahmedabad": [ |
| "Satellite", "Bopal", "Maninagar", "Navrangpura", "Vastrapur", |
| "Thaltej", "Paldi", "Naranpura", "Gota", "Chandkheda", |
| "Isanpur", "Naroda", "Vejalpur", "Prahlad Nagar", "Bodakdev", |
| "SG Highway", "Science City", "Ranip", "Sabarmati", "Odhav", |
| "Vatva", "Nikol", "Bapunagar", "Gomtipur", "Raipur", |
| "Kankaria", "Ellisbridge", "Shahibaug", "Asarwa", "Nava Vadaj", |
| ], |
| "Surat": [ |
| "Adajan", "Vesu", "Piplod", "Katargam", "Varachha", |
| "Udhna", "Athwa", "Dumas", "City Light", "Althan", |
| "Bhatar", "Amroli", "Palanpur Patia", "Sagrampura", "Nanpura", |
| "Rander", "Mota Varachha", "Parvat Patia", "Kapodra", "Umarwada", |
| "Yogi Chowk", "Ghod Dod Road", "Hajira", "Magdalla", "Sachin", |
| ], |
| "Vadodara": [ |
| "Alkapuri", "Gotri", "Manjalpur", "Karelibaug", "Akota", |
| "Harni", "Waghodia", "Nizampura", "Sama", "Fatehgunj", |
| "Makarpura", "Subhanpura", "Gorwa", "Dabhoi Road", |
| "Raopura", "Mandal", "Tarsali", "Vasad", "Karjan", "Savli", |
| ], |
| "Rajkot": [ |
| "Kalawad Road", "Yagnik Road", "150 Feet Ring Road", "Race Course", |
| "Kotecha Chowk", "Mavdi", "University Road", "Raiya Road", |
| "Amin Marg", "Bedipara", "Gondal Road", "Dhebar Road", |
| "Sadhu Vaswani Road", "Bhaktinagar", "Shapar", "Kuvadava Road", |
| "Nana Mavdi", "Junction Plot", "Sadhuvaswani", "Madhapar", |
| "Gokul", "Kothariya", "Bhavani", "Sanala Road", |
| ], |
| "Bhavnagar": [ |
| "Kaliyabid", "Nilambaug", "Krishna Nagar", "Sardarnagar", |
| "Ghogh Circle", "Takhteshwar", "Vidyanagar", "Chitra", "Ratanpara", |
| "Adarsh Nagar", "Shastrinagar", "Malvav", "Kumbharwada", |
| ], |
| "Jamnagar": [ |
| "Indira Gandhi Marg", "Darbargadh", "Palanpur", "Udyognagar", |
| "Shanker Tekri", "Gurukul", "Vijay Nagar", "Digvijay Plot", |
| "Vibhapar", "Lakhota", "Airport Road", "Bedis Road", |
| ], |
| "Junagadh": [ |
| "Joshipura", "Moti Baug", "MG Road", "Kalwa Chowk", |
| "Zanzarda", "Bhavnath", "Sardar Baug", "Keshod Road", |
| "Kadiya", "Satasi", "Vanthali", "Bhesan", |
| ], |
| "Gandhinagar": [ |
| "Sector 1", "Sector 2", "Sector 3", "Sector 4", "Sector 5", |
| "Sector 6", "Sector 7", "Sector 8", "Sector 9", "Sector 10", |
| "Sector 11", "Sector 12", "Sector 13", "Sector 14", "Sector 15", |
| "Sector 16", "Sector 17", "Sector 18", "Sector 19", "Sector 20", |
| "Sector 21", "Sector 22", "Sector 23", "Sector 24", "Sector 25", |
| "Sector 26", "Sector 27", "Sector 28", "Sector 29", "Sector 30", |
| "GIDC", "Infinite City", "Sargasan", "Kudasan", "Randesan", |
| "Pethapur", "Raysan", |
| ], |
| "Anand": [ |
| "Vallabh Vidyanagar", "Borsad Road", "Anand Station Road", |
| "Gamdi", "Bakrol", "Chikhodra", "Mogri", "Karamsad", "Vitthal Udyognagar", |
| ], |
| "Nadiad": [ |
| "Ghogha Cross Road", "Santram Road", "Mahagujarat Society", |
| "Vadtal Road", "Bhatta", "Shahpur", "Barejadi", "Rameshwar Road", |
| "Marine Lines", "Mahadev Faliya", |
| ], |
| "Morbi": [ |
| "Bagdana", "Wankaner Road", "Sanala Road", "Shapur", |
| "Ghanshyam Nagar", "Shastri Nagar", "Tankara Road", |
| "Bharatnagar", "Gundala", "Haripar", |
| ], |
| "Bharuch": [ |
| "Zadeshwar", "Ankleshwar", "Panchbhatti", |
| "Station Road", "Vadia", "Netang", "Kosamdi", "Vadia", |
| ], |
| "Navsari": [ |
| "Chhapra Road", "Sanskar Society", "Canal Road", |
| "Eru", "Jalalpore", "Vijalpore", "Mohan Nagar", "Dhulia", |
| ], |
| "Valsad": [ |
| "Station Road", "Tithal Road", "Halpati Vasahat", |
| "Nana Khajod", "Chharwada", "Atul", "Dharampur", "Umargam", |
| ], |
| "Mehsana": [ |
| "Gulabpura", "Highway Road", "Radhanpur Road", |
| "Vishnu Nagar", "Jagudan", "Modhera", "Kheralu", "Vadnagar", |
| "Gojariya", "Jethal", |
| ], |
| "Palanpur": [ |
| "Highway Road", "Johari Bazar", "Gunjar", |
| "Mithi Road", "Bhogal", "Ambaji Road", "Kankrej", "Dhanera Road", |
| ], |
| "Porbandar": [ |
| "Ghodbunder", "Mistry Plot", "Kutiyana Road", |
| "Bhadrod", "Prabhas Patan", "Madhavpur", "Miyani", |
| ], |
| "Surendranagar": [ |
| "Wadhwan", "Chhipdi", "Highway Road", |
| "Lakhtar Road", "Dharangadhra", "Limdi", "Muli Road", |
| "Thangadh", "Halvad", |
| ], |
| "Bhuj": [ |
| "Hospital Road", "Madhapar", "Mundra Road", |
| "Bhimasar", "Rapar", "Mirzapur", "Naranpur", "Kukma", |
| "Lakadia", "Kera", |
| ], |
| "Gandhidham": [ |
| "Plot 1-380", "Kandla", "Sector 1-15", |
| "Adipur", "Galpadar", "Gandhidham Station", "Shivaji Nagar", |
| ], |
| "Godhra": [ |
| "Kalol Road", "Station Road", "Halol Road", |
| "Fatepura", "Dahod Road", "Khanpur", "Timba Road", |
| ], |
| "Dahod": [ |
| "Jhalod Road", "Station Road", "Fatehpura", |
| "Chandwada", "Devgadh Baria Road", "Limkheda", "Singvad", |
| ], |
| "Patan": [ |
| "Chanasma Road", "Siddhpur Road", "Bhabhar", |
| "Santalpur", "Radhanpur", "Sami", "Harij", |
| ], |
| "Himmatnagar": [ |
| "Talod Road", "Prantij", "Station Road", |
| "Khedbrahma Road", "Vijaynagar", "Idar Road", |
| "Vadali", "Bayad", |
| ], |
| "Veraval": [ |
| "Somnath", "Patanvav", "Station Road", |
| "Prabhas Patan", "Kodinar", "Jalgaon", "Mul Dwarka", |
| ], |
| "Amreli": [ |
| "Baba Road", "Khimmat Nagar", "Chalala Road", |
| "Dhari", "Savar Kundla", "Rajula", "Lathi", "Babra", |
| ], |
| "Botad": [ |
| "Ahmedabad Road", "Gadhada", "Station Road", |
| "Rangpur", "Lathidad", "Ranpur", "Barvala", |
| ], |
| "Gondal": [ |
| "Rajkot Road", "Jetpur Road", "Station Road", |
| "Nana Mavdi", "Bhakti Nagar", "Dhoraji Road", "Vadia", |
| ], |
| "Jetpur": [ |
| "Upleta Road", "Dhoraji Road", "Station Road", |
| "Gadhka", "Chital", "Ventrapur", "Moti Vavdi", |
| ], |
| "Kalol": [ |
| "Station Road", "Market Road", "Santram Road", |
| "Nagalpur", "Kadi Road", "Bajwa", "Dharoi Road", |
| ], |
| "Kheda": [ |
| "Station Road", "Borsad Road", "Kapadvanj Road", |
| "Guruvaya", "Chhipdi", "Mahalet", |
| ], |
| "Sidhpur": [ |
| "Station Road", "Bazar Road", "Junagadh Road", |
| "Ambali", "Galisana", "Bhandu", |
| ], |
| "Visnagar": [ |
| "Station Road", "Modhera Road", "Kheralu Road", |
| "Vadnagar Road", "Siddhpur Road", "Kamalpur", |
| ], |
| "Unjha": [ |
| "Station Road", "Market Yard", "Siddhpur Road", |
| "Jagudan", "Chanasma", "Khara", "Rampur", |
| ], |
| "Deesa": [ |
| "Station Road", "Highway Road", "Palanpur Road", |
| "Dhanera Road", "Abu Road", "Mitha", "Bodana", |
| ], |
| "Dhoraji": [ |
| "Station Road", "Jetpur Road", "Upleta Road", |
| "Gondal Road", "Keshod Road", "Kalavad Road", |
| ], |
| "Upleta": [ |
| "Station Road", "Dhoraji Road", "Jetpur Road", |
| "Mahuva Road", "Kotharia", "Patanvav", |
| ], |
| "Mahuva": [ |
| "Station Road", "Palitana Road", "Talaja Road", |
| "Bhavnagar Road", "Sihor Road", "Ghogha Road", |
| ], |
| "Palitana": [ |
| "Station Road", "Bhavnagar Road", "Mahuva Road", |
| "Talaja Road", "Gadhada", "Gariyadhar", |
| ], |
| "Modasa": [ |
| "Station Road", "Himmatnagar Road", "Meghraj Road", |
| "Bhadiadar", "Bhiloda", "Bavali", |
| ], |
| "Vapi": [ |
| "Station Road", "GIDC", "Daman Road", |
| "Silvassa Road", "Pardi", "Bhilad", "Chharwada", |
| ], |
| "Vijapur": [ |
| "Station Road", "Mehsana Road", "Kadi Road", |
| "Visnagar Road", "Palanpur Road", "Kheralu", |
| ], |
| "Kapadvanj": [ |
| "Station Road", "Kheda Road", "Modasa Road", |
| "Lunavada", "Manipur", "Salap", |
| ], |
| "Tharad": [ |
| "Station Road", "Deesa Road", "Santalpur Road", |
| "Vavi", "Dhanera", "Lakhni", |
| ], |
| "Lunavada": [ |
| "Station Road", "Godhra Road", "Modasa Road", |
| "Balasinor", "Santrampur", "Kadana", |
| ], |
| "Chhota Udepur": [ |
| "Station Road", "Jetpur Road", "Kawant Road", |
| "Naswadi", "Pavi Jetpur", "Bodeli", |
| ], |
| } |
|
|
| CATEGORIES = { |
| "plumbing": ["Pipe Repair", "Tap Installation", "Drain Cleaning", "Water Heater Service", "Bathroom Fitting"], |
| "electrical": ["Wiring Repair", "Fan Installation", "MCB Box Repair", "Light Installation", "Smart Home Setup"], |
| "cleaning": ["Deep Cleaning", "Kitchen Cleaning", "Office Cleaning", "Sofa & Carpet Cleaning", "Bathroom Scrubbing"], |
| "carpentry": ["Furniture Repair", "Door Fitting", "Custom Furniture", "Kitchen Cabinet Work", "Wood Polishing"], |
| "painting": ["Wall Painting", "Texture Finish", "Exterior Painting", "Waterproofing", "Furniture Spray Paint"], |
| "ac": ["AC Service", "AC Repair", "Gas Refill", "AC Installation", "Duct Cleaning"], |
| "salon": ["Haircut", "Beard Trim", "Hair Color", "Facial", "Bridal Makeup", "Manicure"], |
| "vehicle": ["Bike Service", "Car Service", "Tyre Change", "Engine Repair", "Battery Replacement", "Denting & Painting"], |
| "pest_control": ["Cockroach Treatment", "Mosquito Control", "Termite Treatment", "Bed Bug Treatment", "Rodent Control"], |
| "packers_movers": ["House Shifting", "Office Relocation", "Loading-Unloading", "Vehicle Transport", "Packing Service"], |
| "photography": ["Wedding Photography", "Portrait Shoot", "Event Coverage", "Product Photography", "Cinematography"], |
| "catering": ["Home Catering", "Party Catering", "Wedding Catering", "Snacks Service", "Lunch Delivery Service"], |
| "laundry": ["Wash & Fold", "Dry Cleaning", "Ironing Service", "Carpet Cleaning", "Curtain Wash"], |
| "tailoring": ["Custom Stitching", "Alterations", "Designer Wear", "Uniform Stitching", "Leather Work"], |
| "fitness": ["Personal Training", "Yoga Classes", "Gym Sessions", "Diet Planning", "Zumba Classes"], |
| "tutoring": ["Home Tuition", "Online Classes", "Subject Tutoring", "Exam Prep", "Language Classes"], |
| "home_renovation": ["Full Renovation", "Kitchen Remodel", "Bathroom Renovation", "Wall Demolition", "Flooring Work"], |
| "interior_design": ["Home Interior", "Office Design", "Space Planning", "Furniture Layout", "Lighting Design"], |
| "appliance_repair": ["Washing Machine Repair", "Fridge Repair", "Microwave Repair", "Water Purifier Service", "Geyser Repair"], |
| "roofing": ["Waterproofing", "Roof Repair", "Terrace Sealing", "Membrane Installation", "Roof Coating"], |
| "flooring": ["Tile Installation", "Marble Polishing", "Wood Flooring", "Vinyl Flooring", "Floor Grouting"], |
| "welding": ["Gate Fabrication", "Railing Work", "Grill Making", "Structure Welding", "Aluminum Work"], |
| "event_planning": ["Birthday Planning", "Wedding Planning", "Corporate Events", "Decor Service", "Catering Coordination"], |
| "security": ["CCTV Installation", "Security Guard", "Biometric System", "Alarm Installation", "Intercom Setup"], |
| "gardening": ["Lawn Mowing", "Garden Design", "Tree Trimming", "Plantation Work", "Irrigation Setup"], |
| "mobile_repair": ["Screen Replacement", "Battery Replacement", "Software Fix", "Charging Port Repair", "Water Damage"], |
| "computer_repair": ["Desktop Repair", "Laptop Service", "Data Recovery", "Virus Removal", "Upgrade Service"], |
| } |
|
|
| CATEGORY_LIST = list(CATEGORIES.keys()) |
|
|
| MALE_NAMES = [ |
| "Arjun", "Rahul", "Karan", "Vikram", "Hardik", "Parth", "Dhruv", "Akash", "Jay", "Rohan", |
| "Manish", "Bhavesh", "Hitesh", "Mihir", "Rakesh", "Dinesh", "Mahesh", "Suresh", "Nilesh", |
| "Rajesh", "Prakash", "Deepak", "Sanjay", "Vijay", "Ajay", "Nikhil", "Amit", "Sunil", "Anil", |
| "Kiran", "Mayur", "Chetan", "Bhavin", "Chirag", "Harsh", "Kaushik", "Dharmesh", "Jignesh", |
| "Tejas", "Vishal", "Mukesh", "Ramesh", "Shailesh", "Jitendra", "Sandeep", "Devendra", |
| "Bharat", "Ashish", "Dhaval", "Keyur", "Sachin", "Ravi", "Savan", "Yash", "Krupal", |
| "Mitul", "Fenil", "Kishan", "Sagar", "Vatsal", "Harshad", "Kishor", "Pinakin", "Pravin", |
| "Kalpesh", "Ashok", "Gaurang", "Bipin", "Nirav", "Jayesh", "Milan", "Tushar", "Jatin", |
| "Yogesh", "Vinod", "Mohan", "Hasmukh", "Kantibhai", "Raman", "Shyam", "Tarun", "Umesh", |
| "Rushabh", "Helly", "Pruthvi", "Shubham", "Mohit", "Krunal", "Ronak", "Ankur", "Brijesh", |
| "Mitesh", "Parag", "Swapnil", "Akshay", "Pranav", "Kushal", "Darshan", "Siddharth", |
| ] |
|
|
| FEMALE_NAMES = [ |
| "Neha", "Priya", "Meena", "Rina", "Komal", "Pooja", "Sheetal", "Anjali", "Divya", "Kavita", |
| "Rupal", "Aarti", "Kiran", "Asha", "Deepa", "Hina", "Darshana", "Urvi", "Hetal", "Sejal", |
| "Bhavna", "Rutvi", "Kruti", "Nidhi", "Mansi", "Disha", "Trupti", "Ridhi", "Smita", "Alpa", |
| "Kinnari", "Shruti", "Reshma", "Jigna", "Mitali", "Krisha", "Jhanvi", "Heena", "Vaishali", |
| "Mamta", "Rekha", "Jyoti", "Rashmi", "Kajal", "Pinal", "Sonal", "Nisha", "Pallavi", |
| "Dipti", "Harsha", "Gauri", "Varsha", "Sangeeta", "Bhavika", "Hardika", "Niyati", |
| ] |
|
|
| LAST_NAMES = [ |
| "Patel", "Shah", "Makwana", "Joshi", "Rathod", "Solanki", "Parmar", "Savani", "Desai", "Mehta", |
| "Dave", "Trivedi", "Pandya", "Rawal", "Vyas", "Acharya", "Jani", "Thakkar", "Gajjar", "Bhavsar", |
| "Vasoya", "Savaliya", "Ramani", "Dobariya", "Kotadiya", "Vaghasiya", "Pipaliya", "Gohil", |
| "Jadav", "Vala", "Sarvaiya", "Rajput", "Chauhan", "Chavda", "Zala", "Padhiyar", "Rana", |
| "Vankar", "Barot", "Koli", "Rathva", "Damor", "Ninama", |
| "Bambhania", "Mevada", "Rank", "Mistry", "Suthar", "Luhar", "Kumbhar", "Vanzara", |
| ] |
|
|
| STORE_PREFIXES = [ |
| "Shree", "Om", "Jay", "Mahadev", "Royal", "Modern", "Perfect", "National", |
| "Super", "Metro", "City", "Star", "Prime", "Elite", "Supreme", "Apex", |
| "Krishna", "Ganesh", "Shiv", "Shakti", "Laxmi", "Sai", "Guru", "Sagar", |
| ] |
|
|
| STORE_SUFFIXES = [ |
| "Hardware", "Electricals", "Services", "Solutions", "Repair Center", |
| "Home Service", "Trading Co", "Enterprises", "Agency", "Store", |
| "Center", "Hub", "Point", "Corner", "House", |
| ] |
|
|
| |
| STREET_NAMES = [ |
| "MG Road", "Station Road", "College Road", "Hospital Road", "Market Road", |
| "Tower Road", "Circle Road", "Cross Roads", "Main Bazaar", "Gandhi Marg", |
| "Nehru Marg", "Patel Marg", "Raj Marg", "School Road", "Temple Road", |
| "Masjid Road", "Lake Road", "Garden Road", "Park Road", "Bus Stand Road", |
| "Highway Road", "Railway Station Road", "Bypass Road", "Mall Road", |
| "Post Office Road", "Bank Road", "Court Road", "Library Road", |
| "Police Station Road", "Dargah Road", "Clock Tower Road", "Kacheri Road", |
| "Bamboo Bazaar", "Main Market Road", "Grain Market Road", |
| "Jalaram Marg", "Swaminarayan Marg", "Ambedkar Marg", "Tagore Marg", |
| "Shahid Bhagat Singh Marg", "Mahatma Gandhi Marg", "Sardar Patel Marg", |
| "Indira Gandhi Marg", "Subhash Marg", "Azad Marg", "Shyamal Cross Road", |
| "Satellite Road", "Bopal Road", "Science City Road", "Airport Road", |
| "Ring Road", "Sarkhej Road", "Narol Road", "Vatva Road", |
| "Panchwati Road", "Rambaug Road", "Nilkanth Mahadev Road", |
| "Vivekanand Road", "Surya Nagar Road", "Mahavir Nagar Road", |
| "Shastri Nagar Road", "Lal Darwaja Road", "Kuber Society Road", |
| ] |
|
|
| LANDMARKS = [ |
| "Hanuman Temple", "Bus Stand", "Railway Station", "Municipal Market", |
| "Post Office", "Police Station", "Government Hospital", "Main Square", |
| "Circle Garden", "Water Tank", "Overbridge", "Petrol Pump", |
| "Dargah", "Church", "Masjid", "Municipal Garden", |
| "Bus Depot", "ST Bus Stop", "Fire Station", "Court Building", |
| ] |
|
|
| DESCRIPTION_TEMPLATES = { |
| "plumbing": "Full-service plumbing store in {area}, {city}. Stocking pipes, fittings, bathroom fixtures, water heaters, and all plumbing essentials.", |
| "electrical": "Complete electrical supply store in {area}, {city}. Wires, switches, MCBs, fans, lights, and electrical accessories.", |
| "cleaning": "Cleaning supplies and equipment store in {area}, {city}. Chemicals, tools, vacuum cleaners, and professional cleaning products.", |
| "carpentry": "Carpentry materials and hardware store in {area}, {city}. Wood, tools, fittings, kitchen components, and finishing supplies.", |
| "painting": "Paint store in {area}, {city}. All brands, wide color range, brushes, rollers, thinners, and waterproofing solutions.", |
| "ac": "AC sales and service center in {area}, {city}. Units, spare parts, tools, gas, and all cooling accessories.", |
| "salon": "Salon supplies store in {area}, {city}. Products, tools, chairs, mirrors, and professional salon equipment.", |
| "vehicle": "Auto parts and accessories store in {area}, {city}. Spares, tires, batteries, lubricants, and vehicle service tools.", |
| "pest_control": "Pest control product store in {area}, {city}. Chemicals, sprays, traps, fumigation equipment, and safety gear.", |
| "packers_movers": "Moving and packing supplies store in {area}, {city}. Boxes, tapes, bubble wrap, ropes, and moving equipment.", |
| "photography": "Photography equipment store in {area}, {city}. Cameras, lenses, lights, backdrops, and studio accessories.", |
| "catering": "Catering supplies store in {area}, {city}. Utensils, disposables, serving equipment, and party essentials.", |
| "laundry": "Laundry and dry cleaning store in {area}, {city}. Wash, fold, iron, dry clean, and specialty fabric care.", |
| "tailoring": "Tailoring shop in {area}, {city}. Fabrics, threads, accessories, and custom stitching services.", |
| "fitness": "Fitness equipment store in {area}, {city}. Weights, mats, machines, supplements, and workout accessories.", |
| "tutoring": "Educational resource center in {area}, {city}. Books, materials, worksheets, and learning aids for all subjects.", |
| "home_renovation": "Home renovation materials store in {area}, {city}. Tiles, cement, paint, plumbing and electrical supplies.", |
| "interior_design": "Interior solutions store in {area}, {city}. Fabrics, wallpapers, decor items, furniture, and lighting.", |
| "appliance_repair": "Appliance repair center in {area}, {city}. Spare parts, tools, and service for all home appliances.", |
| "roofing": "Roofing and waterproofing store in {area}, {city}. Waterproofing solutions, membranes, coatings, and sealants.", |
| "flooring": "Flooring solutions store in {area}, {city}. Tiles, marble, wood, vinyl, and installation tools.", |
| "welding": "Welding supplies store in {area}, {city}. Rods, machines, gas cylinders, safety gear, and fabrication tools.", |
| "event_planning": "Event supply store in {area}, {city}. Decor, tents, lights, chairs, sound systems, and party supplies.", |
| "security": "Security solutions store in {area}, {city}. Cameras, alarms, locks, biometric systems, and surveillance equipment.", |
| "gardening": "Garden supplies store in {area}, {city}. Plants, seeds, pots, fertilizers, tools, and irrigation systems.", |
| "mobile_repair": "Mobile accessories and repair store in {area}, {city}. Parts, tools, screens, batteries, and phone accessories.", |
| "computer_repair": "Computer service center in {area}, {city}. Parts, accessories, cables, tools, and repair services.", |
| } |
|
|
| BIO_TEMPLATES = { |
| "plumbing": "Expert plumber with {exp} years experience in {area}, {city}. Specializing in pipe repair, drainage, water heater installation, and bathroom fittings.", |
| "electrical": "Certified electrician serving {area}, {city} for {exp} years. Expert in wiring, fan installation, switchboard repair, and smart home wiring.", |
| "cleaning": "Professional cleaner serving {area}, {city} with {exp} years of experience. Deep cleaning, kitchen, office, and sofa cleaning services.", |
| "carpentry": "Master carpenter with {exp} years in {area}, {city}. Custom furniture, door fitting, kitchen cabinets, and wood polishing.", |
| "painting": "Experienced painter with {exp} years in {area}, {city}. Wall painting, texture finishes, waterproofing, and exterior work.", |
| "ac": "AC specialist with {exp} years experience in {area}, {city}. Repair, service, gas refill, and installation of all AC brands.", |
| "salon": "Professional beautician serving {area}, {city} for {exp} years. Haircuts, styling, facial, bridal makeup, and grooming services.", |
| "vehicle": "Auto mechanic with {exp} years experience in {area}, {city}. Bike and car service, engine repair, tyre change, and denting.", |
| "pest_control": "Pest control expert with {exp} years in {area}, {city}. Cockroach, termite, mosquito, bed bug, and rodent treatments.", |
| "packers_movers": "Professional packer and mover with {exp} years in {area}, {city}. Safe and reliable shifting services for home and office.", |
| "photography": "Professional photographer with {exp} years in {area}, {city}. Wedding, portrait, event, and product photography services.", |
| "catering": "Experienced caterer serving {area}, {city} for {exp} years. Home, party, and wedding catering with delicious Gujarati cuisine.", |
| "laundry": "Laundry professional with {exp} years in {area}, {city}. Wash, fold, iron, dry cleaning, and carpet cleaning services.", |
| "tailoring": "Master tailor with {exp} years experience in {area}, {city}. Custom stitching, alterations, and designer wear for men and women.", |
| "fitness": "Certified fitness trainer with {exp} years in {area}, {city}. Personal training, yoga, diet planning, and gym sessions.", |
| "tutoring": "Experienced tutor with {exp} years teaching in {area}, {city}. Home tuition, exam prep, and subject coaching for all grades.", |
| "home_renovation": "Home renovation expert with {exp} years in {area}, {city}. Full renovation, kitchen remodel, bathroom work, and flooring.", |
| "interior_design": "Interior designer with {exp} years experience in {area}, {city}. Home interiors, office design, space planning, and decor.", |
| "appliance_repair": "Appliance repair expert with {exp} years in {area}, {city}. Washing machine, fridge, microwave, and water purifier service.", |
| "roofing": "Roofing specialist with {exp} years in {area}, {city}. Waterproofing, terrace sealing, roof repair, and coating services.", |
| "flooring": "Flooring expert with {exp} years in {area}, {city}. Tile installation, marble polishing, wood and vinyl flooring work.", |
| "welding": "Skilled welder with {exp} years experience in {area}, {city}. Gate fabrication, railing, grill making, and structural welding.", |
| "event_planning": "Event planner with {exp} years in {area}, {city}. Birthday, wedding, corporate events, decoration, and coordination.", |
| "security": "Security professional with {exp} years in {area}, {city}. CCTV installation, biometric systems, alarm setup, and security guard.", |
| "gardening": "Gardening expert with {exp} years in {area}, {city}. Lawn care, garden design, tree trimming, and irrigation setup.", |
| "mobile_repair": "Mobile repair technician with {exp} years in {area}, {city}. Screen, battery, software, charging port, and water damage repairs.", |
| "computer_repair": "Computer repair expert with {exp} years in {area}, {city}. Desktop, laptop, data recovery, virus removal, and upgrades.", |
| } |
|
|
| TAGS_BY_CATEGORY = { |
| "plumbing": ["pipe", "leak", "bathroom", "tap", "drain", "water", "heater", "fitting"], |
| "electrical": ["wiring", "fan", "light", "MCB", "switch", "smart", "electric"], |
| "cleaning": ["deep clean", "kitchen", "office", "sofa", "carpet", "bathroom"], |
| "carpentry": ["furniture", "door", "cabinet", "wood", "kitchen", "custom"], |
| "painting": ["wall paint", "texture", "exterior", "waterproof", "spray"], |
| "ac": ["AC", "cooling", "gas refill", "repair", "installation"], |
| "salon": ["haircut", "beard", "facial", "makeup", "grooming", "beauty"], |
| "vehicle": ["bike", "car", "tyre", "engine", "battery", "service"], |
| "pest_control": ["pest", "termite", "mosquito", "cockroach", "rodent", "fumigation"], |
| "packers_movers": ["shifting", "relocation", "packing", "loading", "moving"], |
| "photography": ["wedding", "portrait", "event", "product", "cinema"], |
| "catering": ["catering", "party", "wedding", "food", "home delivery"], |
| "laundry": ["wash", "dry clean", "iron", "fold", "carpet"], |
| "tailoring": ["stitching", "alteration", "designer", "uniform", "custom"], |
| "fitness": ["gym", "yoga", "trainer", "diet", "zumba", "workout"], |
| "tutoring": ["tuition", "classes", "subject", "exam", "coaching"], |
| "home_renovation": ["renovation", "remodel", "kitchen", "bathroom", "flooring"], |
| "interior_design": ["interior", "design", "decor", "furniture", "space"], |
| "appliance_repair": ["washing machine", "fridge", "microwave", "purifier", "geyser"], |
| "roofing": ["roof", "waterproof", "terrace", "sealing", "coating"], |
| "flooring": ["tiles", "marble", "wood", "vinyl", "grouting"], |
| "welding": ["gate", "railing", "grill", "fabrication", "aluminum"], |
| "event_planning": ["party", "wedding", "decoration", "event", "planning"], |
| "security": ["CCTV", "alarm", "biometric", "security", "surveillance"], |
| "gardening": ["garden", "lawn", "plants", "irrigation", "landscaping"], |
| "mobile_repair": ["mobile", "screen", "battery", "software", "repair"], |
| "computer_repair": ["computer", "laptop", "data", "virus", "upgrade"], |
| } |
|
|
| CERTIFICATIONS_BY_CATEGORY = { |
| "plumbing": ["Plumbing Diploma", "Water Systems Certification", "Safety Training"], |
| "electrical": ["Electrical License", "Wiring Certification", "Safety Training"], |
| "cleaning": ["Deep Cleaning Certified", "Chemical Safety Training"], |
| "carpentry": ["Carpentry Diploma", "Woodworking Certification"], |
| "painting": ["Painting & Coating Certified", "Color Design Certification"], |
| "ac": ["HVAC Certified", "AC Manufacturer Training", "Gas Handling License"], |
| "salon": ["Beauty License", "Salon Management Certified", "Hair Styling Diploma"], |
| "vehicle": ["Auto Mechanic Diploma", "EV Certification", "Safety Inspection License"], |
| "pest_control": ["Pest Control License", "Chemical Handling Certified", "Fumigation License"], |
| "packers_movers": ["Moving License", "Insurance Certified", "Safety Training"], |
| "photography": ["Professional Photography Diploma", "Adobe Certified"], |
| "catering": ["Food Safety Certified", "Hospitality Diploma", "FSSAI License"], |
| "laundry": ["Dry Cleaning Certified", "Fabric Care Training"], |
| "tailoring": ["Fashion Design Diploma", "Tailoring Certificate"], |
| "fitness": ["Personal Trainer Certified", "Yoga Teacher Training", "Nutrition Certified"], |
| "tutoring": ["Teaching License", "Subject Specialist Certified", "CTET Qualified"], |
| "home_renovation": ["Contractor License", "Safety Certified", "Project Management"], |
| "interior_design": ["Interior Design Degree", "AutoCAD Certified", "Space Planning"], |
| "appliance_repair": ["Appliance Repair Certified", "Electronics Diploma"], |
| "roofing": ["Roofing Contractor License", "Waterproofing Certified"], |
| "flooring": ["Flooring Installation Certified", "Marble Polishing Training"], |
| "welding": ["Welding Diploma", "Arc Welding Certified", "Safety Training"], |
| "event_planning": ["Event Management Diploma", "Wedding Planning Certified"], |
| "security": ["Security License", "CCTV Installation Certified", "Biometric Training"], |
| "gardening": ["Horticulture Diploma", "Landscape Design Certified"], |
| "mobile_repair": ["Mobile Repair Diploma", "Manufacturer Certified"], |
| "computer_repair": ["CompTIA A+", "Hardware Diploma", "Networking Certified"], |
| } |
|
|
| |
|
|
| def random_phone(): |
| return f"+91{random.randint(6000000000, 9999999999)}" |
|
|
| def random_email(name): |
| clean = re.sub(r'[^a-z0-9]', '', name.lower().replace(" ", ".")) |
| return f"{clean}{random.randint(1,999)}@gmail.com" |
|
|
| def random_rating(): |
| return round(random.uniform(3.5, 5.0), 1) |
|
|
| def dice_avatar(seed): |
| return DICE_BEAR % seed.replace(" ", "%20") |
|
|
| def pick_languages(): |
| return random.sample(LANGUAGES, random.randint(1, 3)) |
|
|
| def pick_tags(category): |
| base = TAGS_BY_CATEGORY.get(category, [category]) |
| return random.sample(base, k=min(random.randint(2, 5), len(base))) |
|
|
| def pick_certifications(category): |
| certs = CERTIFICATIONS_BY_CATEGORY.get(category, []) |
| if not certs: return [] |
| return random.sample(certs, k=random.randint(1, min(2, len(certs)))) |
|
|
| def generate_services(category, is_worker=True): |
| services_list = CATEGORIES.get(category, ["General Service"]) |
| selected = random.sample(services_list, k=min(random.randint(2, 4), len(services_list))) |
| result = [] |
| for s in selected: |
| price = random.randint(150, 2500) if is_worker else random.randint(200, 8000) |
| stype = random.choice(["per_hour", "per_job"]) if is_worker else "per_job" |
| result.append({"name": s, "price": price, "type": stype}) |
| return result |
|
|
| def random_gujarati_name(): |
| gender = random.choice(["male", "female"]) |
| first = random.choice(MALE_NAMES if gender == "male" else FEMALE_NAMES) |
| last = random.choice(LAST_NAMES) |
| return f"{first} {last}", "male" if gender == "male" else "female" |
|
|
| def random_store_name(category, area): |
| prefix = random.choice(STORE_PREFIXES) |
| suffix = random.choice(STORE_SUFFIXES) |
| base = f"{prefix} {suffix}" |
| return random.choice([ |
| f"{base} {area}", |
| f"{prefix} {category.title()} {suffix}", |
| f"{area} {suffix}", |
| f"{base}", |
| ]) |
|
|
| |
|
|
| def km_to_deg(lat, km): |
| """Convert km to degrees at given latitude.""" |
| dlat = km / 111.0 |
| dlng = km / (111.0 * math.cos(math.radians(lat))) |
| return dlat, dlng |
|
|
| def generate_grid(lat_center, lng_center, width_km, height_km, spacing_km): |
| """Generate grid points covering a city bounding box.""" |
| dlat, dlng = km_to_deg(lat_center, spacing_km) |
| hw_km, hh_km = width_km / 2.0, height_km / 2.0 |
|
|
| lat_start = lat_center - km_to_deg(lat_center, hh_km)[0] |
| lng_start = lng_center - km_to_deg(lat_center, hw_km)[1] |
|
|
| lat_steps = max(1, int(height_km / spacing_km)) |
| lng_steps = max(1, int(width_km / spacing_km)) |
|
|
| jitter = spacing_km * 0.2 |
| jlat, jlng = km_to_deg(lat_center, jitter) |
|
|
| points = [] |
| for i in range(lat_steps): |
| for j in range(lng_steps): |
| lat = lat_start + (i + 0.5) * dlat + random.uniform(-jlat, jlat) |
| lng = lng_start + (j + 0.5) * dlng + random.uniform(-jlng, jlng) |
| points.append((lat, lng)) |
| return points |
|
|
| def nearest_area(lat, lng, city): |
| """Find the nearest named area for a grid point.""" |
| areas = CITY_AREAS.get(city, [city]) |
| city_data = CITIES.get(city) |
| if not city_data or not areas: |
| return city |
| clat = city_data[0] |
| clng = city_data[1] |
| |
| idx = int(abs(hash(f"{lat:.4f}{lng:.4f}")) % len(areas)) |
| return areas[idx] |
|
|
| def generate_address(city, area): |
| street = random.choice(STREET_NAMES) |
| num = random.randint(1, 150) |
| landmark = random.choice(LANDMARKS) |
| fmt = random.choice(["number_street", "near_landmark", "street_area"]) |
| if fmt == "number_street": |
| return f"{num}, {street}, {area}, {city}, Gujarat" |
| elif fmt == "near_landmark": |
| street2 = random.choice(STREET_NAMES) |
| return f"Near {landmark}, {street2}, {area}, {city}, Gujarat" |
| else: |
| return f"{street}, {area}, {city}, Gujarat" |
|
|
| |
|
|
| def create_worker_doc(city, street_addr, area, lat, lng, category): |
| name, gender = random_gujarati_name() |
| exp = random.randint(1, 25) |
| services = generate_services(category, is_worker=True) |
| tags = pick_tags(category) |
| certs = pick_certifications(category) |
| langs = pick_languages() |
| price = random.randint(150, 1200) |
| rating = random_rating() |
| reviews = random.randint(3, 800) |
| jobs = random.randint(5, 3000) |
|
|
| bio = BIO_TEMPLATES.get(category, "{category} specialist serving {area}, {city}.").format( |
| exp=exp, area=area, city=city |
| ) |
|
|
| return { |
| "name": name, |
| "email": random_email(name), |
| "phone": random_phone(), |
| "whatsapp": random_phone(), |
| "avatar": dice_avatar(name), |
| "city": city, |
| "area": area, |
| "category": category, |
| "services": services, |
| "bio": bio, |
| "experience": exp, |
| "rating": rating, |
| "totalReviews": reviews, |
| "totalJobs": jobs, |
| "pricePerHour": price, |
| "location": {"type": "Point", "coordinates": [lng, lat]}, |
| "address": street_addr, |
| "available": random.random() > 0.15, |
| "verified": random.random() > (0.45 if reviews > 50 else 0.7), |
| "emergencyAvailable": random.random() > 0.75, |
| "responseTime": random.choice([15, 30, 45, 60]), |
| "languages": langs, |
| "certifications": certs, |
| "serviceAreas": [], |
| "tags": tags, |
| "gallery": [], |
| "lastActiveAt": datetime.utcnow() - timedelta(minutes=random.randint(0, 43200)), |
| "createdAt": datetime.utcnow() - timedelta(days=random.randint(0, 730)), |
| "updatedAt": datetime.utcnow(), |
| } |
|
|
| def create_store_doc(city, street_addr, area, lat, lng, category): |
| name = random_store_name(category, area) |
| services = generate_services(category, is_worker=False) |
| tags = pick_tags(category) |
| langs = pick_languages() |
| rating = random_rating() |
| reviews = random.randint(3, 500) |
|
|
| description = DESCRIPTION_TEMPLATES.get(category, "{category} store in {area}, {city}.").format( |
| category=category.title(), area=area, city=city |
| ) |
|
|
| open_hours_list = [ |
| "9:00 AM - 9:00 PM", "10:00 AM - 8:00 PM", "9:30 AM - 7:30 PM", |
| "8:00 AM - 10:00 PM", "10:00 AM - 9:00 PM", "9:00 AM - 6:00 PM", |
| ] |
|
|
| return { |
| "name": name, |
| "email": random_email(name), |
| "phone": random_phone(), |
| "whatsapp": random_phone(), |
| "avatar": dice_avatar(name), |
| "city": city, |
| "area": area, |
| "category": category, |
| "services": services, |
| "description": description, |
| "rating": rating, |
| "totalReviews": reviews, |
| "location": {"type": "Point", "coordinates": [lng, lat]}, |
| "address": street_addr, |
| "verified": random.random() > 0.4, |
| "openHours": random.choice(open_hours_list), |
| "emergencyAvailable": random.random() > 0.8, |
| "languages": langs, |
| "tags": tags, |
| "gallery": [], |
| "createdAt": datetime.utcnow() - timedelta(days=random.randint(0, 730)), |
| "updatedAt": datetime.utcnow(), |
| } |
|
|
| def bulk_insert(collection, docs): |
| if not docs: return |
| ops = [] |
| for doc in docs: |
| ops.append(InsertOne(doc)) |
| if len(ops) >= BATCH_SIZE: |
| try: |
| collection.bulk_write(ops, ordered=False) |
| except BulkWriteError: |
| pass |
| ops = [] |
| if ops: |
| try: |
| collection.bulk_write(ops, ordered=False) |
| except BulkWriteError: |
| pass |
|
|
| |
|
|
| def fetch_places_api(city, area, category): |
| if not GOOGLE_PLACES_API_KEY or not USE_PLACES_API: |
| return [] |
| try: |
| import requests |
| term = f"{CATEGORIES[category][0]} in {area}, {city}, Gujarat" |
| url = "https://maps.googleapis.com/maps/api/place/textsearch/json" |
| params = {"query": term, "key": GOOGLE_PLACES_API_KEY, "region": "in", "language": "en"} |
| resp = requests.get(url, params=params, timeout=10) |
| data = resp.json() |
| if data.get("status") != "OK": |
| return [] |
| results = data.get("results", []) |
| docs = [] |
| for place in results[:5]: |
| lat = place["geometry"]["location"]["lat"] |
| lng = place["geometry"]["location"]["lng"] |
| name = place.get("name", "") |
| address = place.get("formatted_address", f"{area}, {city}, Gujarat") |
| place_id = place.get("place_id", "") |
| rating = place.get("rating", random_rating()) |
| reviews = place.get("user_ratings_total", random.randint(5, 200)) |
|
|
| phone = "" |
| try: |
| detail_url = "https://maps.googleapis.com/maps/api/place/details/json" |
| detail_params = {"place_id": place_id, "key": GOOGLE_PLACES_API_KEY, |
| "fields": "formatted_phone_number,international_phone_number"} |
| detail_resp = requests.get(detail_url, params=detail_params, timeout=10) |
| detail_data = detail_resp.json() |
| phone = detail_data.get("result", {}).get("international_phone_number", "") or \ |
| detail_data.get("result", {}).get("formatted_phone_number", "") |
| except: |
| pass |
|
|
| doc = { |
| "name": name, "email": random_email(name), |
| "phone": phone or random_phone(), "whatsapp": phone or random_phone(), |
| "avatar": dice_avatar(name), |
| "city": city, "area": area, "category": category, |
| "services": generate_services(category), |
| "rating": min(max(rating, 1.0), 5.0), "totalReviews": reviews, |
| "location": {"type": "Point", "coordinates": [lng, lat]}, |
| "address": address, "verified": random.random() > 0.4, |
| "languages": pick_languages(), "tags": pick_tags(category), |
| "googlePlaceId": place_id, |
| "bio": BIO_TEMPLATES.get(category, "").format(exp=random.randint(1,20), area=area, city=city), |
| "experience": random.randint(1, 20), "totalJobs": random.randint(5, 2500), |
| "pricePerHour": random.randint(150, 1200), "available": True, |
| "emergencyAvailable": random.random() > 0.75, "responseTime": random.choice([15, 30, 45, 60]), |
| "certifications": pick_certifications(category), |
| "gallery": [], |
| "lastActiveAt": datetime.utcnow() - timedelta(minutes=random.randint(0, 43200)), |
| "createdAt": datetime.utcnow() - timedelta(days=random.randint(0, 730)), |
| "updatedAt": datetime.utcnow(), |
| } |
| docs.append(doc) |
| return docs |
| except Exception as e: |
| print(f" β οΈ Places API error: {e}") |
| return [] |
|
|
| |
|
|
| def seed(): |
| print("=" * 65) |
| print(" GUJARAT STREET-GRID SEED v3.0") |
| print(" Spacing: {}km | Cities: {} | Categories: {}".format( |
| GRID_SPACING_KM, len(CITIES), len(CATEGORIES))) |
| print("=" * 65) |
|
|
| if not MONGODB_URI: |
| print("β MONGODB_URI not set in .env") |
| sys.exit(1) |
|
|
| google_available = bool(GOOGLE_PLACES_API_KEY) |
| if google_available and USE_PLACES_API: |
| print("β
Google Places API enabled (enrichment)") |
| else: |
| print("βΉοΈ Synthetic mode" + (" (Google API key found but --places-api not set)" if google_available else "")) |
|
|
| client = MongoClient(MONGODB_URI) |
| db = client[DB_NAME] |
| workers_col = db.workers |
| stores_col = db.stores |
|
|
| if CLEAR_EXISTING: |
| print("\nπ Clearing existing data...") |
| w_del = workers_col.delete_many({}).deleted_count |
| s_del = stores_col.delete_many({}).deleted_count |
| print(f" Removed {w_del} workers, {s_del} stores") |
|
|
| |
| print("\nπ Generating grid points per city...") |
| city_grids = {} |
| total_points = 0 |
| for cname, (clat, clng, w, h, s) in sorted(CITIES.items()): |
| pts = generate_grid(clat, clng, w, h, s) |
| city_grids[cname] = pts |
| total_points += len(pts) |
| areas = CITY_AREAS.get(cname, [cname]) |
| print(f" {cname}: {len(pts)} grid points Γ {len(areas)} areas") |
|
|
| print(f"\n Total grid points across all cities: {total_points}") |
| print(f" Target docs: ~{total_points * 4} ({total_points * 3} workers + {total_points} stores)") |
|
|
| |
| all_workers = [] |
| all_stores = [] |
| used_name_hashes = set() |
|
|
| for cname, points in sorted(city_grids.items()): |
| areas = CITY_AREAS.get(cname, [cname]) |
| print(f"\n{'='*40}") |
| print(f" {cname} ({len(points)} points)") |
| print(f"{'='*40}") |
|
|
| city_workers = [] |
| city_stores = [] |
| cat_tracker = {cat: {"workers": 0, "stores": 0} for cat in CATEGORY_LIST} |
|
|
| |
| places_docs = [] |
| if USE_PLACES_API and google_available: |
| for area_sample in random.sample(areas, min(3, len(areas))): |
| for cat_sample in random.sample(CATEGORY_LIST, min(5, len(CATEGORY_LIST))): |
| docs = fetch_places_api(cname, area_sample, cat_sample) |
| places_docs.extend(docs) |
| if len(places_docs) >= 50: |
| break |
| if len(places_docs) >= 50: |
| break |
| for doc in places_docs: |
| h = hash(doc["name"]) % 10**12 |
| if h not in used_name_hashes: |
| used_name_hashes.add(h) |
| city_workers.append(doc) |
| cat_tracker[doc["category"]]["workers"] += 1 |
|
|
| for idx, (lat, lng) in enumerate(points): |
| area = nearest_area(lat, lng, cname) |
| street_addr = generate_address(cname, area) |
|
|
| |
| worker_cats = random.sample(CATEGORY_LIST, 3) |
| for wcat in worker_cats: |
| w = create_worker_doc(cname, street_addr, area, lat, lng, wcat) |
| h = hash(w["name"]) % 10**12 |
| if h not in used_name_hashes: |
| used_name_hashes.add(h) |
| city_workers.append(w) |
| cat_tracker[wcat]["workers"] += 1 |
|
|
| scat = random.choice(CATEGORY_LIST) |
| s = create_store_doc(cname, street_addr, area, lat, lng, scat) |
| h = hash(s["name"]) % 10**12 |
| if h not in used_name_hashes: |
| used_name_hashes.add(h) |
| city_stores.append(s) |
| cat_tracker[scat]["stores"] += 1 |
|
|
| |
| cats_covered_w = sum(1 for v in cat_tracker.values() if v["workers"] > 0) |
| cats_covered_s = sum(1 for v in cat_tracker.values() if v["stores"] > 0) |
| print(f" Workers: {len(city_workers)} (covers {cats_covered_w}/{len(CATEGORY_LIST)} categories)") |
| print(f" Stores: {len(city_stores)} (covers {cats_covered_s}/{len(CATEGORY_LIST)} categories)") |
|
|
| all_workers.extend(city_workers) |
| all_stores.extend(city_stores) |
|
|
| |
| print(f"\n{'='*40}") |
| print(f" INSERTING DATA") |
| print(f"{'='*40}") |
| print(f" Workers: {len(all_workers)}") |
| print(f" Stores: {len(all_stores)}") |
|
|
| if all_workers: |
| print(" Writing workers...") |
| bulk_insert(workers_col, all_workers) |
| if all_stores: |
| print(" Writing stores...") |
| bulk_insert(stores_col, all_stores) |
|
|
| |
| print(f"\nπ Ensuring indexes...") |
| for col in [workers_col, stores_col]: |
| existing = col.index_information() |
| wanted = {"location_2dsphere", "category_1", "city_1_area_1", |
| "emergencyAvailable_1", "tags_1", "pricePerHour_1"} |
| for idx in wanted: |
| if idx not in existing: |
| field = idx.replace("_1", "").replace("_2dsphere", "").replace("_1_-1", "") |
| if "2dsphere" in idx: |
| col.create_index([(field, "2dsphere")]) |
| else: |
| col.create_index([(field, 1)]) |
| print(" β
Done") |
|
|
| |
| actual_workers = workers_col.count_documents({}) |
| actual_stores = stores_col.count_documents({}) |
| print(f"\n{'='*65}") |
| print(f" SEED COMPLETE") |
| print(f"{'='*65}") |
| print(f" Workers: {actual_workers}") |
| print(f" Stores: {actual_stores}") |
| print(f" Total: {actual_workers + actual_stores}") |
| print(f" Cities: {len(CITIES)}") |
| if places_docs: |
| print(f" From Google Places API: {len(places_docs)}") |
|
|
| client.close() |
| print(f"\nπ Done!") |
|
|
| if __name__ == "__main__": |
| args = sys.argv[1:] |
| for i, arg in enumerate(args): |
| if arg == "--places-api": |
| USE_PLACES_API = True |
| elif arg == "--no-clear": |
| CLEAR_EXISTING = False |
| elif arg == "--density" and i + 1 < len(args): |
| GRID_SPACING_KM = float(args[i + 1]) |
| elif arg == "--help" or arg == "-h": |
| print(__doc__) |
| sys.exit(0) |
| seed() |
|
|