def flush_rclone(self):
     if not self.rclone_remote or not self.rclone_queue:
         return
     click.echo()
     for name, data in self.buckets.viewitems():
         if not data['exists']:
             self.create_bucket(name)
             data['exists'] = True
     for bucket, data in self.rclone_queue.viewitems():
         click.echo(
             cformat(
                 'Copying %{cyan}{}%{reset} files (%{cyan}{}%{reset}) to %{cyan}{}%{reset} via rclone'
             ).format(data['files'], do_filesizeformat(data['bytes']),
                      bucket))
         start = datetime.now()
         try:
             subprocess.check_call([
                 'rclone', 'copy', '--copy-links', data['path'],
                 '{}:{}'.format(self.rclone_remote, bucket)
             ])
         except subprocess.CalledProcessError:
             click.secho('\nError while running rclone', fg='red')
             raise
         duration = (datetime.now() - start)
         click.echo('...finished after {}'.format(
             format_human_timedelta(duration, 'minutes', narrow=True)))
         rmlinktree(data['path'])
     self.rclone_queue.clear()
Esempio n. 2
0
 def validate_end_dt(self, field):
     if not self.check_timetable_boundaries:
         return
     if self.update_timetable.data:
         # if we move timetable entries according to the start date
         # change, check that there's enough time at the end.
         start_dt_offset = self.start_dt.data - self.start_dt.object_data
         end_buffer = field.data - max(self.toplevel_timetable_entries,
                                       key=attrgetter('end_dt')).end_dt
         delta = max(timedelta(), start_dt_offset - end_buffer)
         if delta:
             delta_str = format_human_timedelta(delta, 'minutes', True)
             raise ValidationError(
                 _("The event is too short to fit all timetable entries. "
                   "It must be at least {} longer.").format(delta_str))
     else:
         # if we do not update timetable entries, only check that
         # the event does not end before its last timetable entry;
         # a similar check for the start time is done above in that
         # field's validation method.
         max_end_dt = max(self.toplevel_timetable_entries,
                          key=attrgetter('end_dt')).end_dt
         if field.data < max_end_dt:
             raise ValidationError(
                 _("The event cannot end before its last timetable entry, which is at {}."
                   ).format(
                       to_unicode(
                           format_datetime(max_end_dt,
                                           timezone=self.event.tzinfo))))
Esempio n. 3
0
 def validate_duration(self, field):
     super(BaseEntryForm, self).validate_duration(field)
     if self.entry.type == TimetableEntryType.SESSION_BLOCK:
         needed_duration = max(x.end_dt for x in self.entry.children) - min(x.start_dt for x in self.entry.children)
         if field.data < needed_duration:
             raise ValidationError(_("The duration must be at least {duration} to fit the entries within.")
                                   .format(duration=format_human_timedelta(needed_duration, 'minutes')))
 def run(self):
     """Perform the rescheduling."""
     if self.fit_blocks:
         self._fit_blocks()
     if self.mode == RescheduleMode.time:
         self._reschedule_time()
     elif self.mode == RescheduleMode.duration:
         self._reschedule_duration()
     db.session.flush()
     self.event.log(
         EventLogRealm.management,
         EventLogKind.change,
         'Timetable',
         "Entries rescheduled",
         session.user,
         data={
             'Mode':
             self.mode.title,
             'Day':
             format_date(self.day, locale='en_GB'),
             'Fit Blocks':
             self.fit_blocks,
             'Gap':
             format_human_timedelta(self.gap) if self.gap else None,
             'Session':
             self.session.title if self.session else None,
             'Session block':
             self.session_block.full_title if self.session_block else None
         })
Esempio n. 5
0
def generate_spreadsheet_from_contributions(contributions):
    """Return a tuple consisting of spreadsheet columns and respective
    contribution values"""

    headers = ['Id', 'Title', 'Description', 'Date', 'Duration', 'Type', 'Session', 'Track', 'Presenters', 'Materials']
    rows = []
    for c in sorted(contributions, key=attrgetter('friendly_id')):
        contrib_data = {'Id': c.friendly_id, 'Title': c.title, 'Description': c.description,
                        'Duration': format_human_timedelta(c.duration),
                        'Date': format_datetime(c.timetable_entry.start_dt) if c.timetable_entry else None,
                        'Type': c.type.name if c.type else None,
                        'Session': c.session.title if c.session else None,
                        'Track': c.track.title if c.track else None,
                        'Materials': None,
                        'Presenters': ', '.join(speaker.person.full_name for speaker in c.speakers)}

        attachments = []
        attached_items = get_attached_items(c)
        for attachment in attached_items.get('files', []):
            attachments.append(attachment.absolute_download_url)

        for folder in attached_items.get('folders', []):
            for attachment in folder.attachments:
                attachments.append(attachment.absolute_download_url)

        if attachments:
            contrib_data['Materials'] = ', '.join(attachments)
        rows.append(contrib_data)
    return headers, rows
Esempio n. 6
0
 def _render_template(self, template_name, kwargs):
     template_dir = os.path.join(get_root_path('indico'),
                                 'legacy/pdfinterface/latex_templates')
     env = Environment(loader=FileSystemLoader(template_dir),
                       autoescape=False,
                       trim_blocks=True,
                       keep_trailing_newline=True,
                       auto_reload=config.DEBUG,
                       extensions=[LatexEscapeExtension],
                       undefined=StrictUndefined,
                       block_start_string=r'\JINJA{',
                       block_end_string='}',
                       variable_start_string=r'\VAR{',
                       variable_end_string='}',
                       comment_start_string=r'\#{',
                       comment_end_string='}')
     env.filters['format_date'] = EnsureUnicodeExtension.wrap_func(
         format_date)
     env.filters['format_time'] = EnsureUnicodeExtension.wrap_func(
         format_time)
     env.filters['format_duration'] = lambda delta: format_human_timedelta(
         delta, 'minutes')
     env.filters['latex'] = _latex_escape
     env.filters['rawlatex'] = RawLatex
     env.filters['markdown'] = kwargs.pop('markdown')
     env.globals['_'] = _
     env.globals['ngettext'] = ngettext
     env.globals['session'] = session
     template = env.get_or_select_template(template_name)
     distribution = pkg_resources.get_distribution('indico-fonts')
     font_dir = os.path.join(distribution.location, 'indico_fonts',
                             '')  # XXX: trailing slash required
     return template.render(font_dir=font_dir, **kwargs)
Esempio n. 7
0
def generate_spreadsheet_from_contributions(contributions):
    """Return a tuple consisting of spreadsheet columns and respective
    contribution values"""

    headers = ['Id', 'Title', 'Description', 'Date', 'Duration', 'Type', 'Session', 'Track', 'Presenters', 'Materials']
    rows = []
    for c in sorted(contributions, key=attrgetter('friendly_id')):
        contrib_data = {'Id': c.friendly_id, 'Title': c.title, 'Description': c.description,
                        'Duration': format_human_timedelta(c.duration),
                        'Date': c.timetable_entry.start_dt if c.timetable_entry else None,
                        'Type': c.type.name if c.type else None,
                        'Session': c.session.title if c.session else None,
                        'Track': c.track.title if c.track else None,
                        'Materials': None,
                        'Presenters': ', '.join(speaker.person.full_name for speaker in c.speakers)}

        attachments = []
        attached_items = get_attached_items(c)
        for attachment in attached_items.get('files', []):
            attachments.append(attachment.absolute_download_url)

        for folder in attached_items.get('folders', []):
            for attachment in folder.attachments:
                attachments.append(attachment.absolute_download_url)

        if attachments:
            contrib_data['Materials'] = ', '.join(attachments)
        rows.append(contrib_data)
    return headers, rows
Esempio n. 8
0
 def _render_template(self, template_name, kwargs):
     template_dir = os.path.join(get_root_path('indico'), 'legacy/pdfinterface/latex_templates')
     env = Environment(loader=FileSystemLoader(template_dir),
                       autoescape=False,
                       trim_blocks=True,
                       keep_trailing_newline=True,
                       auto_reload=config.DEBUG,
                       extensions=[LatexEscapeExtension],
                       undefined=StrictUndefined,
                       block_start_string=r'\JINJA{', block_end_string='}',
                       variable_start_string=r'\VAR{', variable_end_string='}',
                       comment_start_string=r'\#{', comment_end_string='}')
     env.filters['format_date'] = EnsureUnicodeExtension.wrap_func(format_date)
     env.filters['format_time'] = EnsureUnicodeExtension.wrap_func(format_time)
     env.filters['format_duration'] = lambda delta: format_human_timedelta(delta, 'minutes')
     env.filters['latex'] = _latex_escape
     env.filters['rawlatex'] = RawLatex
     env.filters['markdown'] = kwargs.pop('markdown')
     env.globals['_'] = _
     env.globals['ngettext'] = ngettext
     env.globals['session'] = session
     template = env.get_or_select_template(template_name)
     distribution = pkg_resources.get_distribution('indico-fonts')
     font_dir = os.path.join(distribution.location, 'indico_fonts', '')  # XXX: trailing slash required
     return template.render(font_dir=font_dir, **kwargs)
Esempio n. 9
0
 def _process(self):
     form = ContributionDurationForm(obj=FormDefaults(self.contrib), contrib=self.contrib)
     if form.validate_on_submit():
         with track_time_changes():
             update_contribution(self.contrib, {'duration': form.duration.data})
         return jsonify_data(new_value=format_human_timedelta(self.contrib.duration))
     return jsonify_form(form, back_button=False, disabled_until_change=True)
Esempio n. 10
0
def verbose_iterator(iterable,
                     total,
                     get_id,
                     get_title=None,
                     print_every=10,
                     print_total_time=False):
    """Iterate large iterables verbosely.

    :param iterable: An iterable
    :param total: The number of items in `iterable`
    :param get_id: callable to retrieve the ID of an item
    :param get_title: callable to retrieve the title of an item
    :param print_every: after which number of items to update the progress
    :param print_total_time: whether to print the total time spent at the end
    """
    term_width = terminal_size()[0]
    start_time = time.time()
    fmt = cformat(
        '[%{cyan!}{:6}%{reset}/%{cyan}{}%{reset}  %{yellow!}{:.3f}%{reset}%  %{green!}{}%{reset}]  {:>8}  %{grey!}{}'
    )
    end_fmt = cformat(
        '[%{cyan!}{:6}%{reset}/%{cyan}{}%{reset}  %{yellow!}{:.3f}%{reset}%  %{green!}{}%{reset}]  '
        'Total duration: %{green}{}')

    def _print_text(text):
        print('\r', ' ' * term_width, end='', sep='')
        # terminal width + ansi control code length - trailing reset code (4)
        print('\r',
              text[:term_width + len(text.value_colors) -
                   len(text.value_no_colors) - 4],
              cformat('%{reset}'),
              end='',
              sep='')
        sys.stdout.flush()

    for i, item in enumerate(iterable, 1):
        if i % print_every == 0 or i == total:
            remaining_seconds = int(
                (time.time() - start_time) / i * (total - i))
            minutes, seconds = divmod(remaining_seconds, 60)
            remaining = f'{minutes:02}:{seconds:02}'
            id_ = get_id(item)
            title = get_title(item).replace('\n', ' ') if get_title else ''
            text = fmt.format(i, total, (i / total * 100.0), remaining, id_,
                              title)
            _print_text(text)

        yield item

    if print_total_time:
        total_duration = timedelta(seconds=(time.time() - start_time))
        _print_text(
            end_fmt.format(total, total, 100, '00:00',
                           format_human_timedelta(total_duration)))

    print()
Esempio n. 11
0
 def validate_duration(self, field):
     super(BaseEntryForm, self).validate_duration(field)
     if self.entry.type == TimetableEntryType.SESSION_BLOCK:
         needed_duration = max(x.end_dt for x in self.entry.children) - min(
             x.start_dt for x in self.entry.children)
         if field.data < needed_duration:
             raise ValidationError(
                 _("The duration must be at least {duration} to fit the entries within."
                   ).format(duration=format_human_timedelta(
                       needed_duration, 'minutes')))
Esempio n. 12
0
def _convert_data(event, value):
    if isinstance(value, timedelta):
        value = format_human_timedelta(value)
    elif isinstance(value, datetime):
        value = format_datetime(value, locale="en_GB", timezone=event.timezone)
    elif value.__class__.__name__ == "ContributionType":
        value = value._name
    elif value.__class__.__name__ == "AbstractFieldContent":
        value = '{}: "{}"'.format(convert_to_unicode(value.field._caption), convert_to_unicode(value.value))
    return convert_to_unicode(value).strip()
Esempio n. 13
0
def generate_spreadsheet_from_contributions(contributions):
    """
    Return a tuple consisting of spreadsheet columns and respective
    contribution values.
    """

    has_board_number = any(c.board_number for c in contributions)
    has_authors = any(pl.author_type != AuthorType.none for c in contributions
                      for pl in c.person_links)
    headers = [
        'Id', 'Title', 'Description', 'Date', 'Duration', 'Type', 'Session',
        'Track', 'Presenters', 'Materials'
    ]
    if has_authors:
        headers += ['Authors', 'Co-Authors']
    if has_board_number:
        headers.append('Board number')
    rows = []
    for c in sort_contribs(contributions, sort_by='friendly_id'):
        contrib_data = {
            'Id': c.friendly_id,
            'Title': c.title,
            'Description': c.description,
            'Duration': format_human_timedelta(c.duration),
            'Date': c.timetable_entry.start_dt if c.timetable_entry else None,
            'Type': c.type.name if c.type else None,
            'Session': c.session.title if c.session else None,
            'Track': c.track.title if c.track else None,
            'Materials': None,
            'Presenters':
            ', '.join(speaker.full_name for speaker in c.speakers)
        }
        if has_authors:
            contrib_data.update({
                'Authors':
                ', '.join(author.full_name for author in c.primary_authors),
                'Co-Authors':
                ', '.join(author.full_name for author in c.secondary_authors)
            })
        if has_board_number:
            contrib_data['Board number'] = c.board_number

        attachments = []
        attached_items = get_attached_items(c)
        for attachment in attached_items.get('files', []):
            attachments.append(attachment.absolute_download_url)

        for folder in attached_items.get('folders', []):
            for attachment in folder.attachments:
                attachments.append(attachment.absolute_download_url)

        if attachments:
            contrib_data['Materials'] = ', '.join(attachments)
        rows.append(contrib_data)
    return headers, rows
Esempio n. 14
0
def _convert_data(event, value):
    if isinstance(value, timedelta):
        value = format_human_timedelta(value)
    elif isinstance(value, datetime):
        value = format_datetime(value, locale='en_GB', timezone=event.timezone)
    elif value.__class__.__name__ == 'ContributionType':
        value = value._name
    elif value.__class__.__name__ == 'AbstractFieldContent':
        value = '{}: "{}"'.format(convert_to_unicode(value.field._caption),
                                  convert_to_unicode(value.value))
    return convert_to_unicode(value).strip()
Esempio n. 15
0
 def validate_end_dt(self, field):
     if not self.check_timetable_boundaries:
         return
     start_dt_offset = self.start_dt.data - self.start_dt.object_data
     end_buffer = field.data - max(self.toplevel_timetable_entries,
                                   key=attrgetter('end_dt')).end_dt
     delta = max(timedelta(), start_dt_offset - end_buffer)
     if delta:
         delta_str = format_human_timedelta(delta, 'minutes', True)
         raise ValidationError(
             _("The event is too short to fit all timetable entries. "
               "It must be at least {} longer.").format(delta_str))
Esempio n. 16
0
 def run(self):
     """Perform the rescheduling"""
     if self.fit_blocks:
         self._fit_blocks()
     if self.mode == RescheduleMode.time:
         self._reschedule_time()
     elif self.mode == RescheduleMode.duration:
         self._reschedule_duration()
     db.session.flush()
     self.event.log(EventLogRealm.management, EventLogKind.change, 'Timetable',
                    "Entries rescheduled", session.user,
                    data={'Mode': self.mode.title,
                          'Day': format_date(self.day, locale='en_GB'),
                          'Fit Blocks': self.fit_blocks,
                          'Gap': format_human_timedelta(self.gap) if self.gap else None,
                          'Session': self.session.title if self.session else None,
                          'Session block': self.session_block.full_title if self.session_block else None})
Esempio n. 17
0
 def validate_end_dt(self, field):
     if not self.check_timetable_boundaries:
         return
     if self.update_timetable.data:
         # if we move timetable entries according to the start date
         # change, check that there's enough time at the end.
         start_dt_offset = self.start_dt.data - self.start_dt.object_data
         end_buffer = field.data - max(self.toplevel_timetable_entries, key=attrgetter('end_dt')).end_dt
         delta = max(timedelta(), start_dt_offset - end_buffer)
         if delta:
             delta_str = format_human_timedelta(delta, 'minutes', True)
             raise ValidationError(_("The event is too short to fit all timetable entries. "
                                     "It must be at least {} longer.").format(delta_str))
     else:
         # if we do not update timetable entries, only check that
         # the event does not end before its last timetable entry;
         # a similar check for the start time is done above in that
         # field's validation method.
         max_end_dt = max(self.toplevel_timetable_entries, key=attrgetter('end_dt')).end_dt
         if field.data < max_end_dt:
             raise ValidationError(_("The event cannot end before its last timetable entry, which is at {}.")
                                   .format(to_unicode(format_datetime(max_end_dt, timezone=self.event.tzinfo))))
Esempio n. 18
0
 def run(self):
     """Perform the rescheduling"""
     if self.fit_blocks:
         self._fit_blocks()
     if self.mode == RescheduleMode.time:
         self._reschedule_time()
     elif self.mode == RescheduleMode.duration:
         self._reschedule_duration()
     db.session.flush()
     self.event.log(
         EventLogRealm.management,
         EventLogKind.change,
         "Timetable",
         "Entries rescheduled",
         session.user,
         data={
             "Mode": self.mode.title,
             "Day": format_date(self.day, locale="en_GB"),
             "Fit Blocks": self.fit_blocks,
             "Gap": format_human_timedelta(self.gap) if self.gap else None,
             "Session": self.session.title if self.session else None,
             "Session block": self.session_block.full_title if self.session_block else None,
         },
     )
Esempio n. 19
0
 def flush_rclone(self):
     if not self.rclone_remote or not self.rclone_queue:
         return
     click.echo()
     for name, data in self.buckets.viewitems():
         if not data['exists']:
             self.create_bucket(name)
             data['exists'] = True
     for bucket, data in self.rclone_queue.viewitems():
         click.echo(cformat('Copying %{cyan}{}%{reset} files (%{cyan}{}%{reset}) to %{cyan}{}%{reset} via rclone')
                    .format(data['files'], do_filesizeformat(data['bytes']), bucket))
         start = datetime.now()
         try:
             subprocess.check_call([
                 'rclone', 'copy', '--copy-links',
                 data['path'], '{}:{}'.format(self.rclone_remote, bucket)
             ])
         except subprocess.CalledProcessError:
             click.secho('\nError while running rclone', fg='red')
             raise
         duration = (datetime.now() - start)
         click.echo('...finished after {}'.format(format_human_timedelta(duration, 'minutes', narrow=True)))
         rmlinktree(data['path'])
     self.rclone_queue.clear()
Esempio n. 20
0
def test_format_human_timedelta(delta, granularity, expected):
    assert format_human_timedelta(delta, granularity) == expected
Esempio n. 21
0
 def __call__(self, form, field):
     if field.data is not None and field.data > self.max_duration:
         raise ValidationError(
             _('Duration cannot exceed {}').format(
                 format_human_timedelta(self.max_duration)))
Esempio n. 22
0
def test_format_human_timedelta(delta, granularity, expected):
    assert format_human_timedelta(delta, granularity) == expected
Esempio n. 23
0
 def __call__(self, form, field):
     if field.data is not None and field.data > self.max_duration:
         raise ValidationError(_('Duration cannot exceed {}').format(format_human_timedelta(self.max_duration)))