Skip to content

Commit

Permalink
Light refactoring for teams webhooks / send_report
Browse files Browse the repository at this point in the history
  • Loading branch information
sondregronas committed Aug 26, 2023
1 parent bf4197c commit a872e34
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 76 deletions.
7 changes: 7 additions & 0 deletions BookingSystem/audits.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,10 @@ def get_all() -> list[dict]:
'event': row[2],
'message': Markup(row[3]).unescape()
} for row in rows]


def get_new_deviations(since: float | int | str) -> list:
"""Return a list of new deviations."""
return [a['message'] for a in get_all()
if parser.parse(a['timestamp']) > datetime.fromtimestamp(float(since))
and a['event'] == 'AVVIK']
39 changes: 0 additions & 39 deletions BookingSystem/extra_utils/import_from_labelserver.py

This file was deleted.

8 changes: 5 additions & 3 deletions BookingSystem/inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def api_repr(self) -> dict:
'last_seen': self.last_seen
}

def mail_repr(self) -> str:
def html_repr(self) -> str:
return f'<strong>{self.lender_name}: {self.id}</strong> <small>{self.name} ({self.category}, Frist: {self.order_due_date_fmt})</small>'

def __str__(self) -> str:
Expand Down Expand Up @@ -89,10 +89,12 @@ def lender_association(self) -> str:
return self.user.get('classroom') or 'Lærer'

@property
def lender_association_mail(self) -> str:
def lender_association_html(self) -> str:
if not self.user.get('name'):
return 'Slettet bruker [Sjekk historikk]'
return self.user.get('classroom') or 'Ansatt'
if not self.user.get('classroom'):
return 'Ansatt'
return f'{self.classroom} (<at>{self.teacher}</at>)'

@property
def classroom(self) -> str:
Expand Down
59 changes: 31 additions & 28 deletions BookingSystem/teams.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import flask
import markupsafe
import pymsteams
from dateutil.parser import parser

import audits
import inventory
Expand All @@ -12,28 +11,31 @@
from sanitizer import APIException


def update_last_sent() -> None:
"""Write the current date to 'data/last_sent.txt'."""
Settings.set('report_last_sent', str(datetime.now().timestamp()))
def get_overdue_items_pairs(items: list) -> dict:
"""Return a dictionary of overdue items grouped by lender association (in html representation)."""
pairs = {item.lender_association_html: [item2.html_repr()
for item2 in items
if item2.lender_association_html == item.lender_association_html]
for item in items}
return {key: pairs[key] for key in sorted(pairs) if pairs[key]}


def formatted_overdue_items(items: list) -> str:
"""Return formatted HTML string of overdue items."""
if not items:
return ''
pairs = {item.lender_association_mail: [item2.mail_repr()
for item2 in items
if item2.lender_association_mail == item.lender_association_mail]
for item in items}
sorted_pairs = {key: pairs[key] for key in sorted(pairs) if pairs[key]}
pairs = get_overdue_items_pairs(items)

strings = [
f'<table bordercolor="black" border="1"><tr style="background-color: teal; color: white;"><th><b>&nbsp;Tilhørighet: {key or "Ansatt"}</b></th></tr>\n' +
'\n'.join([f'<tr><td>&nbsp;{item}</td></tr>' for item in sorted_pairs[key]]) +
'<table bordercolor="black" border="1">' +
'<tr style="background-color: teal; color: white;">' +
f'<th><b>&nbsp;Tilhørighet: {association or "Ansatt"}</b></th>' +
f'</tr>\n' +
'\n'.join([f'<tr><td>&nbsp;{item}</td></tr>' for item in pairs[association]]) +
f'</table><br>'
for key in sorted_pairs.keys()]
for association in pairs.keys()]
return '\n'.join(strings) + \
'\n<i>Dersom du kjenner igjen utlåneren, vennligst få dem til å levere utstyret tilbake ASAP.</i>'
'\n<small><i>Dersom du kjenner igjen utlåneren, vennligst få dem til å levere utstyret tilbake ASAP.</i></small>'


def formatted_new_deviations(deviations: list) -> str:
Expand All @@ -43,28 +45,30 @@ def formatted_new_deviations(deviations: list) -> str:
return '\n'.join([f'<li><b>{markupsafe.escape(deviation)}</b></li>' for deviation in deviations])


def generate_card(title, text, color) -> pymsteams.connectorcard:
"""Generate a card."""
card = pymsteams.connectorcard(None)
def generate_card(title, text, color, webhook=None) -> pymsteams.connectorcard:
"""Generate a teams card."""
card = pymsteams.connectorcard(webhook)
card.title(title)
card.color(color)
card.text(text)
return card


def generate_cards(webhook: str = None):
"""Send a report to a webhook."""
def last_sent_within_hour_treshold(debug=False) -> float | int | str:
"""Return True if the last report was sent less than an hour ago."""
last_sent = Settings.get('report_last_sent') or 0
if last_sent:
if datetime.now().timestamp() - float(last_sent) < 3600:
raise APIException('Rapport ble ikke sendt: forrige rapport ble sendt for under en time siden.', 400)
if last_sent and datetime.now().timestamp() - float(last_sent) < 3600:
raise APIException('Rapport ble ikke sendt: forrige rapport ble sendt for under en time siden.', 400)
Settings.set('report_last_sent', str(datetime.now().timestamp()))
return last_sent

update_last_sent()

overdue_items = [item for item in inventory.get_all_unavailable() if item.overdue]
new_deviations = [audit['message'] for audit in audits.get_all()
if parser().parse(audit['timestamp']) > datetime.fromtimestamp(float(last_sent))
and audit['event'] == 'AVVIK']
def generate_cards() -> list[pymsteams.connectorcard]:
"""Send a report to a webhook."""
last_sent = last_sent_within_hour_treshold()

overdue_items = inventory.get_all_overdue()
new_deviations = audits.get_new_deviations(since=last_sent)

cards = list()
if overdue_items:
Expand Down Expand Up @@ -92,7 +96,6 @@ def send_report() -> flask.Response:
[send_card_to_hook(card, webhook)
for webhook in TEAMS_WEBHOOKS
for card in generate_cards()]
return flask.Response('Rapport ble sendt til alle konfigurerte teamskanaler!', 200)
except pymsteams.TeamsWebhookException:
raise APIException('Rapport ble ikke sendt: ugyldig webhook.', 400)
finally:
return flask.Response('Rapport sendt.', status=200)
2 changes: 1 addition & 1 deletion BookingSystem/templates/admin_settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ <h3>En beskjed på toppen av siden. La feltene stå tomme for å fjerne bulletin
url: "{{url_for('api.send_report')}}",
type: 'POST',
success: function (result) {
customAlert('Sendt!', 'Rapporten ble sendt ut.', 'success', true)
customAlert('Sendt!', result, 'success', true)
},
error: function (result) {
customAlert('Info', result.responseText, 'info', true)
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
[![codecov](https://codecov.io/gh/sondregronas/FeideUtstyrbase/branch/main/graph/badge.svg?token=JNLY5WWC3X)](https://codecov.io/gh/sondregronas/FeideUtstyrbase)
[![License](https://img.shields.io/github/license/sondregronas/FeideUtstyrbase)](https://github.com/sondregronas/FeideUtstyrbase/blob/main/LICENSE)

For Vågen VGS, might be possible to use for other schools with some modifications - for now this fits our needs, but will most likely not fit yours. (Work in progress, this is NOT plug and play)
For Vågen VGS, might be possible to use for other schools with some modifications. (Work in progress, this is NOT plug and play)

## Setup
Proper README coming soon.

- Register app at Dataporten (must be approved by a Feide Administrator)
- Set callback url to `https://<your-domain>/login/feide/callback`
- Setup https://github.com/VaagenIM/EtikettServer (Currently not optional)
- Only supports SMTP for now.
- Setup https://github.com/VaagenIM/EtikettServer (currently not optional)
- Add as many Teams webhooks as you want, they need to be comma separated in the `TEAMS_WEBHOOKS` environment variable. (using cron to send out reports - will be built in sooner or later, see `cron_examples.txt` for an example use case)
- Run the `docker-compose.yml` file after setting up the environment variables.

Must be accessed through a reverse proxy, as the server does not support https. Use something like NginxProxyManager. Port `5000` is exposed.
Must be accessed through a reverse proxy, such as [NginxProxyManager](https://nginxproxymanager.com/).

If you need a kiosk mode, set up a separate reverse proxy with a valid access control (see NgixnProxyManager) and set the `KIOSK_MODE` environment variable to the FQDN of the proxy.
If you need a kiosk, set up a separate reverse proxy with proper access controls (limit to specified IP) and set `KIOSK_FQDN` to the FQDN of the kiosk proxy.

Logo and favicon can be changed by replacing the files in `/overrides/static/` with your own within the container, requiring the mapping of the `/overrides` directory. You can also change any of the files inside `BookingSystem` by putting them in `/overrides` should you need to.

Expand Down

0 comments on commit a872e34

Please sign in to comment.