From 7ac700ca91b64430d1ce87700808d1ec07df2e4a Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 16:15:05 -0500 Subject: update gitignore --- .gitignore | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index acab611..48d472c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,23 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/python,vscode,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=python,vscode,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -50,6 +70,7 @@ coverage.xml *.py,cover .hypothesis/ .pytest_cache/ +pytestdebug.log # Translations *.mo @@ -70,6 +91,7 @@ instance/ # Sphinx documentation docs/_build/ +doc/_build/ # PyBuilder target/ @@ -109,6 +131,7 @@ venv/ ENV/ env.bak/ venv.bak/ +pythonenv* # Spyder project settings .spyderproject @@ -128,8 +151,20 @@ dmypy.json # Pyre type checker .pyre/ -# Don't commit secrets! -config.py +# pytype static type analyzer +.pytype/ + +# profiling data +.prof -# don't commit data -data/ +### vscode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# End of https://www.toptal.com/developers/gitignore/api/python,vscode,linux + +config.py -- cgit v1.2.3 From a21a8ea339f611957a4752a43a9ddd6f18a7e46f Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 16:25:47 -0500 Subject: remove legacy code --- .travis.yml | 12 ---- migrations.py | 65 ----------------- poller/__init__.py | 202 ----------------------------------------------------- poller/db.py | 67 ------------------ poller/dedup.py | 71 ------------------- 5 files changed, 417 deletions(-) delete mode 100644 .travis.yml delete mode 100644 migrations.py delete mode 100644 poller/__init__.py delete mode 100644 poller/db.py delete mode 100644 poller/dedup.py diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9935a5f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: python -python: - - "3.8" -services: - - "docker" - -install: - - "pip install -r requirements.txt" -script: - - "pylint poller" - - "docker build -t poller ." - diff --git a/migrations.py b/migrations.py deleted file mode 100644 index 5f692cb..0000000 --- a/migrations.py +++ /dev/null @@ -1,65 +0,0 @@ -import os -import json -import sqlite3 - -from poller.db import create_tables, get_latest_from_db - -LATEST_DATA={} - -def update_db(): - db_conn = sqlite3.connect('./data/data.sqlite3') - c = db_conn.cursor() - sql = f'INSERT INTO `alertlevel` VALUES (\'{LATEST_DATA["last_updated"]}\', \'{LATEST_DATA["alert_level"]}\');' - c.execute(sql) - sql = f'INSERT INTO `total` VALUES (\'{LATEST_DATA["last_updated"]}\', {LATEST_DATA["total_students"]}, {LATEST_DATA["total_staff"]});' - c.execute(sql) - sql = f'INSERT INTO `new` VALUES (\'{LATEST_DATA["last_updated"]}\', {LATEST_DATA["new_students"]}, {LATEST_DATA["new_staff"]});' - c.execute(sql) - sql = f'INSERT INTO `quarantine` VALUES (\'{LATEST_DATA["last_updated"]}\', {LATEST_DATA["quarantine_on_campus"]}, {LATEST_DATA["quarantine_off_campus"]});' - c.execute(sql) - sql = f'INSERT INTO `isolation` VALUES (\'{LATEST_DATA["last_updated"]}\', {LATEST_DATA["isolation_on_campus"]}, {LATEST_DATA["isolation_off_campus"]});' - c.execute(sql) - sql = f'INSERT INTO `beds` VALUES (\'{LATEST_DATA["last_updated"]}\', {LATEST_DATA["beds_available"]});' - c.execute(sql) - sql = f'INSERT INTO `tests` VALUES (\'{LATEST_DATA["last_updated"]}\', {LATEST_DATA["tests_administered"]});' - c.execute(sql) - db_conn.commit() - db_conn.close() - -def db_is_same(): - global LATEST_DATA - latest_data = get_latest_from_db() - if latest_data is None or LATEST_DATA is None: - return False - for key in list(latest_data.keys()): - if key != 'last_updated' and LATEST_DATA[key] != latest_data[key]: - return False - return True - -if not os.path.exists('./data'): - os.mkdir('./data') - -create_tables() - -with open('history/history.json', 'r') as fd: - print('importing data...') - data = json.loads(fd.read()) - for day in data: - print(day) - LATEST_DATA = { - 'alert_level': str(day['alert_level']), - 'total_students': int(day['total_students']), - 'total_staff': int(day['total_staff']), - 'new_students': int(day['new_students']), - 'new_staff': int(day['new_staff']), - 'quarantine_on_campus': int(day['quarantine_on_campus']), - 'quarantine_off_campus': int(day['quarantine_off_campus']), - 'isolation_on_campus': int(day['isolation_on_campus']), - 'isolation_off_campus': int(day['isolation_off_campus']), - 'beds_available': int(day['beds_available']), - 'tests_administered': int(day['tests_administered']), - 'last_updated': day['last_updated'] - } - update_db() - print('data imported!') - diff --git a/poller/__init__.py b/poller/__init__.py deleted file mode 100644 index ebb2653..0000000 --- a/poller/__init__.py +++ /dev/null @@ -1,202 +0,0 @@ -""" A small flask Hello World """ - -import os -import threading -import sqlite3 -import atexit -import datetime -import json - -from flask import Flask, jsonify, request, make_response -import requests -from bs4 import BeautifulSoup - -from .dedup import dedup -from .db import create_tables, get_all_from_db, get_latest_from_db - -POOL_TIME = 5 * 60 # Seconds -DASHBOARD_URL = 'https://rit.edu/ready/spring-dashboard' -LATEST_DATA = None -data_thread = threading.Thread() -db_lock = threading.Lock() - -if not os.path.exists('./data'): - os.mkdir('./data') - -def update_db(): - print('updating db') - with db_lock: - db_conn = sqlite3.connect('data/data.sqlite3') - c = db_conn.cursor() - sql = f'INSERT INTO `alertlevel` VALUES (datetime(\'now\'), \'{LATEST_DATA["alert_level"]}\');' - c.execute(sql) - sql = f'INSERT INTO `total` VALUES (datetime(\'now\'), {LATEST_DATA["total_students"]}, {LATEST_DATA["total_staff"]});' - c.execute(sql) - sql = f'INSERT INTO `new` VALUES (datetime(\'now\'), {LATEST_DATA["new_students"]}, {LATEST_DATA["new_staff"]});' - c.execute(sql) - sql = f'INSERT INTO `quarantine` VALUES (datetime(\'now\'), {LATEST_DATA["quarantine_on_campus"]}, {LATEST_DATA["quarantine_off_campus"]});' - c.execute(sql) - sql = f'INSERT INTO `isolation` VALUES (datetime(\'now\'), {LATEST_DATA["isolation_on_campus"]}, {LATEST_DATA["isolation_off_campus"]});' - c.execute(sql) - sql = f'INSERT INTO `beds` VALUES (datetime(\'now\'), {LATEST_DATA["beds_available"]});' - c.execute(sql) - sql = f'INSERT INTO `tests` VALUES (datetime(\'now\'), {LATEST_DATA["tests_administered"]});' - c.execute(sql) - db_conn.commit() - db_conn.close() - dedup() - -def data_is_same(current_data): - global LATEST_DATA - if LATEST_DATA is None or current_data is None: - return False - for key in list(LATEST_DATA.keys()): - if key != 'last_updated' and current_data[key] != LATEST_DATA[key]: - return False - return True - -def db_is_same(current_data): - latest_data = get_latest_from_db() - if latest_data is None or current_data is None: - return False - for key in list(latest_data.keys()): - if key != 'last_updated' and current_data[key] != latest_data[key]: - return False - return True - -def get_data(): - print('fetching data') - global data_thread - data_thread = threading.Timer(POOL_TIME, get_data, ()) - data_thread.start() - create_tables() - page = requests.get(DASHBOARD_URL, headers={'Cache-Control': 'no-cache'}) - soup = BeautifulSoup(page.content, 'html.parser') - total_students = int(soup.find('div', attrs={'class': 'statistic-13872'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) - total_staff = int(soup.find('div', attrs={'class': 'statistic-13875'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) - new_students = int(soup.find('div', attrs={'class': 'statistic-14332'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) - new_staff = int(soup.find('div', attrs={'class': 'statistic-14335'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) - quarantine_on_campus = int(soup.find('div', attrs={'class': 'statistic-13893'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) - quarantine_off_campus = int(soup.find('div', attrs={'class': 'statistic-13896'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) - isolation_on_campus = int(soup.find('div', attrs={'class': 'statistic-13905'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) - isolation_off_campus = int(soup.find('div', attrs={'class': 'statistic-13908'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) - beds_available = int(soup.find('div', attrs={'class': 'statistic-13935'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip().strip('%')) - tests_administered = int(soup.find('div', attrs={'class': 'statistic-13923'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip().replace("*", " ").replace(",", "")) - container = soup.find('div', attrs={'id': 'pandemic-message-container'}) - alert_level = container.find("a").text - color = "" - if "Green" in alert_level: - color = 'green' - elif "Yellow" in alert_level: - color = 'yellow' - elif "Orange" in alert_level: - color = 'orange' - elif "Red" in alert_level: - color = 'red' - global LATEST_DATA - - fall_data = None - with open('history/fall-2020.json', 'r') as fd: - fall_data = json.loads(fd.read()) - current_data = { - 'alert_level': color, - 'total_students': total_students + fall_data['total_students'], - 'total_staff': total_staff + fall_data['total_staff'], - 'new_students': new_students, - 'new_staff': new_staff, - 'quarantine_on_campus': quarantine_on_campus, - 'quarantine_off_campus': quarantine_off_campus, - 'isolation_on_campus': isolation_on_campus, - 'isolation_off_campus': isolation_off_campus, - 'beds_available': beds_available, - 'tests_administered': tests_administered + fall_data['tests_administered'], - 'last_updated': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') - } - LATEST_DATA = current_data - if not db_is_same(current_data): - update_db() - return current_data - -get_data() - -APP = Flask(__name__) - -# Load file based configuration overrides if present -if os.path.exists(os.path.join(os.getcwd(), 'config.py')): - APP.config.from_pyfile(os.path.join(os.getcwd(), 'config.py')) -else: - APP.config.from_pyfile(os.path.join(os.getcwd(), 'config.env.py')) - -APP.secret_key = APP.config['SECRET_KEY'] - -@APP.route('/api/v0/latest') -def _api_v0_latest(): - return jsonify(LATEST_DATA) - -@APP.route('/api/v0/latestdb') -def _api_v0_latestdb(): - data = get_latest_from_db() - return jsonify(data) - -@APP.route('/api/v0/history') -def _api_v0_history(): - data = get_all_from_db() - return jsonify(data) - -@APP.route('/api/v0/difference') -def _api_v0_difference(): - data = get_all_from_db() - latest = data[-1] - prev = data[-2] - data = { - 'alert_level': f'{prev["alert_level"]} -> {latest["alert_level"]}', - 'total_students': latest["total_students"] - prev["total_students"], - 'total_staff': latest["total_staff"] - prev["total_staff"], - 'new_students': latest["new_students"] - prev["new_students"], - 'new_staff': latest["new_staff"] - prev["new_staff"], - 'quarantine_on_campus': latest["quarantine_on_campus"] - prev["quarantine_on_campus"], - 'quarantine_off_campus': latest["quarantine_off_campus"] - prev["quarantine_off_campus"], - 'isolation_on_campus': latest["isolation_on_campus"] - prev["isolation_on_campus"], - 'isolation_off_campus': latest["isolation_off_campus"] - prev["isolation_off_campus"], - 'beds_available': latest["beds_available"] - prev["beds_available"], - 'tests_administered': latest["tests_administered"] - prev["tests_administered"], - } - return jsonify(data) - -@APP.route('/api/v0/diff') -def _api_v0_diff(): - first = request.args.get('first') - last = request.args.get('last') - data = get_all_from_db() - if first is None: - first = 0 - else: - try: - first = int(first) - except: - first = 0 - if last is None: - last = len(data) - 1 - else: - try: - last = int(last) - except: - last = len(data) - 1 - latest = data[last] - prev = data[first] - data = { - 'alert_level': f'{prev["alert_level"]} -> {latest["alert_level"]}', - 'total_students': latest["total_students"] - prev["total_students"], - 'total_staff': latest["total_staff"] - prev["total_staff"], - 'new_students': latest["new_students"] - prev["new_students"], - 'new_staff': latest["new_staff"] - prev["new_staff"], - 'quarantine_on_campus': latest["quarantine_on_campus"] - prev["quarantine_on_campus"], - 'quarantine_off_campus': latest["quarantine_off_campus"] - prev["quarantine_off_campus"], - 'isolation_on_campus': latest["isolation_on_campus"] - prev["isolation_on_campus"], - 'isolation_off_campus': latest["isolation_off_campus"] - prev["isolation_off_campus"], - 'beds_available': latest["beds_available"] - prev["beds_available"], - 'tests_administered': latest["tests_administered"] - prev["tests_administered"], - 'description': f'day {first} to {last}' - } - return jsonify(data) - diff --git a/poller/db.py b/poller/db.py deleted file mode 100644 index d3836a7..0000000 --- a/poller/db.py +++ /dev/null @@ -1,67 +0,0 @@ -import threading -import sqlite3 - -db_lock = threading.Lock() - -def create_tables(): - with db_lock: - db_conn = sqlite3.connect('data/data.sqlite3') - c = db_conn.cursor() - sql = f'CREATE TABLE IF NOT EXISTS `alertlevel` (time DATETIME PRIMARY KEY NOT NULL, color CHAR(50) NOT NULL);' - c.execute(sql) - sql = f'CREATE TABLE IF NOT EXISTS `total` (time DATETIME PRIMARY KEY NOT NULL, total_students INT NOT NULL, total_staff INT NOT NULL);' - c.execute(sql) - sql = f'CREATE TABLE IF NOT EXISTS `new` (time DATETIME PRIMARY KEY NOT NULL, new_students INT NOT NULL, new_staff INT NOT NULL);' - c.execute(sql) - sql = f'CREATE TABLE IF NOT EXISTS `quarantine` (time DATETIME PRIMARY KEY NOT NULL, quarantine_on_campus INT NOT NULL, quarantine_off_campus INT NOT NULL);' - c.execute(sql) - sql = f'CREATE TABLE IF NOT EXISTS `isolation` (time DATETIME PRIMARY KEY NOT NULL, isolation_on_campus INT NOT NULL, isolation_off_campus INT NOT NULL);' - c.execute(sql) - sql = f'CREATE TABLE IF NOT EXISTS `beds` (time DATETIME PRIMARY KEY NOT NULL, beds_available INT NOT NULL);' - c.execute(sql) - sql = f'CREATE TABLE IF NOT EXISTS `tests` (time DATETIME PRIMARY KEY NOT NULL, tests_administered INT NOT NULL);' - c.execute(sql) - db_conn.commit() - db_conn.close() - - -def get_latest_from_db(): - return get_all_from_db()[-1] - -def get_all_from_db(): - with db_lock: - db_conn = sqlite3.connect('data/data.sqlite3') - c = db_conn.cursor() - sql = 'SELECT alertlevel.time, alertlevel.color, total.total_students, total.total_staff, new.new_students, new.new_staff, ' + \ - 'quarantine.quarantine_on_campus, quarantine.quarantine_off_campus, isolation.isolation_on_campus, isolation.isolation_off_campus, ' + \ - 'beds.beds_available, tests.tests_administered ' + \ - 'FROM `alertlevel` ' + \ - 'INNER JOIN `total` ' + \ - 'ON alertlevel.time = total.time ' + \ - 'INNER JOIN `new` ' + \ - 'ON alertlevel.time = new.time ' + \ - 'INNER JOIN `quarantine` ' + \ - 'ON alertlevel.time = quarantine.time ' + \ - 'INNER JOIN `isolation` ' + \ - 'ON alertlevel.time = isolation.time ' + \ - 'INNER JOIN `beds` ' + \ - 'ON alertlevel.time = beds.time ' + \ - 'INNER JOIN `tests` ' + \ - 'ON alertlevel.time = tests.time' - c.execute(sql) - - data = [{ - 'alert_level': d[1], - 'total_students': d[2], - 'total_staff': d[3], - 'new_students': d[4], - 'new_staff': d[5], - 'quarantine_on_campus': d[6], - 'quarantine_off_campus': d[7], - 'isolation_on_campus': d[8], - 'isolation_off_campus': d[9], - 'beds_available': d[10], - 'tests_administered': d[11], - 'last_updated': d[0] - } for d in c.fetchall()] - return data diff --git a/poller/dedup.py b/poller/dedup.py deleted file mode 100644 index ba68be8..0000000 --- a/poller/dedup.py +++ /dev/null @@ -1,71 +0,0 @@ -import sqlite3 - -def get_all_from_db(): - db_conn = sqlite3.connect('data/data.sqlite3') - c = db_conn.cursor() - sql = 'SELECT alertlevel.time, alertlevel.color, total.total_students, total.total_staff, new.new_students, new.new_staff, ' + \ - 'quarantine.quarantine_on_campus, quarantine.quarantine_off_campus, isolation.isolation_on_campus, isolation.isolation_off_campus, ' + \ - 'beds.beds_available, tests.tests_administered ' + \ - 'FROM `alertlevel` ' + \ - 'INNER JOIN `total` ' + \ - 'ON alertlevel.time = total.time ' + \ - 'INNER JOIN `new` ' + \ - 'ON alertlevel.time = new.time ' + \ - 'INNER JOIN `quarantine` ' + \ - 'ON alertlevel.time = quarantine.time ' + \ - 'INNER JOIN `isolation` ' + \ - 'ON alertlevel.time = isolation.time ' + \ - 'INNER JOIN `beds` ' + \ - 'ON alertlevel.time = beds.time ' + \ - 'INNER JOIN `tests` ' + \ - 'ON alertlevel.time = tests.time' - c.execute(sql) - - data = [{ - 'alert_level': d[1], - 'total_students': d[2], - 'total_staff': d[3], - 'new_students': d[4], - 'new_staff': d[5], - 'quarantine_on_campus': d[6], - 'quarantine_off_campus': d[7], - 'isolation_on_campus': d[8], - 'isolation_off_campus': d[9], - 'beds_available': d[10], - 'tests_administered': d[11], - 'last_updated': d[0] - } for d in c.fetchall()] - return data - - -def drop_by_date(date): - db_conn = sqlite3.connect('data/data.sqlite3') - c = db_conn.cursor() - sql = f'DELETE FROM `alertlevel` WHERE time=\'{date}\';' - c.execute(sql) - sql = f'DELETE FROM `total` WHERE time=\'{date}\';' - c.execute(sql) - sql = f'DELETE FROM `new` WHERE time=\'{date}\';' - c.execute(sql) - sql = f'DELETE FROM `quarantine` WHERE time=\'{date}\';' - c.execute(sql) - sql = f'DELETE FROM `isolation` WHERE time=\'{date}\';' - c.execute(sql) - sql = f'DELETE FROM `beds` WHERE time=\'{date}\';' - c.execute(sql) - sql = f'DELETE FROM `tests` WHERE time=\'{date}\';' - c.execute(sql) - db_conn.commit() - db_conn.close() - - -def dedup(): - data = get_all_from_db() - # get first date - starting_date = data[-1]['last_updated'].split(' ')[0] - for i in range(len(data)-2, 0, -1): - if data[i]['last_updated'].split(' ')[0] != starting_date: - starting_date = data[i]['last_updated'].split(' ')[0] - else: - drop_by_date(data[i]['last_updated']) - print('dropped ' + data[i]['last_updated']) -- cgit v1.2.3 From b53bfac995c843f3595a816878a14fbfc0fbfef0 Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 16:26:33 -0500 Subject: Update history.json --- history/history.json | 2984 +++++++++++++++++++++++++++----------------------- 1 file changed, 1625 insertions(+), 1359 deletions(-) diff --git a/history/history.json b/history/history.json index 3e3e75b..04ef64f 100644 --- a/history/history.json +++ b/history/history.json @@ -1,1360 +1,1626 @@ [ - { - "alert_level": "green", - "beds_available": 95, - "isolation_off_campus": 4, - "isolation_on_campus": 3, - "last_updated": "2020-08-27 16:00:00", - "new_staff": 1, - "new_students": 2, - "quarantine_off_campus": 4, - "quarantine_on_campus": 3, - "tests_administered": 0, - "total_staff": 1, - "total_students": 3 - }, - { - "alert_level": "green", - "beds_available": 96, - "isolation_off_campus": 3, - "isolation_on_campus": 0, - "last_updated": "2020-08-28 16:00:00", - "new_staff": 1, - "new_students": 2, - "quarantine_off_campus": 7, - "quarantine_on_campus": 3, - "tests_administered": 0, - "total_staff": 1, - "total_students": 3 - }, - { - "alert_level": "green", - "beds_available": 95, - "isolation_off_campus": 1, - "isolation_on_campus": 0, - "last_updated": "2020-08-31 16:00:00", - "new_staff": 0, - "new_students": 1, - "quarantine_off_campus": 3, - "quarantine_on_campus": 4, - "tests_administered": 0, - "total_staff": 1, - "total_students": 3 - }, - { - "alert_level": "green", - "beds_available": 92, - "isolation_off_campus": 0, - "isolation_on_campus": 2, - "last_updated": "2020-09-01 16:00:00", - "new_staff": 0, - "new_students": 0, - "quarantine_off_campus": 2, - "quarantine_on_campus": 7, - "tests_administered": 0, - "total_staff": 1, - "total_students": 3 - }, - { - "alert_level": "green", - "beds_available": 93, - "isolation_off_campus": 0, - "isolation_on_campus": 3, - "last_updated": "2020-09-02 16:00:00", - "new_staff": 0, - "new_students": 0, - "quarantine_off_campus": 2, - "quarantine_on_campus": 6, - "tests_administered": 0, - "total_staff": 1, - "total_students": 3 - }, - { - "alert_level": "green", - "beds_available": 92, - "isolation_off_campus": 0, - "isolation_on_campus": 3, - "last_updated": "2020-09-03 16:00:00", - "new_staff": 0, - "new_students": 0, - "quarantine_off_campus": 3, - "quarantine_on_campus": 11, - "tests_administered": 0, - "total_staff": 1, - "total_students": 3 - }, - { - "alert_level": "green", - "beds_available": 96, - "isolation_off_campus": 1, - "isolation_on_campus": 0, - "last_updated": "2020-09-04 16:00:00", - "new_staff": 1, - "new_students": 3, - "quarantine_off_campus": 5, - "quarantine_on_campus": 1, - "tests_administered": 0, - "total_staff": 1, - "total_students": 4 - }, - { - "alert_level": "green", - "beds_available": 97, - "isolation_off_campus": 1, - "isolation_on_campus": 0, - "last_updated": "2020-09-06 16:00:00", - "new_staff": 0, - "new_students": 2, - "quarantine_off_campus": 5, - "quarantine_on_campus": 3, - "tests_administered": 0, - "total_staff": 1, - "total_students": 5 - }, - { - "alert_level": "green", - "beds_available": 95, - "isolation_off_campus": 6, - "isolation_on_campus": 1, - "last_updated": "2020-09-07 16:00:00", - "new_staff": 0, - "new_students": 3, - "quarantine_off_campus": 17, - "quarantine_on_campus": 5, - "tests_administered": 0, - "total_staff": 1, - "total_students": 6 - }, - { - "alert_level": "green", - "beds_available": 98, - "isolation_off_campus": 3, - "isolation_on_campus": 0, - "last_updated": "2020-09-10 16:00:00", - "new_staff": 0, - "new_students": 3, - "quarantine_off_campus": 17, - "quarantine_on_campus": 1, - "tests_administered": 0, - "total_staff": 1, - "total_students": 6 - }, - { - "alert_level": "green", - "beds_available": 96, - "isolation_off_campus": 4, - "isolation_on_campus": 2, - "last_updated": "2020-09-11 16:00:00", - "new_staff": 0, - "new_students": 3, - "quarantine_off_campus": 17, - "quarantine_on_campus": 1, - "tests_administered": 0, - "total_staff": 1, - "total_students": 6 - }, - { - "alert_level": "green", - "beds_available": 95, - "isolation_off_campus": 1, - "isolation_on_campus": 2, - "last_updated": "2020-09-14 16:00:00", - "new_staff": 0, - "new_students": 4, - "quarantine_off_campus": 16, - "quarantine_on_campus": 4, - "tests_administered": 0, - "total_staff": 1, - "total_students": 6 - }, - { - "alert_level": "green", - "beds_available": 92, - "isolation_off_campus": 2, - "isolation_on_campus": 3, - "last_updated": "2020-09-15 16:00:00", - "new_staff": 0, - "new_students": 4, - "quarantine_off_campus": 19, - "quarantine_on_campus": 9, - "tests_administered": 0, - "total_staff": 1, - "total_students": 7 - }, - { - "alert_level": "green", - "beds_available": 90, - "isolation_off_campus": 2, - "isolation_on_campus": 3, - "last_updated": "2020-09-16 16:00:00", - "new_staff": 0, - "new_students": 5, - "quarantine_off_campus": 19, - "quarantine_on_campus": 12, - "tests_administered": 0, - "total_staff": 1, - "total_students": 8 - }, - { - "alert_level": "green", - "beds_available": 88, - "isolation_off_campus": 1, - "isolation_on_campus": 2, - "last_updated": "2020-09-17 16:00:00", - "new_staff": 1, - "new_students": 4, - "quarantine_off_campus": 18, - "quarantine_on_campus": 16, - "tests_administered": 0, - "total_staff": 2, - "total_students": 8 - }, - { - "alert_level": "green", - "beds_available": 89, - "isolation_off_campus": 1, - "isolation_on_campus": 2, - "last_updated": "2020-09-18 16:00:00", - "new_staff": 1, - "new_students": 4, - "quarantine_off_campus": 16, - "quarantine_on_campus": 14, - "tests_administered": 0, - "total_staff": 2, - "total_students": 9 - }, - { - "alert_level": "green", - "beds_available": 94, - "isolation_off_campus": 1, - "isolation_on_campus": 2, - "last_updated": "2020-09-21 16:00:00", - "new_staff": 1, - "new_students": 4, - "quarantine_off_campus": 14, - "quarantine_on_campus": 8, - "tests_administered": 0, - "total_staff": 2, - "total_students": 10 - }, - { - "alert_level": "green", - "beds_available": 91, - "isolation_off_campus": 1, - "isolation_on_campus": 4, - "last_updated": "2020-09-22 16:00:00", - "new_staff": 1, - "new_students": 4, - "quarantine_off_campus": 9, - "quarantine_on_campus": 10, - "tests_administered": 0, - "total_staff": 2, - "total_students": 10 - }, - { - "alert_level": "green", - "beds_available": 88, - "isolation_off_campus": 1, - "isolation_on_campus": 5, - "last_updated": "2020-09-24 16:00:00", - "new_staff": 1, - "new_students": 4, - "quarantine_off_campus": 8, - "quarantine_on_campus": 15, - "tests_administered": 0, - "total_staff": 2, - "total_students": 10 - }, - { - "alert_level": "green", - "beds_available": 92, - "isolation_off_campus": 0, - "isolation_on_campus": 2, - "last_updated": "2020-09-25 16:00:00", - "new_staff": 1, - "new_students": 4, - "quarantine_off_campus": 12, - "quarantine_on_campus": 10, - "tests_administered": 3747, - "total_staff": 2, - "total_students": 10 - }, - { - "alert_level": "green", - "beds_available": 98, - "isolation_off_campus": 0, - "isolation_on_campus": 1, - "last_updated": "2020-09-28 16:00:00", - "new_staff": 1, - "new_students": 2, - "quarantine_off_campus": 8, - "quarantine_on_campus": 1, - "tests_administered": 3751, - "total_staff": 2, - "total_students": 10 - }, - { - "alert_level": "green", - "beds_available": 98, - "isolation_off_campus": 0, - "isolation_on_campus": 2, - "last_updated": "2020-09-29 16:00:00", - "new_staff": 1, - "new_students": 2, - "quarantine_off_campus": 9, - "quarantine_on_campus": 0, - "tests_administered": 3753, - "total_staff": 2, - "total_students": 10 - }, - { - "alert_level": "green", - "beds_available": 99, - "isolation_off_campus": 2, - "isolation_on_campus": 1, - "last_updated": "2020-10-01 16:00:00", - "new_staff": 0, - "new_students": 0, - "quarantine_off_campus": 8, - "quarantine_on_campus": 0, - "tests_administered": 4038, - "total_staff": 2, - "total_students": 10 - }, - { - "alert_level": "green", - "beds_available": 98, - "isolation_off_campus": 0, - "isolation_on_campus": 1, - "last_updated": "2020-10-02 16:00:00", - "new_staff": 0, - "new_students": 0, - "quarantine_off_campus": 6, - "quarantine_on_campus": 1, - "tests_administered": 4135, - "total_staff": 2, - "total_students": 10 - }, - { - "alert_level": "green", - "beds_available": 97, - "isolation_off_campus": 0, - "isolation_on_campus": 1, - "last_updated": "2020-10-05 16:00:00", - "new_staff": 0, - "new_students": 1, - "quarantine_off_campus": 1, - "quarantine_on_campus": 3, - "tests_administered": 4136, - "total_staff": 2, - "total_students": 10 - }, - { - "alert_level": "green", - "beds_available": 90, - "isolation_off_campus": 0, - "isolation_on_campus": 5, - "last_updated": "2020-10-06 16:00:00", - "new_staff": 0, - "new_students": 1, - "quarantine_off_campus": 5, - "quarantine_on_campus": 11, - "tests_administered": 4139, - "total_staff": 2, - "total_students": 10 - }, - { - "alert_level": "yellow", - "beds_available": 93, - "isolation_off_campus": 1, - "isolation_on_campus": 4, - "last_updated": "2020-10-08 16:00:00", - "new_staff": 0, - "new_students": 3, - "quarantine_off_campus": 10, - "quarantine_on_campus": 7, - "tests_administered": 4482, - "total_staff": 2, - "total_students": 12 - }, - { - "alert_level": "yellow", - "beds_available": 92, - "isolation_off_campus": 0, - "isolation_on_campus": 4, - "last_updated": "2020-10-09 16:00:00", - "new_staff": 0, - "new_students": 3, - "quarantine_off_campus": 14, - "quarantine_on_campus": 8, - "tests_administered": 4625, - "total_staff": 2, - "total_students": 12 - }, - { - "alert_level": "yellow", - "beds_available": 93, - "isolation_off_campus": 4, - "isolation_on_campus": 3, - "last_updated": "2020-10-12 16:00:00", - "new_staff": 0, - "new_students": 4, - "quarantine_off_campus": 20, - "quarantine_on_campus": 8, - "tests_administered": 5053, - "total_staff": 2, - "total_students": 13 - }, - { - "alert_level": "yellow", - "beds_available": 92, - "isolation_off_campus": 2, - "isolation_on_campus": 4, - "last_updated": "2020-10-13 16:00:00", - "new_staff": 0, - "new_students": 4, - "quarantine_off_campus": 24, - "quarantine_on_campus": 9, - "tests_administered": 5055, - "total_staff": 2, - "total_students": 13 - }, - { - "alert_level": "yellow", - "beds_available": 93, - "isolation_off_campus": 4, - "isolation_on_campus": 4, - "last_updated": "2020-10-15 16:00:00", - "new_staff": 0, - "new_students": 5, - "quarantine_off_campus": 27, - "quarantine_on_campus": 8, - "tests_administered": 5471, - "total_staff": 2, - "total_students": 14 - }, - { - "alert_level": "yellow", - "beds_available": 96, - "isolation_off_campus": 5, - "isolation_on_campus": 1, - "last_updated": "2020-10-16 16:00:00", - "new_staff": 0, - "new_students": 6, - "quarantine_off_campus": 25, - "quarantine_on_campus": 5, - "tests_administered": 5590, - "total_staff": 2, - "total_students": 15 - }, - { - "alert_level": "yellow", - "beds_available": 95, - "isolation_off_campus": 4, - "isolation_on_campus": 3, - "last_updated": "2020-10-19 16:00:00", - "new_staff": 0, - "new_students": 6, - "quarantine_off_campus": 23, - "quarantine_on_campus": 6, - "tests_administered": 5591, - "total_staff": 2, - "total_students": 16 - }, - { - "alert_level": "yellow", - "beds_available": 92, - "isolation_off_campus": 6, - "isolation_on_campus": 3, - "last_updated": "2020-10-20 16:00:00", - "new_staff": 0, - "new_students": 7, - "quarantine_off_campus": 29, - "quarantine_on_campus": 11, - "tests_administered": 5598, - "total_staff": 2, - "total_students": 17 - }, - { - "alert_level": "yellow", - "beds_available": 92, - "isolation_off_campus": 5, - "isolation_on_campus": 3, - "last_updated": "2020-10-21 16:00:00", - "new_staff": 0, - "new_students": 6, - "quarantine_off_campus": 32, - "quarantine_on_campus": 11, - "tests_administered": 5887, - "total_staff": 2, - "total_students": 17 - }, - { - "alert_level": "yellow", - "beds_available": 95, - "isolation_off_campus": 5, - "isolation_on_campus": 1, - "last_updated": "2020-10-23 16:00:00", - "new_staff": 0, - "new_students": 7, - "quarantine_off_campus": 33, - "quarantine_on_campus": 8, - "tests_administered": 6141, - "total_staff": 2, - "total_students": 19 - }, - { - "alert_level": "yellow", - "beds_available": 90, - "isolation_off_campus": 4, - "isolation_on_campus": 2, - "last_updated": "2020-10-26 16:00:00", - "new_staff": 0, - "new_students": 8, - "quarantine_off_campus": 35, - "quarantine_on_campus": 16, - "tests_administered": 6146, - "total_staff": 2, - "total_students": 21 - }, - { - "alert_level": "yellow", - "beds_available": 91, - "isolation_off_campus": 4, - "isolation_on_campus": 2, - "last_updated": "2020-10-27 16:00:00", - "new_staff": 0, - "new_students": 9, - "quarantine_off_campus": 36, - "quarantine_on_campus": 14, - "tests_administered": 6150, - "total_staff": 2, - "total_students": 22 - }, - { - "alert_level": "yellow", - "beds_available": 92, - "isolation_off_campus": 4, - "isolation_on_campus": 3, - "last_updated": "2020-10-28 16:00:00", - "new_staff": 1, - "new_students": 10, - "quarantine_off_campus": 34, - "quarantine_on_campus": 12, - "tests_administered": 6392, - "total_staff": 3, - "total_students": 23 - }, - { - "alert_level": "yellow", - "beds_available": 91, - "isolation_off_campus": 9, - "isolation_on_campus": 3, - "last_updated": "2020-10-29 16:00:00", - "new_staff": 2, - "new_students": 10, - "quarantine_off_campus": 31, - "quarantine_on_campus": 13, - "tests_administered": 6490, - "total_staff": 4, - "total_students": 24 - }, - { - "alert_level": "yellow", - "beds_available": 86, - "isolation_off_campus": 9, - "isolation_on_campus": 6, - "last_updated": "2020-10-30 16:00:00", - "new_staff": 2, - "new_students": 17, - "quarantine_off_campus": 27, - "quarantine_on_campus": 18, - "tests_administered": 6638, - "total_staff": 4, - "total_students": 32 - }, - { - "alert_level": "yellow", - "beds_available": 87, - "isolation_off_campus": 10, - "isolation_on_campus": 5, - "last_updated": "2020-11-02 19:27:39", - "new_staff": 3, - "new_students": 19, - "quarantine_off_campus": 30, - "quarantine_on_campus": 17, - "tests_administered": 7318, - "total_staff": 5, - "total_students": 36 - }, - { - "alert_level": "yellow", - "beds_available": 83, - "isolation_off_campus": 8, - "isolation_on_campus": 7, - "last_updated": "2020-11-03 19:47:40", - "new_staff": 3, - "new_students": 20, - "quarantine_off_campus": 34, - "quarantine_on_campus": 23, - "tests_administered": 7391, - "total_staff": 5, - "total_students": 37 - }, - { - "alert_level": "yellow", - "beds_available": 78, - "isolation_off_campus": 8, - "isolation_on_campus": 10, - "last_updated": "2020-11-04 20:05:45", - "new_staff": 5, - "new_students": 20, - "quarantine_off_campus": 35, - "quarantine_on_campus": 27, - "tests_administered": 7514, - "total_staff": 7, - "total_students": 38 - }, - { - "alert_level": "yellow", - "beds_available": 81, - "isolation_off_campus": 14, - "isolation_on_campus": 8, - "last_updated": "2020-11-05 19:36:46", - "new_staff": 5, - "new_students": 21, - "quarantine_off_campus": 41, - "quarantine_on_campus": 25, - "tests_administered": 7794, - "total_staff": 7, - "total_students": 40 - }, - { - "alert_level": "yellow", - "beds_available": 80, - "isolation_off_campus": 12, - "isolation_on_campus": 7, - "last_updated": "2020-11-06 20:30:25", - "new_staff": 5, - "new_students": 24, - "quarantine_off_campus": 42, - "quarantine_on_campus": 26, - "tests_administered": 8278, - "total_staff": 7, - "total_students": 43 - }, - { - "alert_level": "orange", - "beds_available": 57, - "isolation_off_campus": 11, - "isolation_on_campus": 10, - "last_updated": "2020-11-09 14:40:26", - "new_staff": 5, - "new_students": 29, - "quarantine_off_campus": 59, - "quarantine_on_campus": 63, - "tests_administered": 8622, - "total_staff": 7, - "total_students": 51 - }, - { - "alert_level": "orange", - "beds_available": 61, - "isolation_off_campus": 11, - "isolation_on_campus": 8, - "last_updated": "2020-11-10 21:02:02", - "new_staff": 3, - "new_students": 33, - "quarantine_off_campus": 66, - "quarantine_on_campus": 57, - "tests_administered": 8712, - "total_staff": 7, - "total_students": 55 - }, - { - "alert_level": "orange", - "beds_available": 56, - "isolation_off_campus": 17, - "isolation_on_campus": 11, - "last_updated": "2020-11-11 18:38:08", - "new_staff": 4, - "new_students": 34, - "quarantine_off_campus": 66, - "quarantine_on_campus": 63, - "tests_administered": 8732, - "total_staff": 8, - "total_students": 57 - }, - { - "alert_level": "orange", - "beds_available": 55, - "isolation_off_campus": 20, - "isolation_on_campus": 11, - "last_updated": "2020-11-12 19:43:09", - "new_staff": 5, - "new_students": 33, - "quarantine_off_campus": 67, - "quarantine_on_campus": 63, - "tests_administered": 8819, - "total_staff": 9, - "total_students": 60 - }, - { - "alert_level": "orange", - "beds_available": 57, - "isolation_off_campus": 15, - "isolation_on_campus": 12, - "last_updated": "2020-11-13 20:28:09", - "new_staff": 5, - "new_students": 30, - "quarantine_off_campus": 72, - "quarantine_on_campus": 60, - "tests_administered": 8969, - "total_staff": 9, - "total_students": 63 - }, - { - "alert_level": "orange", - "beds_available": 77, - "isolation_off_campus": 8, - "isolation_on_campus": 9, - "last_updated": "2020-11-16 19:35:59", - "new_staff": 5, - "new_students": 29, - "quarantine_off_campus": 67, - "quarantine_on_campus": 30, - "tests_administered": 9009, - "total_staff": 9, - "total_students": 65 - }, - { - "alert_level": "orange", - "beds_available": 81, - "isolation_off_campus": 9, - "isolation_on_campus": 6, - "last_updated": "2020-11-17 18:55:59", - "new_staff": 7, - "new_students": 27, - "quarantine_off_campus": 63, - "quarantine_on_campus": 27, - "tests_administered": 9383, - "total_staff": 13, - "total_students": 65 - }, - { - "alert_level": "orange", - "beds_available": 83, - "isolation_off_campus": 16, - "isolation_on_campus": 7, - "last_updated": "2020-11-18 19:00:59", - "new_staff": 6, - "new_students": 31, - "quarantine_off_campus": 63, - "quarantine_on_campus": 23, - "tests_administered": 9959, - "total_staff": 13, - "total_students": 69 - }, - { - "alert_level": "orange", - "beds_available": 83, - "isolation_off_campus": 22, - "isolation_on_campus": 9, - "last_updated": "2020-11-19 21:05:59", - "new_staff": 8, - "new_students": 35, - "quarantine_off_campus": 70, - "quarantine_on_campus": 33, - "tests_administered": 9959, - "total_staff": 15, - "total_students": 77 - }, - { - "alert_level": "orange", - "beds_available": 75, - "isolation_off_campus": 22, - "isolation_on_campus": 6, - "last_updated": "2020-11-20 22:20:05", - "new_staff": 8, - "new_students": 35, - "quarantine_off_campus": 70, - "quarantine_on_campus": 36, - "tests_administered": 11963, - "total_staff": 15, - "total_students": 79 - }, - { - "alert_level": "orange", - "beds_available": 81, - "isolation_off_campus": 21, - "isolation_on_campus": 6, - "last_updated": "2020-11-23 18:09:50", - "new_staff": 10, - "new_students": 36, - "quarantine_off_campus": 68, - "quarantine_on_campus": 28, - "tests_administered": 12041, - "total_staff": 18, - "total_students": 89 - }, - { - "alert_level": "orange", - "beds_available": 85, - "isolation_off_campus": 22, - "isolation_on_campus": 4, - "last_updated": "2020-11-24 19:54:50", - "new_staff": 12, - "new_students": 34, - "quarantine_off_campus": 61, - "quarantine_on_campus": 21, - "tests_administered": 12147, - "total_staff": 20, - "total_students": 90 - }, - { - "alert_level": "orange", - "beds_available": 88, - "isolation_off_campus": 24, - "isolation_on_campus": 4, - "last_updated": "2020-11-25 18:59:50", - "new_staff": 11, - "new_students": 35, - "quarantine_off_campus": 65, - "quarantine_on_campus": 16, - "tests_administered": 12218, - "total_staff": 20, - "total_students": 93 - }, - { - "alert_level": "orange", - "beds_available": 96, - "isolation_off_campus": 11, - "isolation_on_campus": 2, - "last_updated": "2020-11-30 19:29:52", - "new_staff": 12, - "new_students": 31, - "quarantine_off_campus": 64, - "quarantine_on_campus": 4, - "tests_administered": 12219, - "total_staff": 24, - "total_students": 96 - }, - { - "alert_level": "orange", - "beds_available": 96, - "isolation_off_campus": 8, - "isolation_on_campus": 2, - "last_updated": "2020-12-01 19:14:51", - "new_staff": 11, - "new_students": 29, - "quarantine_off_campus": 61, - "quarantine_on_campus": 4, - "tests_administered": 12289, - "total_staff": 26, - "total_students": 97 - }, - { - "alert_level": "orange", - "beds_available": 98, - "isolation_off_campus": 12, - "isolation_on_campus": 1, - "last_updated": "2020-12-02 20:04:51", - "new_staff": 11, - "new_students": 28, - "quarantine_off_campus": 41, - "quarantine_on_campus": 2, - "tests_administered": 12290, - "total_staff": 28, - "total_students": 99 - }, - { - "alert_level": "orange", - "beds_available": 98, - "isolation_off_campus": 16, - "isolation_on_campus": 2, - "last_updated": "2020-12-03 17:59:51", - "new_staff": 11, - "new_students": 23, - "quarantine_off_campus": 40, - "quarantine_on_campus": 2, - "tests_administered": 12364, - "total_staff": 28, - "total_students": 103 - }, - { - "alert_level": "orange", - "beds_available": 99, - "isolation_off_campus": 19, - "isolation_on_campus": 1, - "last_updated": "2020-12-04 19:24:52", - "new_staff": 11, - "new_students": 22, - "quarantine_off_campus": 45, - "quarantine_on_campus": 1, - "tests_administered": 12364, - "total_staff": 30, - "total_students": 105 - }, - { - "alert_level": "orange", - "beds_available": 99, - "isolation_off_campus": 17, - "isolation_on_campus": 1, - "last_updated": "2020-12-07 20:04:52", - "new_staff": 12, - "new_students": 19, - "quarantine_off_campus": 46, - "quarantine_on_campus": 0, - "tests_administered": 12364, - "total_staff": 30, - "total_students": 109 - }, - { - "alert_level": "orange", - "beds_available": 99, - "isolation_off_campus": 20, - "isolation_on_campus": 0, - "last_updated": "2020-12-08 19:09:52", - "new_staff": 13, - "new_students": 20, - "quarantine_off_campus": 48, - "quarantine_on_campus": 1, - "tests_administered": 12428, - "total_staff": 35, - "total_students": 110 - }, - { - "alert_level": "orange", - "beds_available": 99, - "isolation_off_campus": 23, - "isolation_on_campus": 0, - "last_updated": "2020-12-09 21:24:52", - "new_staff": 15, - "new_students": 16, - "quarantine_off_campus": 46, - "quarantine_on_campus": 1, - "tests_administered": 12432, - "total_staff": 37, - "total_students": 110 - }, - { - "alert_level": "orange", - "beds_available": 99, - "isolation_off_campus": 24, - "isolation_on_campus": 0, - "last_updated": "2020-12-10 18:19:52", - "new_staff": 13, - "new_students": 17, - "quarantine_off_campus": 44, - "quarantine_on_campus": 1, - "tests_administered": 12500, - "total_staff": 37, - "total_students": 111 - }, - { - "alert_level": "orange", - "beds_available": 99, - "isolation_off_campus": 19, - "isolation_on_campus": 0, - "last_updated": "2020-12-11 19:29:52", - "new_staff": 14, - "new_students": 19, - "quarantine_off_campus": 43, - "quarantine_on_campus": 1, - "tests_administered": 12500, - "total_staff": 39, - "total_students": 113 - }, - { - "alert_level": "orange", - "beds_available": 99, - "isolation_off_campus": 13, - "isolation_on_campus": 1, - "last_updated": "2020-12-14 20:44:53", - "new_staff": 13, - "new_students": 20, - "quarantine_off_campus": 30, - "quarantine_on_campus": 0, - "tests_administered": 12553, - "total_staff": 43, - "total_students": 117 - }, - { - "alert_level": "orange", - "beds_available": 99, - "isolation_off_campus": 12, - "isolation_on_campus": 1, - "last_updated": "2020-12-15 19:34:56", - "new_staff": 14, - "new_students": 21, - "quarantine_off_campus": 30, - "quarantine_on_campus": 0, - "tests_administered": 12593, - "total_staff": 45, - "total_students": 119 - }, - { - "alert_level": "orange", - "beds_available": 99, - "isolation_off_campus": 16, - "isolation_on_campus": 1, - "last_updated": "2020-12-16 20:34:53", - "new_staff": 16, - "new_students": 24, - "quarantine_off_campus": 28, - "quarantine_on_campus": 0, - "tests_administered": 12629, - "total_staff": 49, - "total_students": 124 - }, - { - "alert_level": "orange", - "beds_available": 100, - "isolation_off_campus": 13, - "isolation_on_campus": 1, - "last_updated": "2020-12-17 18:34:53", - "new_staff": 18, - "new_students": 22, - "quarantine_off_campus": 25, - "quarantine_on_campus": 0, - "tests_administered": 12630, - "total_staff": 52, - "total_students": 126 - }, - { - "alert_level": "orange", - "beds_available": 98, - "isolation_off_campus": 12, - "isolation_on_campus": 3, - "last_updated": "2020-12-18 20:09:53", - "new_staff": 18, - "new_students": 22, - "quarantine_off_campus": 23, - "quarantine_on_campus": 1, - "tests_administered": 12670, - "total_staff": 54, - "total_students": 127 - }, - { - "alert_level": "orange", - "beds_available": 100, - "isolation_off_campus": 9, - "isolation_on_campus": 1, - "last_updated": "2020-12-21 21:00:25", - "new_staff": 17, - "new_students": 17, - "quarantine_off_campus": 12, - "quarantine_on_campus": 1, - "tests_administered": 12706, - "total_staff": 54, - "total_students": 127 - }, - { - "alert_level": "orange", - "beds_available": 100, - "isolation_off_campus": 7, - "isolation_on_campus": 1, - "last_updated": "2020-12-22 19:20:26", - "new_staff": 14, - "new_students": 16, - "quarantine_off_campus": 9, - "quarantine_on_campus": 1, - "tests_administered": 12706, - "total_staff": 52, - "total_students": 126 - }, - { - "alert_level": "orange", - "beds_available": 100, - "isolation_off_campus": 6, - "isolation_on_campus": 1, - "last_updated": "2020-12-23 21:10:25", - "new_staff": 17, - "new_students": 18, - "quarantine_off_campus": 7, - "quarantine_on_campus": 1, - "tests_administered": 12746, - "total_staff": 55, - "total_students": 128 - }, - { - "alert_level": "orange", - "beds_available": 100, - "isolation_off_campus": 2, - "isolation_on_campus": 0, - "last_updated": "2021-01-04 21:30:27", - "new_staff": 11, - "new_students": 6, - "quarantine_off_campus": 4, - "quarantine_on_campus": 0, - "tests_administered": 12939, - "total_staff": 64, - "total_students": 132 - }, - { - "alert_level": "orange", - "beds_available": 96, - "isolation_off_campus": 4, - "isolation_on_campus": 0, - "last_updated": "2021-01-05 20:56:42", - "new_staff": 13, - "new_students": 5, - "quarantine_off_campus": 6, - "quarantine_on_campus": 0, - "tests_administered": 13002, - "total_staff": 70, - "total_students": 133 - }, - { - "alert_level": "orange", - "beds_available": 96, - "isolation_off_campus": 5, - "isolation_on_campus": 0, - "last_updated": "2021-01-06 20:51:42", - "new_staff": 15, - "new_students": 5, - "quarantine_off_campus": 7, - "quarantine_on_campus": 0, - "tests_administered": 13002, - "total_staff": 73, - "total_students": 134 - }, - { - "alert_level": "orange", - "beds_available": 96, - "isolation_off_campus": 5, - "isolation_on_campus": 0, - "last_updated": "2021-01-07 20:41:49", - "new_staff": 18, - "new_students": 4, - "quarantine_off_campus": 9, - "quarantine_on_campus": 0, - "tests_administered": 13064, - "total_staff": 77, - "total_students": 134 - }, - { - "alert_level": "orange", - "beds_available": 99, - "isolation_off_campus": 5, - "isolation_on_campus": 0, - "last_updated": "2021-01-08 20:41:42", - "new_staff": 19, - "new_students": 4, - "quarantine_off_campus": 10, - "quarantine_on_campus": 1, - "tests_administered": 13064, - "total_staff": 80, - "total_students": 134 - }, - { - "alert_level": "orange", - "beds_available": 95, - "isolation_off_campus": 6, - "isolation_on_campus": 1, - "last_updated": "2021-01-11 20:40:10", - "new_staff": 29, - "new_students": 6, - "quarantine_off_campus": 10, - "quarantine_on_campus": 1, - "tests_administered": 13129, - "total_staff": 94, - "total_students": 136 - }, - { - "alert_level": "orange", - "beds_available": 96, - "isolation_off_campus": 9, - "isolation_on_campus": 1, - "last_updated": "2021-01-12 20:50:10", - "new_staff": 33, - "new_students": 9, - "quarantine_off_campus": 15, - "quarantine_on_campus": 1, - "tests_administered": 13193, - "total_staff": 100, - "total_students": 139 - }, - { - "alert_level": "orange", - "beds_available": 96, - "isolation_off_campus": 10, - "isolation_on_campus": 1, - "last_updated": "2021-01-13 20:35:11", - "new_staff": 39, - "new_students": 8, - "quarantine_off_campus": 31, - "quarantine_on_campus": 5, - "tests_administered": 13193, - "total_staff": 110, - "total_students": 139 - }, - { - "alert_level": "orange", - "beds_available": 95, - "isolation_off_campus": 14, - "isolation_on_campus": 3, - "last_updated": "2021-01-14 21:35:10", - "new_staff": 40, - "new_students": 9, - "quarantine_off_campus": 28, - "quarantine_on_campus": 6, - "tests_administered": 13257, - "total_staff": 115, - "total_students": 141 - }, - { - "alert_level": "orange", - "beds_available": 95, - "isolation_off_campus": 19, - "isolation_on_campus": 3, - "last_updated": "2021-01-15 20:35:10", - "new_staff": 40, - "new_students": 11, - "quarantine_off_campus": 31, - "quarantine_on_campus": 6, - "tests_administered": 13257, - "total_staff": 118, - "total_students": 143 - }, - { - "alert_level": "orange", - "beds_available": 95, - "isolation_off_campus": 17, - "isolation_on_campus": 3, - "last_updated": "2021-01-18 20:59:05", - "new_staff": 42, - "new_students": 17, - "quarantine_off_campus": 24, - "quarantine_on_campus": 6, - "tests_administered": 13401, - "total_staff": 125, - "total_students": 150 - }, - { - "alert_level": "orange", - "beds_available": 95, - "isolation_off_campus": 24, - "isolation_on_campus": 3, - "last_updated": "2021-01-19 20:49:05", - "new_staff": 48, - "new_students": 17, - "quarantine_off_campus": 35, - "quarantine_on_campus": 6, - "tests_administered": 13456, - "total_staff": 133, - "total_students": 150 - }, - { - "alert_level": "orange", - "beds_available": 92, - "isolation_off_campus": 23, - "isolation_on_campus": 3, - "last_updated": "2021-01-20 20:39:07", - "new_staff": 50, - "new_students": 17, - "quarantine_off_campus": 48, - "quarantine_on_campus": 11, - "tests_administered": 13457, - "total_staff": 136, - "total_students": 151 - }, - { - "alert_level": "orange", - "beds_available": 92, - "isolation_off_campus": 22, - "isolation_on_campus": 5, - "last_updated": "2021-01-21 20:44:06", - "new_staff": 55, - "new_students": 21, - "quarantine_off_campus": 44, - "quarantine_on_campus": 6, - "tests_administered": 13531, - "total_staff": 143, - "total_students": 156 - }, - { - "alert_level": "yellow", - "beds_available": 90, - "isolation_off_campus": 28, - "isolation_on_campus": 5, - "last_updated": "2021-01-22 21:04:06", - "new_staff": 56, - "new_students": 22, - "quarantine_off_campus": 47, - "quarantine_on_campus": 10, - "tests_administered": 13782, - "total_staff": 147, - "total_students": 158 - }, - { - "alert_level": "yellow", - "beds_available": 93, - "isolation_off_campus": 33, - "isolation_on_campus": 10, - "last_updated": "2021-01-25 16:00:00", - "new_staff": 55, - "new_students": 30, - "quarantine_off_campus": 54, - "quarantine_on_campus": 9, - "tests_administered": 13843, - "total_staff": 149, - "total_students": 166 - }, - { - "alert_level": "yellow", - "beds_available": 92, - "isolation_off_campus": 35, - "isolation_on_campus": 10, - "last_updated": "2021-01-26 16:00:00", - "new_staff": 51, - "new_students": 32, - "quarantine_off_campus": 64, - "quarantine_on_campus": 13, - "tests_administered": 13902, - "total_staff": 151, - "total_students": 171 - }, - { - "alert_level": "yellow", - "beds_available": 91, - "isolation_off_campus": 35, - "isolation_on_campus": 13, - "last_updated": "2021-01-27 21:11:47", - "new_staff": 39, - "new_students": 27, - "quarantine_off_campus": 66, - "quarantine_on_campus": 16, - "tests_administered": 13842, - "total_staff": 149, - "total_students": 166 - }, - { - "alert_level": "yellow", - "beds_available": 89, - "isolation_off_campus": 30, - "isolation_on_campus": 17, - "last_updated": "2021-01-28 20:46:47", - "new_staff": 37, - "new_students": 29, - "quarantine_off_campus": 64, - "quarantine_on_campus": 18, - "tests_administered": 15303, - "total_staff": 152, - "total_students": 170 - }, - { - "alert_level": "yellow", - "beds_available": 89, - "isolation_off_campus": 28, - "isolation_on_campus": 16, - "last_updated": "2021-01-29 20:46:47", - "new_staff": 34, - "new_students": 34, - "quarantine_off_campus": 56, - "quarantine_on_campus": 21, - "tests_administered": 17121, - "total_staff": 152, - "total_students": 177 - } -] \ No newline at end of file + { + "alert_level": "green", + "beds_available": 95, + "isolation_off_campus": 4, + "isolation_on_campus": 3, + "last_updated": "2020-08-27 16:00:00", + "new_staff": 1, + "new_students": 2, + "quarantine_off_campus": 4, + "quarantine_on_campus": 3, + "tests_administered": 0, + "total_staff": 1, + "total_students": 3 + }, + { + "alert_level": "green", + "beds_available": 96, + "isolation_off_campus": 3, + "isolation_on_campus": 0, + "last_updated": "2020-08-28 16:00:00", + "new_staff": 1, + "new_students": 2, + "quarantine_off_campus": 7, + "quarantine_on_campus": 3, + "tests_administered": 0, + "total_staff": 1, + "total_students": 3 + }, + { + "alert_level": "green", + "beds_available": 95, + "isolation_off_campus": 1, + "isolation_on_campus": 0, + "last_updated": "2020-08-31 16:00:00", + "new_staff": 0, + "new_students": 1, + "quarantine_off_campus": 3, + "quarantine_on_campus": 4, + "tests_administered": 0, + "total_staff": 1, + "total_students": 3 + }, + { + "alert_level": "green", + "beds_available": 92, + "isolation_off_campus": 0, + "isolation_on_campus": 2, + "last_updated": "2020-09-01 16:00:00", + "new_staff": 0, + "new_students": 0, + "quarantine_off_campus": 2, + "quarantine_on_campus": 7, + "tests_administered": 0, + "total_staff": 1, + "total_students": 3 + }, + { + "alert_level": "green", + "beds_available": 93, + "isolation_off_campus": 0, + "isolation_on_campus": 3, + "last_updated": "2020-09-02 16:00:00", + "new_staff": 0, + "new_students": 0, + "quarantine_off_campus": 2, + "quarantine_on_campus": 6, + "tests_administered": 0, + "total_staff": 1, + "total_students": 3 + }, + { + "alert_level": "green", + "beds_available": 92, + "isolation_off_campus": 0, + "isolation_on_campus": 3, + "last_updated": "2020-09-03 16:00:00", + "new_staff": 0, + "new_students": 0, + "quarantine_off_campus": 3, + "quarantine_on_campus": 11, + "tests_administered": 0, + "total_staff": 1, + "total_students": 3 + }, + { + "alert_level": "green", + "beds_available": 96, + "isolation_off_campus": 1, + "isolation_on_campus": 0, + "last_updated": "2020-09-04 16:00:00", + "new_staff": 1, + "new_students": 3, + "quarantine_off_campus": 5, + "quarantine_on_campus": 1, + "tests_administered": 0, + "total_staff": 1, + "total_students": 4 + }, + { + "alert_level": "green", + "beds_available": 97, + "isolation_off_campus": 1, + "isolation_on_campus": 0, + "last_updated": "2020-09-06 16:00:00", + "new_staff": 0, + "new_students": 2, + "quarantine_off_campus": 5, + "quarantine_on_campus": 3, + "tests_administered": 0, + "total_staff": 1, + "total_students": 5 + }, + { + "alert_level": "green", + "beds_available": 95, + "isolation_off_campus": 6, + "isolation_on_campus": 1, + "last_updated": "2020-09-07 16:00:00", + "new_staff": 0, + "new_students": 3, + "quarantine_off_campus": 17, + "quarantine_on_campus": 5, + "tests_administered": 0, + "total_staff": 1, + "total_students": 6 + }, + { + "alert_level": "green", + "beds_available": 98, + "isolation_off_campus": 3, + "isolation_on_campus": 0, + "last_updated": "2020-09-10 16:00:00", + "new_staff": 0, + "new_students": 3, + "quarantine_off_campus": 17, + "quarantine_on_campus": 1, + "tests_administered": 0, + "total_staff": 1, + "total_students": 6 + }, + { + "alert_level": "green", + "beds_available": 96, + "isolation_off_campus": 4, + "isolation_on_campus": 2, + "last_updated": "2020-09-11 16:00:00", + "new_staff": 0, + "new_students": 3, + "quarantine_off_campus": 17, + "quarantine_on_campus": 1, + "tests_administered": 0, + "total_staff": 1, + "total_students": 6 + }, + { + "alert_level": "green", + "beds_available": 95, + "isolation_off_campus": 1, + "isolation_on_campus": 2, + "last_updated": "2020-09-14 16:00:00", + "new_staff": 0, + "new_students": 4, + "quarantine_off_campus": 16, + "quarantine_on_campus": 4, + "tests_administered": 0, + "total_staff": 1, + "total_students": 6 + }, + { + "alert_level": "green", + "beds_available": 92, + "isolation_off_campus": 2, + "isolation_on_campus": 3, + "last_updated": "2020-09-15 16:00:00", + "new_staff": 0, + "new_students": 4, + "quarantine_off_campus": 19, + "quarantine_on_campus": 9, + "tests_administered": 0, + "total_staff": 1, + "total_students": 7 + }, + { + "alert_level": "green", + "beds_available": 90, + "isolation_off_campus": 2, + "isolation_on_campus": 3, + "last_updated": "2020-09-16 16:00:00", + "new_staff": 0, + "new_students": 5, + "quarantine_off_campus": 19, + "quarantine_on_campus": 12, + "tests_administered": 0, + "total_staff": 1, + "total_students": 8 + }, + { + "alert_level": "green", + "beds_available": 88, + "isolation_off_campus": 1, + "isolation_on_campus": 2, + "last_updated": "2020-09-17 16:00:00", + "new_staff": 1, + "new_students": 4, + "quarantine_off_campus": 18, + "quarantine_on_campus": 16, + "tests_administered": 0, + "total_staff": 2, + "total_students": 8 + }, + { + "alert_level": "green", + "beds_available": 89, + "isolation_off_campus": 1, + "isolation_on_campus": 2, + "last_updated": "2020-09-18 16:00:00", + "new_staff": 1, + "new_students": 4, + "quarantine_off_campus": 16, + "quarantine_on_campus": 14, + "tests_administered": 0, + "total_staff": 2, + "total_students": 9 + }, + { + "alert_level": "green", + "beds_available": 94, + "isolation_off_campus": 1, + "isolation_on_campus": 2, + "last_updated": "2020-09-21 16:00:00", + "new_staff": 1, + "new_students": 4, + "quarantine_off_campus": 14, + "quarantine_on_campus": 8, + "tests_administered": 0, + "total_staff": 2, + "total_students": 10 + }, + { + "alert_level": "green", + "beds_available": 91, + "isolation_off_campus": 1, + "isolation_on_campus": 4, + "last_updated": "2020-09-22 16:00:00", + "new_staff": 1, + "new_students": 4, + "quarantine_off_campus": 9, + "quarantine_on_campus": 10, + "tests_administered": 0, + "total_staff": 2, + "total_students": 10 + }, + { + "alert_level": "green", + "beds_available": 88, + "isolation_off_campus": 1, + "isolation_on_campus": 5, + "last_updated": "2020-09-24 16:00:00", + "new_staff": 1, + "new_students": 4, + "quarantine_off_campus": 8, + "quarantine_on_campus": 15, + "tests_administered": 0, + "total_staff": 2, + "total_students": 10 + }, + { + "alert_level": "green", + "beds_available": 92, + "isolation_off_campus": 0, + "isolation_on_campus": 2, + "last_updated": "2020-09-25 16:00:00", + "new_staff": 1, + "new_students": 4, + "quarantine_off_campus": 12, + "quarantine_on_campus": 10, + "tests_administered": 3747, + "total_staff": 2, + "total_students": 10 + }, + { + "alert_level": "green", + "beds_available": 98, + "isolation_off_campus": 0, + "isolation_on_campus": 1, + "last_updated": "2020-09-28 16:00:00", + "new_staff": 1, + "new_students": 2, + "quarantine_off_campus": 8, + "quarantine_on_campus": 1, + "tests_administered": 3751, + "total_staff": 2, + "total_students": 10 + }, + { + "alert_level": "green", + "beds_available": 98, + "isolation_off_campus": 0, + "isolation_on_campus": 2, + "last_updated": "2020-09-29 16:00:00", + "new_staff": 1, + "new_students": 2, + "quarantine_off_campus": 9, + "quarantine_on_campus": 0, + "tests_administered": 3753, + "total_staff": 2, + "total_students": 10 + }, + { + "alert_level": "green", + "beds_available": 99, + "isolation_off_campus": 2, + "isolation_on_campus": 1, + "last_updated": "2020-10-01 16:00:00", + "new_staff": 0, + "new_students": 0, + "quarantine_off_campus": 8, + "quarantine_on_campus": 0, + "tests_administered": 4038, + "total_staff": 2, + "total_students": 10 + }, + { + "alert_level": "green", + "beds_available": 98, + "isolation_off_campus": 0, + "isolation_on_campus": 1, + "last_updated": "2020-10-02 16:00:00", + "new_staff": 0, + "new_students": 0, + "quarantine_off_campus": 6, + "quarantine_on_campus": 1, + "tests_administered": 4135, + "total_staff": 2, + "total_students": 10 + }, + { + "alert_level": "green", + "beds_available": 97, + "isolation_off_campus": 0, + "isolation_on_campus": 1, + "last_updated": "2020-10-05 16:00:00", + "new_staff": 0, + "new_students": 1, + "quarantine_off_campus": 1, + "quarantine_on_campus": 3, + "tests_administered": 4136, + "total_staff": 2, + "total_students": 10 + }, + { + "alert_level": "green", + "beds_available": 90, + "isolation_off_campus": 0, + "isolation_on_campus": 5, + "last_updated": "2020-10-06 16:00:00", + "new_staff": 0, + "new_students": 1, + "quarantine_off_campus": 5, + "quarantine_on_campus": 11, + "tests_administered": 4139, + "total_staff": 2, + "total_students": 10 + }, + { + "alert_level": "yellow", + "beds_available": 93, + "isolation_off_campus": 1, + "isolation_on_campus": 4, + "last_updated": "2020-10-08 16:00:00", + "new_staff": 0, + "new_students": 3, + "quarantine_off_campus": 10, + "quarantine_on_campus": 7, + "tests_administered": 4482, + "total_staff": 2, + "total_students": 12 + }, + { + "alert_level": "yellow", + "beds_available": 92, + "isolation_off_campus": 0, + "isolation_on_campus": 4, + "last_updated": "2020-10-09 16:00:00", + "new_staff": 0, + "new_students": 3, + "quarantine_off_campus": 14, + "quarantine_on_campus": 8, + "tests_administered": 4625, + "total_staff": 2, + "total_students": 12 + }, + { + "alert_level": "yellow", + "beds_available": 93, + "isolation_off_campus": 4, + "isolation_on_campus": 3, + "last_updated": "2020-10-12 16:00:00", + "new_staff": 0, + "new_students": 4, + "quarantine_off_campus": 20, + "quarantine_on_campus": 8, + "tests_administered": 5053, + "total_staff": 2, + "total_students": 13 + }, + { + "alert_level": "yellow", + "beds_available": 92, + "isolation_off_campus": 2, + "isolation_on_campus": 4, + "last_updated": "2020-10-13 16:00:00", + "new_staff": 0, + "new_students": 4, + "quarantine_off_campus": 24, + "quarantine_on_campus": 9, + "tests_administered": 5055, + "total_staff": 2, + "total_students": 13 + }, + { + "alert_level": "yellow", + "beds_available": 93, + "isolation_off_campus": 4, + "isolation_on_campus": 4, + "last_updated": "2020-10-15 16:00:00", + "new_staff": 0, + "new_students": 5, + "quarantine_off_campus": 27, + "quarantine_on_campus": 8, + "tests_administered": 5471, + "total_staff": 2, + "total_students": 14 + }, + { + "alert_level": "yellow", + "beds_available": 96, + "isolation_off_campus": 5, + "isolation_on_campus": 1, + "last_updated": "2020-10-16 16:00:00", + "new_staff": 0, + "new_students": 6, + "quarantine_off_campus": 25, + "quarantine_on_campus": 5, + "tests_administered": 5590, + "total_staff": 2, + "total_students": 15 + }, + { + "alert_level": "yellow", + "beds_available": 95, + "isolation_off_campus": 4, + "isolation_on_campus": 3, + "last_updated": "2020-10-19 16:00:00", + "new_staff": 0, + "new_students": 6, + "quarantine_off_campus": 23, + "quarantine_on_campus": 6, + "tests_administered": 5591, + "total_staff": 2, + "total_students": 16 + }, + { + "alert_level": "yellow", + "beds_available": 92, + "isolation_off_campus": 6, + "isolation_on_campus": 3, + "last_updated": "2020-10-20 16:00:00", + "new_staff": 0, + "new_students": 7, + "quarantine_off_campus": 29, + "quarantine_on_campus": 11, + "tests_administered": 5598, + "total_staff": 2, + "total_students": 17 + }, + { + "alert_level": "yellow", + "beds_available": 92, + "isolation_off_campus": 5, + "isolation_on_campus": 3, + "last_updated": "2020-10-21 16:00:00", + "new_staff": 0, + "new_students": 6, + "quarantine_off_campus": 32, + "quarantine_on_campus": 11, + "tests_administered": 5887, + "total_staff": 2, + "total_students": 17 + }, + { + "alert_level": "yellow", + "beds_available": 95, + "isolation_off_campus": 5, + "isolation_on_campus": 1, + "last_updated": "2020-10-23 16:00:00", + "new_staff": 0, + "new_students": 7, + "quarantine_off_campus": 33, + "quarantine_on_campus": 8, + "tests_administered": 6141, + "total_staff": 2, + "total_students": 19 + }, + { + "alert_level": "yellow", + "beds_available": 90, + "isolation_off_campus": 4, + "isolation_on_campus": 2, + "last_updated": "2020-10-26 16:00:00", + "new_staff": 0, + "new_students": 8, + "quarantine_off_campus": 35, + "quarantine_on_campus": 16, + "tests_administered": 6146, + "total_staff": 2, + "total_students": 21 + }, + { + "alert_level": "yellow", + "beds_available": 91, + "isolation_off_campus": 4, + "isolation_on_campus": 2, + "last_updated": "2020-10-27 16:00:00", + "new_staff": 0, + "new_students": 9, + "quarantine_off_campus": 36, + "quarantine_on_campus": 14, + "tests_administered": 6150, + "total_staff": 2, + "total_students": 22 + }, + { + "alert_level": "yellow", + "beds_available": 92, + "isolation_off_campus": 4, + "isolation_on_campus": 3, + "last_updated": "2020-10-28 16:00:00", + "new_staff": 1, + "new_students": 10, + "quarantine_off_campus": 34, + "quarantine_on_campus": 12, + "tests_administered": 6392, + "total_staff": 3, + "total_students": 23 + }, + { + "alert_level": "yellow", + "beds_available": 91, + "isolation_off_campus": 9, + "isolation_on_campus": 3, + "last_updated": "2020-10-29 16:00:00", + "new_staff": 2, + "new_students": 10, + "quarantine_off_campus": 31, + "quarantine_on_campus": 13, + "tests_administered": 6490, + "total_staff": 4, + "total_students": 24 + }, + { + "alert_level": "yellow", + "beds_available": 86, + "isolation_off_campus": 9, + "isolation_on_campus": 6, + "last_updated": "2020-10-30 16:00:00", + "new_staff": 2, + "new_students": 17, + "quarantine_off_campus": 27, + "quarantine_on_campus": 18, + "tests_administered": 6638, + "total_staff": 4, + "total_students": 32 + }, + { + "alert_level": "yellow", + "beds_available": 87, + "isolation_off_campus": 10, + "isolation_on_campus": 5, + "last_updated": "2020-11-02 19:27:39", + "new_staff": 3, + "new_students": 19, + "quarantine_off_campus": 30, + "quarantine_on_campus": 17, + "tests_administered": 7318, + "total_staff": 5, + "total_students": 36 + }, + { + "alert_level": "yellow", + "beds_available": 83, + "isolation_off_campus": 8, + "isolation_on_campus": 7, + "last_updated": "2020-11-03 19:47:40", + "new_staff": 3, + "new_students": 20, + "quarantine_off_campus": 34, + "quarantine_on_campus": 23, + "tests_administered": 7391, + "total_staff": 5, + "total_students": 37 + }, + { + "alert_level": "yellow", + "beds_available": 78, + "isolation_off_campus": 8, + "isolation_on_campus": 10, + "last_updated": "2020-11-04 20:05:45", + "new_staff": 5, + "new_students": 20, + "quarantine_off_campus": 35, + "quarantine_on_campus": 27, + "tests_administered": 7514, + "total_staff": 7, + "total_students": 38 + }, + { + "alert_level": "yellow", + "beds_available": 81, + "isolation_off_campus": 14, + "isolation_on_campus": 8, + "last_updated": "2020-11-05 19:36:46", + "new_staff": 5, + "new_students": 21, + "quarantine_off_campus": 41, + "quarantine_on_campus": 25, + "tests_administered": 7794, + "total_staff": 7, + "total_students": 40 + }, + { + "alert_level": "yellow", + "beds_available": 80, + "isolation_off_campus": 12, + "isolation_on_campus": 7, + "last_updated": "2020-11-06 20:30:25", + "new_staff": 5, + "new_students": 24, + "quarantine_off_campus": 42, + "quarantine_on_campus": 26, + "tests_administered": 8278, + "total_staff": 7, + "total_students": 43 + }, + { + "alert_level": "orange", + "beds_available": 57, + "isolation_off_campus": 11, + "isolation_on_campus": 10, + "last_updated": "2020-11-09 14:40:26", + "new_staff": 5, + "new_students": 29, + "quarantine_off_campus": 59, + "quarantine_on_campus": 63, + "tests_administered": 8622, + "total_staff": 7, + "total_students": 51 + }, + { + "alert_level": "orange", + "beds_available": 61, + "isolation_off_campus": 11, + "isolation_on_campus": 8, + "last_updated": "2020-11-10 21:02:02", + "new_staff": 3, + "new_students": 33, + "quarantine_off_campus": 66, + "quarantine_on_campus": 57, + "tests_administered": 8712, + "total_staff": 7, + "total_students": 55 + }, + { + "alert_level": "orange", + "beds_available": 56, + "isolation_off_campus": 17, + "isolation_on_campus": 11, + "last_updated": "2020-11-11 18:38:08", + "new_staff": 4, + "new_students": 34, + "quarantine_off_campus": 66, + "quarantine_on_campus": 63, + "tests_administered": 8732, + "total_staff": 8, + "total_students": 57 + }, + { + "alert_level": "orange", + "beds_available": 55, + "isolation_off_campus": 20, + "isolation_on_campus": 11, + "last_updated": "2020-11-12 19:43:09", + "new_staff": 5, + "new_students": 33, + "quarantine_off_campus": 67, + "quarantine_on_campus": 63, + "tests_administered": 8819, + "total_staff": 9, + "total_students": 60 + }, + { + "alert_level": "orange", + "beds_available": 57, + "isolation_off_campus": 15, + "isolation_on_campus": 12, + "last_updated": "2020-11-13 20:28:09", + "new_staff": 5, + "new_students": 30, + "quarantine_off_campus": 72, + "quarantine_on_campus": 60, + "tests_administered": 8969, + "total_staff": 9, + "total_students": 63 + }, + { + "alert_level": "orange", + "beds_available": 77, + "isolation_off_campus": 8, + "isolation_on_campus": 9, + "last_updated": "2020-11-16 19:35:59", + "new_staff": 5, + "new_students": 29, + "quarantine_off_campus": 67, + "quarantine_on_campus": 30, + "tests_administered": 9009, + "total_staff": 9, + "total_students": 65 + }, + { + "alert_level": "orange", + "beds_available": 81, + "isolation_off_campus": 9, + "isolation_on_campus": 6, + "last_updated": "2020-11-17 18:55:59", + "new_staff": 7, + "new_students": 27, + "quarantine_off_campus": 63, + "quarantine_on_campus": 27, + "tests_administered": 9383, + "total_staff": 13, + "total_students": 65 + }, + { + "alert_level": "orange", + "beds_available": 83, + "isolation_off_campus": 16, + "isolation_on_campus": 7, + "last_updated": "2020-11-18 19:00:59", + "new_staff": 6, + "new_students": 31, + "quarantine_off_campus": 63, + "quarantine_on_campus": 23, + "tests_administered": 9959, + "total_staff": 13, + "total_students": 69 + }, + { + "alert_level": "orange", + "beds_available": 83, + "isolation_off_campus": 22, + "isolation_on_campus": 9, + "last_updated": "2020-11-19 21:05:59", + "new_staff": 8, + "new_students": 35, + "quarantine_off_campus": 70, + "quarantine_on_campus": 33, + "tests_administered": 9959, + "total_staff": 15, + "total_students": 77 + }, + { + "alert_level": "orange", + "beds_available": 75, + "isolation_off_campus": 22, + "isolation_on_campus": 6, + "last_updated": "2020-11-20 22:20:05", + "new_staff": 8, + "new_students": 35, + "quarantine_off_campus": 70, + "quarantine_on_campus": 36, + "tests_administered": 11963, + "total_staff": 15, + "total_students": 79 + }, + { + "alert_level": "orange", + "beds_available": 81, + "isolation_off_campus": 21, + "isolation_on_campus": 6, + "last_updated": "2020-11-23 18:09:50", + "new_staff": 10, + "new_students": 36, + "quarantine_off_campus": 68, + "quarantine_on_campus": 28, + "tests_administered": 12041, + "total_staff": 18, + "total_students": 89 + }, + { + "alert_level": "orange", + "beds_available": 85, + "isolation_off_campus": 22, + "isolation_on_campus": 4, + "last_updated": "2020-11-24 19:54:50", + "new_staff": 12, + "new_students": 34, + "quarantine_off_campus": 61, + "quarantine_on_campus": 21, + "tests_administered": 12147, + "total_staff": 20, + "total_students": 90 + }, + { + "alert_level": "orange", + "beds_available": 88, + "isolation_off_campus": 24, + "isolation_on_campus": 4, + "last_updated": "2020-11-25 18:59:50", + "new_staff": 11, + "new_students": 35, + "quarantine_off_campus": 65, + "quarantine_on_campus": 16, + "tests_administered": 12218, + "total_staff": 20, + "total_students": 93 + }, + { + "alert_level": "orange", + "beds_available": 96, + "isolation_off_campus": 11, + "isolation_on_campus": 2, + "last_updated": "2020-11-30 19:29:52", + "new_staff": 12, + "new_students": 31, + "quarantine_off_campus": 64, + "quarantine_on_campus": 4, + "tests_administered": 12219, + "total_staff": 24, + "total_students": 96 + }, + { + "alert_level": "orange", + "beds_available": 96, + "isolation_off_campus": 8, + "isolation_on_campus": 2, + "last_updated": "2020-12-01 19:14:51", + "new_staff": 11, + "new_students": 29, + "quarantine_off_campus": 61, + "quarantine_on_campus": 4, + "tests_administered": 12289, + "total_staff": 26, + "total_students": 97 + }, + { + "alert_level": "orange", + "beds_available": 98, + "isolation_off_campus": 12, + "isolation_on_campus": 1, + "last_updated": "2020-12-02 20:04:51", + "new_staff": 11, + "new_students": 28, + "quarantine_off_campus": 41, + "quarantine_on_campus": 2, + "tests_administered": 12290, + "total_staff": 28, + "total_students": 99 + }, + { + "alert_level": "orange", + "beds_available": 98, + "isolation_off_campus": 16, + "isolation_on_campus": 2, + "last_updated": "2020-12-03 17:59:51", + "new_staff": 11, + "new_students": 23, + "quarantine_off_campus": 40, + "quarantine_on_campus": 2, + "tests_administered": 12364, + "total_staff": 28, + "total_students": 103 + }, + { + "alert_level": "orange", + "beds_available": 99, + "isolation_off_campus": 19, + "isolation_on_campus": 1, + "last_updated": "2020-12-04 19:24:52", + "new_staff": 11, + "new_students": 22, + "quarantine_off_campus": 45, + "quarantine_on_campus": 1, + "tests_administered": 12364, + "total_staff": 30, + "total_students": 105 + }, + { + "alert_level": "orange", + "beds_available": 99, + "isolation_off_campus": 17, + "isolation_on_campus": 1, + "last_updated": "2020-12-07 20:04:52", + "new_staff": 12, + "new_students": 19, + "quarantine_off_campus": 46, + "quarantine_on_campus": 0, + "tests_administered": 12364, + "total_staff": 30, + "total_students": 109 + }, + { + "alert_level": "orange", + "beds_available": 99, + "isolation_off_campus": 20, + "isolation_on_campus": 0, + "last_updated": "2020-12-08 19:09:52", + "new_staff": 13, + "new_students": 20, + "quarantine_off_campus": 48, + "quarantine_on_campus": 1, + "tests_administered": 12428, + "total_staff": 35, + "total_students": 110 + }, + { + "alert_level": "orange", + "beds_available": 99, + "isolation_off_campus": 23, + "isolation_on_campus": 0, + "last_updated": "2020-12-09 21:24:52", + "new_staff": 15, + "new_students": 16, + "quarantine_off_campus": 46, + "quarantine_on_campus": 1, + "tests_administered": 12432, + "total_staff": 37, + "total_students": 110 + }, + { + "alert_level": "orange", + "beds_available": 99, + "isolation_off_campus": 24, + "isolation_on_campus": 0, + "last_updated": "2020-12-10 18:19:52", + "new_staff": 13, + "new_students": 17, + "quarantine_off_campus": 44, + "quarantine_on_campus": 1, + "tests_administered": 12500, + "total_staff": 37, + "total_students": 111 + }, + { + "alert_level": "orange", + "beds_available": 99, + "isolation_off_campus": 19, + "isolation_on_campus": 0, + "last_updated": "2020-12-11 19:29:52", + "new_staff": 14, + "new_students": 19, + "quarantine_off_campus": 43, + "quarantine_on_campus": 1, + "tests_administered": 12500, + "total_staff": 39, + "total_students": 113 + }, + { + "alert_level": "orange", + "beds_available": 99, + "isolation_off_campus": 13, + "isolation_on_campus": 1, + "last_updated": "2020-12-14 20:44:53", + "new_staff": 13, + "new_students": 20, + "quarantine_off_campus": 30, + "quarantine_on_campus": 0, + "tests_administered": 12553, + "total_staff": 43, + "total_students": 117 + }, + { + "alert_level": "orange", + "beds_available": 99, + "isolation_off_campus": 12, + "isolation_on_campus": 1, + "last_updated": "2020-12-15 19:34:56", + "new_staff": 14, + "new_students": 21, + "quarantine_off_campus": 30, + "quarantine_on_campus": 0, + "tests_administered": 12593, + "total_staff": 45, + "total_students": 119 + }, + { + "alert_level": "orange", + "beds_available": 99, + "isolation_off_campus": 16, + "isolation_on_campus": 1, + "last_updated": "2020-12-16 20:34:53", + "new_staff": 16, + "new_students": 24, + "quarantine_off_campus": 28, + "quarantine_on_campus": 0, + "tests_administered": 12629, + "total_staff": 49, + "total_students": 124 + }, + { + "alert_level": "orange", + "beds_available": 100, + "isolation_off_campus": 13, + "isolation_on_campus": 1, + "last_updated": "2020-12-17 18:34:53", + "new_staff": 18, + "new_students": 22, + "quarantine_off_campus": 25, + "quarantine_on_campus": 0, + "tests_administered": 12630, + "total_staff": 52, + "total_students": 126 + }, + { + "alert_level": "orange", + "beds_available": 98, + "isolation_off_campus": 12, + "isolation_on_campus": 3, + "last_updated": "2020-12-18 20:09:53", + "new_staff": 18, + "new_students": 22, + "quarantine_off_campus": 23, + "quarantine_on_campus": 1, + "tests_administered": 12670, + "total_staff": 54, + "total_students": 127 + }, + { + "alert_level": "orange", + "beds_available": 100, + "isolation_off_campus": 9, + "isolation_on_campus": 1, + "last_updated": "2020-12-21 21:00:25", + "new_staff": 17, + "new_students": 17, + "quarantine_off_campus": 12, + "quarantine_on_campus": 1, + "tests_administered": 12706, + "total_staff": 54, + "total_students": 127 + }, + { + "alert_level": "orange", + "beds_available": 100, + "isolation_off_campus": 7, + "isolation_on_campus": 1, + "last_updated": "2020-12-22 19:20:26", + "new_staff": 14, + "new_students": 16, + "quarantine_off_campus": 9, + "quarantine_on_campus": 1, + "tests_administered": 12706, + "total_staff": 52, + "total_students": 126 + }, + { + "alert_level": "orange", + "beds_available": 100, + "isolation_off_campus": 6, + "isolation_on_campus": 1, + "last_updated": "2020-12-23 21:10:25", + "new_staff": 17, + "new_students": 18, + "quarantine_off_campus": 7, + "quarantine_on_campus": 1, + "tests_administered": 12746, + "total_staff": 55, + "total_students": 128 + }, + { + "alert_level": "orange", + "beds_available": 100, + "isolation_off_campus": 2, + "isolation_on_campus": 0, + "last_updated": "2021-01-04 21:30:27", + "new_staff": 11, + "new_students": 6, + "quarantine_off_campus": 4, + "quarantine_on_campus": 0, + "tests_administered": 12939, + "total_staff": 64, + "total_students": 132 + }, + { + "alert_level": "orange", + "beds_available": 96, + "isolation_off_campus": 4, + "isolation_on_campus": 0, + "last_updated": "2021-01-05 20:56:42", + "new_staff": 13, + "new_students": 5, + "quarantine_off_campus": 6, + "quarantine_on_campus": 0, + "tests_administered": 13002, + "total_staff": 70, + "total_students": 133 + }, + { + "alert_level": "orange", + "beds_available": 96, + "isolation_off_campus": 5, + "isolation_on_campus": 0, + "last_updated": "2021-01-06 20:51:42", + "new_staff": 15, + "new_students": 5, + "quarantine_off_campus": 7, + "quarantine_on_campus": 0, + "tests_administered": 13002, + "total_staff": 73, + "total_students": 134 + }, + { + "alert_level": "orange", + "beds_available": 96, + "isolation_off_campus": 5, + "isolation_on_campus": 0, + "last_updated": "2021-01-07 20:41:49", + "new_staff": 18, + "new_students": 4, + "quarantine_off_campus": 9, + "quarantine_on_campus": 0, + "tests_administered": 13064, + "total_staff": 77, + "total_students": 134 + }, + { + "alert_level": "orange", + "beds_available": 99, + "isolation_off_campus": 5, + "isolation_on_campus": 0, + "last_updated": "2021-01-08 20:41:42", + "new_staff": 19, + "new_students": 4, + "quarantine_off_campus": 10, + "quarantine_on_campus": 1, + "tests_administered": 13064, + "total_staff": 80, + "total_students": 134 + }, + { + "alert_level": "orange", + "beds_available": 95, + "isolation_off_campus": 6, + "isolation_on_campus": 1, + "last_updated": "2021-01-11 20:40:10", + "new_staff": 29, + "new_students": 6, + "quarantine_off_campus": 10, + "quarantine_on_campus": 1, + "tests_administered": 13129, + "total_staff": 94, + "total_students": 136 + }, + { + "alert_level": "orange", + "beds_available": 96, + "isolation_off_campus": 9, + "isolation_on_campus": 1, + "last_updated": "2021-01-12 20:50:10", + "new_staff": 33, + "new_students": 9, + "quarantine_off_campus": 15, + "quarantine_on_campus": 1, + "tests_administered": 13193, + "total_staff": 100, + "total_students": 139 + }, + { + "alert_level": "orange", + "beds_available": 96, + "isolation_off_campus": 10, + "isolation_on_campus": 1, + "last_updated": "2021-01-13 20:35:11", + "new_staff": 39, + "new_students": 8, + "quarantine_off_campus": 31, + "quarantine_on_campus": 5, + "tests_administered": 13193, + "total_staff": 110, + "total_students": 139 + }, + { + "alert_level": "orange", + "beds_available": 95, + "isolation_off_campus": 14, + "isolation_on_campus": 3, + "last_updated": "2021-01-14 21:35:10", + "new_staff": 40, + "new_students": 9, + "quarantine_off_campus": 28, + "quarantine_on_campus": 6, + "tests_administered": 13257, + "total_staff": 115, + "total_students": 141 + }, + { + "alert_level": "orange", + "beds_available": 95, + "isolation_off_campus": 19, + "isolation_on_campus": 3, + "last_updated": "2021-01-15 20:35:10", + "new_staff": 40, + "new_students": 11, + "quarantine_off_campus": 31, + "quarantine_on_campus": 6, + "tests_administered": 13257, + "total_staff": 118, + "total_students": 143 + }, + { + "alert_level": "orange", + "beds_available": 95, + "isolation_off_campus": 17, + "isolation_on_campus": 3, + "last_updated": "2021-01-18 20:59:05", + "new_staff": 42, + "new_students": 17, + "quarantine_off_campus": 24, + "quarantine_on_campus": 6, + "tests_administered": 13401, + "total_staff": 125, + "total_students": 150 + }, + { + "alert_level": "orange", + "beds_available": 95, + "isolation_off_campus": 24, + "isolation_on_campus": 3, + "last_updated": "2021-01-19 20:49:05", + "new_staff": 48, + "new_students": 17, + "quarantine_off_campus": 35, + "quarantine_on_campus": 6, + "tests_administered": 13456, + "total_staff": 133, + "total_students": 150 + }, + { + "alert_level": "orange", + "beds_available": 92, + "isolation_off_campus": 23, + "isolation_on_campus": 3, + "last_updated": "2021-01-20 20:39:07", + "new_staff": 50, + "new_students": 17, + "quarantine_off_campus": 48, + "quarantine_on_campus": 11, + "tests_administered": 13457, + "total_staff": 136, + "total_students": 151 + }, + { + "alert_level": "orange", + "beds_available": 92, + "isolation_off_campus": 22, + "isolation_on_campus": 5, + "last_updated": "2021-01-21 20:44:06", + "new_staff": 55, + "new_students": 21, + "quarantine_off_campus": 44, + "quarantine_on_campus": 6, + "tests_administered": 13531, + "total_staff": 143, + "total_students": 156 + }, + { + "alert_level": "yellow", + "beds_available": 90, + "isolation_off_campus": 28, + "isolation_on_campus": 5, + "last_updated": "2021-01-22 21:04:06", + "new_staff": 56, + "new_students": 22, + "quarantine_off_campus": 47, + "quarantine_on_campus": 10, + "tests_administered": 13782, + "total_staff": 147, + "total_students": 158 + }, + { + "alert_level": "yellow", + "beds_available": 93, + "isolation_off_campus": 33, + "isolation_on_campus": 10, + "last_updated": "2021-01-25 16:00:00", + "new_staff": 55, + "new_students": 30, + "quarantine_off_campus": 54, + "quarantine_on_campus": 9, + "tests_administered": 13843, + "total_staff": 149, + "total_students": 166 + }, + { + "alert_level": "yellow", + "beds_available": 92, + "isolation_off_campus": 35, + "isolation_on_campus": 10, + "last_updated": "2021-01-26 16:00:00", + "new_staff": 51, + "new_students": 32, + "quarantine_off_campus": 64, + "quarantine_on_campus": 13, + "tests_administered": 13902, + "total_staff": 151, + "total_students": 171 + }, + { + "alert_level": "yellow", + "beds_available": 91, + "isolation_off_campus": 35, + "isolation_on_campus": 13, + "last_updated": "2021-01-27 21:11:47", + "new_staff": 39, + "new_students": 27, + "quarantine_off_campus": 66, + "quarantine_on_campus": 16, + "tests_administered": 13842, + "total_staff": 149, + "total_students": 166 + }, + { + "alert_level": "yellow", + "beds_available": 89, + "isolation_off_campus": 30, + "isolation_on_campus": 17, + "last_updated": "2021-01-28 20:46:47", + "new_staff": 37, + "new_students": 29, + "quarantine_off_campus": 64, + "quarantine_on_campus": 18, + "tests_administered": 15303, + "total_staff": 152, + "total_students": 170 + }, + { + "alert_level": "yellow", + "beds_available": 89, + "isolation_off_campus": 28, + "isolation_on_campus": 16, + "last_updated": "2021-01-29 20:46:47", + "new_staff": 34, + "new_students": 34, + "quarantine_off_campus": 56, + "quarantine_on_campus": 21, + "tests_administered": 17121, + "total_staff": 152, + "total_students": 177 + }, + { + "alert_level": "yellow", + "beds_available": 89, + "isolation_off_campus": 19, + "isolation_on_campus": 11, + "last_updated": "2021-02-01 20:44:21", + "new_staff": 28, + "new_students": 34, + "quarantine_off_campus": 32, + "quarantine_on_campus": 26, + "tests_administered": 19365, + "total_staff": 153, + "total_students": 184 + }, + { + "alert_level": "yellow", + "beds_available": 89, + "isolation_off_campus": 18, + "isolation_on_campus": 11, + "last_updated": "2021-02-02 20:44:21", + "new_staff": 20, + "new_students": 36, + "quarantine_off_campus": 30, + "quarantine_on_campus": 26, + "tests_administered": 21168, + "total_staff": 153, + "total_students": 186 + }, + { + "alert_level": "yellow", + "beds_available": 89, + "isolation_off_campus": 20, + "isolation_on_campus": 9, + "last_updated": "2021-02-03 20:54:21", + "new_staff": 17, + "new_students": 39, + "quarantine_off_campus": 23, + "quarantine_on_campus": 28, + "tests_administered": 23278, + "total_staff": 153, + "total_students": 190 + }, + { + "alert_level": "yellow", + "beds_available": 91, + "isolation_off_campus": 20, + "isolation_on_campus": 10, + "last_updated": "2021-02-04 20:39:22", + "new_staff": 11, + "new_students": 38, + "quarantine_off_campus": 21, + "quarantine_on_campus": 22, + "tests_administered": 24935, + "total_staff": 154, + "total_students": 194 + }, + { + "alert_level": "yellow", + "beds_available": 94, + "isolation_off_campus": 18, + "isolation_on_campus": 6, + "last_updated": "2021-02-05 21:14:22", + "new_staff": 8, + "new_students": 39, + "quarantine_off_campus": 24, + "quarantine_on_campus": 16, + "tests_administered": 26703, + "total_staff": 155, + "total_students": 197 + }, + { + "alert_level": "yellow", + "beds_available": 93, + "isolation_off_campus": 12, + "isolation_on_campus": 5, + "last_updated": "2021-02-08 20:59:22", + "new_staff": 7, + "new_students": 35, + "quarantine_off_campus": 25, + "quarantine_on_campus": 18, + "tests_administered": 28801, + "total_staff": 156, + "total_students": 201 + }, + { + "alert_level": "yellow", + "beds_available": 95, + "isolation_off_campus": 13, + "isolation_on_campus": 6, + "last_updated": "2021-02-09 20:43:25", + "new_staff": 7, + "new_students": 37, + "quarantine_off_campus": 34, + "quarantine_on_campus": 12, + "tests_administered": 30646, + "total_staff": 157, + "total_students": 203 + }, + { + "alert_level": "yellow", + "beds_available": 96, + "isolation_off_campus": 14, + "isolation_on_campus": 5, + "last_updated": "2021-02-10 20:58:25", + "new_staff": 7, + "new_students": 34, + "quarantine_off_campus": 40, + "quarantine_on_campus": 12, + "tests_administered": 30646, + "total_staff": 159, + "total_students": 205 + }, + { + "alert_level": "yellow", + "beds_available": 97, + "isolation_off_campus": 12, + "isolation_on_campus": 3, + "last_updated": "2021-02-11 20:38:25", + "new_staff": 7, + "new_students": 31, + "quarantine_off_campus": 35, + "quarantine_on_campus": 10, + "tests_administered": 34388, + "total_staff": 159, + "total_students": 207 + }, + { + "alert_level": "yellow", + "beds_available": 96, + "isolation_off_campus": 14, + "isolation_on_campus": 3, + "last_updated": "2021-02-12 20:48:26", + "new_staff": 7, + "new_students": 28, + "quarantine_off_campus": 37, + "quarantine_on_campus": 13, + "tests_administered": 36180, + "total_staff": 159, + "total_students": 209 + }, + { + "alert_level": "yellow", + "beds_available": 97, + "isolation_off_campus": 10, + "isolation_on_campus": 4, + "last_updated": "2021-02-15 20:36:28", + "new_staff": 6, + "new_students": 28, + "quarantine_off_campus": 23, + "quarantine_on_campus": 7, + "tests_administered": 38314, + "total_staff": 159, + "total_students": 213 + }, + { + "alert_level": "yellow", + "beds_available": 97, + "isolation_off_campus": 11, + "isolation_on_campus": 2, + "last_updated": "2021-02-16 20:51:28", + "new_staff": 6, + "new_students": 23, + "quarantine_off_campus": 27, + "quarantine_on_campus": 7, + "tests_administered": 40104, + "total_staff": 159, + "total_students": 213 + }, + { + "alert_level": "yellow", + "beds_available": 100, + "isolation_off_campus": 10, + "isolation_on_campus": 1, + "last_updated": "2021-02-17 20:46:28", + "new_staff": 4, + "new_students": 19, + "quarantine_off_campus": 15, + "quarantine_on_campus": 1, + "tests_administered": 41611, + "total_staff": 159, + "total_students": 213 + }, + { + "alert_level": "yellow", + "beds_available": 99, + "isolation_off_campus": 9, + "isolation_on_campus": 2, + "last_updated": "2021-02-18 20:43:06", + "new_staff": 4, + "new_students": 17, + "quarantine_off_campus": 17, + "quarantine_on_campus": 2, + "tests_administered": 43313, + "total_staff": 160, + "total_students": 213 + }, + { + "alert_level": "yellow", + "beds_available": 99, + "isolation_off_campus": 11, + "isolation_on_campus": 4, + "last_updated": "2021-02-19 20:38:05", + "new_staff": 4, + "new_students": 17, + "quarantine_off_campus": 13, + "quarantine_on_campus": 2, + "tests_administered": 45361, + "total_staff": 160, + "total_students": 216 + }, + { + "alert_level": "yellow", + "beds_available": 96, + "isolation_off_campus": 9, + "isolation_on_campus": 4, + "last_updated": "2021-02-22 20:38:05", + "new_staff": 2, + "new_students": 19, + "quarantine_off_campus": 14, + "quarantine_on_campus": 9, + "tests_administered": 47827, + "total_staff": 160, + "total_students": 221 + }, + { + "alert_level": "yellow", + "beds_available": 96, + "isolation_off_campus": 8, + "isolation_on_campus": 4, + "last_updated": "2021-02-23 20:38:06", + "new_staff": 1, + "new_students": 19, + "quarantine_off_campus": 15, + "quarantine_on_campus": 8, + "tests_administered": 49834, + "total_staff": 160, + "total_students": 223 + }, + { + "alert_level": "yellow", + "beds_available": 97, + "isolation_off_campus": 12, + "isolation_on_campus": 3, + "last_updated": "2021-02-24 20:43:06", + "new_staff": 2, + "new_students": 23, + "quarantine_off_campus": 16, + "quarantine_on_campus": 6, + "tests_administered": 50954, + "total_staff": 161, + "total_students": 229 + }, + { + "alert_level": "yellow", + "beds_available": 95, + "isolation_off_campus": 15, + "isolation_on_campus": 5, + "last_updated": "2021-02-25 20:48:06", + "new_staff": 1, + "new_students": 23, + "quarantine_off_campus": 34, + "quarantine_on_campus": 12, + "tests_administered": 52753, + "total_staff": 161, + "total_students": 232 + } +] -- cgit v1.2.3 From 6746140ea31c9f3a2d1b098f8c9cbb70cb3b86b7 Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 16:35:17 -0500 Subject: Update requirements and add flask-sqlalchemy --- requirements.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8b69505..7e8c448 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,8 @@ +click==7.1.2 Flask==1.1.2 -gunicorn==20.0.4 -pylint==2.6.0 -pylint-quotes==0.2.1 -requests==2.24.0 -beautifulsoup4==4.9.3 \ No newline at end of file +Flask-SQLAlchemy==2.4.4 +itsdangerous==1.1.0 +Jinja2==2.11.3 +MarkupSafe==1.1.1 +SQLAlchemy==1.3.23 +Werkzeug==1.0.1 -- cgit v1.2.3 From 2db9fcf921c5302336dfd3bc72983692b345d2aa Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 20:54:00 -0500 Subject: reqs stuff and remove swagger html --- config.env.py | 2 - requirements.txt | 16 + swagger.html | 3057 ------------------------------------------------------ 3 files changed, 16 insertions(+), 3059 deletions(-) delete mode 100644 swagger.html diff --git a/config.env.py b/config.env.py index fd7c9ac..6726cb3 100644 --- a/config.env.py +++ b/config.env.py @@ -12,6 +12,4 @@ import os # Defaults for flask configuration IP = os.environ.get('IP', '127.0.0.1') PORT = os.environ.get('PORT', 5000) -#SERVER_NAME = os.environ.get('SERVER_NAME', 'localhost:5000') SECRET_KEY = os.environ.get('SESSION_KEY', default=''.join(secrets.token_hex(16))) - diff --git a/requirements.txt b/requirements.txt index 7e8c448..ea4aff3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,24 @@ +astroid==2.5 +beautifulsoup4==4.9.3 +certifi==2020.12.5 +chardet==3.0.4 click==7.1.2 Flask==1.1.2 Flask-SQLAlchemy==2.4.4 +gunicorn==20.0.4 +idna==2.10 +isort==5.7.0 itsdangerous==1.1.0 Jinja2==2.11.3 +lazy-object-proxy==1.5.2 MarkupSafe==1.1.1 +mccabe==0.6.1 +pylint==2.6.0 +pylint-quotes==0.2.1 +requests==2.24.0 +soupsieve==2.2 SQLAlchemy==1.3.23 +toml==0.10.2 +urllib3==1.25.11 Werkzeug==1.0.1 +wrapt==1.12.1 diff --git a/swagger.html b/swagger.html deleted file mode 100644 index 575ae4a..0000000 --- a/swagger.html +++ /dev/null @@ -1,3057 +0,0 @@ - - - - - RIT COVID Poller - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
-
-

RIT COVID Poller

-
-
-
- -
-
-

V0

-
-
-
-

getv0History

-

Gets all historical data

-
-
-
-

-

Returns data from every day where the dashboard changed

-

-
-
/v0/history
-

-

Usage and SDK Samples

-

- - -
-
-
curl -X GET "https://ritcoviddashboard.com/api/v0/history"
-
-
-
import io.swagger.client.*;
-import io.swagger.client.auth.*;
-import io.swagger.client.model.*;
-import io.swagger.client.api.V0Api;
-
-import java.io.File;
-import java.util.*;
-
-public class V0ApiExample {
-
-    public static void main(String[] args) {
-        
-        V0Api apiInstance = new V0Api();
-        try {
-            array[Day] result = apiInstance.getv0History();
-            System.out.println(result);
-        } catch (ApiException e) {
-            System.err.println("Exception when calling V0Api#getv0History");
-            e.printStackTrace();
-        }
-    }
-}
-
- -
-
import io.swagger.client.api.V0Api;
-
-public class V0ApiExample {
-
-    public static void main(String[] args) {
-        V0Api apiInstance = new V0Api();
-        try {
-            array[Day] result = apiInstance.getv0History();
-            System.out.println(result);
-        } catch (ApiException e) {
-            System.err.println("Exception when calling V0Api#getv0History");
-            e.printStackTrace();
-        }
-    }
-}
-
- -
-

-V0Api *apiInstance = [[V0Api alloc] init];
-
-// Gets all historical data
-[apiInstance getv0HistoryWithCompletionHandler: 
-              ^(array[Day] output, NSError* error) {
-                            if (output) {
-                                NSLog(@"%@", output);
-                            }
-                            if (error) {
-                                NSLog(@"Error: %@", error);
-                            }
-                        }];
-
-
- -
-
var RitCovidPoller = require('rit_covid_poller');
-
-var api = new RitCovidPoller.V0Api()
-
-var callback = function(error, data, response) {
-  if (error) {
-    console.error(error);
-  } else {
-    console.log('API called successfully. Returned data: ' + data);
-  }
-};
-api.getv0History(callback);
-
-
- - -
-
using System;
-using System.Diagnostics;
-using IO.Swagger.Api;
-using IO.Swagger.Client;
-using IO.Swagger.Model;
-
-namespace Example
-{
-    public class getv0HistoryExample
-    {
-        public void main()
-        {
-            
-            var apiInstance = new V0Api();
-
-            try
-            {
-                // Gets all historical data
-                array[Day] result = apiInstance.getv0History();
-                Debug.WriteLine(result);
-            }
-            catch (Exception e)
-            {
-                Debug.Print("Exception when calling V0Api.getv0History: " + e.Message );
-            }
-        }
-    }
-}
-
-
- -
-
<?php
-require_once(__DIR__ . '/vendor/autoload.php');
-
-$api_instance = new Swagger\Client\Api\V0Api();
-
-try {
-    $result = $api_instance->getv0History();
-    print_r($result);
-} catch (Exception $e) {
-    echo 'Exception when calling V0Api->getv0History: ', $e->getMessage(), PHP_EOL;
-}
-?>
-
- -
-
use Data::Dumper;
-use WWW::SwaggerClient::Configuration;
-use WWW::SwaggerClient::V0Api;
-
-my $api_instance = WWW::SwaggerClient::V0Api->new();
-
-eval { 
-    my $result = $api_instance->getv0History();
-    print Dumper($result);
-};
-if ($@) {
-    warn "Exception when calling V0Api->getv0History: $@\n";
-}
-
- -
-
from __future__ import print_statement
-import time
-import swagger_client
-from swagger_client.rest import ApiException
-from pprint import pprint
-
-# create an instance of the API class
-api_instance = swagger_client.V0Api()
-
-try: 
-    # Gets all historical data
-    api_response = api_instance.getv0_history()
-    pprint(api_response)
-except ApiException as e:
-    print("Exception when calling V0Api->getv0History: %s\n" % e)
-
-
- -

Parameters

- - - - - - -

Responses

-

Status: 200 - successful operation

- - - -
-
-
- -
- -
-
- -
-
-
-
-
-
-

getv0Latest

-

Gets the latest data

-
-
-
-

-

Returns the most recent data from the official dashboard

-

-
-
/v0/latest
-

-

Usage and SDK Samples

-

- - -
-
-
curl -X GET "https://ritcoviddashboard.com/api/v0/latest"
-
-
-
import io.swagger.client.*;
-import io.swagger.client.auth.*;
-import io.swagger.client.model.*;
-import io.swagger.client.api.V0Api;
-
-import java.io.File;
-import java.util.*;
-
-public class V0ApiExample {
-
-    public static void main(String[] args) {
-        
-        V0Api apiInstance = new V0Api();
-        try {
-            Day result = apiInstance.getv0Latest();
-            System.out.println(result);
-        } catch (ApiException e) {
-            System.err.println("Exception when calling V0Api#getv0Latest");
-            e.printStackTrace();
-        }
-    }
-}
-
- -
-
import io.swagger.client.api.V0Api;
-
-public class V0ApiExample {
-
-    public static void main(String[] args) {
-        V0Api apiInstance = new V0Api();
-        try {
-            Day result = apiInstance.getv0Latest();
-            System.out.println(result);
-        } catch (ApiException e) {
-            System.err.println("Exception when calling V0Api#getv0Latest");
-            e.printStackTrace();
-        }
-    }
-}
-
- -
-

-V0Api *apiInstance = [[V0Api alloc] init];
-
-// Gets the latest data
-[apiInstance getv0LatestWithCompletionHandler: 
-              ^(Day output, NSError* error) {
-                            if (output) {
-                                NSLog(@"%@", output);
-                            }
-                            if (error) {
-                                NSLog(@"Error: %@", error);
-                            }
-                        }];
-
-
- -
-
var RitCovidPoller = require('rit_covid_poller');
-
-var api = new RitCovidPoller.V0Api()
-
-var callback = function(error, data, response) {
-  if (error) {
-    console.error(error);
-  } else {
-    console.log('API called successfully. Returned data: ' + data);
-  }
-};
-api.getv0Latest(callback);
-
-
- - -
-
using System;
-using System.Diagnostics;
-using IO.Swagger.Api;
-using IO.Swagger.Client;
-using IO.Swagger.Model;
-
-namespace Example
-{
-    public class getv0LatestExample
-    {
-        public void main()
-        {
-            
-            var apiInstance = new V0Api();
-
-            try
-            {
-                // Gets the latest data
-                Day result = apiInstance.getv0Latest();
-                Debug.WriteLine(result);
-            }
-            catch (Exception e)
-            {
-                Debug.Print("Exception when calling V0Api.getv0Latest: " + e.Message );
-            }
-        }
-    }
-}
-
-
- -
-
<?php
-require_once(__DIR__ . '/vendor/autoload.php');
-
-$api_instance = new Swagger\Client\Api\V0Api();
-
-try {
-    $result = $api_instance->getv0Latest();
-    print_r($result);
-} catch (Exception $e) {
-    echo 'Exception when calling V0Api->getv0Latest: ', $e->getMessage(), PHP_EOL;
-}
-?>
-
- -
-
use Data::Dumper;
-use WWW::SwaggerClient::Configuration;
-use WWW::SwaggerClient::V0Api;
-
-my $api_instance = WWW::SwaggerClient::V0Api->new();
-
-eval { 
-    my $result = $api_instance->getv0Latest();
-    print Dumper($result);
-};
-if ($@) {
-    warn "Exception when calling V0Api->getv0Latest: $@\n";
-}
-
- -
-
from __future__ import print_statement
-import time
-import swagger_client
-from swagger_client.rest import ApiException
-from pprint import pprint
-
-# create an instance of the API class
-api_instance = swagger_client.V0Api()
-
-try: 
-    # Gets the latest data
-    api_response = api_instance.getv0_latest()
-    pprint(api_response)
-except ApiException as e:
-    print("Exception when calling V0Api->getv0Latest: %s\n" % e)
-
-
- -

Parameters

- - - - - - -

Responses

-

Status: 200 - successful operation

- - - -
-
-
- -
- -
-
- -
-
-
-
-
- -
-
-
- - - - - - - - - - - - - - -- cgit v1.2.3 From 72b07e05e50c2c93d6f195a3ebe15544c4afe171 Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 21:15:24 -0500 Subject: Update config file --- config.env.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/config.env.py b/config.env.py index 6726cb3..8334eeb 100644 --- a/config.env.py +++ b/config.env.py @@ -1,15 +1,18 @@ -import secrets -import os +""" +Default configuration settings and environment variable based configuration logic + See the readme for more information +""" -# Values in this file are loaded into the flask app instance, `demo.APP` in this -# demo. This file sources values from the environment if they exist, otherwise a -# set of defaults are used. This is useful for keeping secrets secret, as well -# as facilitating configuration in a container. Defaults may be overriden either -# by defining the environment variables, or by creating a `config.py` file that -# contains locally set secrets or config values. +from os import environ +import secrets +# Flask config +DEBUG = False +IP = environ.get('POLLER_IP', 'localhost') +PORT = environ.get('POLLER_PORT', '8000') +PROTOCOL = environ.get('POLLER_PROTOCOL', 'http://') +SECRET_KEY = environ.get('POLLER_SECRET_KEY', default=''.join(secrets.token_hex(16))) -# Defaults for flask configuration -IP = os.environ.get('IP', '127.0.0.1') -PORT = os.environ.get('PORT', 5000) -SECRET_KEY = os.environ.get('SESSION_KEY', default=''.join(secrets.token_hex(16))) +# SQLAlchemy config +SQLALCHEMY_DATABASE_URI = environ.get('POLLER_DATABASE_URI', 'sqlite:////tmp/rit-covid-poller.db') +SQLALCHEMY_TRACK_MODIFICATIONS = False -- cgit v1.2.3 From 07ef35d0892ce652b2421ef2d101c743593ccf46 Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 22:00:18 -0500 Subject: Clean up config and wsgi --- config.env.py | 5 +++-- wsgi.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config.env.py b/config.env.py index 8334eeb..5241003 100644 --- a/config.env.py +++ b/config.env.py @@ -9,9 +9,10 @@ import secrets # Flask config DEBUG = False IP = environ.get('POLLER_IP', 'localhost') -PORT = environ.get('POLLER_PORT', '8000') -PROTOCOL = environ.get('POLLER_PROTOCOL', 'http://') +PORT = environ.get('POLLER_PORT', '5000') SECRET_KEY = environ.get('POLLER_SECRET_KEY', default=''.join(secrets.token_hex(16))) +LOG_LEVEL = environ.get('PACKET_LOG_LEVEL', 'INFO') +VERSION = '0.1.0' # SQLAlchemy config SQLALCHEMY_DATABASE_URI = environ.get('POLLER_DATABASE_URI', 'sqlite:////tmp/rit-covid-poller.db') diff --git a/wsgi.py b/wsgi.py index 31fc3f5..cd51ea3 100644 --- a/wsgi.py +++ b/wsgi.py @@ -4,5 +4,5 @@ Primary entry point for the app from poller import APP -if __name__ == "__main__": - APP.run(host=APP.config["IP"], port=int(APP.config["PORT"])) +if __name__ == '__main__': + APP.run(host=APP.config['IP'], port=int(APP.config['PORT'])) -- cgit v1.2.3 From 7a66f7bdf01e004f55cfdf0ff9ff5cda35f65c3b Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 22:01:13 -0500 Subject: Add startup and model --- poller/__init__.py | 28 ++++++++++++++++++++++++++++ poller/models.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 poller/__init__.py create mode 100644 poller/models.py diff --git a/poller/__init__.py b/poller/__init__.py new file mode 100644 index 0000000..67b7ef2 --- /dev/null +++ b/poller/__init__.py @@ -0,0 +1,28 @@ +""" +Startup code +""" + +import os +import logging +from flask import Flask +from flask_sqlalchemy import SQLAlchemy + +APP = Flask(__name__) + +# Load default configuration and any environment variable overrides +_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +APP.config.from_pyfile(os.path.join(_root_dir, 'config.env.py')) + +# Load file based configuration overrides if present +_pyfile_config = os.path.join(_root_dir, 'config.py') +if os.path.exists(_pyfile_config): + APP.config.from_pyfile(_pyfile_config) + +# Logger configuration +logging.getLogger().setLevel(APP.config['LOG_LEVEL']) +#pylint: disable=no-member +APP.logger.info('Launching rit-covid-poller v' + APP.config['VERSION']) + +db = SQLAlchemy(APP) +APP.logger.info('SQLAlchemy pointed at ' + repr(db.engine.url)) +#pylint: enable=no-member diff --git a/poller/models.py b/poller/models.py new file mode 100644 index 0000000..a5dd2fa --- /dev/null +++ b/poller/models.py @@ -0,0 +1,34 @@ +""" +Defines models +""" +from datetime import datetime +from sqlalchemy import Column, Integer, String, DateTime + +from . import db + +#pylint: disable=too-few-public-methods +class Day(db.Model): + """ + Model for each day's data + """ + __tablename__ = 'days' + last_updated = Column(DateTime, default=datetime.now, onupdate=datetime.now, \ + primary_key=True, nullable=False) + alert_level = Column(String(16), nullable=False) + beds_available = Column(Integer, nullable=False) + isolation_off_campus = Column(Integer, nullable=False) + isolation_on_campus = Column(Integer, nullable=False) + new_staff = Column(Integer, nullable=False) + new_students = Column(Integer, nullable=False) + quarantine_off_campus = Column(Integer, nullable=False) + quarantine_on_campus = Column(Integer, nullable=False) + tests_administered = Column(Integer, nullable=False) + total_staff = Column(Integer, nullable=False) + total_students = Column(Integer, nullable=False) + + @classmethod + def get_all(cls): + """ + Helper to get all values from the database + """ + return cls.query.all() -- cgit v1.2.3 From 987a1db2a7b5d84aa906c23eb586930df7cd0d50 Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 22:01:45 -0500 Subject: import models correctly --- poller/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/poller/__init__.py b/poller/__init__.py index 67b7ef2..e6eda4d 100644 --- a/poller/__init__.py +++ b/poller/__init__.py @@ -26,3 +26,6 @@ APP.logger.info('Launching rit-covid-poller v' + APP.config['VERSION']) db = SQLAlchemy(APP) APP.logger.info('SQLAlchemy pointed at ' + repr(db.engine.url)) #pylint: enable=no-member + +#pylint: disable=wrong-import-position +from . import models -- cgit v1.2.3 From 51079aa249b509e9160dd675fed13b3bacda661c Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 22:31:38 -0500 Subject: Add import history command --- .gitignore | 2 +- config.env.py | 2 +- poller/__init__.py | 3 +++ poller/commands.py | 36 ++++++++++++++++++++++++++++++++++++ requirements.txt | 2 ++ 5 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 poller/commands.py diff --git a/.gitignore b/.gitignore index 48d472c..e1dec52 100644 --- a/.gitignore +++ b/.gitignore @@ -166,5 +166,5 @@ dmypy.json *.code-workspace # End of https://www.toptal.com/developers/gitignore/api/python,vscode,linux - config.py +data.db diff --git a/config.env.py b/config.env.py index 5241003..a56a930 100644 --- a/config.env.py +++ b/config.env.py @@ -15,5 +15,5 @@ LOG_LEVEL = environ.get('PACKET_LOG_LEVEL', 'INFO') VERSION = '0.1.0' # SQLAlchemy config -SQLALCHEMY_DATABASE_URI = environ.get('POLLER_DATABASE_URI', 'sqlite:////tmp/rit-covid-poller.db') +SQLALCHEMY_DATABASE_URI = environ.get('POLLER_DATABASE_URI', f'sqlite:///data.db') SQLALCHEMY_TRACK_MODIFICATIONS = False diff --git a/poller/__init__.py b/poller/__init__.py index e6eda4d..c0c25c0 100644 --- a/poller/__init__.py +++ b/poller/__init__.py @@ -29,3 +29,6 @@ APP.logger.info('SQLAlchemy pointed at ' + repr(db.engine.url)) #pylint: disable=wrong-import-position from . import models +from . import commands + +db.create_all() diff --git a/poller/commands.py b/poller/commands.py new file mode 100644 index 0000000..fc45dc7 --- /dev/null +++ b/poller/commands.py @@ -0,0 +1,36 @@ +""" +CLI commands for data management +""" +import json +import click +from dateutil import parser + +from . import APP, db +from .models import Day + +@APP.cli.command('import-history') +@click.argument('history_file') +def import_history(history_file): + """ + Import history from JSON file + """ + data = '{}' + with open(history_file, 'r') as file: + data = file.read() + parsed = json.loads(data) + for item in parsed: + if not parser.parse(item['last_updated']) in [day.last_updated for day in Day.get_all()]: + db.session.add(Day( + last_updated=parser.parse(item['last_updated']), + alert_level=item['alert_level'], + beds_available=item['beds_available'], + isolation_off_campus=item['isolation_off_campus'], + isolation_on_campus=item['isolation_on_campus'], + new_staff=item['new_staff'], + new_students=item['new_students'], + quarantine_off_campus=item['quarantine_off_campus'], + quarantine_on_campus=item['quarantine_on_campus'], + tests_administered=item['tests_administered'], + total_staff=item['total_staff'], + total_students=item['total_students'])) + db.session.commit() diff --git a/requirements.txt b/requirements.txt index ea4aff3..16b231a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,9 @@ MarkupSafe==1.1.1 mccabe==0.6.1 pylint==2.6.0 pylint-quotes==0.2.1 +python-dateutil==2.8.1 requests==2.24.0 +six==1.15.0 soupsieve==2.2 SQLAlchemy==1.3.23 toml==0.10.2 -- cgit v1.2.3 From 52acb063dd83c7856d5c8d2be9618ea2000d4470 Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 22:45:12 -0500 Subject: Add history route --- poller/__init__.py | 1 + poller/models.py | 19 +++++++++++++++++++ poller/routes.py | 7 +++++++ 3 files changed, 27 insertions(+) create mode 100644 poller/routes.py diff --git a/poller/__init__.py b/poller/__init__.py index c0c25c0..1e1580b 100644 --- a/poller/__init__.py +++ b/poller/__init__.py @@ -30,5 +30,6 @@ APP.logger.info('SQLAlchemy pointed at ' + repr(db.engine.url)) #pylint: disable=wrong-import-position from . import models from . import commands +from . import routes db.create_all() diff --git a/poller/models.py b/poller/models.py index a5dd2fa..15b223d 100644 --- a/poller/models.py +++ b/poller/models.py @@ -32,3 +32,22 @@ class Day(db.Model): Helper to get all values from the database """ return cls.query.all() + + def serialize(self): + """ + used for json serialization + """ + return { + 'last_updated': self.last_updated, + 'alert_level': self.alert_level, + 'beds_available': self.beds_available, + 'isolation_off_campus': self.isolation_off_campus, + 'isolation_on_campus': self.isolation_on_campus, + 'new_staff': self.new_staff, + 'new_students': self.new_students, + 'quarantine_off_campus': self.quarantine_off_campus, + 'quarantine_on_campus': self.quarantine_on_campus, + 'tests_administered': self.tests_administered, + 'total_staff': self.total_staff, + 'total_students': self.total_students, + } diff --git a/poller/routes.py b/poller/routes.py new file mode 100644 index 0000000..4c0010b --- /dev/null +++ b/poller/routes.py @@ -0,0 +1,7 @@ +from flask import Flask, jsonify +from . import APP +from .models import Day + +@APP.route('/api/v0/history') +def _get_api_v0_history(): + return jsonify([day.serialize() for day in Day.get_all()]) -- cgit v1.2.3 From 79f606ccb9a190d9efde490c1c4a3121815544fe Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Thu, 25 Feb 2021 22:46:14 -0500 Subject: Add latest route --- poller/routes.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/poller/routes.py b/poller/routes.py index 4c0010b..6911fdd 100644 --- a/poller/routes.py +++ b/poller/routes.py @@ -5,3 +5,7 @@ from .models import Day @APP.route('/api/v0/history') def _get_api_v0_history(): return jsonify([day.serialize() for day in Day.get_all()]) + +@APP.route('/api/v0/latest') +def _get_api_v0_latest(): + return jsonify(Day.get_all()[-1].serialize()) -- cgit v1.2.3 From 0d57a01ba2c4da533ad4459fcf5a45b79a17cd53 Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Fri, 26 Feb 2021 10:51:43 -0500 Subject: Add background loop and dedup --- poller/__init__.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ poller/models.py | 2 +- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/poller/__init__.py b/poller/__init__.py index 1e1580b..2562f2d 100644 --- a/poller/__init__.py +++ b/poller/__init__.py @@ -3,10 +3,20 @@ Startup code """ import os +import json import logging +import requests +import datetime +import threading from flask import Flask +from bs4 import BeautifulSoup from flask_sqlalchemy import SQLAlchemy + +POOL_TIME = 5 * 60 # Seconds +DASHBOARD_URL = 'https://rit.edu/ready/spring-dashboard' +DATA_THREAD = threading.Thread() + APP = Flask(__name__) # Load default configuration and any environment variable overrides @@ -33,3 +43,84 @@ from . import commands from . import routes db.create_all() + +from .models import Day + +def data_are_same(old, new): + return old.total_students == new.total_students and \ + old.total_staff == new.total_staff and \ + old.new_students == new.new_students and \ + old.new_staff == new.new_staff and \ + old.quarantine_on_campus == new.quarantine_on_campus and \ + old.quarantine_off_campus == new.quarantine_off_campus and \ + old.isolation_on_campus == new.isolation_on_campus and \ + old.isolation_off_campus == new.isolation_off_campus and \ + old.beds_available == new.beds_available and \ + old.tests_administered == new.tests_administered and \ + old.alert_level == new.alert_level + + +def get_data(): + print('fetching data') + global DATA_THREAD + DATA_THREAD = threading.Timer(POOL_TIME, get_data, ()) + DATA_THREAD.start() + page = requests.get(DASHBOARD_URL, headers={'Cache-Control': 'no-cache'}) + soup = BeautifulSoup(page.content, 'html.parser') + total_students = int(soup.find('div', attrs={'class': 'statistic-13872'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) + total_staff = int(soup.find('div', attrs={'class': 'statistic-13875'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) + new_students = int(soup.find('div', attrs={'class': 'statistic-14332'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) + new_staff = int(soup.find('div', attrs={'class': 'statistic-14335'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) + quarantine_on_campus = int(soup.find('div', attrs={'class': 'statistic-13893'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) + quarantine_off_campus = int(soup.find('div', attrs={'class': 'statistic-13896'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) + isolation_on_campus = int(soup.find('div', attrs={'class': 'statistic-13905'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) + isolation_off_campus = int(soup.find('div', attrs={'class': 'statistic-13908'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip()) + beds_available = int(soup.find('div', attrs={'class': 'statistic-13935'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip().strip('%')) + tests_administered = int(soup.find('div', attrs={'class': 'statistic-13923'}).find_all("p", attrs={'class': 'card-header'})[0].text.strip().replace("*", " ").replace(",", "")) + container = soup.find('div', attrs={'id': 'pandemic-message-container'}) + alert_level = container.find('a').text + color = "" + if "Green" in alert_level: + color = 'green' + elif "Yellow" in alert_level: + color = 'yellow' + elif "Orange" in alert_level: + color = 'orange' + elif "Red" in alert_level: + color = 'red' + + fall_data = None + with open('history/fall-2020.json', 'r') as fd: + fall_data = json.loads(fd.read()) + current_data = Day( + last_updated=datetime.datetime.now(), + alert_level=color, + beds_available=beds_available, + isolation_off_campus=isolation_off_campus, + isolation_on_campus=isolation_on_campus, + new_staff=new_staff, + new_students=new_students, + quarantine_off_campus=quarantine_off_campus, + quarantine_on_campus=quarantine_on_campus, + tests_administered=tests_administered + fall_data['tests_administered'], + total_staff=total_staff + fall_data['total_staff'], + total_students=total_students + fall_data['total_students']) + print(current_data.serialize()) + if not data_are_same(Day.get_all()[-1], current_data): + db.session.add(current_data) + dedup() + return current_data + +def dedup(): + data = Day.get_all() + # get first date + starting_date = data[-1].serialize()['last_updated'].split(' ')[0] + for i in range(len(data)-2, 0, -1): + if data[i].serialize()['last_updated'].split(' ')[0] != starting_date: + starting_date = data[i].serialize()['last_updated'].split(' ')[0] + else: + db.session.delete(data[i]) + print('dropped ' + data[i].serialize()['last_updated']) + db.session.commit() + +get_data() \ No newline at end of file diff --git a/poller/models.py b/poller/models.py index 15b223d..4bcca06 100644 --- a/poller/models.py +++ b/poller/models.py @@ -38,7 +38,7 @@ class Day(db.Model): used for json serialization """ return { - 'last_updated': self.last_updated, + 'last_updated': self.last_updated.strftime('%Y-%m-%d %H:%M:%S'), 'alert_level': self.alert_level, 'beds_available': self.beds_available, 'isolation_off_campus': self.isolation_off_campus, -- cgit v1.2.3