hack4impact/maps4all

View on GitHub
app/suggestion/views.py

Summary

Maintainability
D
2 days
Test Coverage
import pytz
import os

from datetime import datetime
from flask import abort, flash, redirect, render_template, url_for
from flask.ext.login import login_required
from flask.ext.rq import get_queue
from sqlalchemy.exc import IntegrityError

from . import suggestion
from .. import db
from ..decorators import admin_required
from ..models import Resource, Suggestion, Descriptor, TextAssociation, OptionAssociation
from .forms import SuggestionBasicForm, SuggestionAdvancedForm
from wtforms.fields import TextAreaField, SelectField
from ..single_resource.views import save_associations
from ..single_resource.forms import SingleResourceForm

from app import create_app
from ..email import send_email

@suggestion.route('/')
@login_required
@admin_required
def index():
    """View all suggestions in a list."""
    suggestions = Suggestion.query.all()
    return render_template('suggestion/index.html', suggestions=suggestions)


@suggestion.route('/unread')
@login_required
@admin_required
def unread():
    """Returns the number of unread suggestions."""
    num_unread = Suggestion.query.filter(
        Suggestion.read == False  # noqa
        ).count()
    return "%d" % num_unread


@suggestion.route('/toggle-read/<int:sugg_id>')
@login_required
@admin_required
def toggle_read(sugg_id):
    """Toggles the readability of a given suggestion."""
    suggestion = Suggestion.query.get(sugg_id)
    if suggestion is None:
        abort(404)
    suggestion.read = not suggestion.read
    db.session.add(suggestion)
    try:
        db.session.commit()
    except:
        db.session.rollback()
        flash('Database error occurred. Please try again.', 'error')
    return redirect(url_for('suggestion.index'))


@suggestion.route('/delete/<int:sugg_id>')
@login_required
@admin_required
def delete(sugg_id):
    """Delete a given suggestion."""
    suggestion = Suggestion.query.get(sugg_id)
    if suggestion is None:
        abort(404)
    db.session.delete(suggestion)
    try:
        db.session.commit()
        flash('Suggestion successfully deleted.', 'success')
    except:
        db.session.rollback()
        flash('Database error occurred. Please try again.', 'error')
    return redirect(url_for('suggestion.index'))


@suggestion.route('/new', defaults={'resource_id': None},
                  methods=['GET', 'POST'])
@suggestion.route('/<int:resource_id>',  methods=['GET', 'POST'])
def suggest(resource_id):
    """Create a suggestion for a resource."""
    basic_form = SuggestionBasicForm()
    if resource_id is None:
        name = None
        resource = None
    else:
        resource = Resource.query.get(resource_id)
        if resource is None:
            abort(404)
        name = resource.name
        basic_form.name.data = resource.name
        basic_form.address.data = resource.address
    advanced_form = SuggestionAdvancedForm()
    descriptors = Descriptor.query.all()
    for descriptor in descriptors:
        if descriptor.values:  # Fields for option descriptors.
            choices = [(str(i), v) for i, v in enumerate(descriptor.values)]
            setattr(SuggestionAdvancedForm,
                    descriptor.name,
                    SelectField(choices=choices))
        else:  # Fields for text descriptors
            setattr(SuggestionAdvancedForm, descriptor.name, TextAreaField())
    if basic_form.validate_on_submit():
        suggestion = Suggestion(
            resource_id=resource_id,
            suggestion_text=basic_form.suggestion_text.data,
            contact_name=basic_form.contact_name.data,
            contact_email=basic_form.contact_email.data,
            contact_phone_number=basic_form.contact_phone_number.data,
            resource_name=basic_form.name.data,
            resource_address=basic_form.address.data,
            submission_time=datetime.now(pytz.timezone('US/Eastern'))
        )
        db.session.add(suggestion)
        try:
            db.session.commit()
            app = create_app(os.getenv('FLASK_CONFIG') or 'default')
            contact_email = app.config['ADMIN_EMAIL']
            get_queue().enqueue(
                send_email,
                recipient=contact_email,
                subject='New Suggestion',
                template='suggestion/email/suggestion',
                name=basic_form.contact_name.data,
                email=basic_form.contact_email.data,
                phone=basic_form.contact_phone_number.data,
                message=basic_form.suggestion_text.data,
                resource_name=basic_form.name.data,
                resource_address=basic_form.address.data,
            )
            flash('Thanks for the suggestion!', 'success')
            return redirect(url_for('main.index'))
        except IntegrityError:
            db.session.rollback()
            flash('Database error occurred. Please try again.', 'error')
    return render_template('suggestion/suggest.html', name=name, basic_form=basic_form,
                            advanced_form=advanced_form)


@suggestion.route('/create/<int:sugg_id>', methods=['GET', 'POST'])
@login_required
@admin_required
def create(sugg_id):
    suggestion = Suggestion.query.get(sugg_id)
    if suggestion is None:
        abort(404)
    descriptors = Descriptor.query.all()
    for descriptor in descriptors:
        if descriptor.values:  # Fields for option descriptors.
            choices = [(str(i), v) for i, v in enumerate(descriptor.values)]
            setattr(SingleResourceForm,
                    descriptor.name,
                    SelectField(choices=choices))
        else:  # Fields for text descriptors
            setattr(SingleResourceForm, descriptor.name, TextAreaField())
    form = SingleResourceForm()
    form.name.data = suggestion.resource_name
    if form.validate_on_submit():
        new_resource = Resource(name=form.name.data,
                                address=form.address.data,
                                latitude=form.latitude.data,
                                longitude=form.longitude.data)
        db.session.add(new_resource)
        save_associations(resource=new_resource,
                          form=form,
                          descriptors=descriptors,
                          resource_existed=False)
        try:
            db.session.commit()
            flash('Resource added', 'form-success')
            return redirect(url_for('suggestion.index'))
        except IntegrityError:
            db.session.rollback()
            flash('Error: failed to save resource. Please try again.',
                  'form-error')
    return render_template('suggestion/create.html', form=form, suggestion=suggestion)

@suggestion.route('/edit/<int:sugg_id>', methods=['GET', 'POST'])
@login_required
@admin_required
def edit(sugg_id):
    suggestion = Suggestion.query.get(sugg_id)
    if suggestion is None:
        abort(404)
    resource = Resource.query.get(suggestion.resource_id)
    if resource is None:
        abort(404)
    resource_id = suggestion.resource_id
    resource_field_names = list(Resource.__table__.columns.keys())
    descriptors = Descriptor.query.all()
    for descriptor in descriptors:
        if descriptor.values:  # Fields for option descriptors.
            choices = [(str(i), v) for i, v in enumerate(descriptor.values)]
            default = None
            option_association = OptionAssociation.query.filter_by(
                resource_id=resource_id,
                descriptor_id=descriptor.id
            ).first()
            if option_association is not None:
                default = option_association.option
            setattr(SingleResourceForm,
                    descriptor.name,
                    SelectField(choices=choices, default=default))
        else:  # Fields for text descriptors.
            default = None
            text_association = TextAssociation.query.filter_by(
                resource_id=resource_id,
                descriptor_id=descriptor.id
            ).first()
            if text_association is not None:
                default = text_association.text
            setattr(SingleResourceForm,
                    descriptor.name,
                    TextAreaField(default=default))
    form = SingleResourceForm()
    if form.validate_on_submit():
        # Field id is not needed for the form, hence omitted with [1:].
        for field_name in resource_field_names[1:]:
            setattr(resource, field_name, form[field_name].data)
        save_associations(resource=resource,
                          form=form,
                          descriptors=descriptors,
                          resource_existed=True)
        try:
            db.session.commit()
            flash('Resource updated', 'form-success')
            return redirect(url_for('single_resource.index'))
        except IntegrityError:
            db.session.rollback()
            flash('Error: failed to save resource. Please try again.',
                  'form-error')
    # Field id is not needed for the form, hence omitted with [1:].
    for field_name in resource_field_names[1:]:
        form[field_name].data = resource.__dict__[field_name]

    return render_template('suggestion/edit.html', form=form, suggestion=suggestion, resource_id=resource_id)