Пример #1
0
class Subscription(models.Model):
    project = models.ForeignKey(Project,
                                related_name='subscriptions',
                                on_delete=models.CASCADE)
    email = models.CharField(max_length=1024,
                             null=True,
                             blank=True,
                             validators=[EmailValidator()])
    user = models.ForeignKey(
        User,
        null=True,
        blank=True,
        default=None,
        on_delete=models.CASCADE,
    )

    NOTIFY_ALL_BUILDS = 'all'
    NOTIFY_ON_CHANGE = 'change'
    NOTIFY_ON_REGRESSION = 'regression'

    STRATEGY_CHOICES = (
        (NOTIFY_ALL_BUILDS, N_("All builds")),
        (NOTIFY_ON_CHANGE, N_("Only on change")),
        (NOTIFY_ON_REGRESSION, N_("Only on regression")),
    )

    notification_strategy = models.CharField(max_length=32,
                                             choices=STRATEGY_CHOICES,
                                             default='all')

    class Meta:
        unique_together = (
            'project',
            'user',
        )

    def _validate_email(self):
        if (not self.email) == (not self.user):
            raise ValidationError(
                "Subscription object must have exactly one of 'user' and 'email' fields populated."
            )

    def save(self, *args, **kwargs):
        self._validate_email()
        super().save(*args, **kwargs)

    def clean(self):
        self._validate_email()

    def get_email(self):
        if self.user and self.user.email:
            return self.user.email
        return self.email

    def __str__(self):
        return '%s on %s' % (self.get_email(), self.project)
Пример #2
0
class Group(models.Model, DisplayName):
    objects = GroupManager()

    slug = models.CharField(max_length=100, unique=True, validators=[group_slug_validator], db_index=True, verbose_name=N_('Slug'))
    valid_slug_pattern = slug_pattern
    name = models.CharField(max_length=100, null=True, blank=True, verbose_name=N_('Name'))
    description = models.TextField(null=True, blank=True, verbose_name=N_('Description'))
    members = models.ManyToManyField(User, through='GroupMember', verbose_name=N_('Members'))

    def add_user(self, user, access=None):
        member = GroupMember(group=self, user=user)
        if access:
            member.access = access
        member.save()

    def add_admin(self, user):
        self.add_user(user, 'admin')

    def accessible_to(self, user):
        return GroupMember.objects.filter(group=self, user=user.id).exists() or self.writable_by(user)

    def can_submit(self, user):
        return user.is_superuser or user.is_staff or self.has_access(user, 'admin', 'submitter')

    def writable_by(self, user):
        return user.is_superuser or user.is_staff or self.has_access(user, 'admin')

    def has_access(self, user, *access_levels):
        return GroupMember.objects.filter(
            group=self,
            user=user.id,
            access__in=access_levels
        ).exists()

    def __str__(self):
        return self.slug

    def full_clean(self, **kwargs):
        errors = {}
        try:
            super().full_clean(**kwargs)
        except ValidationError as e:
            errors = e.update_error_dict(errors)
        if self.slug and not re.match(self.valid_slug_pattern, self.slug):
            errors['slug'] = [_('Enter a valid value.')]
        if errors:
            raise ValidationError(errors)

    class Meta:
        ordering = ['slug']
Пример #3
0
class GroupMember(models.Model):
    ACCESS_LEVELS = (
        ('member', N_('Member')),
        ('submitter', N_('Result submitter')),
        ('admin', N_('Administrator')),
    )
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    access = models.CharField(max_length=10, choices=ACCESS_LEVELS, default='member')
    member_since = models.DateField(auto_now_add=True)

    class Meta:
        unique_together = ('group', 'user')
        verbose_name = N_('Group member')
        verbose_name_plural = N_('Group members')
Пример #4
0
class EmailTemplate(models.Model):
    name = models.CharField(max_length=100, unique=True)
    subject = models.CharField(
        max_length=1024,
        null=True,
        blank=True,
        help_text=N_('Jinja2 template for subject (single line)'),
        validators=[jinja2_validator])
    plain_text = models.TextField(help_text=N_('Jinja2 template for text/plain content'), validators=[jinja2_validator])
    html = models.TextField(blank=True, null=True, help_text=N_('Jinja2 template for text/html content'), validators=[jinja2_validator])

    # If any of the attributes need not to be tracked, just pass excluded_fields=['attr']
    history = HistoricalRecords(cascade_delete_history=True)

    def __str__(self):
        return self.name
Пример #5
0
    def clean(self):
        cleaned_data = super().clean()

        if cleaned_data['confirmation'] != self.deletable.slug:
            self.add_error('confirmation', N_(self.no_match_message))

        return cleaned_data
Пример #6
0
    def _time_remaining(self, secs_left):
        """Returns a string representing the time remaining for the operation.

        This is a simplified version of Django's timeuntil() function that
        does fewer calculations in order to reduce the amount of time we
        have to spend every loop. For instance, it doesn't bother with
        constructing datetimes and recomputing deltas, since we already
        have those, and it doesn't rebuild the TIME_REMAINING_CHUNKS
        every time it's called. It also handles seconds.
        """
        delta = timedelta(seconds=secs_left)
        since = delta.days * 24 * 60 * 60 + delta.seconds

        if since < 60:
            return N_('%d second', '%d seconds') % since

        for i, (seconds, name) in enumerate(self.TIME_REMAINING_CHUNKS):
            count = since // seconds

            if count != 0:
                break

        result = name % count

        if i + 1 < len(self.TIME_REMAINING_CHUNKS):
            seconds2, name2 = self.TIME_REMAINING_CHUNKS[i + 1]
            count2 = (since - (seconds * count)) // seconds2

            if count2 != 0:
                result += ', ' + name2 % count2

        return result
Пример #7
0
class TestjobFilter(django_filters.FilterSet):
    has_errors = django_filters.BooleanFilter(field_name='failure',
                                              lookup_expr='isnull',
                                              label=N_('Success'))
    name = django_filters.CharFilter(field_name='name',
                                     lookup_expr='icontains',
                                     label=N_('Name'))
    job_status = django_filters.CharFilter(field_name='job_status',
                                           lookup_expr='icontains',
                                           label=N_('Job Status'))
    submitted = django_filters.BooleanFilter(field_name='submitted',
                                             lookup_expr='exact',
                                             label=N_('Is submitted'))
    fetched = django_filters.BooleanFilter(field_name='fetched',
                                           lookup_expr='exact',
                                           label=N_('Is Fetched'))
    job_id = django_filters.CharFilter(field_name='job_id',
                                       lookup_expr='icontains',
                                       label=N_('Job ID'))
    environment = django_filters.CharFilter(field_name='environment',
                                            lookup_expr='icontains',
                                            label=N_('Environment'))

    class Meta:
        model = TestJob
        fields = [
            'name', 'job_status', 'submitted', 'fetched', 'job_id',
            'environment'
        ]
Пример #8
0
class DeleteConfirmationForm(forms.Form):
    confirmation = forms.CharField(
        label=N_('Type the slug (the name used in URLs) to confirm'))
    label = None
    no_match_message = N_('The confirmation does not match the slug')

    def __init__(self, *args, **kwargs):
        self.deletable = kwargs.pop('deletable')
        super().__init__(*args, **kwargs)
        if self.label:
            self.fields['confirmation'].label = N_(self.label)

    def clean(self):
        cleaned_data = super().clean()

        if cleaned_data['confirmation'] != self.deletable.slug:
            self.add_error('confirmation', N_(self.no_match_message))

        return cleaned_data
Пример #9
0
def BlocklyPlanetsCodesView(request):
    FirstSaveTag = "firstSave"
    pid = request.session["PlayerID"]
    service = createEngineClient()
    player = None

    message = ""
    errorMessage = ""
    if request.method == "POST":
        if "save_button_error" in request.POST and len(request.POST["save_button_error"]):
            errorMessage = request.POST["save_button_error"]
            logger = logging.getLogger(__name__)
            logger.error("javascript error when saving blockly code : " + message);
        elif "blocklyXML" in request.POST:
            service.setPlayerPlanetCode(pid, request.POST["scriptXML"].encode("utf8"))
            service.setPlayerPlanetBlocklyCode(pid, request.POST["blocklyXML"].encode("utf8"))
            player = service.getPlayer(pid)
            message = _("Code successfully saved")
            firstSave = player.tutoDisplayed.get(FirstSaveTag, 0);
            if firstSave == 0:
                service.incrementTutoDisplayed(pid, FirstSaveTag, 1)
                message = _("See in planets tab if the building is in progress")
   
    if player == None:
        player = service.getPlayer(pid)
    plLvl = player.tutoDisplayed.get(CoddingLevelTag, 0)
    codeData = player.planetsCode
    tutosText = N_("BLOCKLY_TUTO_" + str(plLvl)) if plLvl <= 8 else None
    
    if not CodeViewTutoTag in player.tutoDisplayed:
        helpMessage = _("CODE_TUTOS")
        service.incrementTutoDisplayed(pid, CodeViewTutoTag, 1)
    else:
        helpMessage = None
    
    timeInfo = service.getTimeInfo()
    
    tutoInfoIsSeen = checkIfTutoInfoSeen(service, player)
    
    return render(request, 'codesview/blockly_planet.html', {
        "name": "Planet",
        "level": plLvl,
        "message": message,
        "errorMessage": errorMessage,
        "codeData": codeData,
        "tutosText": tutosText,
        "mode": "blockly",
        "helpMessage": helpMessage,
        "timeInfo": timeInfo,
        "tutoInfoIsSeen": tutoInfoIsSeen,
        'player':player,
    })
Пример #10
0
def BlocklyFleetsCodesView(request):
    pid = request.session["PlayerID"]
    service = createEngineClient()

    message = ""
    errorMessage = ""
    if request.method == "POST":
        if "save_button_error" in request.POST and len(request.POST["save_button_error"]):
            errorMessage = request.POST["save_button_error"]
            logger = logging.getLogger(__name__)
            logger.error("javascript error when saving blockly code : " + message);
        elif "blocklyXML" in request.POST:
            service.setPlayerFleetCode(pid, request.POST["scriptXML"].encode("utf8"))
            service.setPlayerFleetBlocklyCode(pid, request.POST["blocklyXML"].encode("utf8"))
            message = _("Code successfully saved")
   
   
    player = service.getPlayer(pid)
    codeData = player.fleetsCode
    plLvl = player.tutoDisplayed.get(CoddingLevelTag, 0)
    tutosText = N_("BLOCKLY_TUTO_" + str(plLvl)) if plLvl <= 8 else None
    timeInfo = service.getTimeInfo()

    tutoInfoIsSeen = checkIfTutoInfoSeen(service, player)
     
    return render(request, 'codesview/blockly_fleet.html', {
        "name": "Fleet",
        "level": plLvl,
        "message": message,
        "errorMessage": errorMessage,
        "codeData": codeData,
        "tutosText": tutosText,
        "mode": "blockly",
        'timeInfo': timeInfo,
        'tutoInfoIsSeen': tutoInfoIsSeen,
        'player':player,
    })
Пример #11
0
 def __init__(self, *args, **kwargs):
     self.deletable = kwargs.pop('deletable')
     super().__init__(*args, **kwargs)
     if self.label:
         self.fields['confirmation'].label = N_(self.label)
Пример #12
0
class Project(models.Model, DisplayName):
    objects = ProjectManager()

    group = models.ForeignKey(Group, related_name='projects')
    slug = models.CharField(max_length=100,
                            validators=[slug_validator],
                            db_index=True,
                            verbose_name=N_('Slug'))
    name = models.CharField(max_length=100,
                            null=True,
                            blank=True,
                            verbose_name=N_('Name'))
    is_public = models.BooleanField(default=True, verbose_name=N_('Is public'))
    html_mail = models.BooleanField(default=True)
    moderate_notifications = models.BooleanField(default=False)
    custom_email_template = models.ForeignKey(EmailTemplate,
                                              null=True,
                                              blank=True)
    description = models.TextField(null=True,
                                   blank=True,
                                   verbose_name=N_('Description'))
    important_metadata_keys = models.TextField(null=True, blank=True)
    enabled_plugins_list = PluginListField(
        null=True,
        blank=True,
        features=[
            Plugin.postprocess_testrun,
            Plugin.postprocess_testjob,
        ],
    )

    wait_before_notification = models.IntegerField(
        help_text=N_('Wait this many seconds before sending notifications'),
        null=True,
        blank=True,
    )
    notification_timeout = models.IntegerField(
        help_text=N_(
            'Force sending build notifications after this many seconds'),
        null=True,
        blank=True,
    )

    data_retention_days = models.IntegerField(
        default=0,
        help_text=N_(
            "Delete builds older than this number of days. Set to 0 or any negative number to disable."
        ),
    )

    project_settings = models.TextField(null=True,
                                        blank=True,
                                        validators=[yaml_validator])

    is_archived = models.BooleanField(
        default=False,
        help_text=N_(
            'Makes the project hidden from the group page by default'),
        verbose_name=N_('Is archived'),
    )

    def __init__(self, *args, **kwargs):
        super(Project, self).__init__(*args, **kwargs)
        self.__status__ = None

    @property
    def status(self):
        if not self.__status__:
            try:
                self.__status__ = ProjectStatus.objects.filter(
                    build__project=self).latest('created_at')
            except ProjectStatus.DoesNotExist:
                pass
        return self.__status__

    def accessible_to(self, user):
        return self.is_public or self.group.accessible_to(user)

    def can_submit(self, user):
        return self.group.can_submit(user)

    def writable_by(self, user):
        return self.group.writable_by(user)

    @property
    def full_name(self):
        return str(self.group) + '/' + self.slug

    def __str__(self):
        return self.full_name

    class Meta:
        unique_together = (
            'group',
            'slug',
        )
        ordering = ['group', 'slug']

    @property
    def enabled_plugins(self):
        return self.enabled_plugins_list
Пример #13
0
 class Meta:
     unique_together = ('group', 'user')
     verbose_name = N_('Group member')
     verbose_name_plural = N_('Group members')
Пример #14
0
class DeleteGroupForm(DeleteConfirmationForm):

    label = N_('Type the group slug (the name used in URLs) to confirm')
    no_match_message = N_('The confirmation does not match the group slug')
Пример #15
0
class Command(NoArgsCommand):
    help = ('Condenses the diffs stored in the database, reducing space '
            'requirements')

    DELAY_SHOW_REMAINING_SECS = 30

    TIME_REMAINING_CHUNKS = ((60 * 60 * 24 * 365, N_('%d year', '%d years')),
                             (60 * 60 * 24 * 30, N_('%d month', '%d months')),
                             (60 * 60 * 24 * 7, N_('%d week', '%d weeks')),
                             (60 * 60 * 24, N_('%d day', '%d days')),
                             (60 * 60, N_('%d hour', '%d hours')),
                             (60, N_('%d minute', '%d minutes')))

    # We add a bunch of spaces in order to override any previous
    # content on the line, for when it shrinks.
    TIME_REMAINING_STR = _('%s remaining                                  ')

    CALC_TIME_REMAINING_STR = _('Calculating time remaining')

    def handle_noargs(self, **options):
        counts = FileDiff.objects.get_migration_counts()
        total_count = counts['total_count']

        if total_count == 0:
            self.stdout.write(_('All diffs have already been migrated.\n'))
            return

        self.stdout.write(
            _('Processing %(count)d diffs for duplicates...\n'
              '\n'
              'This may take a while. It is safe to continue using '
              'Review Board while this is\n'
              'processing, but it may temporarily run slower.\n'
              '\n') % {'count': total_count})

        # Don't allow queries to be stored.
        settings.DEBUG = False

        self.start_time = datetime.now()
        self.prev_prefix_len = 0
        self.prev_time_remaining_s = ''
        self.show_remaining = False

        info = FileDiff.objects.migrate_all(self._on_batch_done, counts)

        old_diff_size = info['old_diff_size']
        new_diff_size = info['new_diff_size']

        self.stdout.write(
            _('\n'
              '\n'
              'Condensed stored diffs from %(old_size)s bytes to '
              '%(new_size)s bytes (%(savings_pct)0.2f%% savings)\n') % {
                  'old_size':
                  intcomma(old_diff_size),
                  'new_size':
                  intcomma(new_diff_size),
                  'savings_pct': (float(old_diff_size - new_diff_size) /
                                  float(old_diff_size) * 100),
              })

    def _on_batch_done(self, processed_count, total_count):
        """Handler for when a batch of diffs are processed.

        This will report the progress of the operation, showing the estimated
        amount of time remaining.
        """
        pct = processed_count * 100 / total_count
        delta = datetime.now() - self.start_time

        # XXX: This can be replaced with total_seconds() once we no longer have
        # to support Python 2.6
        delta_secs = ((delta.microseconds +
                       (delta.seconds + delta.days * 24 * 3600) * 10**6) /
                      10**6)

        if (not self.show_remaining
                and delta_secs >= self.DELAY_SHOW_REMAINING_SECS):
            self.show_remaining = True

        if self.show_remaining:
            secs_left = ((delta_secs // processed_count) *
                         (total_count - processed_count))

            time_remaining_s = (self.TIME_REMAINING_STR %
                                self._time_remaining(secs_left))
        else:
            time_remaining_s = self.CALC_TIME_REMAINING_STR

        prefix_s = '  [%d%%] %s/%s - ' % (pct, processed_count, total_count)

        # NOTE: We use sys.stdout here instead of self.stderr in order
        #       to control newlines. Command.stderr will force a \n for
        #       each write.
        sys.stdout.write(prefix_s)

        # Only write out the time remaining string if it has changed or
        # there's been a shift in the length of the prefix. This reduces
        # how much we have to write to the terminal, and how often, by
        # a fair amount.
        if (self.prev_prefix_len != len(prefix_s)
                or self.prev_time_remaining_s != time_remaining_s):
            # Something has changed, so output the string and then cache
            # the values for the next call.
            sys.stdout.write(time_remaining_s)

            self.prev_prefix_len = len(prefix_s)
            self.prev_time_remaining_s = time_remaining_s

        sys.stdout.write('\r')
        sys.stdout.flush()

    def _time_remaining(self, secs_left):
        """Returns a string representing the time remaining for the operation.

        This is a simplified version of Django's timeuntil() function that
        does fewer calculations in order to reduce the amount of time we
        have to spend every loop. For instance, it doesn't bother with
        constructing datetimes and recomputing deltas, since we already
        have those, and it doesn't rebuild the TIME_REMAINING_CHUNKS
        every time it's called. It also handles seconds.
        """
        delta = timedelta(seconds=secs_left)
        since = delta.days * 24 * 60 * 60 + delta.seconds

        if since < 60:
            return N_('%d second', '%d seconds') % since

        for i, (seconds, name) in enumerate(self.TIME_REMAINING_CHUNKS):
            count = since // seconds

            if count != 0:
                break

        result = name % count

        if i + 1 < len(self.TIME_REMAINING_CHUNKS):
            seconds2, name2 = self.TIME_REMAINING_CHUNKS[i + 1]
            count2 = (since - (seconds * count)) // seconds2

            if count2 != 0:
                result += ', ' + name2 % count2

        return result
Пример #16
0
class Command(BaseCommand):
    """Management command to condense stored diffs in the database."""

    help = _('Condenses the diffs stored in the database, reducing space '
             'requirements')

    DELAY_SHOW_REMAINING_SECS = 30

    TIME_REMAINING_CHUNKS = ((60 * 60 * 24 * 365, N_('%d year', '%d years')),
                             (60 * 60 * 24 * 30, N_('%d month', '%d months')),
                             (60 * 60 * 24 * 7, N_('%d week', '%d weeks')),
                             (60 * 60 * 24, N_('%d day', '%d days')),
                             (60 * 60, N_('%d hour', '%d hours')),
                             (60, N_('%d minute', '%d minutes')))

    # We add a bunch of spaces in order to override any previous
    # content on the line, for when it shrinks.
    TIME_REMAINING_STR = _('%s remaining                                  ')

    CALC_TIME_REMAINING_STR = _('Calculating time remaining')

    def add_arguments(self, parser):
        """Add arguments to the command.

        Args:
            parser (argparse.ArgumentParser):
                The argument parser for the command.
        """
        parser.add_argument(
            '--show-counts-only',
            action='store_true',
            dest='show_counts',
            default=False,
            help=_("Show the number of diffs that are expected to be "
                   "migrated, but don't perform a migration."))
        parser.add_argument(
            '--no-progress',
            action='store_false',
            dest='show_progress',
            default=True,
            help=_("Don't show progress information or totals while "
                   "migrating. You might want to use this if your database "
                   "is taking too long to generate total migration counts."))
        parser.add_argument(
            '--max-diffs',
            action='store',
            dest='max_diffs',
            type=int,
            default=None,
            help=_("The maximum number of migrations to perform. This is "
                   "useful if you have a lot of diffs to migrate and want "
                   "to do it over several sessions."))

    def handle(self, **options):
        """Handle the command.

        Args:
            **options (dict, unused):
                Options parsed on the command line.

        Raises:
            django.core.management.CommandError:
                There was an error performing a diff migration.
        """
        self.show_progress = options['show_progress']
        max_diffs = options['max_diffs']

        if options['show_counts']:
            counts = FileDiff.objects.get_migration_counts()
            self.stdout.write(
                _('%d unmigrated Review Board pre-1.7 diffs\n') %
                counts['filediffs'])
            self.stdout.write(
                _('%d unmigrated Review Board 1.7-2.5 diffs\n') %
                counts['legacy_file_diff_data'])
            self.stdout.write(
                _('%d total unmigrated diffs\n') % counts['total_count'])
            return
        elif self.show_progress:
            counts = FileDiff.objects.get_migration_counts()
            total_count = counts['total_count']

            if total_count == 0:
                self.stdout.write(_('All diffs have already been migrated.\n'))
                return

            warning = counts.get('warning')

            if warning:
                self.stderr.write(_('Warning: %s\n\n') % warning)

            if max_diffs is None:
                process_count = total_count
            else:
                process_count = min(max_diffs, total_count)

            self.stdout.write(
                _('Processing %(count)d unmigrated diffs...\n') %
                {'count': process_count})
        else:
            # Set to an empty dictionary to force migrate_all() to not
            # look up its own counts.
            counts = {}

            self.stdout.write(_('Processing all unmigrated diffs...\n'))

        self.stdout.write(
            _('\n'
              'This may take a while. It is safe to continue using '
              'Review Board while this is\n'
              'processing, but it may temporarily run slower.\n'
              '\n'))

        # Don't allow queries to be stored.
        settings.DEBUG = False

        self.start_time = datetime.now()
        self.prev_prefix_len = 0
        self.prev_time_remaining_s = ''
        self.show_remaining = False

        info = FileDiff.objects.migrate_all(batch_done_cb=self._on_batch_done,
                                            counts=counts,
                                            max_diffs=max_diffs)

        if info['diffs_migrated'] == 0:
            self.stdout.write(_('All diffs have already been migrated.\n'))
        else:
            old_diff_size = info['old_diff_size']
            new_diff_size = info['new_diff_size']

            self.stdout.write(
                _('\n'
                  '\n'
                  'Condensed stored diffs from %(old_size)s bytes to '
                  '%(new_size)s bytes (%(savings_pct)0.2f%% savings)\n') % {
                      'old_size':
                      intcomma(old_diff_size),
                      'new_size':
                      intcomma(new_diff_size),
                      'savings_pct': (float(old_diff_size - new_diff_size) /
                                      float(old_diff_size) * 100),
                  })

    def _on_batch_done(self, total_diffs_migrated, total_count=None, **kwargs):
        """Handler for when a batch of diffs are processed.

        This will report the progress of the operation, showing the estimated
        amount of time remaining.

        Args:
            total_diffs_migrated (int):
                The total number of diffs migrated so far in this
                condensediffs operation.

            total_count (int, optional):
                The total number of diffs to migrate in the database. This
                may be ``None``, in which case the output won't contain
                progress and time estimation.

            **kwargs (dict, unused):
                Unused keyword arguments.
        """
        # NOTE: We use sys.stdout when writing instead of self.stderr in order
        #       to control newlines. Command.stderr will force a \n for each
        #       write.
        if self.show_progress:
            # We may be receiving an estimate for the total number of diffs
            # that is less than the actual count. If we've gone past the
            # initial total, just bump up the total to the current count.
            total_count = max(total_diffs_migrated, total_count)

            pct = total_diffs_migrated * 100 / total_count

            delta = datetime.now() - self.start_time
            delta_secs = delta.total_seconds()

            if (not self.show_remaining
                    and delta_secs >= self.DELAY_SHOW_REMAINING_SECS):
                self.show_remaining = True

            if self.show_remaining:
                secs_left = ((delta_secs // total_diffs_migrated) *
                             (total_count - total_diffs_migrated))

                time_remaining_s = (self.TIME_REMAINING_STR %
                                    self._time_remaining(secs_left))
            else:
                time_remaining_s = self.CALC_TIME_REMAINING_STR

            prefix_s = '  [%d%%] %s/%s - ' % (pct, total_diffs_migrated,
                                              total_count)

            sys.stdout.write(prefix_s)

            # Only write out the time remaining string if it has changed or
            # there's been a shift in the length of the prefix. This reduces
            # how much we have to write to the terminal, and how often, by
            # a fair amount.
            if (self.prev_prefix_len != len(prefix_s)
                    or self.prev_time_remaining_s != time_remaining_s):
                # Something has changed, so output the string and then cache
                # the values for the next call.
                sys.stdout.write(time_remaining_s)

                self.prev_prefix_len = len(prefix_s)
                self.prev_time_remaining_s = time_remaining_s
        else:
            sys.stdout.write(' %s diffs migrated' % total_diffs_migrated)

        sys.stdout.write('\r')
        sys.stdout.flush()

    def _time_remaining(self, secs_left):
        """Return a string representing the time remaining for the operation.

        This is a simplified version of Django's timeuntil() function that
        does fewer calculations in order to reduce the amount of time we
        have to spend every loop. For instance, it doesn't bother with
        constructing datetimes and recomputing deltas, since we already
        have those, and it doesn't rebuild the TIME_REMAINING_CHUNKS
        every time it's called. It also handles seconds.

        Args:
            secs_left (int):
                The estimated number of seconds remaining.

        Returns:
            unicode:
            The text containing the time remaining.
        """
        delta = timedelta(seconds=secs_left)
        since = delta.days * 24 * 60 * 60 + delta.seconds

        if since < 60:
            return N_('%d second', '%d seconds') % since

        for i, (seconds, name) in enumerate(self.TIME_REMAINING_CHUNKS):
            count = since // seconds

            if count != 0:
                break

        result = name % count

        if i + 1 < len(self.TIME_REMAINING_CHUNKS):
            seconds2, name2 = self.TIME_REMAINING_CHUNKS[i + 1]
            count2 = (since - (seconds * count)) // seconds2

            if count2 != 0:
                result += ', ' + name2 % count2

        return result
Пример #17
0
def shipbrief(value):
    return N_("SHIP_BRIEF_%i" % (value))
Пример #18
0
def buildingbrief(value):
    return N_("BUILDING_BRIEF_%i" % (value))
Пример #19
0
def cannonbrief(value):
    return N_("CANNON_BRIEF_%i" % (value))