|
| 1 | +import logging |
| 2 | + |
| 3 | +import requests |
| 4 | +from django.core.files.base import ContentFile |
| 5 | +from django.core.files.storage import default_storage |
| 6 | +from django.core.management.base import BaseCommand |
| 7 | +from django.utils.text import slugify |
| 8 | + |
| 9 | +from website.models import Organization, Tag |
| 10 | + |
| 11 | +# ANSI escape codes for colors |
| 12 | +COLOR_RED = "\033[91m" |
| 13 | +COLOR_GREEN = "\033[92m" |
| 14 | +COLOR_YELLOW = "\033[93m" |
| 15 | +COLOR_BLUE = "\033[94m" |
| 16 | +COLOR_RESET = "\033[0m" |
| 17 | + |
| 18 | +logger = logging.getLogger(__name__) |
| 19 | + |
| 20 | +GSoC_API_URL = "https://summerofcode.withgoogle.com/api/program/2025/organizations/" |
| 21 | + |
| 22 | + |
| 23 | +class Command(BaseCommand): |
| 24 | + help = "Fetches organizations from Google Summer of Code and stores them in the database." |
| 25 | + |
| 26 | + def handle(self, *args, **kwargs): |
| 27 | + logger.info(f"{COLOR_BLUE}Fetching organizations from GSoC 2025 API...{COLOR_RESET}") |
| 28 | + |
| 29 | + try: |
| 30 | + response = requests.get(GSoC_API_URL, headers={"User-Agent": "GSOC-Fetcher/1.0"}) |
| 31 | + response.raise_for_status() |
| 32 | + organizations = response.json() |
| 33 | + except requests.RequestException as e: |
| 34 | + logger.error(f"{COLOR_RED}Error fetching data from GSoC API: {str(e)}{COLOR_RESET}") |
| 35 | + return |
| 36 | + |
| 37 | + for org_data in organizations: |
| 38 | + self.process_organization(org_data) |
| 39 | + |
| 40 | + logger.info(f"{COLOR_GREEN}Finished fetching organizations!{COLOR_RESET}") |
| 41 | + |
| 42 | + def process_organization(self, org_data): |
| 43 | + data = org_data |
| 44 | + slug = slugify(data["slug"]) |
| 45 | + |
| 46 | + try: |
| 47 | + org, created = Organization.objects.update_or_create( |
| 48 | + slug=slug, |
| 49 | + defaults={ |
| 50 | + "name": data["name"], |
| 51 | + "description": data.get("description", "")[:500], |
| 52 | + "url": data.get("website_url"), |
| 53 | + "tagline": data.get("tagline", ""), |
| 54 | + "license": data.get("license", ""), |
| 55 | + "categories": data.get("categories", []), |
| 56 | + "contributor_guidance_url": data.get("contributor_guidance_url", ""), |
| 57 | + "tech_tags": data.get("tech_tags", []), |
| 58 | + "topic_tags": data.get("topic_tags", []), |
| 59 | + "source_code": data.get("website_url"), |
| 60 | + "is_active": True, |
| 61 | + }, |
| 62 | + ) |
| 63 | + |
| 64 | + # Handle Logo |
| 65 | + if data.get("logo_url"): |
| 66 | + self.download_logo(data["logo_url"], org, slug) |
| 67 | + |
| 68 | + # Handle Tags (tech_tags + topic_tags) |
| 69 | + self.assign_tags(org, data.get("tech_tags", []) + data.get("topic_tags", [])) |
| 70 | + |
| 71 | + # Handle Contact Links |
| 72 | + self.assign_contacts(org, data.get("contact_links", [])) |
| 73 | + |
| 74 | + org.save() |
| 75 | + status = "Added" if created else "Updated" |
| 76 | + logger.info(f"{COLOR_GREEN}{status}: {data['name']}{COLOR_RESET}") |
| 77 | + |
| 78 | + except Exception as e: |
| 79 | + logger.error(f"{COLOR_RED}Failed to save {data['name']}: {str(e)}{COLOR_RESET}") |
| 80 | + |
| 81 | + def download_logo(self, logo_url, org, slug): |
| 82 | + """Downloads and saves the organization’s logo.""" |
| 83 | + try: |
| 84 | + response = requests.get(logo_url) |
| 85 | + response.raise_for_status() |
| 86 | + |
| 87 | + logo_path = f"{slug}.png" |
| 88 | + if default_storage.exists(logo_path): |
| 89 | + default_storage.delete(logo_path) |
| 90 | + |
| 91 | + org.logo.save(logo_path, ContentFile(response.content)) |
| 92 | + logger.info(f"{COLOR_BLUE}Downloaded logo for {org.name}{COLOR_RESET}") |
| 93 | + |
| 94 | + except requests.RequestException as e: |
| 95 | + logger.warning(f"{COLOR_YELLOW}Failed to download logo for {org.name}: {str(e)}{COLOR_RESET}") |
| 96 | + |
| 97 | + def assign_tags(self, org, tags): |
| 98 | + """Assigns tags to an organization.""" |
| 99 | + for tag_name in tags: |
| 100 | + tag_slug = slugify(tag_name) |
| 101 | + tag, _ = Tag.objects.get_or_create(slug=tag_slug, defaults={"name": tag_name}) |
| 102 | + org.tags.add(tag) |
| 103 | + |
| 104 | + def assign_contacts(self, org, contacts): |
| 105 | + for contact in contacts: |
| 106 | + if contact["name"].lower() == "email": |
| 107 | + org.email = contact["value"] |
| 108 | + elif contact["name"].lower() == "chat": |
| 109 | + org.twitter = contact["value"] # Adjust this if needed |
| 110 | + |
| 111 | + org.save() |
0 commit comments