Allow restricting results in main page, and downloading the results.

This commit is contained in:
Dan 2020-11-03 16:01:50 -05:00
parent ee34a7ea20
commit a8e897f344
6 changed files with 116 additions and 17 deletions

View File

@ -1,10 +1,13 @@
import csv
import io
import logging
import os
from functools import wraps
import connexion
import sentry_sdk
from flask import render_template, request, redirect, url_for, flash, abort
from flask import render_template, request, redirect, url_for, flash, abort, Response, send_file
from flask_assets import Environment
from flask_cors import CORS
from flask_mail import Mail
@ -12,6 +15,7 @@ from flask_marshmallow import Marshmallow
from flask_migrate import Migrate
from flask_paginate import Pagination, get_page_parameter
from flask_sqlalchemy import SQLAlchemy
from pyparsing import unicode
from sentry_sdk.integrations.flask import FlaskIntegration
from webassets import Bundle
from flask_executor import Executor
@ -98,24 +102,93 @@ def superuser(f):
return decorated_function
@app.route('/', methods=['GET'])
@app.route('/', methods=['GET', 'POST'])
@superuser
def index():
from communicator.models import Sample
from communicator.tables import SampleTable
# display results
page = request.args.get(get_page_parameter(), type=int, default=1)
download = False
form = forms.SearchForm(request.form)
action = BASE_HREF + "/"
samples = db.session.query(Sample).order_by(Sample.date.desc())
if request.method == 'POST' and form.validate():
if form.startDate.data:
samples = samples.filter(Sample.date >= form.startDate.data)
if form.endDate.data:
samples = samples.filter(Sample.date <= form.endDate.data)
if form.studentId.data:
samples = samples.filter(Sample.student_id == form.studentId.data)
if form.location.data:
samples = samples.filter(Sample.location == form.location.data)
if form.download.data:
download = True
# display results
if download:
csv = __make_csv(samples)
return send_file(csv, attachment_filename='data_export.csv', as_attachment=True)
else:
page = request.args.get(get_page_parameter(), type=int, default=1)
pagination = Pagination(page=page, total=samples.count(), search=False, record_name='samples')
table = SampleTable(samples.paginate(page,10,error_out=False).items)
return render_template(
'index.html',
form=form,
table=table,
action=action,
pagination=pagination,
base_href=BASE_HREF
base_href=BASE_HREF,
description_map={}
)
def __make_csv(sample_query):
csvfile = io.StringIO()
headers = [
'barcode',
'student_id',
'date',
'location',
'phone',
'email',
'result_code',
'ivy_file',
'email_notified',
'text_notified'
]
writer = csv.DictWriter(csvfile, headers)
writer.writeheader()
for sample in sample_query.all():
writer.writerow(
{
'barcode': sample.barcode,
'student_id': sample.student_id,
'date': sample.date,
'location': sample.location,
'phone': sample.phone,
'email': sample.email,
'result_code': sample.result_code,
'ivy_file': sample.ivy_file,
'email_notified': sample.email_notified,
'text_notified': sample.text_notified,
}
)
# Creating the byteIO object from the StringIO Object
mem = io.BytesIO()
mem.write(csvfile.getvalue().encode('utf-8'))
# seeking was necessary. Python 3.5.2, Flask 0.12.2
mem.seek(0)
csvfile.close()
return mem
@app.route('/invitation', methods=['GET', 'POST'])
@superuser
def send_invitation():

View File

@ -112,7 +112,8 @@ def _notify_by_text(file_name=None, retry=False):
if file_name:
sample_query = sample_query.filter(Sample.ivy_file == file_name)
sample_query = sample_query.limit(150) # Only send out 150 texts at a time.
# Do not limit texts, as errors pile up we end up sending less and less, till none go out.
# sample_query = sample_query.limit(150) # Only send out 150 texts at a time.
samples = sample_query.all()
for sample in samples:
last_failure = sample.last_failure_by_type(TEXT_TYPE)

View File

@ -3,7 +3,7 @@ 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, \
ValidationError
ValidationError, DateField
from wtforms.widgets import TextArea
@ -18,3 +18,11 @@ class InvitationForm(FlaskForm):
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.')
class SearchForm(FlaskForm):
startDate = DateField("Start Date (YYYY-MM-DD)", validators=[validators.Optional()])
endDate = DateField("End Date (YYYY-MM-DD)", validators=[validators.Optional()])
studentId = TextAreaField('Student Id')
location = TextAreaField('Location')
download = BooleanField('Download Results')

View File

@ -114,7 +114,7 @@ select.multi {
.form-field {
display: flex;
max-width: 100vw;
margin-bottom: 40px;
margin-bottom: 5px;
padding: 2em; }
.form-field.hidden {
display: none; }

View File

@ -215,7 +215,7 @@ select.multi {
.form-field {
display: flex;
max-width: 100vw;
margin-bottom: 40px;
margin-bottom: 5px;
padding: 2em;
&.hidden {

View File

@ -15,6 +15,23 @@
<h2>UVA Be Safe Communicator</h2>
<a href="{{base_href + '/invitation'}}">Send Invitations</a> | <a href="{{base_href + '/imported_files'}}">View imported files</a>
<form action="{{ action }}" method="post">
{{ form.csrf_token() }}
{% for field in form if field.name != "csrf_token" %}
<div class="form-field {{ field.widget.input_type }}">
<div class="form-field-label">{{ field.label() }}:</div>
<div class="form-field-input">{{ field }}</div>
<div class="form-field-help">{{ description_map[field.name] }}</div>
{% for error in field.errors %}
<div class="form-field-error">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<button class="btn btn-primary" type="submit">Submit</button>
<a href="{{ url_for('index') }}" class="btn btn-default">Cancel</a>
</form>
<h3>Records to be processed</h3>
{{ pagination.info }}
{{ pagination.links }}