diff --git a/src/character/management/commands/import_capabilities.py b/src/character/management/commands/import_capabilities.py new file mode 100644 index 0000000..788a837 --- /dev/null +++ b/src/character/management/commands/import_capabilities.py @@ -0,0 +1,89 @@ +from django.core.management import BaseCommand +from selenium import webdriver +from selenium.webdriver import Keys +from selenium.webdriver.common.by import By +from selenium.webdriver.remote.webelement import WebElement + +from character.models import Capability, Path + + +class Command(BaseCommand): + def handle(self, *args, **options): + url = "https://www.co-drs.org/fr/jeu/capacites" + self.setup_selenium() + self.selenium.get(url) + cards = [] + expected_capability_count = 430 + while len(cards) < expected_capability_count: + self.selenium.find_element(By.TAG_NAME, "body").send_keys(Keys.END) + cards = self.selenium.find_elements( + By.CSS_SELECTOR, ".col-md-4.col-sm-6.col-12.mb-4.views-row" + ) + for card in cards: + try: + self.import_capability(card) + except Exception as e: + print(f"{type(e)}: {e}") + self.stdout.write(f"Finished processing {len(cards)} caps.") + + def import_capability(self, card: WebElement): + title = ( + card.find_element(By.CSS_SELECTOR, ".card-front .card-title .fw-bold") + .text.strip() + .split(" | ") + ) + name = title[0].replace("’", "'").strip() + rank = int(title[1].replace("rang ", "")) + path = self.get_path(card, name) + limited = False + if "(L)" in name: + limited = True + name = name.replace("(L)", "") + spell = False + if "*" in name: + spell = True + name = name.replace("*", "") + name = name.strip() + try: + capability, _ = Capability.objects.update_or_create( + name=name, + defaults={ + "rank": rank, + "path": path, + "limited": limited, + "spell": spell, + }, + ) + self.stdout.write(self.style.SUCCESS(f"Created/updated cap {capability}")) + except Exception as e: + self.stdout.write( + self.style.ERROR(f"Couldn't create/update cap {name}: {e}") + ) + + def get_path(self, card: WebElement, name: str) -> Path | None: + try: + path_name = ( + card.find_element(By.CSS_SELECTOR, ".card-back .paths a") + .text.replace("’", "'") + .strip() + ) + except Exception: + self.stdout.write( + self.style.WARNING(f"Couldn't find path in card for cap '{name}'.") + ) + return None + try: + path = Path.objects.get(name__iexact=path_name) + return path + except Exception: + self.stdout.write( + self.style.WARNING( + f"Couldn't find path name '{path_name}' for cap '{name}'." + ) + ) + return None + + def setup_selenium(self): + options = webdriver.FirefoxOptions() + options.add_argument("-headless") + self.selenium = webdriver.Firefox(options=options) diff --git a/src/character/migrations/0007_alter_capability_limited_alter_capability_spell.py b/src/character/migrations/0007_alter_capability_limited_alter_capability_spell.py new file mode 100644 index 0000000..91c9bcf --- /dev/null +++ b/src/character/migrations/0007_alter_capability_limited_alter_capability_spell.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.2 on 2022-10-29 09:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("character", "0006_alter_path_category"), + ] + + operations = [ + migrations.AlterField( + model_name="capability", + name="limited", + field=models.BooleanField(blank=True, default=False), + ), + migrations.AlterField( + model_name="capability", + name="spell", + field=models.BooleanField(blank=True, default=False), + ), + ] diff --git a/src/character/migrations/max_migration.txt b/src/character/migrations/max_migration.txt index c04a8d9..5cdfc74 100644 --- a/src/character/migrations/max_migration.txt +++ b/src/character/migrations/max_migration.txt @@ -1 +1 @@ -0006_alter_path_category +0007_alter_capability_limited_alter_capability_spell diff --git a/src/character/models/capabilities.py b/src/character/models/capabilities.py index df68a05..ffccd67 100644 --- a/src/character/models/capabilities.py +++ b/src/character/models/capabilities.py @@ -36,8 +36,8 @@ class Capability(UniquelyNamedModel, TimeStampedModel, models.Model): rank = models.PositiveSmallIntegerField( validators=[MinValueValidator(1), MaxValueValidator(5)] ) - limited = models.BooleanField(blank=True, null=False) - spell = models.BooleanField(blank=True, null=False) + limited = models.BooleanField(blank=True, null=False, default=False) + spell = models.BooleanField(blank=True, null=False, default=False) description = models.TextField() class Meta: