diff --git a/backend/donations/management/commands/generate_orgs.py b/backend/donations/management/commands/generate_orgs.py new file mode 100644 index 00000000..cd97b8cd --- /dev/null +++ b/backend/donations/management/commands/generate_orgs.py @@ -0,0 +1,200 @@ +import random +from typing import Any, Dict, List + +from django.contrib.auth import get_user_model +from django.core.management import BaseCommand +from django.db import IntegrityError +from faker import Faker +from localflavor.ro.ro_counties import COUNTIES_CHOICES + +from donations.models import Ngo + +fake = Faker("ro_RO") + +MOCK_NGO_NAMES = { + "types": [ + "Asociația", + "Fundația", + "Organizația", + "", + ], + "names": [ + # These names were automatically generated using an LLM; while we have manually removed some + # of the less appropriate names, there may still be some that can be considered offensive + # or inappropriate. If you find any, please open an issue on GitHub. + "Acces Medical pentru Toți", + "Accesibil pentru Toți", + "Acțiune pentru Schimbare", + "Acțiune pentru Sănătate Mintală", + "Ajutor pentru Boli Rare", + "Ajutor pentru Refugiați", + "Ajutor pentru Satele Izolate", + "Ajută Copiii", + "Artă pentru Incluziune", + "Asistență Medicală Mobilă", + "Asistență pentru Persoanele Bolnave de Cancer", + "Casa Speranței", + "Copiii Lumii", + "Copiii Speranței", + "Copiii Sănătoși", + "Copiii Împreună", + "Cărțile Deschise", + "Drepturile Migranților", + "Educație Medicală pentru Toți", + "Educație Plus", + "Educație pentru Toți", + "Educație pentru Viitor", + "Femei Puternice", + "Grija pentru Sănătatea Maternă", + "Hrana pentru Toți", + "Incluziune pentru Toți", + "Inițiativa pentru Egalitate de Gen", + "Îmbunătățirea Sănătății Mintale", + "Împreună pentru Boli Infecțioase", + "Împreună pentru Drepturile Femeilor", + "Împreună pentru Sănătate", + "Împreună pentru Toleranță", + "Împreună pentru Viață", + "Înfruntă HIV/SIDA", + "Îngrijire pentru Bolnavi", + "Învinge Dependenta", + "Învinge Diabetul", + "Învinge Sărăcia", + "Lupta împotriva Cancerului", + "ONG-ul Inimii", + "Oameni și Planeta", + "Ochi pentru Nevoiași", + "Prietenii Naturii", + "Prietenii Planetei", + "Prietenii Sănătății", + "Promovarea Vaccinării", + "Protecția Animalelor Sălbatice", + "Protecția Copiilor Bolnavi", + "Protecția Copiilor Rămași Orfani", + "Protecția Drepturilor Omului", + "Protecția Sănătății Femeilor", + "Protecția Sănătății Oculare", + "Protecția Sănătății Respiratorii", + "Protecția Victimelor Violenței", + "Pădurea Vieții", + "Renașterea Satească", + "Salvarea Animalelor", + "Salvați Flora și Fauna", + "Salvați Vieți", + "Solidaritate pentru Oameni În Vârstă", + "Solidaritate pentru Sănătate", + "Speranța Vieții", + "Speranță pentru Orfani", + "Speranță pentru Sănătate", + "Sprijin pentru Pacienți", + "Sprijin pentru Persoanele Fără Adăpost", + "Sprijin pentru Persoanele cu Boli Cronice", + "Sprijin pentru Persoanele cu Boli Rare", + "Sprijin pentru Persoanele cu Dizabilități", + "Sprijin pentru Persoanele cu Handicap Fizic", + "Sprijin pentru Victimele Dezastrelor Naturale", + "Sprijin pentru Viitor", + "Sprijină Educația", + "Sprijină Sănătatea", + "Sănătate pentru Comunitate", + "Sănătate pentru Oameni în Vârstă", + "Sănătate pentru Persoanele cu Boli Mintale", + "Sănătate pentru Persoanele cu Dizabilități", + "Sănătate pentru Satele Izolate", + "Un Viitor Mai Verde", + "Un Zâmbet pentru Bătrânețe", + "Viitor Luminos", + "Viitor Sănătos", + ], +} + + +class Command(BaseCommand): + help = "Generate fake Organizations for testing purposes" + + def add_arguments(self, parser): + parser.add_argument( + "total_orgs", + type=int, + help="How many organizations to create", + default=10, + ) + + def handle(self, *args, **options): + total_organizations = options["total_orgs"] + + organizations: List[Dict[str, Any]] = [] + generated_organization_names: List[str] = [] + + user_model = get_user_model() + + self.stdout.write(self.style.SUCCESS(f"Generating {total_organizations} organization(s) to the database.")) + + consecutive_identical_names: int = 0 + while len(organizations) < total_organizations: + county = COUNTIES_CHOICES[random.randint(0, len(COUNTIES_CHOICES) - 1)][0] + address = fake.street_address() + + type_ = MOCK_NGO_NAMES["types"][random.randint(0, len(MOCK_NGO_NAMES["types"]) - 1)] + name_ = MOCK_NGO_NAMES["names"][random.randint(0, len(MOCK_NGO_NAMES["names"]) - 1)] + + name = " ".join((type_, name_)).strip() + + if name in generated_organization_names: + consecutive_identical_names += 1 + if consecutive_identical_names > 5: + self.stdout.write( + self.style.ERROR("Too many consecutive identical names. Aborting and writing to the database.") + ) + break + continue + + consecutive_identical_names = 0 + generated_organization_names.append(name) + + clean_name = name.lower().replace('"', "").replace(".", "").replace(",", "") + kebab_case_name = "-".join(clean_name.split(" ")) + owner_email = fake.email() + + try: + owner = user_model.objects.create_user( + email=owner_email, + password=owner_email.split("@")[0], + first_name=fake.first_name(), + last_name=fake.last_name(), + is_verified=random.choice([True, False]), + ) + owner.save() + except IntegrityError: + continue + + should_not_have_ngo = random.choice(range(0, 6)) == 3 + if should_not_have_ngo: + continue + + organization_details = { + "name": name, + "slug": kebab_case_name, + "description": fake.paragraph(nb_sentences=3, variable_nb_sentences=True), + "logo_url": "https://storage.googleapis.com/redirectioneaza/logo_bw.png", + "bank_account": fake.iban(), + "registration_number": fake.ssn(), + "address": address, + "county": county, + "active_region": county, + "phone": fake.phone_number(), + "email": random.choice([owner_email, fake.email()]), + "website": fake.url(), + } + try: + org = Ngo.objects.create(**organization_details) + org.save() + except IntegrityError: + owner.delete() + continue + + organizations.append(org) + + owner.ngo = org + + self.stdout.write(self.style.SUCCESS(f"Successfully created {len(organizations)} organization(s).")) diff --git a/backend/requirements-dev.in b/backend/requirements-dev.in index ae6a2651..45fd9d86 100644 --- a/backend/requirements-dev.in +++ b/backend/requirements-dev.in @@ -4,4 +4,6 @@ pip-tools~= 7.3.0 black~=23.12.1 ruff~=0.1.9 +faker~=22.2.0 + -r requirements.txt diff --git a/backend/requirements-dev.txt b/backend/requirements-dev.txt index 852271cb..4f218896 100644 --- a/backend/requirements-dev.txt +++ b/backend/requirements-dev.txt @@ -74,6 +74,8 @@ django-q2==1.6.1 # via -r requirements.txt django-storages==1.14.2 # via -r requirements.txt +faker==22.2.0 + # via -r requirements-dev.in gevent==23.9.1 # via -r requirements.txt greenlet==3.0.3 @@ -132,6 +134,7 @@ python-dateutil==2.8.2 # -r requirements.txt # botocore # croniter + # faker python-geoip==1.2 # via # -r requirements.txt