Skip to content

Commit

Permalink
Merge pull request #262 from Windham-High-School/release-1.6.0
Browse files Browse the repository at this point in the history
Release 1.6.0
  • Loading branch information
snorklerjoe authored May 8, 2023
2 parents d60ec0d + 7863a40 commit 47760eb
Show file tree
Hide file tree
Showing 22 changed files with 272 additions and 110 deletions.
2 changes: 1 addition & 1 deletion Docker/CubeServer-api/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Gunicorn==20.1.0 # WSGI server
Flask==2.2.2
Flask==2.3.2
Flask-RESTful==0.3.9
Flask-HTTPAuth==4.7.0
jsonpickle==3.0.1
Expand Down
1 change: 0 additions & 1 deletion Docker/CubeServer-app/package_lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ cd $WORKING_DIR

git clone $1
cd "$(basename "$1" .git)"
rm -r .git
cp ../"$(basename "$2")" ./lib/ || cp ../"$(basename "$2")" ./
mv "$(bash package.sh | tail -n1)" "$3"
cd /tmp
Expand Down
4 changes: 2 additions & 2 deletions Docker/CubeServer-app/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Gunicorn==20.1.0 # WSGI server
Flask==2.2.3 # Flask core for web app
Flask==2.3.2 # Flask core for web app
Flask-PyMongo==2.3.0 # MongoDB access
better-profanity==0.7.0 # Profanity checking/filtering
Flask-WTF==1.0.1 # Flask-WTForms for handling forms and captcha
Flask-WTF==1.1.1 # Flask-WTForms for handling forms and captcha
wtforms[email]==3.0.1 # A package for facilitating email validation
Flask-Bootstrap==3.3.7.1 # Bootstrap integration with Flask
uptime==3.0.0 # For server stats
Expand Down
2 changes: 1 addition & 1 deletion src/CubeServer-api/cubeserver_api/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.5.3"
__version__ = "1.6.0"
2 changes: 1 addition & 1 deletion src/CubeServer-app/cubeserver_app/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.5.3"
__version__ = "1.6.0"
95 changes: 72 additions & 23 deletions src/CubeServer-app/cubeserver_app/blueprints/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from cubeserver_common.models.beaconmessage import OutputDestination
from cubeserver_common.models.multiplier import Multiplier, MassMultiplier, VolumeMultiplier, CostMultiplier, VolumeUnit
from cubeserver_common.models.mail import Message
from cubeserver_common.models.beaconmessage import BeaconMessage, BeaconMessageEncoding
from cubeserver_common.models.beaconmessage import BeaconMessage, BeaconMessageEncoding, SentStatus
from cubeserver_common.models.reference import ReferencePoint
from cubeserver_common.config import FROM_NAME, FROM_ADDR, INTERNAL_SECRET_LENGTH, TEMP_PATH
from cubeserver_common.reference_api import protocol as ref_protocol
Expand Down Expand Up @@ -97,23 +97,54 @@ def admin_home():
conf_form.banner_message.data = db_conf.banner_message
conf_form.beacon_polling_period.data = db_conf.beacon_polling_period

# Reserved / internal team handling:
reserved_links = []
for name in Team.RESERVED_NAMES:
if Team.find_by_name(name) is not None:
reserved_links += [(name, None)]
team = Team.find_by_name(name)
if team is not None:
reserved_links += [
(
name,
None,
url_for('.referencetest', station_id=team.reference_id)
if team.is_reference else None
)
]
else:
reserved_links += [(name, url_for('.gen_reserved', name=name))]

beacon_form = ImmediateBeaconForm()
beacon_form.instant.data = datetime.now()
reserved_links += [
(
name,
url_for('.gen_reserved', name=name),
None
)
]

# Beacon Status:
txd = 0
sched = 0
missed = 0
for msg in BeaconMessage.find_since(timedelta(days=1)):
if msg.status == SentStatus.TRANSMITTED:
txd += 1
elif msg.status == SentStatus.MISSED:
missed += 1
elif msg.status == SentStatus.SCHEDULED:
sched += 1
beacon_status = {
'transmitted_today': txd,
'missed_today': missed,
'scheduled_today': sched,
'queued': len(BeaconMessage.find_by_status(SentStatus.QUEUED)),
'transmitted': len(BeaconMessage.find_by_status(SentStatus.TRANSMITTED)),
'missed': len(BeaconMessage.find_by_status(SentStatus.MISSED))
}

# Render the template:
return render_template(
'console.html.jinja2',
teams_table = teams_table.__html__(),
user_form = InvitationForm(),
config_form = conf_form,
beacon_form = beacon_form,
email_groups = {
TeamLevel.JUNIOR_VARSITY.value: base64.urlsafe_b64encode(', '.join([
', '.join(team.emails) for team in
Expand All @@ -128,6 +159,7 @@ def admin_home():
Team.find()
]).encode())
}.items(),
beacon_stats = beacon_status,
reserved_names=reserved_links,
rand=randint(0, 1000000)
)
Expand Down Expand Up @@ -155,6 +187,8 @@ def beacon_csv():
try:
csv_reader = csv.DictReader(reopened)
for row in csv_reader:
if row['time'] == '' or row['body'] == '' or row['output'] == '' or row['encoding'] == '' or row['misfire grace time'] == '' or row['intensity'] == '':
continue
BeaconMessage(
instant=datetime.fromisoformat(row['time']),
division=TeamLevel(row['division']),
Expand Down Expand Up @@ -564,9 +598,14 @@ def beacon_table():
if current_user.level != UserLevel.ADMIN:
return abort(403)
table = BeaconMessageTable(BeaconMessage.find())

beacon_form = ImmediateBeaconForm()
beacon_form.instant.data = datetime.now()

return render_template(
'beacon_table.html.jinja2',
beacon_table=table.__html__(),
beacon_form=beacon_form,
current_time = str(datetime.now())
)

Expand Down Expand Up @@ -717,23 +756,33 @@ def package_beacon_code():
)
return response

@bp.route('/referencetest')
@bp.route('/referencetest/<station_id>')
@login_required
def referencetest():
def referencetest(station_id: str | int = ""):
"""Tests reference stations"""
# Check admin status:
if current_user.level != UserLevel.ADMIN:
return abort(403)
request = ref_protocol.ReferenceRequest(
id = b'0x00',
signal=ref_protocol.ReferenceSignal.ENQ,
command=ref_protocol.ReferenceCommand.MEAS,
param=ref_protocol.MeasurementType.TEMP.value
)
with DispatcherClient() as client:
response = client.request(request)
return render_template(
'reference_test.html.jinja2',
request_pre = pformat(request.dump(), indent=4),
response_pre = pformat(response.dump(), indent=4)
)
try:
request = ref_protocol.ReferenceRequest(
id = int(station_id).to_bytes(1, 'big'),
signal=ref_protocol.ReferenceSignal.ENQ,
command=ref_protocol.ReferenceCommand.MEAS,
param=ref_protocol.MeasurementType.TEMP.value
)
with DispatcherClient() as client:
response = client.request(request)
if response is None:
return render_template(
'errorpages/500.html.jinja2',
message="No response received"
)
return render_template(
'reference_test.html.jinja2',
request_pre = pformat(request.dump(), indent=4),
response_pre = pformat(response.dump(), indent=4)
)
except:
tb = traceback.format_exc()
logging.error(tb)
return render_template('errorpages/500.html.jinja2', message=tb)
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,64 @@
{% block title %}Beacon Schedule{% endblock %}

{% block header %}
<h4>Beacon Messages</h4>
{% endblock %}

{% block content %}
<form method="POST" enctype="multipart/form-data" action="{{ url_for('admin.beacon_csv') }}">
Upload CSV<br>
<a href="https://gist.github.com/snorklerjoe/70d627482c4ce41ba48bc05478ddf868">CSV Template</a><br>
<a href="https://gist.githubusercontent.com/snorklerjoe/70d627482c4ce41ba48bc05478ddf868/raw/602d67ef5cde05f40cbb18080401b2ee921864a9/example.csv" download>CSV Template</a><br>
<input type="file" name="file" class="btn">
<input type="submit" value="Upload" class="btn btn-submit btn-danger">
</form>
<hr>
<h4>Scheduled Transmissions</h4>
<p>
(as of {{current_time}})
<h4>Beacon Message Status Steps:</h4>
<ol>
<li><b>Queued</b> - Message is entered to the database</li>
<li><b>Scheduled</b> - Database entry is read by the beaconserver and a task to transmit the message is scheduled</li>
<li><b>Missed</b> - Message could not be transmitted and the grace window passed</li>
<li><b>Transmitting...</b> - Message is <i>presently</i> being transmitted</li>
<li><b>Transmitted</b> - Message <i>was</i> transmitted by the beacon</li>
</ol>
<i>Note that beacon messages cannot be de-commissioned or unscheduled without shutting down the server.</i>
</p>
{% endblock %}

{% block content %}
<div>
{% block body %}
{{ super() }}
<div class="justify-content-md-center container container-fluid text-center">
{{macros.startbox("Beacon Message Database", "col")}}

<h4>Scheduled Transmissions</h4>
<p>
(as of {{current_time}})
</p>
<div class="col">
{{ beacon_table }}
</div>
{{macros.endbox()}}

<div class="col">
{{macros.startbox("Live Schedule", "col")}}
<p>
Schedule a message now:
</p>
<form method="POST" action="beaconnow" class="form">
{{ beacon_form.hidden_tag() }}
{{ wtf.form_errors(beacon_form, hiddens="only") }}

<i>Leave time as is and use adequate misfire grace period in order to transmit immediately</i>
{{ wtf.form_field(beacon_form.instant) }}
{{ wtf.form_field(beacon_form.message) }}
{{ wtf.form_field(beacon_form.division) }}
{{ wtf.form_field(beacon_form.destination) }}
{{ wtf.form_field(beacon_form.msg_format) }}
{{ wtf.form_field(beacon_form.intensity) }}
{{ wtf.form_field(beacon_form.misfire_grace) }}
{{ wtf.form_field(beacon_form.submit) }}
</form>
{{macros.endbox()}}
</div>
{% endblock %}

{% block scripts %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,23 @@
{{macros.startbox("Beacon", "col")}}
<a href={{ url_for('admin.beacon_table') }} class="btn">Manage Message Queue</a>
<hr>
<p>
Schedule a message now:
</p>
<form method="POST" action="beaconnow" class="form">
{{ beacon_form.hidden_tag() }}
{{ wtf.form_errors(beacon_form, hiddens="only") }}

{{ wtf.form_field(beacon_form.instant) }}
{{ wtf.form_field(beacon_form.message) }}
{{ wtf.form_field(beacon_form.division) }}
{{ wtf.form_field(beacon_form.destination) }}
{{ wtf.form_field(beacon_form.msg_format) }}
{{ wtf.form_field(beacon_form.intensity) }}
{{ wtf.form_field(beacon_form.misfire_grace) }}
{{ wtf.form_field(beacon_form.submit) }}
</form>

<br>

<u>Beacon Status-</u><br><br>

<b>Statistics- 24 hrs-</b>
<ul>
<li>Messages Transmitted: <code style="float:right;">{{ beacon_stats['transmitted_today'] }}</code></li>
<li>Messages Scheduled: <code style="float:right;">{{ beacon_stats['scheduled_today'] }}</code></li>
<li>Messages Missed: <code style="float:right;">{{ beacon_stats['missed_today'] }}</code></li>
</ul>
<b>All time-</b>
<ul>
<li>Messages Queued: <code style="float:right;">{{ beacon_stats['queued'] }}</code></li>
<li>Messages Transmitted: <code style="float:right;">{{ beacon_stats['transmitted'] }}</code></li>
<li>Messages Missed: <code style="float:right;">{{ beacon_stats['missed'] }}</code></li>
</ul>
{{macros.endbox()}}

</div>
Expand Down Expand Up @@ -116,9 +117,9 @@
{{macros.startbox("Internal API \"Team\"s", "col")}}
<b>Reserved team names </b> (click to generate)<b>:</b>
<ul>
{% for name, url in reserved_names %}
{% for name, url, url2 in reserved_names %}
{% if url == None %}
<li>{{name}}</li>
<li>{{name}} {% if url2 != None %}<a href={{url2}}>Test</a>{% endif %}</li>
{% else %}
<li><a href={{url}}>{{name}}</a></li>
{% endif %}
Expand Down
6 changes: 3 additions & 3 deletions src/CubeServer-app/cubeserver_app/tables/beaconmessages.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""Tables for team-related things."""

from typing import List
from flask_table import Table, Col, DatetimeCol
from flask_table import Table, Col

from cubeserver_common.models.beaconmessage import BeaconMessage
from cubeserver_app.tables.columns import EnumCol, PreCol
from cubeserver_app.tables.columns import EnumCol, PreCol, DateTimeCol

__all__ = ['BeaconMessageTable']

Expand All @@ -16,7 +16,7 @@ class BeaconMessageTable(Table):
thead_classes = ["thead-dark"]
border = True

send_at = DatetimeCol('Scheduled Time')
send_at = DateTimeCol('Scheduled Time')
str_status = Col('Status')
division = EnumCol('Division')
destination = EnumCol('Output')
Expand Down
35 changes: 35 additions & 0 deletions src/CubeServer-app/cubeserver_app/tables/columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,41 @@ def td(self, item, attr):
escape_content=False,
attrs=td_attrs | self.td_html_attrs)

class DateTimeCol(HTMLCol):
def generate_html(
self,
content: Any,
identifier: str,
attr_list: List[str]
) -> str:
"""An abstract method for generating a cell's HTML
Args:
content (Any): The Python object represented by the cell
identifier (str): If item.id exists, the string value of item.id for use in pymongo document-modifying forms
attr_list (List[str]): attr_list from Col.__init__
Returns:
str: HTML for this cell
"""
return f"<span class='datetime'>{content}</span>"
def generate_attrs(
self,
content: Any
) -> Mapping[str, str]:
"""Generate any attributes to the td element
Args:
content (Any): the python object from which the cell contents are derived
Returns:
Mapping[str, str]
"""
return {
'data-search': str(content.isoformat()),
'data-order': str(content.timestamp())
}

class DropDownEnumCol(Col):
"""A Column with a drop-down box to select an option from an Enum"""

Expand Down
Loading

0 comments on commit 47760eb

Please sign in to comment.