class Player(models.Model): user = fields.OneToOneField(models.User, primary_key=True, on_delete=fields.CASCADE) challonge_username = fields.CharField(null=True, blank=True, unique=True, max_length=64) challonge_user_id = fields.BigIntegerField(null=True, blank=True, unique=True) region = RegionField(null=True, blank=True, db_index=True) rating = fields.IntegerField(db_index=True, default=1500) deviation = fields.IntegerField(default=350) volatility = fields.FloatField(default=0.06) @async_using_db def get_last_ranked_match(self): from ..models import Match user = self.user qs = (Match.objects.filter( guild__guildsetup__verified=True, ranked=True, player_1=user) | Match.objects.filter(guild__guildsetup__verified=True, ranked=True, player_2=user)) return qs.latest()
class Participant(models.Model): member = fields.OneToOneField(models.Member, primary_key=True, on_delete=fields.CASCADE) challonge_id = fields.IntegerField(db_index=True) tournament = fields.ForeignKey(Tournament, db_index=True, on_delete=fields.CASCADE) current_match = fields.ForeignKey('Match', null=True, blank=True, on_delete=fields.SET_NULL) starting_elo = fields.IntegerField() starting_guild_elo = fields.IntegerField() match_count = fields.SmallIntegerField(default=0) forfeit_count = fields.SmallIntegerField(default=0)
class ParticipantTeam(models.Model): challonge_id = fields.IntegerField(db_index=True) member_1 = fields.MemberField(on_delete=fields.CASCADE) member_2 = fields.MemberField(on_delete=fields.CASCADE) tournament = fields.ForeignKey(Tournament, on_delete=fields.CASCADE) current_match = fields.ForeignKey('DoublesMatch', null=True, on_delete=fields.SET_NULL) starting_elo = fields.IntegerField() starting_guild_elo = fields.IntegerField() match_count = fields.SmallIntegerField(default=0) forfeit_count = fields.SmallIntegerField(default=0)
class GuildTeam(models.Model): class Meta: unique_together = (('team', 'guild'), ) team = fields.ForeignKey(Team, on_delete=fields.CASCADE) guild = fields.GuildField(db_index=True, on_delete=fields.CASCADE) guild_elo = fields.IntegerField(db_index=True, default=1000)
class Ruleset(models.Model): class Meta: unique_together = (('name', 'guild', 'version'), ) get_latest_by = 'version' name = fields.CharField(max_length=128) guild = fields.GuildField(db_index=True, on_delete=fields.CASCADE) version = fields.IntegerField(default=1) starter_stages = fields.SeparatedValuesField( default=Stage.get_default_starters, max_length=64, converter=Stage.parse, serializer=Stage.serialize) counterpick_stages = fields.SeparatedValuesField( default=Stage.get_default_counterpicks, max_length=64, converter=Stage.parse, serializer=Stage.serialize) counterpick_bans = fields.SmallIntegerField(default=2) dsr = DSRField(default=DSR('on')) @classmethod async def convert(cls, ctx, argument): try: argument = int(argument) except ValueError: raise BadArgument( f"{argument} is not a valid identifier for a ruleset") return await cls.async_get(pk=argument) def __str__(self): return self.name
class GuildPlayer(models.Model): member = fields.OneToOneField(models.Member, primary_key=True, on_delete=fields.CASCADE) rating = fields.IntegerField(db_index=True, default=1500) deviation = fields.IntegerField(default=350) volatility = fields.FloatField(default=0.06) @async_using_db def get_last_ranked_match(self): from ..models import Match guild = self.member.guild user = self.member.user qs = (Match.objects.filter(guild=guild, ranked=True, player_1=user) | Match.objects.filter(guild=guild, ranked=True, player_2=user)) return qs.latest()
class ScheduledTask(models.Model): extension_name = fields.CharField(max_length=64) method_name = fields.CharField(max_length=128) kwargs = JSONField(null=True, blank=True) when = fields.DateTimeField(db_index=True) time_tolerance = fields.IntegerField( default=300, null=True) # seconds | None means infinite time tolerance context = fields.ForeignKey(Context, null=True, blank=True, on_delete=fields.SET_NULL)
class Team(models.Model): class Meta: unique_together = (('member_1', 'member_2'), ) # member_1 is always the user with the lower user ID # to avoid duplicate teams member_1 = fields.ForeignKey(Player, db_index=True, on_delete=fields.CASCADE) member_2 = fields.ForeignKey(Player, db_index=True, on_delete=fields.CASCADE) custom_name = fields.CharField(null=True, max_length=64) current_tournament = fields.ForeignKey(Tournament, null=True, blank=True, on_delete=fields.SET_NULL) current_participant_team = fields.ForeignKey(ParticipantTeam, null=True, blank=True, on_delete=fields.SET_NULL) elo = fields.IntegerField(db_index=True, default=1000)
class Match(models.Model): class Meta: get_latest_by = 'started_at' id = fields.BigAutoField(primary_key=True) channel = fields.TextChannelField(null=True, blank=True, db_index=True, unique=True, on_delete=fields.SET_NULL) guild = fields.GuildField(on_delete=fields.CASCADE) voice_channel = fields.VoiceChannelField(null=True, blank=True, on_delete=fields.SET_NULL) # if tournament is None, it's a matchmaking match tournament = fields.ForeignKey(Tournament, null=True, blank=True, on_delete=fields.CASCADE) setup = fields.ForeignKey(MatchmakingSetup, null=True, blank=True, on_delete=fields.SET_NULL) management_message = fields.MessageField(null=True, blank=True, on_delete=fields.SET_NULL) ranked = fields.BooleanField() in_dms = fields.BooleanField() # if matchmaking match, looking is player_1, offering is player_2 player_1 = fields.UserField(db_index=True, on_delete=fields.CASCADE) player_1_rating = fields.IntegerField(null=True, blank=True) player_1_deviation = fields.IntegerField(null=True, blank=True) player_1_volatility = fields.FloatField(null=True, blank=True) player_1_global_rating = fields.IntegerField(null=True, blank=True) player_1_global_deviation = fields.IntegerField(null=True, blank=True) player_1_global_volatility = fields.FloatField(null=True, blank=True) player_1_score = fields.SmallIntegerField(default=0) player_2 = fields.UserField(db_index=True, on_delete=fields.CASCADE) player_2_rating = fields.IntegerField(null=True, blank=True) player_2_deviation = fields.IntegerField(null=True, blank=True) player_2_volatility = fields.FloatField(null=True, blank=True) player_2_global_rating = fields.IntegerField(null=True, blank=True) player_2_global_deviation = fields.IntegerField(null=True, blank=True) player_2_global_volatility = fields.FloatField(null=True, blank=True) player_2_score = fields.SmallIntegerField(default=0) current_game = fields.SmallIntegerField(default=1) wins_required = fields.SmallIntegerField(default=3) ruleset = fields.ForeignKey(Ruleset, null=True, blank=True, on_delete=fields.PROTECT) # if winner is None, match is active / ongoing # if winner is Purah, it was a friendly match winner = fields.UserField(null=True, blank=True, db_index=True, on_delete=fields.CASCADE) started_at = fields.DateTimeField(auto_now_add=True, db_index=True) ended_at = fields.DateTimeField(null=True, blank=True) spectating_message = fields.MessageField(null=True, blank=True, on_delete=fields.SET_NULL) match_end_message = fields.MessageField(null=True, blank=True, on_delete=fields.SET_NULL) @classmethod def ranked_matches_today_qs(cls, player_1, player_2, guild=None): one_day_ago = datetime.now() - timedelta(hours=18) # let's be generous if guild is None: qs = (cls.objects.filter(started_at__gt=one_day_ago, tournament=None, ranked=True, player_1=player_1, player_2=player_2) | cls.objects.filter(started_at__gt=one_day_ago, tournament=None, ranked=True, player_1=player_2, player_2=player_1)) else: qs = (cls.objects.filter(guild=guild, started_at__gt=one_day_ago, tournament=None, ranked=True, player_1=player_1, player_2=player_2) | cls.objects.filter(guild=guild, started_at__gt=one_day_ago, tournament=None, ranked=True, player_1=player_2, player_2=player_1)) return qs @async_using_db def get_match_participants(self): if self.tournament is None: return None, None member_1 = models.Member.objects.get(user=self.player_1, guild=self.guild) participant_1 = Participant.objects.get(pk=member_1) member_2 = models.Member.objects.get(user=self.player_2, guild=self.guild) participant_2 = Participant.objects.get(pk=member_2) return participant_1, participant_2
class TournamentSeries(models.Model): class Meta: unique_together = (('name', 'guild'), ) key_prefix = fields.CharField(primary_key=True, max_length=128) guild = fields.GuildField(db_index=True, on_delete=fields.CASCADE) name = fields.CharField(max_length=128) next_iteration = fields.IntegerField(default=1) ranked = fields.BooleanField() admins = fields.ManyToManyField(Player, null=True, blank=True) participant_role = fields.RoleField( null=True, unique=True, on_delete=fields.SET_NULL ) # cancel tournament creation if not found on Discord organizer_role = fields.RoleField( null=True, on_delete=fields.SET_NULL ) # cancel tournament creation if not found on Discord streamer_role = fields.RoleField( null=True, blank=True, on_delete=fields.SET_NULL) # delete if not found on Discord # signup_emoji = fields.EmojiField(default=get_default_emoji, on_delete=fields.SET_DEFAULT) # checkin_emoji = fields.EmojiField(default=get_default_emoji, on_delete=fields.SET_DEFAULT) announcements_channel = fields.TextChannelField( null=True, on_delete=fields.SET_NULL) # if None, cancel tournament creation talk_channel = fields.TextChannelField(null=True, blank=True, on_delete=fields.SET_NULL) introduction = fields.TextField(max_length=2048) default_participants_limit = fields.IntegerField(default=512) last_start_time = fields.DateTimeField(null=True, blank=True) delay_start = fields.SmallIntegerField(null=True, blank=True) # minutes interval = IntervalField(null=True, blank=True) doubles = fields.BooleanField(db_index=True) format = FormatField(default=Formats.double_elimination) affects_elo = fields.BooleanField(default=True) allow_matches_in_dms = fields.BooleanField() ruleset = fields.ForeignKey(Ruleset, null=True, blank=True, on_delete=fields.SET_NULL) cancelled = fields.BooleanField(default=False) @property def next_start_time(self): self.last_start_time: datetime.datetime if self.last_start_time is None or self.interval is None: return None if self.interval == Intervals.MONTHLY: weekday = self.last_start_time.weekday() day_number = self.last_start_time.day @classmethod async def convert(cls, ctx, argument): try: tournament_series = await cls.async_get(pk=argument) except ObjectDoesNotExist: try: guild = await models.Guild.from_discord_obj(ctx.guild) tournament_series = await cls.async_get(guild=guild, name=argument) except ObjectDoesNotExist: raise BadArgument( f"{argument} does not seem to be a valid tournament series." ) return tournament_series