mirror of
https://github.com/sartography/uva-covid19-testing-communicator.git
synced 2025-02-23 04:18:11 +00:00
Added Imported Files Page
This commit is contained in:
parent
46ddf269e1
commit
9c7df39fe8
@ -161,9 +161,8 @@ def index():
|
||||
graph = GraphService()
|
||||
|
||||
if request.method == "POST": session["index_filter"] = {} # Clear out the session if it is invalid.
|
||||
|
||||
if "index_filter" not in session: session["index_filter"] = {}
|
||||
if form.validate():
|
||||
session["index_filter"] = {}
|
||||
if form.dateRange.data:
|
||||
start, end = form.dateRange.data.split("-")
|
||||
session["index_filter"]["start_date"] = datetime.strptime(start.strip(), "%m/%d/%Y").date()
|
||||
@ -176,7 +175,13 @@ def index():
|
||||
session["index_filter"]["compute_id"] = form.compute_id.data
|
||||
if form.include_tests.data:
|
||||
session["index_filter"]["include_tests"] = form.include_tests.data
|
||||
graph.update_search_filters(session["index_filter"])
|
||||
|
||||
if type(session["index_filter"]["start_date"]) == str:
|
||||
session["index_filter"]["start_date"] = datetime.strptime(session["index_filter"]["start_date"].strip(), "%Y-%m-%d").date()
|
||||
if type(session["index_filter"]["end_date"]) == str:
|
||||
session["index_filter"]["end_date"] = datetime.strptime(session["index_filter"]["end_date"].strip(), "%Y-%m-%d").date()
|
||||
|
||||
graph.update_search_filters(session["index_filter"])
|
||||
|
||||
samples = db.session.query(Sample).order_by(Sample.date.desc())
|
||||
filtered_samples = graph.apply_filters(samples)
|
||||
@ -189,21 +194,14 @@ def index():
|
||||
"hourly":{},
|
||||
"weekday":{}
|
||||
}
|
||||
|
||||
important_dates = {}
|
||||
overall_totals_data = {
|
||||
"one_week_ago":0,
|
||||
"two_week_ago":0,
|
||||
"search":0,
|
||||
}
|
||||
|
||||
location_stats_data = {}
|
||||
|
||||
chart_ticks = []
|
||||
|
||||
timeFormat = "%m/%d"
|
||||
|
||||
# Count by Day
|
||||
bounds = daterange(graph.start_date, graph.end_date, days=1, hours=0)
|
||||
for i in range(len(bounds) - 1):
|
||||
chart_ticks.append(f"{bounds[i].strftime(timeFormat)}")
|
||||
@ -236,7 +234,6 @@ def index():
|
||||
|
||||
table = SampleTable(group_columns(filtered_samples[(page - 1) * 10:((page - 1) * 10) + 10]))
|
||||
|
||||
|
||||
return render_template('layouts/default.html',
|
||||
base_href=BASE_HREF,
|
||||
content=render_template(
|
||||
@ -354,15 +351,17 @@ def list_imported_files_from_ivy():
|
||||
page = request.args.get(get_page_parameter(), type=int, default=1)
|
||||
files = db.session.query(IvyFile).order_by(IvyFile.date_added.desc())
|
||||
pagination = Pagination(page=page, total=files.count(),
|
||||
search=False, record_name='samples')
|
||||
search=False, record_name='samples', css_framework='bootstrap4')
|
||||
|
||||
table = IvyFileTable(files.paginate(page, 10, error_out=False).items)
|
||||
return render_template(
|
||||
'imported_files.html',
|
||||
table=table,
|
||||
pagination=pagination,
|
||||
base_href=BASE_HREF
|
||||
)
|
||||
return render_template('layouts/default.html',
|
||||
base_href=BASE_HREF,
|
||||
content=render_template(
|
||||
'pages/imported_files.html',
|
||||
table=table,
|
||||
pagination=pagination,
|
||||
base_href=BASE_HREF
|
||||
))
|
||||
|
||||
|
||||
@app.route('/sso')
|
||||
|
@ -16,7 +16,7 @@ class InvitationForm(FlaskForm):
|
||||
|
||||
def validate_emails(form, field):
|
||||
all_emails = field.data.splitlines()
|
||||
EMAIL_REGEX = re.compile('^[a-z0-9]+[._a-z0-9]+[@]\w+[.]\w{2,3}$')
|
||||
EMAIL_REGEX = re.compile(r'^[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.')
|
||||
|
@ -10,17 +10,20 @@ import datetime as dt
|
||||
from sqlalchemy import func, and_, case
|
||||
|
||||
def dow_count(start, end):
|
||||
# Sunday, Monday, ...
|
||||
counts = [0 for _ in range(7)]
|
||||
curr = start
|
||||
curr = start
|
||||
while curr <= end:
|
||||
counts[(1 + curr.weekday()) % 7] += 1
|
||||
curr += dt.timedelta(1)
|
||||
curr += dt.timedelta(1)
|
||||
return counts
|
||||
|
||||
|
||||
def date2datetime(_date):
|
||||
return dt.datetime.combine(_date, dt.datetime.min.time())
|
||||
|
||||
def daterange(start, stop, days = 1, hours = 0):
|
||||
|
||||
def daterange(start, stop, days=1, hours=0):
|
||||
if (type(start) == dt.date):
|
||||
start = date2datetime(start)
|
||||
if (type(stop) == dt.date):
|
||||
@ -29,41 +32,44 @@ def daterange(start, stop, days = 1, hours = 0):
|
||||
date_list = []
|
||||
while time <= stop:
|
||||
date_list.append(time)
|
||||
time += dt.timedelta(days=days,hours=hours)
|
||||
time += dt.timedelta(days=days, hours=hours)
|
||||
return date_list
|
||||
|
||||
|
||||
class GraphService(object):
|
||||
def __init__(self):
|
||||
self.filters = dict()
|
||||
self.filters["start_date"] = Sample.date >= dt.date.today()
|
||||
self.start_date = dt.date.today()
|
||||
self.filters["end_date"] = Sample.date <= dt.date.today() + dt.timedelta(1)
|
||||
self.filters["end_date"] = Sample.date <= dt.date.today() + \
|
||||
dt.timedelta(1)
|
||||
self.end_date = dt.date.today() + dt.timedelta(1)
|
||||
|
||||
def apply_filters(self, query, ignore_dates = False):
|
||||
def apply_filters(self, query, ignore_dates=False):
|
||||
for key in self.filters:
|
||||
if ignore_dates and "date" in key:
|
||||
continue
|
||||
query = query.filter(self.filters[key])
|
||||
return query
|
||||
|
||||
"""Handles the collection and syncing of data from various sources. """
|
||||
def get_totals_last_week(self):
|
||||
location_stats_data = dict()
|
||||
# Count by range
|
||||
cases = [func.count(case([(and_(Sample.date >= self.start_date - dt.timedelta(14), Sample.date <= self.end_date - dt.timedelta(14)), 1)])),
|
||||
func.count(case([(and_(Sample.date >= self.start_date - dt.timedelta(7), Sample.date <= self.end_date - dt.timedelta(7)), 1)])),
|
||||
func.count(case([(and_(Sample.date >= self.start_date, Sample.date <= self.end_date), 1)]))]
|
||||
|
||||
func.count(case([(and_(Sample.date >= self.start_date - dt.timedelta(
|
||||
7), Sample.date <= self.end_date - dt.timedelta(7)), 1)])),
|
||||
func.count(case([(and_(Sample.date >= self.start_date, Sample.date <= self.end_date), 1)]))]
|
||||
|
||||
q = db.session.query(Sample.location,
|
||||
*cases\
|
||||
).group_by(Sample.location)
|
||||
*cases
|
||||
).group_by(Sample.location)
|
||||
|
||||
q = self.apply_filters(q, ignore_dates=True)
|
||||
|
||||
for result in q:
|
||||
location = result[0]
|
||||
if location not in location_stats_data: location_stats_data[location] = dict()
|
||||
if location not in location_stats_data:
|
||||
location_stats_data[location] = dict()
|
||||
location_stats_data[location]["two_week_ago"] = result[1]
|
||||
location_stats_data[location]["one_week_ago"] = result[2]
|
||||
location_stats_data[location]["search"] = result[3]
|
||||
@ -72,84 +78,102 @@ class GraphService(object):
|
||||
def get_totals_by_hour(self):
|
||||
hourly_charts_data = dict()
|
||||
days_in_search = (self.end_date - self.start_date).days
|
||||
|
||||
cases = [ ]
|
||||
|
||||
cases = []
|
||||
for i in range(24):
|
||||
cases.append(func.count(case([(func.extract('hour', Sample.date) == i, 1)])))
|
||||
|
||||
cases.append(func.count(
|
||||
case([(func.extract('hour', Sample.date) == i, 1)])))
|
||||
|
||||
q = db.session.query(Sample.location, Sample.station,
|
||||
*cases\
|
||||
).group_by(Sample.location, Sample.station)
|
||||
*cases
|
||||
).group_by(Sample.location, Sample.station)
|
||||
|
||||
q = self.apply_filters(q)
|
||||
for result in q:
|
||||
location, station = result[0], result[1]
|
||||
if location not in hourly_charts_data: hourly_charts_data[location] = dict()
|
||||
# Here I'm accounting for the difference in UTC and GMT time zones
|
||||
# by moving the five results from the start to the end.
|
||||
offset = 6
|
||||
if location not in hourly_charts_data:
|
||||
hourly_charts_data[location] = dict()
|
||||
# Here I'm accounting for the difference in UTC and GMT time zones
|
||||
# by moving the five results from the start to the end.
|
||||
offset = 6
|
||||
counts = result[2:]
|
||||
counts = counts[offset:] + counts[:offset]
|
||||
hourly_charts_data[location][station] = [round(i/days_in_search + .4) for i in counts]
|
||||
hourly_charts_data[location][station] = [
|
||||
round(i/days_in_search + .4) for i in counts]
|
||||
return hourly_charts_data
|
||||
|
||||
def get_totals_by_day(self):
|
||||
daily_charts_data = dict()
|
||||
bounds = daterange(self.start_date, self.end_date, days=1, hours=0)
|
||||
cases = [ ]
|
||||
cases = []
|
||||
for i in range(len(bounds) - 1):
|
||||
cases.append(func.count(case([(and_(Sample.date >= bounds[i], Sample.date <= bounds[i+1]), 1)])))
|
||||
|
||||
cases.append(func.count(
|
||||
case([(and_(Sample.date >= bounds[i], Sample.date <= bounds[i+1]), 1)])))
|
||||
|
||||
q = db.session.query(Sample.location, Sample.station,
|
||||
*cases\
|
||||
).group_by(Sample.location, Sample.station)
|
||||
*cases
|
||||
).group_by(Sample.location, Sample.station)
|
||||
q = self.apply_filters(q)
|
||||
|
||||
for result in q:
|
||||
location, station = result[0], result[1]
|
||||
if location not in daily_charts_data: daily_charts_data[location] = dict()
|
||||
if location not in daily_charts_data:
|
||||
daily_charts_data[location] = dict()
|
||||
daily_charts_data[location][station] = result[2:]
|
||||
|
||||
|
||||
return daily_charts_data
|
||||
|
||||
|
||||
def get_totals_by_weekday(self):
|
||||
weekday_charts_data = dict()
|
||||
dow_counts = dow_count(self.start_date, self.end_date - dt.timedelta(1))
|
||||
cases = [ ]
|
||||
dow_counts = dow_count(
|
||||
self.start_date, self.end_date - dt.timedelta(1))
|
||||
cases = []
|
||||
for i in range(7):
|
||||
cases.append(func.count(case([(func.extract('dow', Sample.date) == i, 1)])))
|
||||
|
||||
cases.append(func.count(
|
||||
case([(func.extract('dow', Sample.date) == i, 1)])))
|
||||
|
||||
q = db.session.query(Sample.location, Sample.station,
|
||||
*cases\
|
||||
).group_by(Sample.location, Sample.station)
|
||||
*cases
|
||||
).group_by(Sample.location, Sample.station)
|
||||
q = self.apply_filters(q)
|
||||
|
||||
for result in q:
|
||||
location, station = result[0], result[1]
|
||||
if location not in weekday_charts_data: weekday_charts_data[location] = dict()
|
||||
if location not in weekday_charts_data:
|
||||
weekday_charts_data[location] = dict()
|
||||
weekday_charts_data[location][station] = []
|
||||
for dow, total in zip(range(7),result[2:]):
|
||||
for dow, total in zip(range(7), result[2:]):
|
||||
if dow_counts[dow] > 0:
|
||||
weekday_charts_data[location][station].append(round(total/dow_counts[dow] + .4))
|
||||
weekday_charts_data[location][station].append(
|
||||
round(total/dow_counts[dow] + .4))
|
||||
else:
|
||||
weekday_charts_data[location][station].append(total)
|
||||
return weekday_charts_data
|
||||
|
||||
|
||||
def update_search_filters(self, filters):
|
||||
try:
|
||||
if "student_id" in filters: self.filters["student_id"] = Sample.student_id.in_(filters["student_id"].split())
|
||||
if "location" in filters: self.filters["location"] = Sample.location.in_(filters["location"].split())
|
||||
if "station" in filters: self.filters["station"] = Sample.station.in_(filters["station"].split())
|
||||
if "compute_id" in filters: self.filters["compute_id"] = Sample.computing_id.in_(filters["compute_id"].split())
|
||||
if "start_date" in filters:
|
||||
if "student_id" in filters:
|
||||
self.filters["student_id"] = Sample.student_id.in_(
|
||||
filters["student_id"].split())
|
||||
if "location" in filters:
|
||||
self.filters["location"] = Sample.location.in_(
|
||||
filters["location"].split())
|
||||
if "station" in filters:
|
||||
self.filters["station"] = Sample.station.in_(
|
||||
filters["station"].split())
|
||||
if "compute_id" in filters:
|
||||
self.filters["compute_id"] = Sample.computing_id.in_(
|
||||
filters["compute_id"].split())
|
||||
if "start_date" in filters:
|
||||
self.filters["start_date"] = Sample.date >= filters["start_date"]
|
||||
self.start_date = filters["start_date"]
|
||||
if "end_date" in filters:
|
||||
self.filters["end_date"] = Sample.date <= filters["end_date"]
|
||||
self.end_date = filters["end_date"]
|
||||
if not "include_tests" in filters:
|
||||
if "end_date" in filters:
|
||||
self.filters["end_date"] = Sample.date <= filters["end_date"]
|
||||
self.end_date = filters["end_date"]
|
||||
if not "include_tests" in filters:
|
||||
self.filters["include_tests"] = Sample.student_id != 0
|
||||
else:
|
||||
del self.filters["include_tests"]
|
||||
del self.filters["include_tests"]
|
||||
|
||||
except Exception as e:
|
||||
logging.error(
|
||||
|
@ -45,12 +45,14 @@ class SampleTable(Table):
|
||||
class IvyFileTable(Table):
|
||||
def sort_url(self, col_id, reverse=False):
|
||||
pass
|
||||
classes = ["table","align-items-center","table-flush"]
|
||||
file_name = Col('File Name')
|
||||
date_added = BetterDatetimeCol('Date', "medium", tzinfo=get_timezone('US/Eastern'), locale='en')
|
||||
sample_count = Col('Total Records')
|
||||
|
||||
|
||||
class InvitationTable(Table):
|
||||
classes = ["table","align-items-center","table-flush"]
|
||||
def sort_url(self, col_id, reverse=False):
|
||||
pass
|
||||
date_sent = BetterDatetimeCol('Date Sent', "medium", tzinfo=get_timezone('US/Eastern'), locale='en')
|
||||
|
@ -25,8 +25,15 @@
|
||||
<a class="nav-link" href="/">
|
||||
<em class="ni ni-tv-2 text-primary"></em> Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/imported_files">
|
||||
<em class="ni ni-tv-2 text-primary"></em> Imported Files
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<!-- Divider -->
|
||||
<hr class="my-3">
|
||||
<!-- Heading -->
|
||||
|
@ -2,14 +2,15 @@
|
||||
<div class="container-fluid">
|
||||
<div class="header-body">
|
||||
<!-- Card stats -->
|
||||
{% if dates %}
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-lg-6">
|
||||
<div class="card card-stats mb-4 mb-xl-0">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">Total Samples within {{dates.search}}</h5>
|
||||
<span id="stats_today" class="h2 font-weight-bold mb-0">{{overall_totals_data.search}}</span>
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">Total Samples within {{dates.search | safe }}</h5>
|
||||
<span id="stats_today" class="h2 font-weight-bold mb-0">{{overall_totals_data.search| safe }}</span>
|
||||
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
@ -26,9 +27,9 @@
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">(1 Week Back) {{dates.one_week_ago}}</h5>
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">(1 Week Back) {{dates.one_week_ago| safe }}</h5>
|
||||
|
||||
<span id="stats_one_week" class="h2 font-weight-bold mb-0">{{overall_totals_data.one_week_ago}}</span>
|
||||
<span id="stats_one_week" class="h2 font-weight-bold mb-0">{{overall_totals_data.one_week_ago| safe }}</span>
|
||||
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
@ -45,10 +46,10 @@
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">(2 Weeks Back) {{dates.two_weeks_ago}}
|
||||
<h5 class="card-title text-uppercase text-muted mb-0">(2 Weeks Back) {{dates.two_weeks_ago| safe }}
|
||||
</h5>
|
||||
|
||||
<span id="stats_two_weeks" class="h2 font-weight-bold mb-0">{{overall_totals_data.two_week_ago}}</span>
|
||||
<span id="stats_two_weeks" class="h2 font-weight-bold mb-0">{{overall_totals_data.two_week_ago| safe }}</span>
|
||||
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
@ -61,6 +62,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -17,8 +17,20 @@
|
||||
<body>
|
||||
|
||||
{% include 'includes/sidenav.html' %}
|
||||
<div class="main-content">
|
||||
|
||||
<!-- Top navbar -->
|
||||
<nav class="navbar navbar-top navbar-expand-md navbar-dark" id="navbar-main">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Brand -->
|
||||
<a class="h4 mb-0 text-white text-uppercase d-none d-lg-inline-block" href="./index.html">UVA Communicator
|
||||
Dashboard</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{{ content | safe }}
|
||||
</div>
|
||||
|
||||
<!-- % include 'includes/footer.html' % -->
|
||||
</body>
|
||||
|
@ -1,25 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>UVA Be Safe Communicator</title>
|
||||
<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>
|
||||
<body>
|
||||
<a href="{{base_href}}"><< Home</a>
|
||||
{% include 'includes/top-stats.html' %}
|
||||
<div class="container-fluid mt--7">
|
||||
<div class="row mt-4">
|
||||
<div class="col mb-5 mb-xl-0">
|
||||
<div class="card shadow">
|
||||
<div class="card-header border-0">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<h3 class="mb-0">The following files were imported from IVY</h3>{{ pagination.info }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>UVA Be Safe Communicator</h2>
|
||||
|
||||
<h3>The following files were imported from IVY</h3>
|
||||
{{ pagination.info }}
|
||||
{{ pagination.links }}
|
||||
{{ table }}
|
||||
{{ pagination.links }}
|
||||
</body>
|
||||
</html>
|
||||
<div class="table-responsive">
|
||||
<!-- Projects table -->
|
||||
{{ table }}
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
{{ pagination.links }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,16 +1,3 @@
|
||||
<!-- Main content -->
|
||||
|
||||
<div class="main-content">
|
||||
|
||||
<!-- Top navbar -->
|
||||
<nav class="navbar navbar-top navbar-expand-md navbar-dark" id="navbar-main">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Brand -->
|
||||
<a class="h4 mb-0 text-white text-uppercase d-none d-lg-inline-block" href="./index.html">UVA Communicator
|
||||
Dashboard</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Header -->
|
||||
{% include 'includes/top-stats.html' %}
|
||||
@ -123,7 +110,7 @@
|
||||
<div class="card-header border-0">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<h3 class="mb-0">Records to be processed</h3>
|
||||
<h3 class="mb-0">Records to be processed</h3>{{pagination.info}}
|
||||
</div>
|
||||
<div class="col text-right">
|
||||
<a href="{{ url_for('index') }}?download=true" class="btn btn-sm btn-primary">Download all</a>
|
||||
@ -144,7 +131,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
|
@ -1,43 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>UVA Be Safe Communicator</title>
|
||||
<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>
|
||||
<body>
|
||||
<h2>UVA Communicator Be Safe </h2>
|
||||
<a href="{{base_href + '/invitation'}}">Send Invitations</a> | <a href="{{base_href + '/imported_files'}}">View imported files</a>
|
||||
|
||||
<h3>Records to be processed</h3>
|
||||
{{ pagination.info }}
|
||||
{{ pagination.links }}
|
||||
{{ table }}
|
||||
{{ pagination.links }}
|
||||
|
||||
|
||||
<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') }}?cancel=true" class="btn btn-default">Cancel</a>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,62 @@
|
||||
import datetime
|
||||
|
||||
import pytz
|
||||
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
from communicator import app
|
||||
from communicator.models import Sample
|
||||
from communicator.services.graph_service import GraphService
|
||||
|
||||
|
||||
class TestGraphService(BaseTest):
|
||||
|
||||
def test_get_totals_last_week(self):
|
||||
graph = GraphService()
|
||||
graph.update_search_filters({
|
||||
"start_date": datetime.date(2020,11,1),
|
||||
"end_date": datetime.date(2020,11,1),
|
||||
})
|
||||
result = graph.get_totals_last_week()
|
||||
for location in result:
|
||||
for station in location:
|
||||
self.assertEqual(result[location][station], 0)
|
||||
|
||||
|
||||
def test_get_totals_by_hour(self):
|
||||
graph = GraphService()
|
||||
graph.update_search_filters({
|
||||
"start_date": datetime.date(2020,11,1),
|
||||
"end_date": datetime.date(2020,11,1),
|
||||
})
|
||||
result = graph.get_totals_by_hour()
|
||||
for location in result:
|
||||
for station in location:
|
||||
self.assertEqual(result[location][station], 0)
|
||||
|
||||
def test_get_totals_by_day(self):
|
||||
graph = GraphService()
|
||||
graph.update_search_filters({
|
||||
"start_date": datetime.date(2020,11,1),
|
||||
"end_date": datetime.date(2020,11,1),
|
||||
})
|
||||
result = graph.get_totals_by_day()
|
||||
for location in result:
|
||||
for station in location:
|
||||
self.assertEqual(result[location][station], 0)
|
||||
|
||||
def test_get_totals_by_weekday(self):
|
||||
graph = GraphService()
|
||||
graph.update_search_filters({
|
||||
"start_date": datetime.date(2020,11,1),
|
||||
"end_date": datetime.date(2020,11,14),
|
||||
"location": "50"
|
||||
})
|
||||
result = graph.get_totals_by_weekday()
|
||||
self.assertEqual(result[50][00][1],17)
|
||||
self.assertEqual(result[50][10][1],16)
|
||||
self.assertEqual(result[50][20][1],14)
|
||||
self.assertEqual(result[50][30][1],17)
|
||||
self.assertEqual(result[50][40][1],20)
|
||||
self.assertEqual(result[50][50][1],14)
|
@ -3,10 +3,10 @@ import os
|
||||
import unittest
|
||||
import globus_sdk
|
||||
|
||||
from communicator.models.ivy_file import IvyFile
|
||||
|
||||
from communicator import app, db
|
||||
|
||||
from communicator.models.ivy_file import IvyFile
|
||||
|
||||
from communicator.errors import CommError
|
||||
from communicator.services.ivy_service import IvyService
|
||||
|
||||
@ -25,9 +25,9 @@ class IvyServiceTest(BaseTest):
|
||||
IvyService.samples_from_ivy_file(ivy_incorrect_file)
|
||||
|
||||
def test_load_directory(self):
|
||||
self.assertEquals(0, db.session.query(IvyFile).count())
|
||||
self.assertEqual(0, db.session.query(IvyFile).count())
|
||||
app.config['IVY_IMPORT_DIR'] = os.path.join(app.root_path, '..', 'tests', 'data', 'import_directory')
|
||||
files, records = IvyService().load_directory()
|
||||
self.assertEquals(4, len(files))
|
||||
files, _ = IvyService().load_directory()
|
||||
self.assertEqual(4, len(files))
|
||||
|
||||
|
||||
|
@ -20,28 +20,28 @@ class TestSampleEndpoint(BaseTest):
|
||||
def test_create_sample(self):
|
||||
# Test add sample
|
||||
samples = db.session.query(Sample).all()
|
||||
self.assertEquals(0, len(samples))
|
||||
self.assertEqual(0, len(samples))
|
||||
|
||||
rv = self.app.post('/v1.0/sample',
|
||||
content_type="application/json",
|
||||
data=json.dumps(self.sample_json))
|
||||
|
||||
samples = db.session.query(Sample).all()
|
||||
self.assertEquals(1, len(samples))
|
||||
self.assertEqual(1, len(samples))
|
||||
|
||||
def test_create_sample_gets_correct_location_and_station(self):
|
||||
# Test add sample
|
||||
samples = db.session.query(Sample).all()
|
||||
self.assertEquals(0, len(samples))
|
||||
self.assertEqual(0, len(samples))
|
||||
|
||||
rv = self.app.post('/v1.0/sample',
|
||||
content_type="application/json",
|
||||
data=json.dumps(self.sample_json))
|
||||
|
||||
samples = db.session.query(Sample).all()
|
||||
self.assertEquals(1, len(samples))
|
||||
self.assertEquals(1, samples[0].location)
|
||||
self.assertEquals(2, samples[0].station)
|
||||
self.assertEqual(1, len(samples))
|
||||
self.assertEqual(1, samples[0].location)
|
||||
self.assertEqual(2, samples[0].station)
|
||||
|
||||
def test_create_sample_has_last_updated(self):
|
||||
rv = self.app.post('/v1.0/sample',
|
||||
@ -49,13 +49,13 @@ class TestSampleEndpoint(BaseTest):
|
||||
data=json.dumps(self.sample_json))
|
||||
|
||||
samples = db.session.query(Sample).all()
|
||||
self.assertEquals(1, len(samples))
|
||||
self.assertEqual(1, len(samples))
|
||||
self.assertIsNotNone(samples[0].last_modified)
|
||||
|
||||
def test_create_duplicate_sample_does_not_raise_error(self):
|
||||
# Test add sample
|
||||
samples = db.session.query(Sample).all()
|
||||
self.assertEquals(0, len(samples))
|
||||
self.assertEqual(0, len(samples))
|
||||
|
||||
rv = self.app.post('/v1.0/sample', content_type="application/json", data=json.dumps(self.sample_json))
|
||||
rv = self.app.post('/v1.0/sample', content_type="application/json", data=json.dumps(self.sample_json))
|
||||
@ -63,7 +63,7 @@ class TestSampleEndpoint(BaseTest):
|
||||
rv = self.app.post('/v1.0/sample', content_type="application/json", data=json.dumps(self.sample_json))
|
||||
|
||||
samples = db.session.query(Sample).all()
|
||||
self.assertEquals(1, len(samples))
|
||||
self.assertEqual(1, len(samples))
|
||||
|
||||
def test_notify_by_email_by_file_name(self):
|
||||
db.session.add(Sample(barcode="000000111-202009091449-4321",
|
||||
@ -83,12 +83,12 @@ class TestSampleEndpoint(BaseTest):
|
||||
db.session.commit()
|
||||
admin._notify_by_email('xxx')
|
||||
samples = db.session.query(Sample).filter(Sample.email_notified == True).all()
|
||||
self.assertEquals(1, len(samples))
|
||||
self.assertEqual(1, len(samples))
|
||||
samples = db.session.query(Sample).filter(Sample.email_notified != True).all()
|
||||
self.assertEquals(1, len(samples))
|
||||
self.assertEqual(1, len(samples))
|
||||
admin._notify_by_email()
|
||||
samples = db.session.query(Sample).filter(Sample.email_notified == True).all()
|
||||
self.assertEquals(2, len(samples))
|
||||
self.assertEqual(2, len(samples))
|
||||
|
||||
def test_get_all_samples(self):
|
||||
s1 = Sample(barcode="000000111-202009091449-4321",
|
||||
@ -164,17 +164,17 @@ class TestSampleEndpoint(BaseTest):
|
||||
rv = self.app.get(f'/v1.0/sample', content_type="application/json",
|
||||
headers={'X-CR-API-KEY': app.config.get('API_TOKEN')})
|
||||
data = json.loads(rv.get_data(as_text=True))
|
||||
self.assertEquals(2, len(data))
|
||||
self.assertEqual(2, len(data))
|
||||
|
||||
last_modified_arg = d1.isoformat()
|
||||
rv = self.app.get(f'/v1.0/sample?last_modified={last_modified_arg}', content_type="application/json",
|
||||
headers={'X-CR-API-KEY': app.config.get('API_TOKEN')})
|
||||
data = json.loads(rv.get_data(as_text=True))
|
||||
self.assertEquals(1, len(data))
|
||||
self.assertEquals(s2.barcode, data[0]['barcode'])
|
||||
self.assertEqual(1, len(data))
|
||||
self.assertEqual(s2.barcode, data[0]['barcode'])
|
||||
|
||||
last_modified_arg = d2.isoformat()
|
||||
rv = self.app.get(f'/v1.0/sample?last_modified={last_modified_arg}', content_type="application/json",
|
||||
headers={'X-CR-API-KEY': app.config.get('API_TOKEN')})
|
||||
data = json.loads(rv.get_data(as_text=True))
|
||||
self.assertEquals(0, len(data))
|
||||
self.assertEqual(0, len(data))
|
||||
|
@ -94,15 +94,15 @@ class IvyServiceTest(BaseTest):
|
||||
delta = datetime.now() - s1.last_modified
|
||||
self.assertGreater(delta.days, 1) # Last modified is in the past.
|
||||
|
||||
self.assertEquals(2, len(db.session.query(Sample).all()))
|
||||
self.assertEqual(2, len(db.session.query(Sample).all()))
|
||||
service.merge_similar_records()
|
||||
self.assertEquals(1, len(db.session.query(Sample).all()))
|
||||
self.assertEqual(1, len(db.session.query(Sample).all()))
|
||||
sample = db.session.query(Sample).first()
|
||||
self.assertEquals("dan@sartography.com", sample.email)
|
||||
self.assertEquals("111111111-AAA-202010050000-0000", sample.barcode)
|
||||
self.assertEquals(1, len(sample.notifications))
|
||||
self.assertEqual("dan@sartography.com", sample.email)
|
||||
self.assertEqual("111111111-AAA-202010050000-0000", sample.barcode)
|
||||
self.assertEqual(1, len(sample.notifications))
|
||||
delta = datetime.now() - sample.last_modified
|
||||
self.assertEquals(0, delta.days) # Last modified is updated on merge.
|
||||
self.assertEqual(0, delta.days) # Last modified is updated on merge.
|
||||
|
||||
def test_merge_non_similar_records(self):
|
||||
service = SampleService()
|
||||
@ -117,7 +117,7 @@ class IvyServiceTest(BaseTest):
|
||||
email="dan@sartography.com",
|
||||
phone="555-555-5555"))
|
||||
service.merge_similar_records()
|
||||
self.assertEquals(2, len(db.session.query(Sample).all()))
|
||||
self.assertEqual(2, len(db.session.query(Sample).all()))
|
||||
|
||||
|
||||
def test_correct_computing_id(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user