예제 #1
0
class SpeedRun(models.Model):
    objects = SpeedRunManager()
    event = models.ForeignKey('Event',
                              on_delete=models.PROTECT,
                              default=LatestEvent)
    name = models.CharField(max_length=64)
    display_name = models.TextField(
        max_length=256,
        blank=True,
        verbose_name='Display Name',
        help_text='How to display this game on the stream.')
    # This field is now deprecated, we should eventually set up a way to migrate the old set-up to use the donor links
    deprecated_runners = models.CharField(max_length=1024,
                                          blank=True,
                                          verbose_name='*DEPRECATED* Runners',
                                          editable=False,
                                          validators=[runners_exists])
    console = models.CharField(max_length=32, blank=True)
    commentators = models.CharField(max_length=1024, blank=True)
    description = models.TextField(max_length=1024, blank=True)
    starttime = models.DateTimeField(verbose_name='Start Time',
                                     editable=False,
                                     null=True)
    endtime = models.DateTimeField(verbose_name='End Time',
                                   editable=False,
                                   null=True)
    # can be temporarily null when moving runs around, or null when they haven't been slotted in yet
    order = models.IntegerField(
        blank=True,
        null=True,
        help_text='Please note that using the schedule editor is much easier',
        validators=[positive])
    run_time = TimestampField(always_show_h=True)
    setup_time = TimestampField(always_show_h=True)
    runners = models.ManyToManyField('Runner')
    coop = models.BooleanField(
        default=False,
        help_text=
        'Cooperative runs should be marked with this for layout purposes')
    category = models.CharField(max_length=64,
                                blank=True,
                                null=True,
                                help_text='The type of run being performed')
    release_year = models.IntegerField(
        blank=True,
        null=True,
        verbose_name='Release Year',
        help_text='The year the game was released')
    giantbomb_id = models.IntegerField(
        blank=True,
        null=True,
        verbose_name='GiantBomb Database ID',
        help_text=
        'Identifies the game in the GiantBomb database, to allow auto-population of game data.'
    )
    tech_notes = models.TextField(blank=True,
                                  help_text='Notes for the tech crew')

    class Meta:
        app_label = 'tracker'
        verbose_name = 'Speed Run'
        unique_together = (('name', 'category', 'event'), ('event', 'order'))
        ordering = ['event__datetime', 'order']
        permissions = (('can_view_tech_notes', 'Can view tech notes'), )

    def natural_key(self):
        return (self.name, self.event.natural_key())

    def clean(self):
        if not self.name:
            raise ValidationError('Name cannot be blank')
        if not self.display_name:
            self.display_name = self.name
        if not self.order:
            self.order = None

    def save(self, fix_time=True, fix_runners=True, *args, **kwargs):
        i = TimestampField.time_string_to_int
        can_fix_time = self.order is not None and (i(self.run_time) != 0
                                                   or i(self.setup_time) != 0)

        # fix our own time
        if fix_time and can_fix_time:
            prev = SpeedRun.objects.filter(event=self.event,
                                           order__lt=self.order).last()
            if prev:
                self.starttime = prev.starttime + \
                    datetime.timedelta(milliseconds=i(
                        prev.run_time) + i(prev.setup_time))
            else:
                self.starttime = self.event.datetime
            self.endtime = self.starttime + \
                datetime.timedelta(milliseconds=i(
                    self.run_time) + i(self.setup_time))

        if fix_runners and self.id:
            if not self.runners.exists():
                try:
                    self.runners.add(*[
                        Runner.objects.get_by_natural_key(r)
                        for r in util.natural_list_parse(
                            self.deprecated_runners, symbol_only=True)
                    ])
                except Runner.DoesNotExist:
                    pass
            if self.runners.exists():
                self.deprecated_runners = u', '.join(
                    unicode(r) for r in self.runners.all())

        super(SpeedRun, self).save(*args, **kwargs)

        # fix up all the others if requested
        if fix_time:
            if can_fix_time:
                next = SpeedRun.objects.filter(event=self.event,
                                               order__gt=self.order).first()
                starttime = self.starttime + \
                    datetime.timedelta(milliseconds=i(
                        self.run_time) + i(self.setup_time))
                if next and next.starttime != starttime:
                    return [self] + next.save(*args, **kwargs)
            elif self.starttime:
                prev = SpeedRun.objects.filter(
                    event=self.event,
                    starttime__lte=self.starttime).exclude(order=None).last()
                if prev:
                    self.starttime = prev.starttime + datetime.timedelta(
                        milliseconds=i(prev.run_time) + i(prev.setup_time))
                else:
                    self.starttime = self.event.timezone.localize(
                        datetime.datetime.combine(self.event.date,
                                                  datetime.time(12)))
                next = SpeedRun.objects.filter(
                    event=self.event,
                    starttime__gte=self.starttime).exclude(order=None).first()
                if next and next.starttime != self.starttime:
                    return [self] + next.save(*args, **kwargs)
        return [self]

    def name_with_category(self):
        categoryString = ' ' + self.category if self.category else ''
        return u'{0}{1}'.format(self.name, categoryString)

    def __unicode__(self):
        return u'{0} ({1})'.format(self.name_with_category(), self.event)
예제 #2
0
class SpeedRun(models.Model):
    objects = SpeedRunManager()
    event = models.ForeignKey('Event',
                              on_delete=models.PROTECT,
                              default=LatestEvent)
    name = models.CharField(max_length=64)
    display_name = models.TextField(
        max_length=256,
        blank=True,
        verbose_name='Display Name',
        help_text='How to display this game on the stream.',
    )
    shortname = models.CharField(max_length=15, blank=True)
    twitch_name = models.TextField(
        max_length=256,
        blank=True,
        verbose_name='Twitch Name',
        help_text='What game name to use on Twitch',
    )
    # This field is now deprecated, we should eventually set up a way to migrate the old set-up to use the donor links
    deprecated_runners = models.CharField(
        max_length=1024,
        blank=True,
        verbose_name='*DEPRECATED* Runners',
        editable=False,
        validators=[runners_exists],
    )
    console = models.CharField(max_length=32, blank=True)
    commentators = models.CharField(max_length=1024, blank=True)
    description = models.TextField(max_length=1024, blank=True)
    starttime = models.DateTimeField(verbose_name='Start Time',
                                     editable=False,
                                     null=True)
    endtime = models.DateTimeField(verbose_name='End Time',
                                   editable=False,
                                   null=True)
    # can be temporarily null when moving runs around, or null when they haven't been slotted in yet
    order = models.IntegerField(
        blank=True,
        null=True,
        help_text='Please note that using the schedule editor is much easier',
        validators=[positive],
    )
    run_time = TimestampField(always_show_h=True)
    setup_time = TimestampField(always_show_h=True)
    runners = models.ManyToManyField('Runner')
    coop = models.BooleanField(
        default=False,
        help_text=
        'Cooperative runs should be marked with this for layout purposes',
    )
    category = models.CharField(
        max_length=64,
        blank=True,
        null=True,
        help_text='The type of run being performed',
    )
    release_year = models.IntegerField(
        blank=True,
        null=True,
        verbose_name='Release Year',
        help_text='The year the game was released',
    )
    giantbomb_id = models.IntegerField(
        blank=True,
        null=True,
        verbose_name='GiantBomb Database ID',
        help_text=
        'Identifies the game in the GiantBomb database, to allow auto-population of game data.',
    )
    tech_notes = models.TextField(blank=True, help_text='Layout to Use')
    layout_prefix = models.CharField(max_length=30, blank=True)
    discord = models.CharField(max_length=20, blank=True)
    coms_layout = models.CharField(max_length=20, blank=True)
    tracker_mode = models.CharField(max_length=20, blank=True)
    chat_group = models.CharField(max_length=30, blank=True, default='ZSRM')
    show_seeding = models.BooleanField(
        default=False,
        help_text='Tournament matches should show runner seedings',
    )
    twitch_game = models.CharField(max_length=50, blank=True)
    twitch_tags = models.CharField(
        max_length=100,
        blank=True,
        null=True,
        help_text='Array as text',
    )
    youtube = models.CharField(
        max_length=150,
        blank=True,
        null=True,
        help_text='JSON Object as text',
    )
    custom_bg = models.CharField(max_length=30, blank=True)
    custom_cta = models.CharField(max_length=20, blank=True)
    custom_channels = models.CharField(
        max_length=150,
        blank=True,
        null=True,
        help_text='JSON Object as text',
    )

    class Meta:
        app_label = 'tracker'
        verbose_name = 'Speed Run'
        unique_together = (('name', 'category', 'event'), ('event', 'order'))
        ordering = ['event__datetime', 'order']
        permissions = (('can_view_tech_notes', 'Can view tech notes'), )

    def get_absolute_url(self):
        return reverse('tracker:run', args=(self.id, ))

    def natural_key(self):
        return self.name, self.event.natural_key()

    def clean(self):
        if not self.name:
            raise ValidationError('Name cannot be blank')
        if not self.display_name:
            self.display_name = self.name
        if not self.order:
            self.order = None

    def save(self, fix_time=True, fix_runners=True, *args, **kwargs):
        i = TimestampField.time_string_to_int
        can_fix_time = self.order is not None and (i(self.run_time) != 0
                                                   or i(self.setup_time) != 0)

        # fix our own time
        if fix_time and can_fix_time:
            prev = SpeedRun.objects.filter(event=self.event,
                                           order__lt=self.order).last()
            if prev:
                self.starttime = prev.starttime + datetime.timedelta(
                    milliseconds=i(prev.run_time) + i(prev.setup_time))
            else:
                self.starttime = self.event.datetime
            self.endtime = self.starttime + datetime.timedelta(
                milliseconds=i(self.run_time) + i(self.setup_time))

        if fix_runners and self.id:
            self.deprecated_runners = ', '.join(
                sorted(str(r) for r in self.runners.all()))

        # TODO: strip out force_insert and force_delete? causes issues if you try to insert a run in the middle
        # with #create with an order parameter, but nobody should be doing that outside of tests anyway?
        # maybe the admin lets you do it...
        super(SpeedRun, self).save(*args, **kwargs)

        # fix up all the others if requested
        if fix_time:
            if can_fix_time:
                next = SpeedRun.objects.filter(event=self.event,
                                               order__gt=self.order).first()
                starttime = self.starttime + datetime.timedelta(
                    milliseconds=i(self.run_time) + i(self.setup_time))
                if next and next.starttime != starttime:
                    return [self] + next.save(*args, **kwargs)
            elif self.starttime:
                prev = (SpeedRun.objects.filter(
                    event=self.event,
                    starttime__lte=self.starttime).exclude(order=None).last())
                if prev:
                    self.starttime = prev.starttime + datetime.timedelta(
                        milliseconds=i(prev.run_time) + i(prev.setup_time))
                else:
                    self.starttime = self.event.timezone.localize(
                        datetime.datetime.combine(self.event.date,
                                                  datetime.time(12)))
                next = (SpeedRun.objects.filter(
                    event=self.event,
                    starttime__gte=self.starttime).exclude(order=None).first())
                if next and next.starttime != self.starttime:
                    return [self] + next.save(*args, **kwargs)
        return [self]

    def name_with_category(self):
        category_string = f' {self.category}' if self.category else ''
        return f'{self.name}{category_string}'

    def __str__(self):
        return f'{self.name_with_category()} (event_id: {self.event_id})'