Skip to content

Commit

Permalink
Merge pull request #4442 from magfest/guidebook-update-updates
Browse files Browse the repository at this point in the history
Add more improvements to Guidebook syncing
  • Loading branch information
kitsuta authored Jan 16, 2025
2 parents 83329ca + 3582077 commit ebb95b3
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 18 deletions.
4 changes: 4 additions & 0 deletions uber/configspec.ini
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,10 @@ panels_confirm_deadline = integer(default=0)
# be ready, so we just set this to false whenever it is.
hide_schedule = boolean(default=True)

# Emails about changes to schedule items and other Guidebook items are sent TO this address.
# If the address is not set, these emails are disabled.
guidebook_updates_email = string(default="")

# These are the areas from which we'll show events to associated with panel
# applications on the schedule.
panel_rooms = string_list(default=list())
Expand Down
4 changes: 3 additions & 1 deletion uber/models/panels.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,13 @@ class PanelApplication(MagModel):

@presave_adjustment
def update_event_info(self):
if self.event:
if self.event and any([getattr(self.event, key, '') != getattr(self, key, '') for key in [
'name', 'description', 'public_description', 'track']]):
self.event.name = self.name
self.event.description = self.description
self.event.public_description = self.public_description
self.event.track = self.track
self.event.last_updated = self.last_updated

@presave_adjustment
def set_default_dept(self):
Expand Down
71 changes: 66 additions & 5 deletions uber/tasks/panels.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,47 @@
import json

from collections import defaultdict
from datetime import timedelta
from dateutil import parser as dateparser
from sqlalchemy import or_

from uber.config import c
from uber.decorators import render
from uber.models import Session
from uber.models import Email, Session, Tracking
from uber.tasks import celery
from uber.tasks.email import send_email
from uber.utils import GuidebookUtils, localized_now


__all__ = ['panels_waitlist_unaccepted_panels', 'sync_guidebook_models', 'check_stale_guidebook_models']
__all__ = ['panels_waitlist_unaccepted_panels', 'sync_guidebook_models',
'check_deleted_guidebook_models', 'check_stale_guidebook_models']


def _get_deleted_models(session, deleted_since=None):
deleted_synced = session.query(Tracking).filter(Tracking.action == c.DELETED,
Tracking.snapshot.contains('"last_synced": {"data": {"guidebook"'))
if deleted_since:
deleted_synced = deleted_synced.filter(Tracking.when > deleted_since)

deleted_models = defaultdict(list)
model_names = {}

for key, label in c.GUIDEBOOK_MODELS:
model_names[key] = label

for tracking_entry in deleted_synced:
snapshot = json.loads(tracking_entry.snapshot)

model = snapshot['_model']
if model == 'GuestGroup':
model += '_band' if snapshot['group_type'] == c.BAND else '_guest'
elif model == 'Group':
model += '_dealer'

model_name = 'Schedule Item' if model == 'Event' else model_names[model]

deleted_models[model_name].append(snapshot['last_synced']['data']['guidebook']['name'])
return deleted_models


@celery.task
Expand All @@ -31,21 +62,51 @@ def sync_guidebook_models(selected_model, sync_time, id_list):
session.commit()


@celery.schedule(timedelta(hours=12))
def check_deleted_guidebook_models():
if not c.PRE_CON or not c.GUIDEBOOK_UPDATES_EMAIL:
return

with Session() as session:
subject = f"Deleted Guidebook Items: {localized_now().strftime("%A %-I:%M %p")}"
last_email = session.query(Email).filter(Email.subject.contains("Deleted Guidebook Items")
).order_by(Email.when.desc()).first()

deleted_models = _get_deleted_models(session, deleted_since=last_email.when) if last_email else _get_deleted_models(session)

if deleted_models:
body = render('emails/guidebook_deletes.txt', {
'deleted_models': deleted_models,
}, encoding=None)
send_email.delay(c.REPORTS_EMAIL, c.GUIDEBOOK_UPDATES_EMAIL,
subject, body, ident="guidebook_deletes"
)


@celery.schedule(timedelta(minutes=15))
def check_stale_guidebook_models():
if not c.AT_THE_CON:
if not c.AT_THE_CON or not c.GUIDEBOOK_UPDATES_EMAIL:
return

with Session() as session:
cl_updates, schedule_updates = GuidebookUtils.get_changed_models(session)
stale_models = [key for key in cl_updates if cl_updates[key]]
if schedule_updates:
stale_models.append('Schedule')
if stale_models:

last_email = session.query(Email).filter(or_(
Email.subject.contains("Guidebook Updates"),
Email.subject.contains("Deleted Guidebook Items"))
).order_by(Email.when.desc()).first()

deleted_models = _get_deleted_models(session, deleted_since=last_email.when) if last_email else _get_deleted_models(session)

if stale_models or deleted_models:
body = render('emails/guidebook_updates.txt', {
'stale_models': stale_models,
'deleted_models': deleted_models,
}, encoding=None)
send_email.delay(c.REPORTS_EMAIL, "[email protected]",
send_email.delay(c.REPORTS_EMAIL, c.GUIDEBOOK_UPDATES_EMAIL,
f"Guidebook Updates: {localized_now().strftime("%A %-I:%M %p")}",
body, ident="guidebook_updates"
)
Expand Down
7 changes: 7 additions & 0 deletions uber/templates/emails/guidebook_deletes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Attention Guidebook admins! The items below have been deleted from the system.

{% for category in deleted_models %}{{ category }}(s):
{% for item in deleted_models[category] %} - {{ item }}
{% endfor %}{% endfor %}

This email will be the only record of these item deletions. Please keep it until you delete all the above items from Guidebook.
11 changes: 9 additions & 2 deletions uber/templates/emails/guidebook_updates.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Attention Guidebook admins!
The following categories have stale items:

{% if stale_models %}The following categories have been updated in the system:
{% for label in stale_models %}- {{ label }}
{% endfor %}

You can review all pending items here: {{ c.URL_BASE }}/schedule_reports/index
{% endif %}{% if deleted_models %}The following items have been deleted:
{% for category in deleted_models %}{{ category }}(s):
{% for item in deleted_models[category] %} - {{ item }}
{% endfor %}
{% endfor %}

{% endif %}You can review all pending items here: {{ c.URL_BASE }}/schedule_reports/index
23 changes: 13 additions & 10 deletions uber/templates/schedule_reports/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@
});
}

let markItemSynced = function(event){
let form = $(this);
event.preventDefault();
let markItemSynced = function(){
let form = $(this).closest('form');

$.ajax({
method: 'POST',
url: 'mark_item_synced',
Expand Down Expand Up @@ -81,7 +81,10 @@
}

$().ready(function () {
$(".sync-item").submit(markItemSynced);
$('#schedule-table').on('click', '.sync-item', markItemSynced);
{% for model, label in c.GUIDEBOOK_MODELS %}
$('#{{ model }}-table').on('click', '.sync-item', markItemSynced);
{% endfor %}
});
</script>
{% set now = now() %}
Expand Down Expand Up @@ -158,7 +161,7 @@ <h2>Guidebook Exports and Updates -- {{ now|datetime_local("%m/%d/%Y, %-I:%M%p")
</form>
<p>Items in <em>italics</em> has been changed since the last sync.</p>
</div>
<table class="table table-hover datatable">
<table class="table table-hover datatable" id="schedule-table">
<thead>
<tr>
<th>Name</th>
Expand Down Expand Up @@ -203,13 +206,13 @@ <h2>Guidebook Exports and Updates -- {{ now|datetime_local("%m/%d/%Y, %-I:%M%p")
{% if c.HAS_SCHEDULE_ACCESS %}
<a href="../schedule/form?id={{ result.id }}" target="_blank" class="btn btn-primary">View</a>
{% endif %}
<form method="post" class="sync-item" action="mark_item_synced">
<form method="post" action="mark_item_synced">
{{ csrf_token() }}
<input type="hidden" name="selected_model" value="schedule" />
<input type="hidden" name="id" value="{{ result.id }}" />
<input type="hidden" name="sync_time" value="{{ now }}" />
<input type="hidden" name="sync_data" value='{{ current_data|tojson }}' />
<button type="submit" class="btn btn-success">Mark Synced</button>
<button type="button" class="btn btn-success sync-item">Mark Synced</button>
</form>
</div>
</td>
Expand All @@ -234,7 +237,7 @@ <h2>Guidebook Exports and Updates -- {{ now|datetime_local("%m/%d/%Y, %-I:%M%p")
</form>
<p>Items in <em>italics</em> has been changed since the last sync.</p>
</div>
<table class="table table-hover datatable">
<table class="table table-hover datatable" id="{{ model }}-table">
<thead>
<tr>
{% for key, label in c.GUIDEBOOK_PROPERTIES %}
Expand Down Expand Up @@ -275,13 +278,13 @@ <h2>Guidebook Exports and Updates -- {{ now|datetime_local("%m/%d/%Y, %-I:%M%p")
{% if result.guidebook_edit_link.split('/')[1] in (c.GETTABLE_SITE_PAGES[0] + c.ADMIN_ACCESS_SET|list) %}
<a href="{{ result.guidebook_edit_link }}" target="_blank" class="btn btn-primary">View</a>
{% endif %}
<form method="post" class="sync-item" action="mark_item_synced">
<form method="post" action="mark_item_synced">
{{ csrf_token() }}
<input type="hidden" name="selected_model" value="{{ model }}" />
<input type="hidden" name="id" value="{{ result.id }}" />
<input type="hidden" name="sync_time" value="{{ now }}" />
<input type="hidden" name="sync_data" value='{{ current_data|tojson }}' />
<button type="submit" class="btn btn-success">Mark Synced</button>
<button type="button" class="btn btn-success sync-item">Mark Synced</button>
</form>
</div>
</td>
Expand Down

0 comments on commit ebb95b3

Please sign in to comment.