Beispiel #1
0
 def test_notification_flatten(self, render_template):
     """Test notification kwarg flattener
     """
     obj = MagicMock()
     obj.__unicode__ = lambda x: "quux"
     notification = Notification(from_email="*****@*****.**", foo="bar", baz=["qux1", obj])
     self.assertEquals(
         {"foo": "bar", "baz": "qux1; qux2"}, notification.convert_models(dict(foo="bar", baz=["qux1", "qux2"]))
     )
Beispiel #2
0
    def test_notification_reshape(self, render_template):
        """Test notification recipient flattener
        """
        notification = Notification(to_email="*****@*****.**", from_email="*****@*****.**")
        test_recips = [("a",), ("multi",), ["nested", "thing"]]
        self.assertListEqual(["a", "multi", "nested", "thing"], notification.flatten(test_recips))

        test_recips_complex = ["a", ["b", ["c", "d"]], ["e"]]
        self.assertListEqual(["a", "b", "c", "d", "e"], notification.flatten(test_recips_complex))
Beispiel #3
0
 def test_notification_flatten(self, render_template):
     '''Test notification kwarg flattener
     '''
     obj = MagicMock()
     obj.__unicode__ = lambda x: 'quux'
     notification = Notification(from_email='*****@*****.**', foo='bar', baz=['qux1', obj])
     self.assertEquals(
         {'foo': 'bar', 'baz': 'qux1; qux2'},
         notification.convert_models(dict(foo='bar', baz=['qux1', 'qux2']))
     )
    def test_notification_build_multi(self, current_app, render_template):
        '''Test multi-messages only have one recipient
        '''
        current_app.logger = Mock(info=Mock())
        notification = Notification(to_email=['*****@*****.**', '*****@*****.**'], from_email='*****@*****.**')

        # should build two messages on multi send
        msgs = notification._build(multi=True)
        self.assertTrue(len(msgs), 2)
        for msg in msgs:
            self.assertEquals(len(msg.recipients), 1)
Beispiel #5
0
    def test_notification_build_multi(self, current_app, render_template):
        '''Test single build messages have multiple recipients
        '''
        current_app.logger = Mock(info=Mock())
        notification = Notification(to_email=['*****@*****.**', '*****@*****.**'], from_email='*****@*****.**')

        # should build two messages on multi send
        msgs = notification._build(multi=False)
        self.assertTrue(len(msgs), 1)
        for msg in msgs:
            self.assertEquals(len(msg.recipients), 2)
Beispiel #6
0
    def test_notification_build_multi(self, current_app, render_template):
        """Test single build messages have multiple recipients
        """
        current_app.logger = Mock(info=Mock())
        notification = Notification(to_email=["*****@*****.**", "*****@*****.**"], from_email="*****@*****.**")

        # should build two messages on multi send
        msgs = notification._build(multi=False)
        self.assertTrue(len(msgs), 1)
        for msg in msgs:
            self.assertEquals(len(msg.recipients), 2)
Beispiel #7
0
    def test_notification_send_single(self, send, send_email, render_template):
        '''Test non-multi only builds one message even with multiple emails
        '''
        notification = Notification(to_email=['*****@*****.**', '*****@*****.**'], from_email='*****@*****.**')

        notification.build_msg = Mock()
        notification.build_msg.return_value = []

        # should build two messages on multi send
        notification.send(multi=False)
        self.assertTrue(notification.build_msg.called)
        self.assertEquals(notification.build_msg.call_count, 1)
Beispiel #8
0
    def test_notification_send_single(self, send, send_email, render_template):
        """Test non-multi only builds one message even with multiple emails
        """
        notification = Notification(to_email=["*****@*****.**", "*****@*****.**"], from_email="*****@*****.**")

        notification.build_msg = Mock()
        notification.build_msg.return_value = []

        # should build two messages on multi send
        notification.send(multi=False)
        self.assertTrue(notification.build_msg.called)
        self.assertEquals(notification.build_msg.call_count, 1)
Beispiel #9
0
    def test_notification_send_multi(self, send, send_email, render_template):
        """Test multi builds multiple message objects
        """
        notification = Notification(to_email=["*****@*****.**", "*****@*****.**"], from_email="*****@*****.**")

        notification.build_msg = Mock()
        notification.build_msg.return_value = []

        # should build two messages on multi send
        notification.send(multi=True)
        self.assertTrue(notification.build_msg.called)
        self.assertEquals(notification.build_msg.call_count, 2)
Beispiel #10
0
    def test_notification_send_multi(self, send, send_email, render_template):
        '''Test multi builds multiple message objects
        '''
        notification = Notification(to_email=['*****@*****.**', '*****@*****.**'], from_email='*****@*****.**')

        notification.build_msg = Mock()
        notification.build_msg.return_value = []

        # should build two messages on multi send
        notification.send(multi=True)
        self.assertTrue(notification.build_msg.called)
        self.assertEquals(notification.build_msg.call_count, 2)
Beispiel #11
0
    def test_notification_reshape(self, render_template):
        '''Test notification recipient flattener
        '''
        notification = Notification(to_email='*****@*****.**', from_email='*****@*****.**')
        test_recips = [('a',), ('multi',), ['nested', 'thing']]
        self.assertEquals(
            ['a', 'multi', 'nested', 'thing'],
            notification.flatten(test_recips)
        )

        test_recips_complex = ['a', ['b', ['c', 'd']], ['e']]
        self.assertEquals(
            ['a', 'b', 'c', 'd', 'e'],
            notification.flatten(test_recips_complex)
        )
    def send_publish_email(self):
        '''Sends the "new opportunity available" email to subscribed vendors

        If a new Opportunity is created and it has a publish date before or
        on today's date, it will trigger an immediate publish email send. This
        operates in a very similar way to the nightly
        :py:class:`~purchasing.jobs.beacon_nightly.BeaconNewOppotunityOpenJob`.
        It will build a list of all vendors signed up to the Opportunity
        or to any of the categories that describe the Opportunity.
        '''
        if self.is_published and not self.publish_notification_sent:
            vendors = Vendor.query.filter(
                Vendor.categories.any(Category.id.in_(
                    self.get_category_ids()))).all()

            Notification(
                to_email=[i.email for i in vendors],
                subject='A new City of Pittsburgh opportunity from Beacon!',
                html_template='beacon/emails/newopp.html',
                txt_template='beacon/emails/newopp.txt',
                opportunity=self).send(multi=True)

            self.publish_notification_sent = True
            self.published_at = datetime.datetime.utcnow()

            current_app.logger.info(
                '''BEACON PUBLISHED:  ID: {} | Title: {} | Publish Date: {} | Submission Start Date: {} | Submission End Date: {}
                '''.format(self.id, self.title.encode('ascii', 'ignore'),
                           str(self.planned_publish),
                           str(self.planned_submission_start),
                           str(self.planned_submission_end)))
            return True
        return False
    def build_notifications(self):
        '''Implements EmailJobBase build_notifications method

        Returns:
            list of :py:class:`~purchasing.notifications.Notification` objects, one
            for each new Opportunity. For each Opportunity, the ``to_email`` field
            is the union of all followers of the opportunity and any followers of
            any categories that the Opportunity has
        '''
        notifications = []
        for opportunity in self.get_opportunities():
            category_vendors = Vendor.query.filter(
                Vendor.categories.any(Category.id.in_(opportunity.get_category_ids()))
            ).all()

            notifications.append(
                Notification(
                    to_email=set([i.email for i in category_vendors] + [i.email for i in opportunity.vendors]),
                    cc_email=list(),
                    from_email=current_app.config['BEACON_SENDER'],
                    subject='A new City of Pittsburgh opportunity from Beacon!',
                    html_template='beacon/emails/newopp.html',
                    txt_template='beacon/emails/newopp.txt',
                    opportunity=opportunity
                )
            )
            opportunity.raw_update(publish_notification_sent=True)

        return notifications
    def notify_approvals(self, user):
        '''Send the approval notifications to everyone with approval rights

        Arguments:
            user: A :py:class:`~purchasing.models.users.User` object
        '''
        Notification(to_email=[user.email],
                     subject='Your post has been sent to OMB for approval',
                     html_template='beacon/emails/staff_postsubmitted.html',
                     txt_template='beacon/emails/staff_postsubmitted.txt',
                     opportunity=self).send(multi=True)

        Notification(to_email=db.session.query(User.email).join(
            Role, User.role_id == Role.id).filter(
                Role.name.in_(['conductor', 'admin', 'superadmin'])).all(),
                     subject='A new Beacon post needs review',
                     html_template='beacon/emails/admin_postforapproval.html',
                     txt_template='beacon/emails/admin_postforapproval.txt',
                     opportunity=self).send(multi=True)
Beispiel #15
0
 def test_notification_initialization(self, render_template):
     '''Test notifications properly initialize
     '''
     notification = Notification(
         from_email='*****@*****.**', to_email='*****@*****.**', cc_email=[('*****@*****.**',), ('*****@*****.**',)]
     )
     self.assertEquals(notification.to_email, ['*****@*****.**'])
     self.assertEquals(notification.from_email, '*****@*****.**')
     self.assertEquals(notification.cc_email, ['*****@*****.**', '*****@*****.**'])
     self.assertEquals(notification.subject, '')
     self.assertEquals(notification.html_body, 'a test')
     self.assertEquals(notification.txt_body, '')
     self.assertEquals(notification.attachments, [])
Beispiel #16
0
def publish(opportunity_id):
    '''Publish an opportunity

    If an :py:class:`~purchasing.models.front.Opportunity` has
    been created by a non-admin, it will be stuck in a "pending" state
    until it has been approved by an beacon_admin. This view function handles
    the publication event for a specific
    :py:class:`~purchasing.models.front.Opportunity`

    :status 200: Publish the relevant opportunity and send the relevant
        publication emails
    :status 404: :py:class:`~purchasing.models.front.Opportunity`
        not found
    '''
    opportunity = db.session.query(Opportunity).filter(
        Opportunity.id == opportunity_id).first()

    if opportunity:
        flash('Opportunity successfully published!', 'alert-success')
        opportunity.is_public = True
        db.session.flush()

        opportunity.send_publish_email()
        db.session.commit()

        Notification(to_email=[opportunity.created_by.email],
                     subject='OMB approved your opportunity post!',
                     html_template='beacon/emails/staff_postapproved.html',
                     txt_template='beacon/emails/staff_postapproved.txt',
                     opportunity=opportunity).send(multi=True)

        current_app.logger.info(
            '''BEACON APPROVED: ID: {} | Title: {} | Publish Date: {} | Submission Start Date: {} | Submission End Date: {} '''
            .format(opportunity.id,
                    opportunity.title.encode('ascii', 'ignore'),
                    str(opportunity.planned_publish),
                    str(opportunity.planned_submission_start),
                    str(opportunity.planned_submission_end)))

        return redirect(url_for('beacon_admin.pending'))
    abort(404)
    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='beacon/emails/biweeklydigest.html',
                txt_template='beacon/emails/biweeklydigest.txt',
                opportunities=opportunities
            )
        )
        return notifications
Beispiel #18
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(
        'OPPSIGNUP - Vendor has signed up for opportunities: EMAIL: {email} at BUSINESS: {bis_name} signed up for:\n'
        + 'OPPORTUNITY: {opportunities}'.format(
            email=form.data.get('email'),
            business_name=form.data.get('business_name'),
            opportunities=', '.join([
                i.title.encode('ascii', 'ignore') 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='beacon/emails/oppselected.html',
                     txt_template='beacon/emails/oppselected.txt',
                     opportunities=email_opportunities).send()

    return True
Beispiel #19
0
def signup():
    '''The signup page for vendors

    :status 200: Render the
        :py:class:`~purchasing.forms.front.SignupForm`
    :status 302: Process the signup form post, redirect
        the vendor back to the splash page
    '''
    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='beacon/emails/signup.html',
                txt_template='beacon/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('front.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('front.signup'))

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

    form.display_cleanup()

    return render_template(
        'beacon/front/signup.html', form=form,
        categories=form.get_categories(),
        subcategories=form.get_subcategories()
    )