Beispiel #1
0
def delete_vote(self, request, form):
    layout = DeletePageLayout(self, request)

    if form.submitted(request):
        request.session.delete(self)
        request.message(_("Page deleted"), 'success')
        return request.redirect(layout.homepage_url)

    return {
        'layout':
        layout,
        'form':
        form,
        'subtitle':
        self.title,
        'message':
        _('Do you really want to delete "${item}"?',
          mapping={'item': self.title}),
        'button_text':
        _("Delete"),
        'button_class':
        'alert',
        'cancel':
        request.link(self)
    }
Beispiel #2
0
 def __call__(self, field, **kwargs):
     kwargs['class_'] = 'policy-selector'
     kwargs['data-tree'] = dumps(field.tree)
     kwargs['data-placehoder-text'] = field.gettext(
         _("Select Some Options"))
     kwargs['data-no-matches-text'] = field.gettext(_("No results match"))
     return super().__call__(field, **kwargs)
Beispiel #3
0
 def breadcrumbs(self):
     return [
         Link(_("Homepage"), self.homepage_url),
         Link(_("Votes"), self.votes_url),
         Link(self.model.short_title, self.request.link(self.model)),
         Link(self.title, '#'),
     ]
Beispiel #4
0
class PageForm(Form):

    title = StringField(label=_("Title"), validators=[InputRequired()])

    content = QuillField(label=_("Content"),
                         tags=('strong', 'em', 'a', 'h3', 'ol', 'ul',
                               'blockquote'),
                         validators=[InputRequired()])

    @property
    def id(self):
        """ An ID based on the title. """

        id = normalize_for_url(self.title.data or 'page')
        query = self.request.session.query(TranslatablePage)
        while query.filter_by(id=id).first():
            id = increment_name(id)
        return id

    def update_model(self, model):
        model.title = self.title.data
        model.content = self.content.data
        model.id = model.id or self.id

    def apply_model(self, model):
        self.title.data = model.title
        self.content.data = model.content
Beispiel #5
0
def update_votes(self, request, form):
    self = self.default()

    layout = UpdateVotesLayout(self, request)

    if form.submitted(request):
        added, updated = self.update(form.dataset.data)
        request.message(
            _("Dataset updated (${added} added, ${updated} updated)",
              mapping={
                  'added': added,
                  'updated': updated
              }), 'success')

        # Warn if descriptor labels are missing
        missing = set()
        for vote in self.query():
            for policy_area in vote.policy_areas:
                missing |= set(path for path in policy_area.label_path
                               if not isinstance(path, TranslationString))
        if missing:
            request.message(
                _("The dataset contains unknown descriptors: ${items}.",
                  mapping={'items': ', '.join(sorted(missing))}), 'warning')

        return request.redirect(layout.votes_url)

    return {
        'layout': layout,
        'form': form,
        'cancel': request.link(self),
        'button_text': _("Update"),
    }
Beispiel #6
0
def delete_page_attachment(self, request, form):
    """ Delete an attachment. """

    layout = DeletePageAttachmentLayout(self, request)

    if form.submitted(request):
        url = request.link(self.linked_swissvotes_page[0], 'attachments')
        request.session.delete(self)
        request.message(_("Attachment deleted."), 'success')
        return redirect(url)

    return {
        'message':
        _('Do you really want to delete "${item}"?',
          mapping={'item': self.filename}),
        'layout':
        layout,
        'form':
        form,
        'title':
        self.filename,
        'subtitle':
        _("Delete"),
        'button_text':
        _("Delete"),
        'button_class':
        'alert',
        'cancel':
        request.link(self)
    }
Beispiel #7
0
 def editbar_links(self):
     result = []
     if self.request.has_role('admin', 'editor'):
         result.append(
             Link(
                 text=_("Update dataset"),
                 url=self.request.link(self.model.default(), name='update'),
                 attrs={'class': 'upload-icon'}
             )
         )
         result.append(
             Link(
                 text=_("Download dataset (CSV)"),
                 url=self.request.link(self.model.default(), name='csv'),
                 attrs={'class': 'export-icon'}
             )
         )
         result.append(
             Link(
                 text=_("Download dataset (XLSX)"),
                 url=self.request.link(self.model.default(), name='xlsx'),
                 attrs={'class': 'export-icon'}
             )
         )
     if self.request.has_role('admin'):
         result.append(
             Link(
                 text=_("Delete all votes"),
                 url=self.request.link(self.model.default(), name='delete'),
                 attrs={'class': 'delete-icon'}
             )
         )
     return result
Beispiel #8
0
    def breadcrumbs(self):
        if self.model.id == 'home':
            return [Link(_("Homepage"), self.homepage_url)]

        return [
            Link(_("Homepage"), self.homepage_url),
            Link(self.title, '#'),
        ]
class UpdateDatasetForm(Form):

    callout = _("Updating the dataset may take some time.")

    dataset = SwissvoteDatasetField(
        label=_("Dataset"),
        validators=[
            DataRequired()
        ]
    )
Beispiel #10
0
def handle_notfound(self, request):
    """ Displays a nice HTTP 404 error. """
    @request.after
    def set_status_code(response):
        response.status_code = self.code

    return {
        'layout': DefaultLayout(self, request),
        'title': _("Page not Found"),
        'message': _("The page you are looking for could not be found."),
    }
Beispiel #11
0
def handle_forbidden(self, request):
    """ Displays a nice HTTP 403 error. """
    @request.after
    def set_status_code(response):
        response.status_code = self.code

    return {
        'layout':
        DefaultLayout(self, request),
        'title':
        _("Access Denied"),
        'message':
        _("You are trying to open a page for which you are not authorized.")
    }
Beispiel #12
0
def edit_page(self, request, form):
    request.include('quill')

    if form.submitted(request):
        form.update_model(self)
        request.message(_("Page modified."), 'success')
        return request.redirect(request.link(self))

    if not form.errors:
        form.apply_model(self)

    return {
        'layout': EditPageLayout(self, request),
        'form': form,
        'button_text': _("Update"),
    }
Beispiel #13
0
def test_layout_delete_vote(swissvotes_app):
    request = DummyRequest()
    request.app = swissvotes_app
    model = SwissVote(
        title_de="Vote",
        title_fr="Vote",
        short_title_de="Vote",
        short_title_fr="Vote",
    )

    layout = DeleteVoteLayout(model, request)
    assert layout.title == _("Delete vote")
    assert layout.editbar_links == []
    assert path(
        layout.breadcrumbs) == ('Principal/SwissVoteCollection/SwissVote/#')

    # Log in as editor
    request.roles = ['editor']
    layout = DeleteVoteLayout(model, request)
    assert layout.editbar_links == []

    # Log in as admin
    request.roles = ['admin']
    layout = DeleteVoteLayout(model, request)
    assert layout.editbar_links == []
Beispiel #14
0
def test_layout_upload_vote_attachemts(swissvotes_app):
    request = DummyRequest()
    request.app = swissvotes_app
    model = SwissVote(
        title_de="Vote",
        title_fr="Vote",
        short_title_de="Vote",
        short_title_fr="Vote",
    )

    layout = UploadVoteAttachemtsLayout(model, request)
    assert layout.title == _("Manage attachments")
    assert layout.editbar_links == []
    assert path(
        layout.breadcrumbs) == ('Principal/SwissVoteCollection/SwissVote/#')

    # Log in as editor
    request.roles = ['editor']
    layout = UploadVoteAttachemtsLayout(model, request)
    assert layout.editbar_links == []

    # Log in as admin
    request.roles = ['admin']
    layout = UploadVoteAttachemtsLayout(model, request)
    assert layout.editbar_links == []
Beispiel #15
0
def test_layout_votes(swissvotes_app):
    request = DummyRequest()
    request.app = swissvotes_app
    model = SwissVoteCollection(swissvotes_app)

    layout = VotesLayout(model, request)
    assert layout.title == _("Votes")
    assert layout.editbar_links == []
    assert path(layout.breadcrumbs) == 'Principal/SwissVoteCollection'

    # Log in as editor
    request.roles = ['editor']
    layout = VotesLayout(model, request)
    assert list(hrefs(layout.editbar_links)) == [
        'SwissVoteCollection/update', 'SwissVoteCollection/csv',
        'SwissVoteCollection/xlsx'
    ]

    # Log in as admin
    request.roles = ['admin']
    layout = VotesLayout(model, request)
    assert list(hrefs(layout.editbar_links)) == [
        'SwissVoteCollection/update',
        'SwissVoteCollection/csv',
        'SwissVoteCollection/xlsx',
        'SwissVoteCollection/delete',
    ]
Beispiel #16
0
 def editbar_links(self):
     result = []
     if self.request.has_role('admin', 'editor'):
         result.append(
             Link(
                 text=_("Manage attachments"),
                 url=self.request.link(self.model, name='upload'),
                 attrs={'class': 'upload-icon'}
             )
         )
         result.append(
             Link(
                 text=_("Delete vote"),
                 url=self.request.link(self.model, name='delete'),
                 attrs={'class': 'delete-icon'}
             )
         )
     return result
Beispiel #17
0
def delete_votes(self, request, form):
    self = self.default()

    layout = DeleteVotesLayout(self, request)

    if form.submitted(request):
        for vote in self.query():
            request.session.delete(vote)
        request.message(_("All votes deleted"), 'success')
        return request.redirect(layout.votes_url)

    return {
        'layout': layout,
        'form': form,
        'message': _("Do you really want to delete all votes?!"),
        'button_text': _("Delete"),
        'button_class': 'alert',
        'cancel': request.link(self)
    }
Beispiel #18
0
 def top_navigation(self):
     result = [Link(_("Votes"), self.votes_url)]
     for page in self.pages.query():
         if page.id not in self.request.app.static_content_pages:
             result.append(
                 Link(
                     page.title,
                     self.request.link(page),
                     sortable_id=page.id,
                 ))
     return result
Beispiel #19
0
def add_page(self, request, form):
    request.include('quill')

    if form.submitted(request):
        page = TranslatablePage()
        form.update_model(page)
        request.session.add(page)
        request.message(_("Page added."), 'success')
        return request.redirect(request.link(page))

    return {'layout': AddPageLayout(self, request), 'form': form}
Beispiel #20
0
def upload_vote_attachments(self, request, form):
    layout = UploadVoteAttachemtsLayout(self, request)

    if form.submitted(request):
        form.update_model(self)
        request.message(_("Attachments updated"), 'success')
        return request.redirect(request.link(self))

    if not form.errors:
        form.apply_model(self)

    return {'layout': layout, 'form': form, 'cancel': request.link(self)}
Beispiel #21
0
def handle_password_reset_request(self, request, form):
    """ Handles the password reset requests. """

    layout = DefaultLayout(self, request)

    if form.submitted(request):
        users = UserCollection(request.session)
        user = users.by_username(form.email.data)
        if user:
            url = password_reset_url(user, request,
                                     request.link(self, name='reset-password'))

            request.app.send_transactional_email(
                subject=request.translate(_("Password reset")),
                receivers=(user.username, ),
                reply_to=request.app.mail['transactional']['sender'],
                content=render_template(
                    'mail_password_reset.pt', request, {
                        'title': request.translate(_("Password reset")),
                        'model': None,
                        'url': url,
                        'layout': MailLayout(self, request)
                    }))
        else:
            log.info("Failed password reset attempt by {}".format(
                request.client_addr))

        message = _(
            'A password reset link has been sent to ${email}, provided an '
            'account exists for this email address.',
            mapping={'email': form.email.data})
        request.message(message, 'success')
        return request.redirect(layout.homepage_url)

    return {
        'layout': layout,
        'title': _('Reset password'),
        'form': form,
        'button_text': _("Submit"),
    }
Beispiel #22
0
 def set_filename(response):
     bfs_number = self.linked_swissvotes[0].bfs_number
     name = self.name.split('-')[0]
     extension = {'results_by_domain': 'xlsx'}.get(name, 'pdf')
     title = {
         'voting_text': _("Voting text"),
         'brief_description': _("Brief description Swissvotes"),
         'realization': _("Realization"),
         'federal_council_message': _("Federal council message"),
         'parliamentary_debate': _("Parliamentary debate"),
         'voting_booklet': _("Voting booklet"),
         'ad_analysis': _("Analysis of the advertising campaign"),
         'resolution': _("Resolution"),
         'results_by_domain':
         _("Result by canton, district and municipality")
     }.get(name, '')
     title = normalize_for_url(request.translate(title))
     response.headers['Content-Disposition'] = (
         f'inline; filename={bfs_number}-{title}.{extension}')
Beispiel #23
0
 def editbar_links(self):
     result = []
     if self.request.has_role('admin', 'editor'):
         result.append(
             Link(
                 text=_("Edit page"),
                 url=self.request.link(self.model, name='edit'),
                 attrs={'class': 'edit-icon'}
             )
         )
         result.append(
             Link(
                 text=_("Manage attachments"),
                 url=self.request.link(self.model, name='attachments'),
                 attrs={'class': 'upload-icon'}
             )
         )
         if self.model.id not in self.app.static_content_pages:
             result.append(
                 Link(
                     text=_("Delete page"),
                     url=self.request.link(self.model, name='delete'),
                     attrs={'class': 'delete-icon'}
                 )
             )
         result.append(
             LinkGroup(
                 title=_("Add"),
                 links=[
                     Link(
                         text=_("Page"),
                         url=self.request.link(self.pages, name='add'),
                         attrs={'class': 'page-icon'}
                     )
                 ]
             ),
         )
     return result
Beispiel #24
0
def handle_login(self, request, form):
    """ Handles the login requests. """
    layout = DefaultLayout(self, request)

    if form.submitted(request):
        self.to = relative_url(layout.homepage_url)
        response = self.login_to(request=request, **form.login_data)
        form.error_message = _("Wrong username or password")
    else:
        response = None

    return response or {
        'layout':
        layout,
        'title':
        _("Login"),
        'form':
        form,
        'password_reset_link':
        request.link(Auth.from_request(request), name='request-password'),
        'button_text':
        _("Submit"),
    }
Beispiel #25
0
def handle_password_reset(self, request, form):
    """ Handles password reset requests. """

    layout = DefaultLayout(self, request)

    if form.submitted(request):
        if form.update_password(request):
            request.message(_("Password changed."), 'success')
            return request.redirect(layout.login_url)
        else:
            form.error_message = _(
                "Wrong username or password reset link not valid any more.")
            log.info("Failed password reset attempt by {}".format(
                request.client_addr))

    if 'token' in request.params:
        form.token.data = request.params['token']

    return {
        'layout': layout,
        'title': _('Reset password'),
        'form': form,
        'button_text': _("Submit"),
    }
Beispiel #26
0
def view_page_attachments(self, request):
    """ View all attachments of a page and allow to drop new attachments. """

    layout = PageAttachmentsLayout(self, request)
    upload_url = layout.csrf_protected_url(request.link(self, name='upload'))
    files = [file for file in self.files if file.locale == request.locale]

    return {
        'layout': layout,
        'title': self.title,
        'subtitle': _("Attachments"),
        'upload_url': upload_url,
        'files': files,
        'notice': self,
    }
Beispiel #27
0
def test_layout_delete_votes(swissvotes_app):
    request = DummyRequest()
    request.app = swissvotes_app
    model = SwissVoteCollection(swissvotes_app)

    layout = DeleteVotesLayout(model, request)
    assert layout.title == _("Delete all votes")
    assert layout.editbar_links == []
    assert path(layout.breadcrumbs) == 'Principal/SwissVoteCollection/#'

    # Log in as editor
    request.roles = ['editor']
    layout = DeleteVotesLayout(model, request)
    assert layout.editbar_links == []

    # Log in as admin
    request.roles = ['admin']
    layout = DeleteVotesLayout(model, request)
    assert layout.editbar_links == []
Beispiel #28
0
def upload_page_attachment(self, request):
    """ Upload an attachment and add it to the page.

    Raises a HTTP 415 (Unsupported Media Type) if the file format is not
    supported.

    """

    request.assert_valid_csrf_token()

    attachment = TranslatablePageFile(id=random_token())
    attachment.name = '{}-{}'.format(request.locale,
                                     request.params['file'].filename)
    attachment.reference = as_fileintent(request.params['file'].file,
                                         request.params['file'].filename)

    self.files.append(attachment)
    request.message(_("Attachment added."), 'success')
    return redirect(request.link(self, 'attachments'))
Beispiel #29
0
    def codes(attribute):
        """ Returns the codes for the given attribute as defined in the code
        book.

        """
        if attribute == 'legal_form':
            return OrderedDict((
                (1, _("Mandatory referendum")),
                (2, _("Optional referendum")),
                (3, _("Popular initiative")),
                (4, _("Direct counter-proposal")),
            ))
        if attribute == 'result_cantons_accepted':
            return OrderedDict((
                (0, _("Rejected")),
                (1, _("Accepted")),
                (3, _("Majority of the cantons not necessary")),
            ))
        if attribute == 'result' or attribute.endswith('_accepted'):
            return OrderedDict((
                (0, _("Rejected")),
                (1, _("Accepted")),
            ))
        if attribute == 'department_in_charge':
            return OrderedDict((
                (1, _("Federal Department of Foreign Affairs (FDFA)")),
                (2, _("Federal Department of Home Affairs (FDHA)")),
                (3, _("Federal Department of Justice and Police (FDJP)")),
                (4,
                 _("Federal Department of Defence, Civil Protection and "
                   "Sport (DDPS)")),
                (5, _("Federal Department of Finance (FDF)")),
                (6,
                 _("Federal Department of Economic Affairs, Education and "
                   "Research (EAER)")),
                (7,
                 _("Federal Department of the Environment, Transport, "
                   "Energy and Communications (DETEC)")),
                (8, _("Federal Chancellery (FCh)")),
            ))
        if (attribute == 'position_federal_council'
                or attribute == 'position_national_council'
                or attribute == 'position_council_of_states'):
            return OrderedDict(
                ((1, _("Accepting")), (2, _("Rejecting")), (3, _("None"))))
        if attribute == 'position_parliament':
            return OrderedDict((
                (1, _("Accepting")),
                (2, _("Rejecting")),
            ))
        if attribute == 'recommendation':
            # Added ordering how it should be displayed in strengths table
            return OrderedDict(
                ((1, _("Yea")), (2, _("Nay")), (4, _("Empty")),
                 (5, _("Free vote")), (3, _("None")), (66, _("Neutral")),
                 (9999, _("Organization no longer exists")), (None,
                                                              _("unknown"))))

        raise RuntimeError(f"No codes available for '{attribute}'")
Beispiel #30
0
    def post_validate(self, form, validation_stopped):
        """ Make sure the given XLSX is valid (all expected columns are
        present all cells contain reasonable values).

        Converts the XLSX to a list of SwissVote objects, available as
        ``data``.

        """

        super(SwissvoteDatasetField,
              self).post_validate(form, validation_stopped)
        if validation_stopped:
            return

        errors = []
        data = []
        mapper = ColumnMapper()

        try:
            workbook = open_workbook(
                file_contents=self.raw_data[0].file.read())
        except Exception:
            raise ValueError(_("Not a valid XLSX file."))

        if workbook.nsheets < 1:
            raise ValueError(_("No data."))

        data_sheet_name = 'DATA'
        citation_sheet_name = 'CITATION'

        if data_sheet_name not in workbook.sheet_names():
            raise ValueError(_('Sheet DATA is missing.'))

        if citation_sheet_name not in workbook.sheet_names():
            raise ValueError(_('Sheet CITATION is missing.'))

        sheet = workbook.sheet_by_name(data_sheet_name)

        if sheet.nrows <= 1:
            raise ValueError(_("No data."))

        headers = [column.value for column in sheet.row(0)]
        missing = set(mapper.columns.values()) - set(headers)
        if missing:
            raise ValueError(
                _("Some columns are missing: ${columns}.",
                  mapping={'columns': ', '.join(missing)}))

        for index in range(1, sheet.nrows):
            row = sheet.row(index)
            vote = SwissVote()
            for (attribute, column, type_, nullable, precision,
                 scale) in mapper.items():
                cell = row[headers.index(column)]
                try:
                    if cell.ctype == XL_CELL_EMPTY:
                        value = None
                    elif type_ == 'TEXT':
                        value = str(cell.value)
                        value = '' if value == '.' else value
                    elif type_ == 'DATE':
                        if isinstance(cell.value, str):
                            value = parse(cell.value, dayfirst=True).date()
                        else:
                            value = xldate.xldate_as_datetime(
                                cell.value, workbook.datemode).date()
                    elif type_ == 'INTEGER':
                        if isinstance(cell.value, str):
                            value = cell.value
                            value = '' if value == '.' else value
                            value = int(value) if value else None
                        else:
                            value = int(cell.value)
                    elif type_ == 'INT4RANGE':
                        value = NumericRange(
                            *[int(bound) for bound in cell.value.split('-')])
                    elif type_.startswith('NUMERIC'):
                        if isinstance(cell.value, str):
                            value = cell.value
                            value = '' if value == '.' else value
                            value = Decimal(str(value)) if value else None
                        else:
                            value = Decimal(str(cell.value))
                        if value is not None:
                            value = Decimal(
                                format(value, f'{precision}.{scale}f'))

                except Exception:
                    errors.append(
                        (index, column, f"'{value}' ≠ {type_.lower()}"))

                else:
                    if not nullable and value is None:
                        errors.append((index, column, "∅"))
                    mapper.set_value(vote, attribute, value)

            data.append(vote)

        if errors:
            raise ValueError(
                _("Some cells contain invalid values: ${errors}.",
                  mapping={
                      'errors':
                      '; '.join(
                          ['{}:{} {}'.format(*error) for error in errors])
                  }))

        self.data = data