def test_signup_download_staff(self):
        # insert some vendors
        v1 = Vendor.query.filter(Vendor.email == '*****@*****.**').first()
        v1.categories.add(Category.query.first())
        db.session.commit()

        v2 = Vendor.create(**{'email': '*****@*****.**', 'business_name': 'foo'})
        for i in Category.query.all():
            v2.categories.add(i)
        db.session.commit()

        self.login_user(self.staff)
        request = self.client.get('/beacon/admin/signups')
        self.assertEquals(request.mimetype, 'text/tsv')
        self.assertEquals(
            request.headers.get('Content-Disposition'),
            'attachment; filename=vendors-{}.tsv'.format(
                datetime.date.today()))

        # python adds an extra return character to the end,
        # so we chop it off. we should have the header row and
        # the two rows we inserted above
        tsv_data = request.data.split('\n')[:-1]

        self.assertEquals(len(tsv_data), 3)
        for row in tsv_data:
            self.assertEquals(len(row.split('\t')), 11)
    def setUp(self):
        super(TestOpportunitiesPublic, self).setUp()
        self.opportunity3.is_public = False
        self.opportunity3.categories = set([Category.query.all()[-1]])
        self.opportunity3.planned_submission_end = datetime.datetime.today(
        ) + datetime.timedelta(days=5)
        self.vendor = Vendor.create(business_name='foobar',
                                    email='*****@*****.**',
                                    categories=set([Category.query.all()[-1]]))
        db.session.commit()

        self.opportunity1.created_by = self.staff
        self.opportunity3.created_by = self.staff
Exemplo n.º 3
0
    def setUp(self):
        super(TestOpportunitiesAdminBase, self).setUp()
        try:
            mkdir(current_app.config.get('UPLOAD_DESTINATION'))
        except OSError:
            rmtree(current_app.config.get('UPLOAD_DESTINATION'))
            mkdir(current_app.config.get('UPLOAD_DESTINATION'))

        import_nigp(current_app.config.get('PROJECT_ROOT') + '/purchasing_test/mock/nigp.csv')

        self.admin_role = insert_a_role('admin')
        self.staff_role = insert_a_role('staff')
        self.department1 = DepartmentFactory(name='test')
        self.opportunity_type = ContractTypeFactory.create(allow_opportunities=True)

        self.admin = insert_a_user(email='*****@*****.**', role=self.admin_role)
        self.staff = insert_a_user(email='*****@*****.**', role=self.staff_role)

        self.document = insert_a_document()

        self.vendor = Vendor.create(email='*****@*****.**', business_name='foo2')

        self.opportunity1 = insert_an_opportunity(
            contact=self.admin, created_by=self.staff,
            title=u'tést unïcode title', description=u'tést unïcode déscription',
            is_public=True, is_archived=False, planned_publish=datetime.date.today() + datetime.timedelta(1),
            planned_submission_start=datetime.date.today() + datetime.timedelta(2),
            planned_submission_end=datetime.datetime.today() + datetime.timedelta(2),
            documents=[self.document.id], categories=set([Category.query.first()])
        )
        self.opportunity2 = insert_an_opportunity(
            contact=self.admin, created_by=self.staff,
            is_public=True, is_archived=False, planned_publish=datetime.date.today(),
            planned_submission_start=datetime.date.today() + datetime.timedelta(2),
            planned_submission_end=datetime.datetime.today() + datetime.timedelta(2),
            categories=set([Category.query.first()])
        )
        self.opportunity3 = insert_an_opportunity(
            contact=self.admin, created_by=self.staff,
            is_public=True, is_archived=False, planned_publish=datetime.date.today() - datetime.timedelta(2),
            planned_submission_start=datetime.date.today() - datetime.timedelta(2),
            planned_submission_end=datetime.datetime.today() - datetime.timedelta(1),
            categories=set([Category.query.first()])
        )
        self.opportunity4 = insert_an_opportunity(
            contact=self.admin, created_by=self.staff,
            is_public=True, is_archived=False, planned_publish=datetime.date.today() - datetime.timedelta(1),
            planned_submission_start=datetime.date.today(),
            planned_submission_end=datetime.datetime.today() + datetime.timedelta(2),
            title='TEST TITLE!', categories=set([Category.query.first()])
        )
    def build_notifications(self):
        notifications = []

        opportunities = self.get_opportunities()
        notifications.append(
            Notification(
                to_email=set([i.email for i in Vendor.newsletter_subscribers()]),
                subject='Your biweekly Beacon opportunity summary',
                html_template='opportunities/emails/biweeklydigest.html',
                txt_template='opportunities/emails/biweeklydigest.txt',
                opportunities=opportunities
            )
        )
        return notifications
    def setUp(self):
        super(TestOpportunitiesPublic, self).setUp()
        self.opportunity3.is_public = False
        self.opportunity3.categories = set([Category.query.all()[-1]])
        self.opportunity3.planned_submission_end = datetime.datetime.today() + datetime.timedelta(2)
        self.vendor = Vendor.create(
            business_name='foobar',
            email='*****@*****.**',
            categories=set([Category.query.all()[-1]])
        )
        db.session.commit()

        self.opportunity1.created_by = self.staff
        self.opportunity3.created_by = self.staff
    def test_manage_subscriptions(self):
        v = Vendor.create(
            email='*****@*****.**', business_name='foo', subscribed_to_newsletter=True
        )

        for i in Category.query.filter(Category.id.in_([1,2,3])):
            v.categories.add(i)
        db.session.commit()

        manage = self.client.post('/beacon/manage', data=dict(
            email='*****@*****.**'
        ))

        self.assert200(manage)
        form = self.get_context_variable('form')
        self.assertEquals(len(form.categories.choices), 3)

        # it shouldn't unsubscribe you if you click the wrong button
        not_unsub_button = self.client.post('/beacon/manage', data=dict(
            email='*****@*****.**',
            categories=[1, 2],
        ))

        self.assert200(not_unsub_button)
        form = self.get_context_variable('form')
        self.assertEquals(len(form.categories.choices), 3)

        unsubscribe = self.client.post('/beacon/manage', data=dict(
            email='*****@*****.**',
            categories=[1, 2],
            button='Update email preferences'
        ))

        self.assert200(unsubscribe)
        form = self.get_context_variable('form')
        self.assertEquals(len(form.categories.choices), 1)

        # it shouldn't matter if you somehow unsubscribe from things
        # you are accidentally subscribed to
        unsubscribe_all = self.client.post('/beacon/manage', data=dict(
            email='*****@*****.**',
            categories=[3, 5, 6],
            button='Update email preferences',
            subscribed_to_newsletter=False
        ))

        self.assert200(unsubscribe_all)
        self.assertTrue('You are not subscribed to anything!' in unsubscribe_all.data)
Exemplo n.º 7
0
    def build_notifications(self):
        '''Implements EmailJobBase build_notifications method

        Returns:
            list of :py:class:`~purchasing.notifications.Notification` objects, one
            for each non-expired opportunity that has been published since the last
            Beacon newsletter was sent out
        '''
        notifications = []

        opportunities = self.get_opportunities()

        notifications.append(
            Notification(
                to_email=set(
                    [i.email for i in Vendor.newsletter_subscribers()]),
                from_email=current_app.config['BEACON_SENDER'],
                subject='Your biweekly Beacon opportunity summary',
                html_template='opportunities/emails/biweeklydigest.html',
                txt_template='opportunities/emails/biweeklydigest.txt',
                opportunities=opportunities))
        return notifications
    def build_notifications(self):
        '''Implements EmailJobBase build_notifications method

        Returns:
            list of :py:class:`~purchasing.notifications.Notification` objects, one
            for each non-expired opportunity that has been published since the last
            Beacon newsletter was sent out
        '''
        notifications = []

        opportunities = self.get_opportunities()

        notifications.append(
            Notification(
                to_email=set([i.email for i in Vendor.newsletter_subscribers()]),
                from_email=current_app.config['BEACON_SENDER'],
                subject='Your biweekly Beacon opportunity summary',
                html_template='opportunities/emails/biweeklydigest.html',
                txt_template='opportunities/emails/biweeklydigest.txt',
                opportunities=opportunities
            )
        )
        return notifications
Exemplo n.º 9
0
def signup():
    '''The signup page for vendors
    '''
    session_vendor = Vendor.query.filter(
        Vendor.email == session.get('email'),
        Vendor.business_name == session.get('business_name')
    ).first()
    form = init_form(VendorSignupForm, model=session_vendor)

    if form.validate_on_submit():
        vendor = Vendor.query.filter(Vendor.email == form.data.get('email')).first()

        if vendor:
            current_app.logger.info('''
                OPPUPDATEVENDOR - Vendor updated:
                EMAIL: {old_email} -> {email} at
                BUSINESS: {old_bis} -> {bis_name} signed up for:
                CATEGORIES:
                    {old_cats} ->
                    {categories}'''.format(
                old_email=vendor.email, email=form.data['email'],
                old_bis=vendor.business_name, bis_name=form.data['business_name'],
                old_cats=[i.__unicode__() for i in vendor.categories],
                categories=[i.__unicode__() for i in form.data['categories']]
            ))

            vendor.update(
                **form.pop_categories(categories=False)
            )

            flash("You are already signed up! Your profile was updated with this new information", 'alert-info')

        else:
            current_app.logger.info(
                'OPPNEWVENDOR - New vendor signed up: EMAIL: {email} at BUSINESS: {bis_name} signed up for:\n' +
                'CATEGORIES: {categories}'.format(
                    email=form.data['email'],
                    bis_name=form.data['business_name'],
                    categories=[i.__unicode__() for i in form.data['categories']]
                )
            )

            vendor = Vendor.create(
                **form.pop_categories(categories=False)
            )

            confirmation_sent = Notification(
                to_email=vendor.email, subject='Thank you for signing up!',
                html_template='opportunities/emails/signup.html',
                txt_template='opportunities/emails/signup.txt',
                categories=form.data['categories']
            ).send()

            if confirmation_sent:
                admins = db.session.query(User.email).join(Role, User.role_id == Role.id).filter(
                    Role.name.in_(['admin', 'superadmin'])
                ).all()

                Notification(
                    to_email=admins, subject='A new vendor has signed up on beacon',
                    categories=form.data['categories'],
                    vendor=form.data['email'], convert_args=True,
                    business_name=form.data['business_name']
                ).send()

                flash('Thank you for signing up! Check your email for more information', 'alert-success')

            else:
                flash('Uh oh, something went wrong. We are investigating.', 'alert-danger')

        session['email'] = form.data.get('email')
        session['business_name'] = form.data.get('business_name')
        return redirect(url_for('opportunities.splash'))

    page_email = request.args.get('email', None)

    if page_email:
        current_app.logger.info(
            'OPPSIGNUPVIEW - User clicked through to signup with email {}'.format(page_email)
        )
        session['email'] = page_email
        return redirect(url_for('opportunities.signup'))

    if 'email' in session:
        if not form.email.validate(form):
            session.pop('email', None)

    form.display_cleanup()

    return render_template(
        'opportunities/front/signup.html', form=form,
        categories=form.get_categories(),
        subcategories=form.get_subcategories()
    )
Exemplo n.º 10
0
def signup():
    '''
    The signup page for vendors
    '''
    all_categories = Category.query.all()
    categories, subcategories = set(), defaultdict(list)
    for category in all_categories:
        categories.add(category.category)
        subcategories['Select All'].append((category.id, category.subcategory))
        subcategories[category.category].append((category.id, category.subcategory))

    form = SignupForm()

    form.categories.choices = [(None, '---')] + list(sorted(zip(categories, categories))) + [('Select All', 'Select All')]
    form.subcategories.choices = []

    if form.validate_on_submit():

        vendor = Vendor.query.filter(Vendor.email == form.data.get('email')).first()
        form_data = {c.name: form.data.get(c.name, None) for c in Vendor.__table__.columns if c.name not in ['id', 'created_at']}
        form_data['categories'] = []
        subcats = set()

        # manually iterate the form fields
        for k, v in request.form.iteritems():
            if not k.startswith('subcategories-'):
                continue
            else:
                subcat_id = int(k.split('-')[1])
                # make sure the field is checked (or 'on') and we don't have it already
                if v == 'on' and subcat_id not in subcats:
                    subcats.add(subcat_id)
                    subcat = Category.query.get(subcat_id)
                    # make sure it's a valid subcategory
                    if subcat is None:
                        raise ValidationError('{} is not a valid choice!'.format(subcat))
                    form_data['categories'].append(subcat)

        if vendor:
            current_app.logger.info('OPPPUPDATEVENDOR - Vendor updated: EMAIL: {old_email} -> {email} at BUSINESS: {old_bis} -> {bis_name} signed up for:\n CATEGORIES: {old_cats} ->\n {categories}'.format(
                old_email=vendor.email,
                email=form_data['email'],
                old_bis=vendor.business_name,
                bis_name=form_data['business_name'],
                old_cats=[i.__unicode__() for i in vendor.categories],
                categories=[i.__unicode__() for i in form_data['categories']]
            ))

            vendor.update(
                **form_data
            )

            flash("You are already signed up! Your profile was updated with this new information", 'alert-info')

        else:
            current_app.logger.info(
                'OPPNEWVENDOR - New vendor signed up: EMAIL: {email} at BUSINESS: {bis_name} signed up for:\n CATEGORIES: {categories}'.format(
                    email=form_data['email'],
                    bis_name=form_data['business_name'],
                    categories=[i.__unicode__() for i in form_data['categories']]
                )
            )
            vendor = Vendor.create(
                **form_data
            )

            confirmation_sent = vendor_signup(vendor, categories=form_data['categories'])

            if confirmation_sent:
                flash('Thank you for signing up! Check your email for more information', 'alert-success')
            else:
                flash('Uh oh, something went wrong. We are investigating.', 'alert-danger')

        return redirect(url_for('opportunities.index'))

    page_email = request.args.get('email', None)

    if page_email:
        current_app.logger.info('OPPSIGNUPVIEW - User clicked through to signup with email {}'.format(page_email))
        session['email'] = page_email
        return redirect(url_for('opportunities.signup'))

    if 'email' in session:
        form.email.data = session['email']
        form.email.validate(form)
        session.pop('email')

    display_categories = subcategories.keys()
    display_categories.remove('Select All')

    return render_template(
        'opportunities/signup.html', form=form,
        subcategories=json.dumps(subcategories),
        categories=json.dumps(
            sorted(display_categories) + ['Select All']
        )
    )
def signup_for_opp(form, opportunity, multi=False):
    '''Sign a vendor up for an opportunity

    Generic helper method to handle subscriptions from both the list view
    (signing up form multiple opportunities) and the detail view (signing
    up for a single opportunity). Responsible for creation of new Vendor
    objects if necessary, and sending emails based on the opportunities
    selected to receive updates about.

    Arguments:
        form: The relevant subscription form
        opportunity: Either an opportunity model or a list of opportunity ids
        multi: A boolean to flag if there are multiple opportunities that should
            to subscribe to or a single opportunity

    Returns:
        True if email sent successfully, false otherwise
    '''
    send_email = True
    email_opportunities = []
    if opportunity is None or (isinstance(opportunity, list) and len(opportunity) == 0):
        form.errors['opportunities'] = ['You must select at least one opportunity!']
        return False
    # add the email/business name to the session
    session['email'] = form.data.get('email')
    session['business_name'] = form.data.get('business_name')
    # subscribe the vendor to the opportunity
    vendor = Vendor.query.filter(
        Vendor.email == form.data.get('email')
    ).first()

    if vendor is None:
        vendor = Vendor(
            email=form.data.get('email'),
            business_name=form.data.get('business_name')
        )
        db.session.add(vendor)
        db.session.commit()
    else:
        vendor.update(business_name=form.data.get('business_name'))

    if multi:
        for opp in opportunity:
            _opp = Opportunity.query.get(int(opp))
            if not _opp.is_public:
                db.session.rollback()
                form.errors['opportunities'] = ['That\'s not a valid choice.']
                return False
            if _opp in vendor.opportunities:
                send_email = False
            else:
                vendor.opportunities.add(_opp)
                email_opportunities.append(_opp)
    else:
        if opportunity in vendor.opportunities:
            send_email = False
        else:
            vendor.opportunities.add(opportunity)
            email_opportunities.append(opportunity)

    if form.data.get('also_categories'):
        # TODO -- add support for categories
        pass

    db.session.commit()

    current_app.logger.info(
        u'OPPSIGNUP - Vendor has signed up for opportunities: EMAIL: {email} at BUSINESS: {bis_name} signed up for:\n' +
        u'OPPORTUNITY: {opportunities}'.format(
            email=form.data.get('email'),
            business_name=form.data.get('business_name'),
            opportunities=', '.join([i.title for i in email_opportunities])
        )
    )

    if send_email:
        Notification(
            to_email=vendor.email,
            from_email=current_app.config['BEACON_SENDER'],
            subject='Subscription confirmation from Beacon',
            html_template='opportunities/emails/oppselected.html',
            txt_template='opportunities/emails/oppselected.txt',
            opportunities=email_opportunities
        ).send()

    return True
Exemplo n.º 12
0
def signup_for_opp(form, opportunity, multi=False):
    '''Sign a vendor up for an opportunity

    Generic helper method to handle subscriptions from both the list view
    (signing up form multiple opportunities) and the detail view (signing
    up for a single opportunity). Responsible for creation of new Vendor
    objects if necessary, and sending emails based on the opportunities
    selected to receive updates about.

    Arguments:
        form: The relevant subscription form
        opportunity: Either an opportunity model or a list of opportunity ids
        multi: A boolean to flag if there are multiple opportunities that should
            to subscribe to or a single opportunity

    Returns:
        True if email sent successfully, false otherwise
    '''
    send_email = True
    email_opportunities = []
    if opportunity is None or (isinstance(opportunity, list)
                               and len(opportunity) == 0):
        form.errors['opportunities'] = [
            'You must select at least one opportunity!'
        ]
        return False
    # add the email/business name to the session
    session['email'] = form.data.get('email')
    session['business_name'] = form.data.get('business_name')
    # subscribe the vendor to the opportunity
    vendor = Vendor.query.filter(
        Vendor.email == form.data.get('email')).first()

    if vendor is None:
        vendor = Vendor(email=form.data.get('email'),
                        business_name=form.data.get('business_name'))
        db.session.add(vendor)
        db.session.commit()
    else:
        vendor.update(business_name=form.data.get('business_name'))

    if multi:
        for opp in opportunity:
            _opp = Opportunity.query.get(int(opp))
            if not _opp.is_public:
                db.session.rollback()
                form.errors['opportunities'] = ['That\'s not a valid choice.']
                return False
            if _opp in vendor.opportunities:
                send_email = False
            else:
                vendor.opportunities.add(_opp)
                email_opportunities.append(_opp)
    else:
        if opportunity in vendor.opportunities:
            send_email = False
        else:
            vendor.opportunities.add(opportunity)
            email_opportunities.append(opportunity)

    if form.data.get('also_categories'):
        # TODO -- add support for categories
        pass

    db.session.commit()

    current_app.logger.info(
        u'OPPSIGNUP - Vendor has signed up for opportunities: EMAIL: {email} at BUSINESS: {bis_name} signed up for:\n'
        + u'OPPORTUNITY: {opportunities}'.format(
            email=form.data.get('email'),
            business_name=form.data.get('business_name'),
            opportunities=', '.join([i.title for i in email_opportunities])))

    if send_email:
        Notification(to_email=vendor.email,
                     from_email=current_app.config['BEACON_SENDER'],
                     subject='Subscription confirmation from Beacon',
                     html_template='opportunities/emails/oppselected.html',
                     txt_template='opportunities/emails/oppselected.txt',
                     opportunities=email_opportunities).send()

    return True
Exemplo n.º 13
0
def signup():
    """The signup page for vendors
    """
    session_vendor = Vendor.query.filter(
        Vendor.email == session.get("email"), Vendor.business_name == session.get("business_name")
    ).first()
    form = init_form(VendorSignupForm, model=session_vendor)

    if form.validate_on_submit():
        vendor = Vendor.query.filter(Vendor.email == form.data.get("email")).first()

        if vendor:
            current_app.logger.info(
                """
                OPPUPDATEVENDOR - Vendor updated:
                EMAIL: {old_email} -> {email} at
                BUSINESS: {old_bis} -> {bis_name} signed up for:
                CATEGORIES:
                    {old_cats} ->
                    {categories}""".format(
                    old_email=vendor.email,
                    email=form.data["email"],
                    old_bis=vendor.business_name,
                    bis_name=form.data["business_name"],
                    old_cats=[i.__unicode__() for i in vendor.categories],
                    categories=[i.__unicode__() for i in form.data["categories"]],
                )
            )

            vendor.update(**form.pop_categories(categories=False))

            flash("You are already signed up! Your profile was updated with this new information", "alert-info")

        else:
            current_app.logger.info(
                "OPPNEWVENDOR - New vendor signed up: EMAIL: {email} at BUSINESS: {bis_name} signed up for:\n"
                + "CATEGORIES: {categories}".format(
                    email=form.data["email"],
                    bis_name=form.data["business_name"],
                    categories=[i.__unicode__() for i in form.data["categories"]],
                )
            )

            vendor = Vendor.create(**form.pop_categories(categories=False))

            confirmation_sent = Notification(
                to_email=vendor.email,
                subject="Thank you for signing up!",
                html_template="opportunities/emails/signup.html",
                txt_template="opportunities/emails/signup.txt",
                categories=form.data["categories"],
            ).send()

            if confirmation_sent:
                admins = (
                    db.session.query(User.email)
                    .join(Role, User.role_id == Role.id)
                    .filter(Role.name.in_(["admin", "superadmin"]))
                    .all()
                )

                Notification(
                    to_email=admins,
                    subject="A new vendor has signed up on beacon",
                    categories=form.data["categories"],
                    vendor=form.data["email"],
                    convert_args=True,
                    business_name=form.data["business_name"],
                ).send()

                flash("Thank you for signing up! Check your email for more information", "alert-success")

            else:
                flash("Uh oh, something went wrong. We are investigating.", "alert-danger")

        session["email"] = form.data.get("email")
        session["business_name"] = form.data.get("business_name")
        return redirect(url_for("opportunities.splash"))

    page_email = request.args.get("email", None)

    if page_email:
        current_app.logger.info("OPPSIGNUPVIEW - User clicked through to signup with email {}".format(page_email))
        session["email"] = page_email
        return redirect(url_for("opportunities.signup"))

    if "email" in session:
        if not form.email.validate(form):
            session.pop("email", None)

    form.display_cleanup()

    return render_template(
        "opportunities/front/signup.html",
        form=form,
        categories=form.get_categories(),
        subcategories=form.get_subcategories(),
    )
Exemplo n.º 14
0
def manage():
    '''Manage a vendor's signups

    :status 200: render the
        :py:class:`~purchasing.opportunities.forms.UnsubscribeForm`
    :status 302: post the
        :py:class:`~purchasing.opportunities.forms.UnsubscribeForm`
        and change the user's email subscriptions and redirect them
        back to the management page.
    '''
    form = init_form(UnsubscribeForm)
    form_categories = []
    form_opportunities = []
    vendor = None

    if form.validate_on_submit():
        email = form.data.get('email')
        vendor = Vendor.query.filter(Vendor.email == email).first()

        if vendor is None:
            current_app.logger.info(
                'OPPMANAGEVIEW - Unsuccessful search for email {}'.format(
                    email))
            form.email.errors = [
                'We could not find the email {}'.format(email)
            ]

        if request.form.get('button',
                            '').lower() == 'update email preferences':
            remove_categories = set(
                [Category.query.get(i) for i in form.categories.data])
            remove_opportunities = set(
                [Opportunity.query.get(i) for i in form.opportunities.data])

            # remove none types if any
            remove_categories.discard(None)
            remove_opportunities.discard(None)

            vendor.categories = vendor.categories.difference(remove_categories)
            vendor.opportunities = vendor.opportunities.difference(
                remove_opportunities)
            if form.data.get('subscribed_to_newsletter'):
                vendor.subscribed_to_newsletter = False

            current_app.logger.info(
                u'''OPPMANAGEVIEW - Vendor {} unsubscribed from:
                Categories: {}
                Opportunities: {}
                Subscribed from newsletter: {}
                '''.format(
                    email, u', '.join([
                        i.category_friendly_name for i in remove_categories
                        if remove_categories and len(remove_categories) > 0
                    ]), u', '.join([
                        i.description for i in remove_opportunities if
                        remove_opportunities and len(remove_opportunities) > 0
                    ]), vendor.subscribed_to_newsletter))

            db.session.commit()
            flash('Preferences updated!', 'alert-success')

        if vendor:
            for subscription in vendor.categories:
                form_categories.append(
                    (subscription.id, subscription.category_friendly_name))
            for subscription in vendor.opportunities:
                form_opportunities.append(
                    (subscription.id, subscription.title))

    form = init_form(UnsubscribeForm)
    form.opportunities.choices = form_opportunities
    form.categories.choices = form_categories
    return render_template('opportunities/front/manage.html',
                           form=form,
                           vendor=vendor if vendor else Vendor())