Best Python code snippet using pytest-bdd_python
models.py
Source:models.py  
1import json2from copy import deepcopy3from datetime import timedelta4from typing import List, Optional5from urllib.parse import urljoin6import dhooks_lite7from requests.exceptions import HTTPError8from simple_mq import SimpleMQ9from django.contrib.auth.models import Group, User10from django.contrib.staticfiles.storage import staticfiles_storage11from django.core.cache import cache12from django.db import models13from django.utils.timezone import now14from django.utils.translation import gettext_lazy as _15from eveuniverse.helpers import EveEntityNameResolver, meters_to_ly16from eveuniverse.models import (17    EveConstellation,18    EveEntity,19    EveGroup,20    EveRegion,21    EveSolarSystem,22    EveType,23)24from allianceauth.authentication.models import State25from allianceauth.eveonline.evelinks import dotlan, eveimageserver, zkillboard26from allianceauth.eveonline.models import EveAllianceInfo, EveCorporationInfo27from allianceauth.services.hooks import get_extension_logger28from allianceauth.services.modules.discord.models import DiscordUser29from app_utils.django import app_labels30from app_utils.json import JSONDateTimeDecoder, JSONDateTimeEncoder31from app_utils.logging import LoggerAddTag32from app_utils.urls import site_absolute_url33from app_utils.views import humanize_value34from . import APP_NAME, HOMEPAGE_URL, __title__, __version__35from .app_settings import (36    KILLTRACKER_KILLMAIL_MAX_AGE_FOR_TRACKER,37    KILLTRACKER_WEBHOOK_SET_AVATAR,38)39from .core.killmails import ZKB_KILLMAIL_BASEURL, EntityCount, Killmail, TrackerInfo40from .exceptions import WebhookTooManyRequests41from .managers import EveKillmailManager, TrackerManager, WebhookManager42logger = LoggerAddTag(get_extension_logger(__name__), __title__)43class EveKillmail(models.Model):44    id = models.BigIntegerField(primary_key=True)45    time = models.DateTimeField(default=None, null=True, blank=True, db_index=True)46    solar_system = models.ForeignKey(47        EveEntity, on_delete=models.CASCADE, default=None, null=True, blank=True48    )49    updated_at = models.DateTimeField(auto_now=True)50    objects = EveKillmailManager()51    def __str__(self):52        return f"ID:{self.id}"53    def __repr__(self):54        return f"{type(self).__name__}(id={self.id})"55    def load_entities(self):56        """loads unknown entities for this killmail"""57        qs = EveEntity.objects.filter(id__in=self.entity_ids(), name="")58        qs.update_from_esi()59    def entity_ids(self) -> List[int]:60        ids = [61            self.victim.character_id,62            self.victim.corporation_id,63            self.victim.alliance_id,64            self.victim.ship_type_id,65            self.solar_system_id,66        ]67        for attacker in self.attackers.all():68            ids += [69                attacker.character_id,70                attacker.corporation_id,71                attacker.alliance_id,72                attacker.ship_type_id,73                attacker.weapon_type_id,74            ]75        return [int(x) for x in ids if x is not None]76class EveKillmailCharacter(models.Model):77    character = models.ForeignKey(78        EveEntity,79        on_delete=models.CASCADE,80        default=None,81        null=True,82        blank=True,83        related_name="+",84    )85    corporation = models.ForeignKey(86        EveEntity,87        on_delete=models.CASCADE,88        default=None,89        null=True,90        blank=True,91        related_name="+",92    )93    alliance = models.ForeignKey(94        EveEntity,95        on_delete=models.CASCADE,96        default=None,97        null=True,98        blank=True,99        related_name="+",100    )101    faction = models.ForeignKey(102        EveEntity,103        on_delete=models.CASCADE,104        default=None,105        null=True,106        blank=True,107        related_name="+",108    )109    ship_type = models.ForeignKey(110        EveEntity,111        on_delete=models.CASCADE,112        default=None,113        null=True,114        blank=True,115        related_name="+",116    )117    class Meta:118        abstract = True119    def __str__(self) -> str:120        if self.character:121            return str(self.character)122        elif self.corporation:123            return str(self.corporation)124        elif self.alliance:125            return str(self.alliance)126        elif self.faction:127            return str(self.faction)128        else:129            return f"PK:{self.pk}"130class EveKillmailVictim(EveKillmailCharacter):131    killmail = models.OneToOneField(132        EveKillmail, primary_key=True, on_delete=models.CASCADE, related_name="victim"133    )134    damage_taken = models.BigIntegerField(default=None, null=True, blank=True)135class EveKillmailAttacker(EveKillmailCharacter):136    killmail = models.ForeignKey(137        EveKillmail, on_delete=models.CASCADE, related_name="attackers"138    )139    damage_done = models.BigIntegerField(default=None, null=True, blank=True)140    is_final_blow = models.BooleanField(141        default=None, null=True, blank=True, db_index=True142    )143    security_status = models.FloatField(default=None, null=True, blank=True)144    weapon_type = models.ForeignKey(145        EveEntity,146        on_delete=models.CASCADE,147        default=None,148        null=True,149        blank=True,150        related_name="+",151    )152class EveKillmailPosition(models.Model):153    killmail = models.OneToOneField(154        EveKillmail, primary_key=True, on_delete=models.CASCADE, related_name="position"155    )156    x = models.FloatField(default=None, null=True, blank=True)157    y = models.FloatField(default=None, null=True, blank=True)158    z = models.FloatField(default=None, null=True, blank=True)159class EveKillmailZkb(models.Model):160    killmail = models.OneToOneField(161        EveKillmail, primary_key=True, on_delete=models.CASCADE, related_name="zkb"162    )163    location_id = models.PositiveIntegerField(164        default=None, null=True, blank=True, db_index=True165    )166    hash = models.CharField(max_length=64, default="", blank=True)167    fitted_value = models.FloatField(default=None, null=True, blank=True)168    total_value = models.FloatField(default=None, null=True, blank=True, db_index=True)169    points = models.PositiveIntegerField(170        default=None, null=True, blank=True, db_index=True171    )172    is_npc = models.BooleanField(default=None, null=True, blank=True, db_index=True)173    is_solo = models.BooleanField(default=None, null=True, blank=True, db_index=True)174    is_awox = models.BooleanField(default=None, null=True, blank=True, db_index=True)175class Webhook(models.Model):176    """A webhook to receive messages"""177    HTTP_TOO_MANY_REQUESTS = 429178    class WebhookType(models.IntegerChoices):179        DISCORD = 1, _("Discord Webhook")180    name = models.CharField(181        max_length=64, unique=True, help_text="short name to identify this webhook"182    )183    webhook_type = models.IntegerField(184        choices=WebhookType.choices,185        default=WebhookType.DISCORD,186        help_text="type of this webhook",187    )188    url = models.CharField(189        max_length=255,190        unique=True,191        help_text=(192            "URL of this webhook, e.g. "193            "https://discordapp.com/api/webhooks/123456/abcdef"194        ),195    )196    notes = models.TextField(197        blank=True,198        help_text="you can add notes about this webhook here if you want",199    )200    is_enabled = models.BooleanField(201        default=True,202        db_index=True,203        help_text="whether notifications are currently sent to this webhook",204    )205    objects = WebhookManager()206    def __init__(self, *args, **kwargs) -> None:207        super().__init__(*args, **kwargs)208        self.main_queue = self._create_queue("main")209        self.error_queue = self._create_queue("error")210    def __str__(self) -> str:211        return self.name212    def __repr__(self) -> str:213        return "{}(id={}, name='{}')".format(214            self.__class__.__name__, self.id, self.name215        )216    def __getstate__(self):217        # Copy the object's state from self.__dict__ which contains218        # all our instance attributes. Always use the dict.copy()219        # method to avoid modifying the original state.220        state = self.__dict__.copy()221        # Remove the unpicklable entries.222        del state["main_queue"]223        del state["error_queue"]224        return state225    def __setstate__(self, state):226        # Restore instance attributes (i.e., filename and lineno).227        self.__dict__.update(state)228        # Restore the previously opened file's state. To do so, we need to229        # reopen it and read from it until the line count is restored.230        self.main_queue = self._create_queue("main")231        self.error_queue = self._create_queue("error")232    def save(self, *args, **kwargs):233        is_new = self.id is None234        super().save(*args, **kwargs)235        if is_new:236            self.main_queue = self._create_queue("main")237            self.error_queue = self._create_queue("error")238    def _create_queue(self, suffix: str) -> Optional[SimpleMQ]:239        return (240            SimpleMQ(241                cache.get_master_client(), f"{__title__}_webhook_{self.pk}_{suffix}"242            )243            if self.pk244            else None245        )246    def reset_failed_messages(self) -> int:247        """moves all messages from error queue into main queue.248        returns number of moved messages.249        """250        counter = 0251        while True:252            message = self.error_queue.dequeue()253            if message is None:254                break255            else:256                self.main_queue.enqueue(message)257                counter += 1258        return counter259    def enqueue_message(260        self,261        content: str = None,262        embeds: List[dhooks_lite.Embed] = None,263        tts: bool = None,264        username: str = None,265        avatar_url: str = None,266    ) -> int:267        """Enqueues a message to be send with this webhook"""268        username = __title__ if KILLTRACKER_WEBHOOK_SET_AVATAR else username269        brand_url = urljoin(270            site_absolute_url(),271            staticfiles_storage.url("killtracker/killtracker_logo.png"),272        )273        avatar_url = brand_url if KILLTRACKER_WEBHOOK_SET_AVATAR else avatar_url274        return self.main_queue.enqueue(275            self._discord_message_asjson(276                content=content,277                embeds=embeds,278                tts=tts,279                username=username,280                avatar_url=avatar_url,281            )282        )283    @staticmethod284    def _discord_message_asjson(285        content: str = None,286        embeds: List[dhooks_lite.Embed] = None,287        tts: bool = None,288        username: str = None,289        avatar_url: str = None,290    ) -> str:291        """Converts a Discord message to JSON and returns it292        Raises ValueError if mesage is incomplete293        """294        if not content and not embeds:295            raise ValueError("Message must have content or embeds to be valid")296        if embeds:297            embeds_list = [obj.asdict() for obj in embeds]298        else:299            embeds_list = None300        message = dict()301        if content:302            message["content"] = content303        if embeds_list:304            message["embeds"] = embeds_list305        if tts:306            message["tts"] = tts307        if username:308            message["username"] = username309        if avatar_url:310            message["avatar_url"] = avatar_url311        return json.dumps(message, cls=JSONDateTimeEncoder)312    def send_message_to_webhook(self, message_json: str) -> bool:313        """Send given message to webhook314        Params315            message_json: Discord message encoded in JSON316        """317        timeout = cache.ttl(self._blocked_cache_key())318        if timeout:319            raise WebhookTooManyRequests(timeout)320        message = json.loads(message_json, cls=JSONDateTimeDecoder)321        if message.get("embeds"):322            embeds = [323                dhooks_lite.Embed.from_dict(embed_dict)324                for embed_dict in message.get("embeds")325            ]326        else:327            embeds = None328        hook = dhooks_lite.Webhook(329            url=self.url,330            user_agent=dhooks_lite.UserAgent(331                name=APP_NAME, url=HOMEPAGE_URL, version=__version__332            ),333        )334        response = hook.execute(335            content=message.get("content"),336            embeds=embeds,337            username=message.get("username"),338            avatar_url=message.get("avatar_url"),339            wait_for_response=True,340            max_retries=0,  # we will handle retries ourselves341        )342        logger.debug("headers: %s", response.headers)343        logger.debug("status_code: %s", response.status_code)344        logger.debug("content: %s", response.content)345        if response.status_code == self.HTTP_TOO_MANY_REQUESTS:346            logger.error(347                "%s: Received too many requests error from API: %s",348                self,349                response.content,350            )351            try:352                retry_after = int(response.content.get("retry_after")) + 2353            except (ValueError, TypeError):354                retry_after = WebhookTooManyRequests.DEFAULT_RESET_AFTER355            cache.set(356                key=self._blocked_cache_key(), value="BLOCKED", timeout=retry_after357            )358            raise WebhookTooManyRequests(retry_after)359        return response360    def _blocked_cache_key(self) -> str:361        return f"{__title__}_webhook_{self.pk}_blocked"362    @staticmethod363    def create_message_link(name: str, url: str) -> str:364        """Create link for a Discord message"""365        return f"[{str(name)}]({str(url)})"366class Tracker(models.Model):367    ICON_SIZE = 128368    MAIN_MINIMUM_COUNT = 2369    MAIN_MINIMUM_SHARE = 0.25370    class ChannelPingType(models.TextChoices):371        NONE = "PN", "(none)"372        HERE = "PH", "@here"373        EVERYBODY = "PE", "@everybody"374    name = models.CharField(375        max_length=100,376        help_text="name to identify tracker. Will be shown on alerts posts.",377        unique=True,378    )379    description = models.TextField(380        blank=True,381        help_text=(382            "Brief description what this tracker is for. Will not be shown on alerts."383        ),384    )385    color = models.CharField(386        max_length=7,387        default="",388        blank=True,389        help_text=(390            "Optional color for embed on Discord - #000000 / "391            "black means no color selected"392        ),393    )394    origin_solar_system = models.ForeignKey(395        EveSolarSystem,396        on_delete=models.SET_DEFAULT,397        default=None,398        null=True,399        blank=True,400        related_name="+",401        help_text=(402            "Solar system to calculate distance and jumps from. "403            "When provided distance and jumps will be shown on killmail messages"404        ),405    )406    require_max_jumps = models.PositiveIntegerField(407        default=None,408        null=True,409        blank=True,410        help_text=(411            "Require all killmails to be max x jumps away from origin solar system"412        ),413    )414    require_max_distance = models.FloatField(415        default=None,416        null=True,417        blank=True,418        help_text=(419            "Require all killmails to be max x LY away from origin solar system"420        ),421    )422    exclude_attacker_alliances = models.ManyToManyField(423        EveAllianceInfo,424        related_name="+",425        default=None,426        blank=True,427        help_text="exclude killmails with attackers from one of these alliances",428    )429    require_attacker_alliances = models.ManyToManyField(430        EveAllianceInfo,431        related_name="+",432        default=None,433        blank=True,434        help_text="only include killmails with attackers from one of these alliances",435    )436    exclude_attacker_corporations = models.ManyToManyField(437        EveCorporationInfo,438        related_name="+",439        default=None,440        blank=True,441        help_text="exclude killmails with attackers from one of these corporations",442    )443    require_attacker_corporations = models.ManyToManyField(444        EveCorporationInfo,445        related_name="+",446        default=None,447        blank=True,448        help_text="only include killmails with attackers from one of these corporations",449    )450    exclude_attacker_states = models.ManyToManyField(451        State,452        related_name="+",453        default=None,454        blank=True,455        help_text=(456            "exclude killmails with characters belonging "457            "to users with these Auth states"458        ),459    )460    require_attacker_states = models.ManyToManyField(461        State,462        related_name="+",463        default=None,464        blank=True,465        help_text=(466            "only include killmails with characters belonging "467            "to users with these Auth states"468        ),469    )470    require_victim_alliances = models.ManyToManyField(471        EveAllianceInfo,472        related_name="+",473        default=None,474        blank=True,475        help_text=(476            "only include killmails where the victim belongs to one of these alliances"477        ),478    )479    require_victim_corporations = models.ManyToManyField(480        EveCorporationInfo,481        related_name="+",482        default=None,483        blank=True,484        help_text=(485            "only include killmails where the victim belongs "486            "to one of these corporations"487        ),488    )489    require_victim_states = models.ManyToManyField(490        State,491        related_name="+",492        default=None,493        blank=True,494        help_text=(495            "only include killmails where the victim characters belong "496            "to users with these Auth states"497        ),498    )499    identify_fleets = models.BooleanField(500        default=False,501        help_text="when true: kills are interpreted and shown as fleet kills",502    )503    exclude_blue_attackers = models.BooleanField(504        default=False,505        help_text=("exclude killmails with blue attackers"),506    )507    require_blue_victim = models.BooleanField(508        default=False,509        help_text=(510            "only include killmails where the victim has standing with our group"511        ),512    )513    require_min_attackers = models.PositiveIntegerField(514        default=None,515        null=True,516        blank=True,517        help_text="Require killmails to have at least given number of attackers",518    )519    require_max_attackers = models.PositiveIntegerField(520        default=None,521        null=True,522        blank=True,523        help_text="Require killmails to have no more than max number of attackers",524    )525    exclude_high_sec = models.BooleanField(526        default=False,527        help_text=(528            "exclude killmails from high sec. "529            "Also exclude high sec systems in route finder for jumps from origin."530        ),531    )532    exclude_low_sec = models.BooleanField(533        default=False, help_text="exclude killmails from low sec"534    )535    exclude_null_sec = models.BooleanField(536        default=False, help_text="exclude killmails from null sec"537    )538    exclude_w_space = models.BooleanField(539        default=False, help_text="exclude killmails from WH space"540    )541    require_regions = models.ManyToManyField(542        EveRegion,543        default=None,544        blank=True,545        related_name="+",546        help_text=("Only include killmails that occurred in one of these regions"),547    )548    require_constellations = models.ManyToManyField(549        EveConstellation,550        default=None,551        blank=True,552        related_name="+",553        help_text=("Only include killmails that occurred in one of these regions"),554    )555    require_solar_systems = models.ManyToManyField(556        EveSolarSystem,557        default=None,558        blank=True,559        related_name="+",560        help_text=("Only include killmails that occurred in one of these regions"),561    )562    require_min_value = models.PositiveIntegerField(563        default=None,564        null=True,565        blank=True,566        help_text="Require killmail's value to be greater or equal to the given value in M ISK",567    )568    require_attackers_ship_groups = models.ManyToManyField(569        EveGroup,570        related_name="+",571        default=None,572        blank=True,573        help_text=(574            "Only include killmails where at least one attacker "575            "is flying one of these ship groups"576        ),577    )578    require_attackers_ship_types = models.ManyToManyField(579        EveType,580        related_name="+",581        default=None,582        blank=True,583        help_text=(584            "Only include killmails where at least one attacker "585            "is flying one of these ship types"586        ),587    )588    require_victim_ship_groups = models.ManyToManyField(589        EveGroup,590        related_name="+",591        default=None,592        blank=True,593        help_text=(594            "Only include killmails where victim is flying one of these ship groups"595        ),596    )597    require_victim_ship_types = models.ManyToManyField(598        EveType,599        related_name="+",600        default=None,601        blank=True,602        help_text=(603            "Only include killmails where victim is flying one of these ship types"604        ),605    )606    exclude_npc_kills = models.BooleanField(607        default=False, help_text="exclude npc kills"608    )609    require_npc_kills = models.BooleanField(610        default=False, help_text="only include killmails that are npc kills"611    )612    webhook = models.ForeignKey(613        Webhook,614        on_delete=models.CASCADE,615        help_text="Webhook URL for a channel on Discord to sent all alerts to",616    )617    ping_type = models.CharField(618        max_length=2,619        choices=ChannelPingType.choices,620        default=ChannelPingType.NONE,621        verbose_name="channel pings",622        help_text="Option to ping every member of the channel",623    )624    ping_groups = models.ManyToManyField(625        Group,626        default=None,627        blank=True,628        verbose_name="group pings",629        related_name="+",630        help_text="Option to ping specific group members - ",631    )632    is_posting_name = models.BooleanField(633        default=True, help_text="whether posted messages include the tracker's name"634    )635    is_enabled = models.BooleanField(636        default=True,637        db_index=True,638        help_text="toogle for activating or deactivating a tracker",639    )640    objects = TrackerManager()641    def __str__(self) -> str:642        return self.name643    def save(self, *args, **kwargs):644        if self.color == "#000000":645            self.color = ""646        super().save(*args, **kwargs)647    @property648    def has_localization_clause(self) -> bool:649        """returns True if tracker has a clause that needs the killmais's solar system"""650        return (651            self.exclude_high_sec652            or self.exclude_low_sec653            or self.exclude_null_sec654            or self.exclude_w_space655            or self.require_max_distance is not None656            or self.require_max_jumps is not None657            or self.require_regions.all()658            or self.require_constellations.all()659            or self.require_solar_systems.all()660        )661    @property662    def has_type_clause(self) -> bool:663        """returns True if tracker has a clause that needs a type from the killmail,664        e.g. the ship type of the victim665        """666        return (667            self.require_attackers_ship_groups.all()668            or self.require_attackers_ship_types.all()669            or self.require_victim_ship_groups.all()670            or self.require_victim_ship_types.all()671        )672    def process_killmail(673        self, killmail: Killmail, ignore_max_age: bool = False674    ) -> Optional[Killmail]:675        """runs tracker on given killmail676        returns new killmail amended with tracker info if killmail matches677        else returns None678        """679        threshold_date = now() - timedelta(680            minutes=KILLTRACKER_KILLMAIL_MAX_AGE_FOR_TRACKER681        )682        if not ignore_max_age and killmail.time < threshold_date:683            return False684        # pre-calculate shared information685        solar_system = None686        distance = None687        jumps = None688        is_high_sec = None689        is_low_sec = None690        is_null_sec = None691        is_w_space = None692        matching_ship_type_ids = None693        if killmail.solar_system_id and (694            self.origin_solar_system or self.has_localization_clause695        ):696            solar_system, _ = EveSolarSystem.objects.get_or_create_esi(697                id=killmail.solar_system_id698            )699            is_high_sec = solar_system.is_high_sec700            is_low_sec = solar_system.is_low_sec701            is_null_sec = solar_system.is_null_sec702            is_w_space = solar_system.is_w_space703            if self.origin_solar_system:704                distance = meters_to_ly(705                    self.origin_solar_system.distance_to(solar_system)706                )707                jumps = self.origin_solar_system.jumps_to(solar_system)708        # Make sure all ship types are in the local database709        if self.has_type_clause:710            EveType.objects.bulk_get_or_create_esi(711                ids=killmail.ship_type_distinct_ids()712            )713        # apply filters714        is_matching = True715        try:716            if is_matching and self.exclude_high_sec:717                is_matching = not is_high_sec718            if is_matching and self.exclude_low_sec:719                is_matching = not is_low_sec720            if is_matching and self.exclude_null_sec:721                is_matching = not is_null_sec722            if is_matching and self.exclude_w_space:723                is_matching = not is_w_space724            if is_matching and self.require_min_attackers:725                is_matching = len(killmail.attackers) >= self.require_min_attackers726            if is_matching and self.require_max_attackers:727                is_matching = len(killmail.attackers) <= self.require_max_attackers728            if is_matching and self.exclude_npc_kills:729                is_matching = not killmail.zkb.is_npc730            if is_matching and self.require_npc_kills:731                is_matching = killmail.zkb.is_npc732            if is_matching and self.require_min_value:733                is_matching = (734                    killmail.zkb.total_value >= self.require_min_value * 1000000735                )736            if is_matching and self.require_max_distance:737                is_matching = distance is not None and (738                    distance <= self.require_max_distance739                )740            if is_matching and self.require_max_jumps:741                is_matching = jumps is not None and (jumps <= self.require_max_jumps)742            if is_matching and self.require_regions.exists():743                is_matching = (744                    solar_system745                    and self.require_regions.filter(746                        id=solar_system.eve_constellation.eve_region_id747                    ).exists()748                )749            if is_matching and self.require_constellations.exists():750                is_matching = (751                    solar_system752                    and self.require_constellations.filter(753                        id=solar_system.eve_constellation_id754                    ).exists()755                )756            if is_matching and self.require_solar_systems.exists():757                is_matching = (758                    solar_system759                    and self.require_solar_systems.filter(id=solar_system.id).exists()760                )761            if is_matching and self.exclude_attacker_alliances.exists():762                is_matching = self.exclude_attacker_alliances.exclude(763                    alliance_id__in=killmail.attackers_distinct_alliance_ids()764                ).exists()765            if is_matching and self.require_attacker_alliances.exists():766                is_matching = self.require_attacker_alliances.filter(767                    alliance_id__in=killmail.attackers_distinct_alliance_ids()768                ).exists()769            if is_matching and self.exclude_attacker_corporations.exists():770                is_matching = self.exclude_attacker_corporations.exclude(771                    corporation_id__in=killmail.attackers_distinct_corporation_ids()772                ).exists()773            if is_matching and self.require_attacker_corporations.exists():774                is_matching = self.require_attacker_corporations.filter(775                    corporation_id__in=killmail.attackers_distinct_corporation_ids()776                ).exists()777            if is_matching and self.require_victim_alliances.exists():778                is_matching = self.require_victim_alliances.filter(779                    alliance_id=killmail.victim.alliance_id780                ).exists()781            if is_matching and self.require_victim_corporations.exists():782                is_matching = self.require_victim_corporations.filter(783                    corporation_id=killmail.victim.corporation_id784                ).exists()785            if is_matching and self.require_attacker_states.exists():786                is_matching = User.objects.filter(787                    profile__state__in=list(self.require_attacker_states.all()),788                    character_ownerships__character__character_id__in=(789                        killmail.attackers_distinct_character_ids()790                    ),791                ).exists()792            if is_matching and self.exclude_attacker_states.exists():793                is_matching = not User.objects.filter(794                    profile__state__in=list(self.exclude_attacker_states.all()),795                    character_ownerships__character__character_id__in=(796                        killmail.attackers_distinct_character_ids()797                    ),798                ).exists()799            if is_matching and self.require_victim_states.exists():800                is_matching = User.objects.filter(801                    profile__state__in=list(self.require_victim_states.all()),802                    character_ownerships__character__character_id=(803                        killmail.victim.character_id804                    ),805                ).exists()806            if is_matching and self.require_victim_ship_groups.exists():807                ship_types_matching_qs = EveType.objects.filter(808                    eve_group_id__in=list(809                        self.require_victim_ship_groups.values_list("id", flat=True)810                    ),811                    id=killmail.victim.ship_type_id,812                )813                is_matching = ship_types_matching_qs.exists()814                if is_matching:815                    matching_ship_type_ids = list(816                        ship_types_matching_qs.values_list("id", flat=True)817                    )818            if is_matching and self.require_victim_ship_types.exists():819                ship_types_matching_qs = EveType.objects.filter(820                    id__in=list(821                        self.require_victim_ship_types.values_list("id", flat=True)822                    ),823                    id=killmail.victim.ship_type_id,824                )825                is_matching = ship_types_matching_qs.exists()826                if is_matching:827                    matching_ship_type_ids = list(828                        ship_types_matching_qs.values_list("id", flat=True)829                    )830            if is_matching and self.require_attackers_ship_groups.exists():831                ship_types_matching_qs = EveType.objects.filter(832                    id__in=set(killmail.attackers_ship_type_ids())833                ).filter(834                    eve_group_id__in=list(835                        self.require_attackers_ship_groups.values_list("id", flat=True)836                    )837                )838                is_matching = ship_types_matching_qs.exists()839                if is_matching:840                    matching_ship_type_ids = list(841                        ship_types_matching_qs.values_list("id", flat=True)842                    )843            if is_matching and self.require_attackers_ship_types.exists():844                ship_types_matching_qs = EveType.objects.filter(845                    id__in=set(killmail.attackers_ship_type_ids())846                ).filter(847                    id__in=list(848                        self.require_attackers_ship_types.values_list("id", flat=True)849                    )850                )851                is_matching = ship_types_matching_qs.exists()852                if is_matching:853                    matching_ship_type_ids = list(854                        ship_types_matching_qs.values_list("id", flat=True)855                    )856        except AttributeError:857            is_matching = False858        if is_matching:859            killmail_new = deepcopy(killmail)860            killmail_new.tracker_info = TrackerInfo(861                tracker_pk=self.pk,862                jumps=jumps,863                distance=distance,864                main_org=self._killmail_main_attacker_org(killmail),865                main_ship_group=self._killmail_main_attacker_ship_group(killmail),866                matching_ship_type_ids=matching_ship_type_ids,867            )868            return killmail_new869        else:870            return None871    @classmethod872    def _killmail_main_attacker_org(cls, killmail) -> Optional[EntityCount]:873        """returns the main attacker group with count"""874        org_items = []875        for attacker in killmail.attackers:876            if attacker.alliance_id:877                org_items.append(878                    EntityCount(879                        id=attacker.alliance_id, category=EntityCount.CATEGORY_ALLIANCE880                    )881                )882            if attacker.corporation_id:883                org_items.append(884                    EntityCount(885                        id=attacker.corporation_id,886                        category=EntityCount.CATEGORY_CORPORATION,887                    )888                )889        if org_items:890            org_items_2 = [891                EntityCount(id=x.id, category=x.category, count=org_items.count(x))892                for x in set(org_items)893            ]894            max_count = max([x.count for x in org_items_2])895            treshold = max(896                len(killmail.attackers) * cls.MAIN_MINIMUM_SHARE,897                cls.MAIN_MINIMUM_COUNT,898            )899            if max_count >= treshold:900                org_items_3 = [x for x in org_items_2 if x.count == max_count]901                if len(org_items_3) > 1:902                    org_items_4 = [x for x in org_items_3 if x.is_alliance]903                    if len(org_items_4) > 0:904                        return org_items_4[0]905                return org_items_3[0]906        return None907    @classmethod908    def _killmail_main_attacker_ship_group(909        cls, killmail: Killmail910    ) -> Optional[EntityCount]:911        """returns the main attacker group with count"""912        ships_type_ids = killmail.attackers_ship_type_ids()913        ship_types = EveType.objects.filter(id__in=ships_type_ids).select_related(914            "eve_group"915        )916        ship_groups = list()917        for ships_type_id in ships_type_ids:918            try:919                ship_type = ship_types.get(id=ships_type_id)920            except EveType.DoesNotExist:921                continue922            ship_groups.append(923                EntityCount(924                    id=ship_type.eve_group_id,925                    category=EntityCount.CATEGORY_INVENTORY_GROUP,926                    name=ship_type.eve_group.name,927                )928            )929        if ship_groups:930            ship_groups_2 = [931                EntityCount(932                    id=x.id,933                    category=x.category,934                    name=x.name,935                    count=ship_groups.count(x),936                )937                for x in set(ship_groups)938            ]939            max_count = max([x.count for x in ship_groups_2])940            treshold = max(941                len(killmail.attackers) * cls.MAIN_MINIMUM_SHARE,942                cls.MAIN_MINIMUM_COUNT,943            )944            if max_count >= treshold:945                return sorted(ship_groups_2, key=lambda x: x.count).pop()946        return None947    def generate_killmail_message(948        self, killmail: Killmail, intro_text: str = None949    ) -> int:950        """generate a message from given killmail and enqueue for later sending951        returns new queue size952        """953        embed = self._create_embed(killmail)954        content = self._create_content(intro_text)955        return self.webhook.enqueue_message(content=content, embeds=[embed])956    def _create_embed(self, killmail: Killmail) -> dhooks_lite.Embed:957        resolver = EveEntity.objects.bulk_resolve_names(ids=killmail.entity_ids())958        # victim959        if killmail.victim.alliance_id:960            victim_organization = EveEntity.objects.get(id=killmail.victim.alliance_id)961            victim_org_url = zkillboard.alliance_url(killmail.victim.alliance_id)962        elif killmail.victim.corporation_id:963            victim_organization = EveEntity.objects.get(964                id=killmail.victim.corporation_id965            )966            victim_org_url = zkillboard.corporation_url(killmail.victim.corporation_id)967        else:968            victim_organization = None969            victim_org_url = None970        if killmail.victim.corporation_id:971            victim_corporation_zkb_link = self._corporation_zkb_link(972                killmail.victim.corporation_id, resolver973            )974        else:975            victim_corporation_zkb_link = ""976        if killmail.victim.character_id:977            victim_character_zkb_link = self._character_zkb_link(978                killmail.victim.character_id,979                resolver,980            )981            victim_str = f"{victim_character_zkb_link} ({victim_corporation_zkb_link}) "982        elif killmail.victim.corporation_id:983            victim_str = victim_corporation_zkb_link984        else:985            victim_str = ""986        # final attacker987        for attacker in killmail.attackers:988            if attacker.is_final_blow:989                final_attacker = attacker990                break991        else:992            final_attacker = None993        if final_attacker:994            if final_attacker.corporation_id:995                final_attacker_corporation_zkb_link = self._corporation_zkb_link(996                    final_attacker.corporation_id, resolver997                )998            else:999                final_attacker_corporation_zkb_link = ""1000            if final_attacker.character_id and final_attacker.corporation_id:1001                final_attacker_character_zkb_link = self._character_zkb_link(1002                    final_attacker.character_id, resolver1003                )1004                final_attacker_str = (1005                    f"{final_attacker_character_zkb_link} "1006                    f"({final_attacker_corporation_zkb_link})"1007                )1008            elif final_attacker.corporation_id:1009                final_attacker_str = f"{final_attacker_corporation_zkb_link}"1010            elif final_attacker.faction_id:1011                final_attacker_str = (1012                    f"**{resolver.to_name(final_attacker.faction_id)}**"1013                )1014            else:1015                final_attacker_str = "(Unknown final_attacker)"1016            final_attacker_ship_type_name = resolver.to_name(1017                final_attacker.ship_type_id1018            )1019        else:1020            final_attacker_str = ""1021            final_attacker_ship_type_name = ""1022        if killmail.solar_system_id:1023            solar_system, _ = EveSolarSystem.objects.get_or_create_esi(1024                id=killmail.solar_system_id1025            )1026            solar_system_link = self.webhook.create_message_link(1027                name=solar_system.name, url=dotlan.solar_system_url(solar_system.name)1028            )1029            region_name = solar_system.eve_constellation.eve_region.name1030            solar_system_text = f"{solar_system_link} ({region_name})"1031        else:1032            solar_system_text = ""1033        # self info1034        show_as_fleetkill = False1035        distance_text = ""1036        main_org_text = ""1037        main_org_name = ""1038        main_org_icon_url = eveimageserver.alliance_logo_url(1, size=self.ICON_SIZE)1039        main_ship_group_text = ""1040        tracked_ship_types_text = ""1041        embed_color = None1042        if killmail.tracker_info:1043            show_as_fleetkill = self.identify_fleets1044            embed_color = int(self.color[1:], 16) if self.color else None1045            if self.origin_solar_system:1046                origin_solar_system_link = self.webhook.create_message_link(1047                    name=self.origin_solar_system.name,1048                    url=dotlan.solar_system_url(self.origin_solar_system.name),1049                )1050                if killmail.tracker_info.distance is not None:1051                    distance_str = f"{killmail.tracker_info.distance:,.1f}"1052                else:1053                    distance_str = "?"1054                if killmail.tracker_info.jumps is not None:1055                    jumps_str = killmail.tracker_info.jumps1056                else:1057                    jumps_str = "?"1058                distance_text = (1059                    f"\nDistance from {origin_solar_system_link}: "1060                    f"{distance_str} LY | {jumps_str} jumps"1061                )1062            # main group1063            main_org = killmail.tracker_info.main_org1064            if main_org:1065                main_org_name = resolver.to_name(main_org.id)1066                if main_org.is_corporation:1067                    main_org_link = self._corporation_zkb_link(main_org.id, resolver)1068                    main_org_icon_url = eveimageserver.corporation_logo_url(1069                        main_org.id, size=self.ICON_SIZE1070                    )1071                else:1072                    main_org_link = self._alliance_zkb_link(main_org.id, resolver)1073                    main_org_icon_url = eveimageserver.alliance_logo_url(1074                        main_org.id, size=self.ICON_SIZE1075                    )1076                main_org_text = f" | Main group: {main_org_link} ({main_org.count})"1077            else:1078                show_as_fleetkill = False1079            # main ship group1080            main_ship_group = killmail.tracker_info.main_ship_group1081            if main_ship_group:1082                main_ship_group_text = f"\nMain ship class: **{main_ship_group.name}**"1083            # tracked attacker ships1084            matching_ship_type_ids = killmail.tracker_info.matching_ship_type_ids1085            if matching_ship_type_ids:1086                ship_types_text = "**, **".join(1087                    sorted(1088                        [1089                            resolver.to_name(type_id)1090                            for type_id in matching_ship_type_ids1091                        ]1092                    )1093                )1094                tracked_ship_types_text = (1095                    f"\nTracked ship types involved: **{ship_types_text}**"1096                )1097        victim_ship_type_name = resolver.to_name(killmail.victim.ship_type_id)1098        description = (1099            f"{victim_str} lost their **{victim_ship_type_name}** "1100            f"in {solar_system_text} "1101            f"worth **{humanize_value(killmail.zkb.total_value)}** ISK.\n"1102            f"Final blow by {final_attacker_str} "1103            f"in a **{final_attacker_ship_type_name}**.\n"1104            f"Attackers: **{len(killmail.attackers):,}**{main_org_text}"1105            f"{main_ship_group_text}"1106            f"{tracked_ship_types_text}"1107            f"{distance_text}"1108        )1109        solar_system_name = resolver.to_name(killmail.solar_system_id)1110        if show_as_fleetkill:1111            title = f"{solar_system_name} | {main_org_name} | Fleetkill"1112        else:1113            title = f"{solar_system_name} | {victim_ship_type_name} | Killmail"1114        if show_as_fleetkill:1115            thumbnail_url = main_org_icon_url1116        else:1117            thumbnail_url = eveimageserver.type_icon_url(1118                killmail.victim.ship_type_id, size=self.ICON_SIZE1119            )1120        zkb_killmail_url = f"{ZKB_KILLMAIL_BASEURL}{killmail.id}/"1121        # TODO This is a workaround for Embed.Author.name. Address in dhooks_lite1122        author = (1123            dhooks_lite.Author(1124                name=victim_organization.name if victim_organization.name else "?",1125                url=victim_org_url,1126                icon_url=victim_organization.icon_url(),1127            )1128            if victim_organization and victim_org_url1129            else None1130        )1131        zkb_icon_url = urljoin(1132            site_absolute_url(), staticfiles_storage.url("killtracker/zkb_icon.png")1133        )1134        embed = dhooks_lite.Embed(1135            author=author,1136            description=description,1137            title=title,1138            url=zkb_killmail_url,1139            thumbnail=dhooks_lite.Thumbnail(url=thumbnail_url),1140            footer=dhooks_lite.Footer(text="zKillboard", icon_url=zkb_icon_url),1141            timestamp=killmail.time,1142            color=embed_color,1143        )1144        return embed1145    def _create_content(self, intro_text) -> str:1146        intro_parts = []1147        if self.ping_type == Tracker.ChannelPingType.EVERYBODY:1148            intro_parts.append("@everybody")1149        elif self.ping_type == Tracker.ChannelPingType.HERE:1150            intro_parts.append("@here")1151        if self.ping_groups.exists():1152            if "discord" in app_labels():1153                for group in self.ping_groups.all():1154                    try:1155                        role = DiscordUser.objects.group_to_role(group)1156                    except HTTPError:1157                        logger.warning(1158                            "Failed to get Discord roles. Can not ping groups.",1159                            exc_info=True,1160                        )1161                    else:1162                        if role:1163                            intro_parts.append(f"<@&{role['id']}>")1164            else:1165                logger.warning(1166                    "Discord service needs to be installed in order "1167                    "to use groups ping features."1168                )1169        if self.is_posting_name:1170            intro_parts.append(f"Tracker **{self.name}**:")1171        intro_parts_2 = []1172        if intro_text:1173            intro_parts_2.append(intro_text)1174        if intro_parts:1175            intro_parts_2.append(" ".join(intro_parts))1176        return "\n".join(intro_parts_2)1177    def _character_zkb_link(1178        self, entity_id: int, resolver: EveEntityNameResolver1179    ) -> str:1180        return self.webhook.create_message_link(1181            name=resolver.to_name(entity_id), url=zkillboard.character_url(entity_id)1182        )1183    def _corporation_zkb_link(1184        self, entity_id: int, resolver: EveEntityNameResolver1185    ) -> str:1186        return self.webhook.create_message_link(1187            name=resolver.to_name(entity_id), url=zkillboard.corporation_url(entity_id)1188        )1189    def _alliance_zkb_link(1190        self, entity_id: int, resolver: EveEntityNameResolver1191    ) -> str:1192        return self.webhook.create_message_link(1193            name=resolver.to_name(entity_id), url=zkillboard.alliance_url(entity_id)...Exercice_4.py
Source:Exercice_4.py  
1"""2Exercice 4 de l'atelier 4 : Mots croisés3file       = "Exercice_4.py"4author     = "Baptiste Varamo & Jean-François Giammari"5credits    = ["Baptiste Varamo","Jean-François Giammari"]6version    = "1.0"7"""8from Exercice_2 import dictionnaire, mots_Nlettres9def mot_correspond(mot: str, motif: str) -> bool:10    """11    Check if the  string may or may not match the given pattern  string12    mot -- The word13    motif -- The pattern14    return True if match15    """16    is_matching = True17    if len(mot) != len(motif):18        is_matching = False19    else:20        length = len(mot)21        i = 022        while i < length and is_matching:23            if motif[i] != "." and mot[i] != motif[i]:24                is_matching = False25            i += 126    return is_matching27def presente(lettre: str, mot: str) -> int:28    """29    Define the place of a letter in a word30    mot -- The word31    lettre -- The letter32    return index or -1 if the letter dont exist in the word33    """34    return mot.find(lettre)35def mot_possible(mot: str, lettres: str) -> bool:36    """37    Define if the word is writable with the letters38    mot -- The word39    lettres -- The letters40    return True if is writable41    """42    is_matching = True43    length = len(mot)44    i = 045    while i < length and is_matching:46        if lettres.find(mot[i]) == -1:47            is_matching = False48        else:49            lettres = lettres.replace(mot[i], '', 1);50        i += 151    return is_matching52def mot_optimaux(dico: str, lettres: str) -> list:53    """54    Define the list of possible word with the letters55    dico -- The dictionnary56    lettres -- The letters usable57    return list of possible word58    """59    dico = dictionnaire(dico)60    lst_playable = []61    length = len(lettres)62    for i in range(length, 0, -1):63        lst_NFiltered = mots_Nlettres(dico, i)64        for e in lst_NFiltered:65            if mot_possible(e,lettres):66                lst_playable.append(e)67    return lst_playable68def test_exercice4():69    """70    Test for exercice 471    """72    print("Test mot_correspond TRUE (cheval c..v.l) : ", mot_correspond("cheval", "c..v.l"))73    print("Test mot_correspond FALSE (cheval c..v..l) : ", mot_correspond("cheval", "c..v..l"))74    print("Test presente 2 (e,cheval) : ", presente("e", "cheval"))75    print("Test presente -1 (x,cheval) : ", presente("x", "cheval"))76    print("Test mot_possible TRUE (lapin, abilnpq) : ", mot_possible("lapin", "abilnpq"))77    print("Test mot_possible FALSE (cheval, abilnpq) : ", mot_possible("cheval", "abilnpq"))78    print("Test mot_possible TRUE (chapeau, abcehpuva): ", mot_possible("chapeau", "abcehpuva"))79    print("Test mot_possible FALSE (chapeau, abcehpuv) : ", mot_possible("chapeau", "abcehpuv"))80    print("Test mot_optimaux : ", mot_optimaux("littre.txt", "chipeau"))...util.py
Source:util.py  
1from flask import session2from functools import wraps3import bcrypt4def hash_password(plain_text_password):5    # By using bcrypt, the salt is saved into the hash itself6    hashed_bytes = bcrypt.hashpw(plain_text_password.encode('utf-8'), bcrypt.gensalt())7    return hashed_bytes.decode('utf-8')8def verify_password(plain_text_password, hashed_password):9    hashed_bytes_password = hashed_password.encode('utf-8')10    return bcrypt.checkpw(plain_text_password.encode('utf-8'), hashed_bytes_password)11def check_login(wrapped_function):12    @wraps(wrapped_function)13    def decorated_function(*args, **kwargs):14        if "id" not in session:15            pass16        return wrapped_function(*args, **kwargs)17    return decorated_function18if __name__ == '__main__':19    # Test the above functions manually20    original_password = 'my_very_secureP4ssword!'  # From registration form21    print('original_password: ' + original_password)22    hashed_password = hash_password(original_password)  # This shall be saved in the DB23    print('hashed_password: ' + hashed_password)24    user_input_password = 'Hey Siri, what is my password?'  # From a login form, a mistyped input25    is_matching = verify_password(user_input_password, hashed_password)26    print('is_matching: ' + str(is_matching))27    user_input_password = 'my_very_secureP4ssword!'  # From a login form, the correct input28    is_matching = verify_password(user_input_password, hashed_password)...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
