record what invitations were sent so far, and perform some basic validation on the email list provided before sending and recording.

This commit is contained in:
Dan Funk 2020-09-23 17:00:51 -04:00
parent 523b68978c
commit d6b639ca23
10 changed files with 134 additions and 7 deletions

View File

@ -14,7 +14,6 @@ from flask_sqlalchemy import SQLAlchemy
from sentry_sdk.integrations.flask import FlaskIntegration
from webassets import Bundle
logging.basicConfig(level=logging.INFO)
# API, fully defined in api.yml
@ -100,18 +99,29 @@ def index():
@app.route('/invitation', methods=['GET', 'POST'])
def send_invitation():
from communicator.models.invitation import Invitation
from communicator.tables import InvitationTable
form = forms.InvitationForm(request.form)
action = BASE_HREF + "/invitation"
title = "Send invitation to students"
if request.method == 'POST':
if request.method == 'POST' and form.validate():
from communicator.services.notification_service import NotificationService
with NotificationService(app) as ns:
ns.send_invitations(form.date.data, form.location.data, form.emails.data)
# display results
page = request.args.get(get_page_parameter(), type=int, default=1)
invites = db.session.query(Invitation).order_by(Invitation.date.desc())
pagination = Pagination(page=page, total=invites.count(), search=False, record_name='samples')
table = InvitationTable(invites.paginate(page,10,error_out=False).items)
return render_template(
'form.html',
form=form,
table=table,
pagination=pagination,
action=action,
title=title,
description_map={},

View File

@ -1,10 +1,20 @@
import re
from flask_table import Table, Col, LinkCol, BoolCol, DatetimeCol, NestedTableCol
from flask_wtf import FlaskForm
from wtforms import SelectMultipleField, StringField, BooleanField, SelectField, validators, HiddenField, TextAreaField
from wtforms import SelectMultipleField, StringField, BooleanField, SelectField, validators, HiddenField, TextAreaField, \
ValidationError
from wtforms.widgets import TextArea
class InvitationForm(FlaskForm):
location = StringField('Location (ex. Newcomb Hall, South Meeting Room)', [validators.DataRequired()])
date = StringField('Date (ex. Monday, September 23 from 10:00 am-5:00 pm ', [validators.DataRequired()])
emails = TextAreaField('Emails (newline delimited)', render_kw={'rows': 50, 'cols': 50})
emails = TextAreaField('Emails (newline delimited)', render_kw={'rows': 10, 'cols': 50})
def validate_emails(form, field):
all_emails = field.data.splitlines()
EMAIL_REGEX = re.compile('^[a-z0-9]+[._a-z0-9]+[@]\w+[.]\w{2,3}$')
for email in all_emails:
if not re.search(EMAIL_REGEX, email):
raise ValidationError(f'Invalid email \'{email}\', Emails must each be on a seperate line.')

View File

@ -0,0 +1,11 @@
from sqlalchemy import func
from communicator import db
class Invitation(db.Model):
id = db.Column(db.Integer, primary_key=True)
date_sent = db.Column(db.DateTime(timezone=True), default=func.now())
location = db.Column(db.String)
date = db.Column(db.String)
total_recipients = db.Column(db.Integer)

View File

@ -3,12 +3,14 @@ import uuid
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import re
from flask import render_template
from twilio.rest import Client
from communicator import app
from communicator import app, db
from communicator.errors import CommError
from communicator.models.invitation import Invitation
TEST_MESSAGES = []
@ -86,6 +88,10 @@ class NotificationService(object):
self._send_email(subject, recipients=[self.sender], bcc=emails, text_body=text_body, html_body=html_body)
invitation_log = Invitation(location=location, date=date, total_recipients=len(emails))
db.session.add(invitation_log)
db.session.commit()
def _tracking_code(self):
return str(uuid.uuid4())[:16]

View File

@ -18,3 +18,12 @@ class IvyFileTable(Table):
file_name = Col('File Name')
date_added = DatetimeCol('Date', "medium")
sample_count = Col('Total Records')
class InvitationTable(Table):
def sort_url(self, col_id, reverse=False):
pass
date_sent = DatetimeCol('Date Sent', "medium")
location = Col('Location')
date = Col('Date')
total_recipients = Col('# Recipients')

View File

@ -6,6 +6,11 @@
<base href="/">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://use.typekit.net/kwp6dli.css">
<link rel="shortcut icon" href="{{ base_href + url_for('static', filename='favicon.ico') }}">
{% assets 'app_scss' %}
<link href="{{ base_href + ASSET_URL }}" rel="stylesheet" type="text/css">
{% endassets %}
<link rel="shortcut icon" href="{{ base_href + url_for('static', filename='favicon.ico') }}">
</head>
<style type="text/css">
input {
@ -14,6 +19,14 @@
}
</style>
<body>
<a href="{{base_href}}"><< Home</a>
<h3>Previous Entries</h3>
{{ pagination.info }}
{{ pagination.links }}
{{ table }}
{{ pagination.links }}
<h2>{{ title }}</h2>
<p>{{ details|safe }}</p>
<form action="{{ action }}" method="post">
@ -34,5 +47,6 @@
<a href="{{ url_for('index') }}" class="btn btn-default">Cancel</a>
</form>
</body>
</html>

View File

@ -12,6 +12,8 @@
<link rel="shortcut icon" href="{{ base_href + url_for('static', filename='favicon.ico') }}">
</head>
<body>
<a href="{{base_href}}"><< Home</a>
<h2>UVA Be Safe Communicator</h2>
<h3>The following files were imported from IVY</h3>

View File

@ -13,6 +13,8 @@
</head>
<body>
<h2>UVA Be Safe Communicator</h2>
<a href="invitation">Send Invitations</a> | <a href="imported_files">View imported files</a>
<h3>Records to be processed</h3>
{{ pagination.info }}

View File

@ -0,0 +1,35 @@
"""empty message
Revision ID: 3525dcf128cb
Revises: 39a845579587
Create Date: 2020-09-23 16:23:09.383030
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '3525dcf128cb'
down_revision = '39a845579587'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('invitation',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('date_sent', sa.DateTime(timezone=True), nullable=True),
sa.Column('location', sa.String(), nullable=True),
sa.Column('date', sa.String(), nullable=True),
sa.Column('total_recipients', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('invitation')
# ### end Alembic commands ###

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: 39a845579587
Revises: fa4b41e0bfe6
Create Date: 2020-09-23 15:50:44.987745
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '39a845579587'
down_revision = 'fa4b41e0bfe6'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('ivy_file', sa.Column('sample_count', sa.Integer(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('ivy_file', 'sample_count')
# ### end Alembic commands ###