Skip to content

Commit f7dacf4

Browse files
committed
HTML emails to feature owners
1 parent 97267ce commit f7dacf4

File tree

5 files changed

+72
-61
lines changed

5 files changed

+72
-61
lines changed

app.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ handlers:
5353
login: admin # Prevents raw access to this handler. Cron runs as admin.
5454

5555
- url: /tasks/.*
56-
script: admin.app
56+
script: notifier.app
5757
login: admin # Prevents raw access to this handler. Tasks runs as admin.
5858

5959
- url: /admin/gae/.*

notifier.py

+58-48
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ def create_wf_content_list(component):
5050
return []
5151

5252
for url in content:
53-
list += ' - <a href="{url}">{url}</a>. Last updated: {updatedOn})\n'.format(
53+
list += '<li><a href="{url}">{url}</a>. Last updated: {updatedOn})</li>'.format(
5454
url=url['url'], updatedOn=url['updatedOn'])
5555
if not wf_component_content:
56-
list = 'None'
56+
list = '<li>None</li>'
5757
return list
5858

5959
def email_feature_owners(feature, is_update=False, changes=[]):
@@ -63,8 +63,11 @@ def email_feature_owners(feature, is_update=False, changes=[]):
6363
logging.warn('Blink component "%s" not found. Not sending email to owners' % component_name)
6464
return
6565

66-
owner_names = [owner.name for owner in component.owners]
67-
if not owner_names:
66+
# owners = component.owners
67+
# TODO: restrict emails to me for now to see if they're not too noisy.
68+
owners = models.FeatureOwner.all().filter('email = ', '[email protected]').fetch(1)
69+
70+
if not owners:
6871
logging.info('Blink component "%s" has no owners. Skipping email.' % component_name)
6972
return
7073

@@ -77,75 +80,79 @@ def email_feature_owners(feature, is_update=False, changes=[]):
7780

7881
created_on = datetime.datetime.strptime(str(feature.created), "%Y-%m-%d %H:%M:%S.%f").date()
7982
new_msg = """
80-
Hi {owners},
81-
82-
{created_by} added a new feature to chromestatus. You are listed as a web platform owner for "{component_name}".
83-
See https://www.chromestatus.com/feature/{id} for more details.
84-
---
85-
86-
Feature: {name}
87-
88-
Created: {created}
89-
Implementation status: {status}
90-
Milestone: {milestone}
91-
92-
---
93-
Next steps:
94-
- Try the API, write a sample, provide early feedback to eng.
95-
- Consider authoring a new article/update for /web.
96-
- Write a <a href="https://github.com/GoogleChrome/lighthouse/tree/master/docs/recipes/custom-audit">new Lighthouse audit</a>. This can help drive adoption of an API over time.
97-
- Add a sample to https://github.com/GoogleChrome/samples (see <a href="https://github.com/GoogleChrome/samples#contributing-samples">contributing</a>).
98-
- Don't forget add your demo link to the <a href="https://www.chromestatus.com/admin/features/edit/{id}">chromestatus feature entry</a>.
83+
<html><body>
84+
<p>Hi {owners},</p>
85+
86+
<p>You are listed as a web platform owner for "{component_name}". {created_by} added a new feature to this component.</p>
87+
<hr>
88+
89+
<p> <a href="https://www.chromestatus.com/feature/{id}">{name}</a> (added {created})</p>
90+
<p><b>Milestone</b>: {milestone}</p>
91+
<p><b>Implementation status</b>: {status}</p>
92+
93+
<hr>
94+
<p>Next steps:</p>
95+
<ul>
96+
<li>Try the API, write a sample, provide early feedback to eng.</li>
97+
<li>Consider authoring a new article/update for /web.</li>
98+
<li>Write a <a href="https://github.com/GoogleChrome/lighthouse/tree/master/docs/recipes/custom-audit">new Lighthouse audit</a>. This can help drive adoption of an API over time.</li>
99+
<li>Add a sample to https://github.com/GoogleChrome/samples (see <a href="https://github.com/GoogleChrome/samples#contributing-samples">contributing</a>).</li>
100+
<li>Don't forget add your demo link to the <a href="https://www.chromestatus.com/admin/features/edit/{id}">chromestatus feature entry</a>.</li>
101+
</ul>
102+
</body></html>
99103
""".format(name=feature.name, id=feature.key().id(), created=created_on,
100104
created_by=feature.created_by, component_name=component_name,
101-
owners=', '.join(owner_names), milestone=milestone_str,
105+
owners=', '.join([owner.name for owner in owners]), milestone=milestone_str,
102106
status=models.IMPLEMENTATION_STATUS[feature.impl_status_chrome])
103107

104108
updated_on = datetime.datetime.strptime(str(feature.updated), "%Y-%m-%d %H:%M:%S.%f").date()
105109
formatted_changes = ''
106110
for prop in changes:
107-
formatted_changes += '- %s: %s -> %s\n' % (prop['prop_name'], prop['old_val'], prop['new_val'])
111+
formatted_changes += '<li>%s: %s -> %s</li>' % (prop['prop_name'], prop['old_val'], prop['new_val'])
108112
if not formatted_changes:
109-
formatted_changes = 'None'
113+
formatted_changes = '<li>None</li>'
110114

111-
update_msg = """
112-
Hi {owners},
115+
update_msg = """<html><body>
116+
<p>Hi {owners},</p>
113117
114-
{updated_by} updated a feature on chromestatus. You are listed as a web platform owner for "{component_name}".
115-
See https://www.chromestatus.com/feature/{id} for more details.
116-
---
118+
<p>You are listed as a web platform owner for "{component_name}". A new feature was added:</p>
119+
<hr>
117120
118-
Feature: <a href="https://www.chromestatus.com/feature/{id}">{name}</a>
121+
<p><a href="https://www.chromestatus.com/feature/{id}">{name}</a> (updated {updated})</p>
122+
<p><b>Milestone</b>: {milestone}</p>
123+
<p><b>Implementation status</b>: {status}</p>
119124
120-
Updated: {updated}
121-
Implementation status: {status}
122-
Milestone: {milestone}
123-
124-
Changes:
125+
<p>Changes:</p>
126+
<ul>
125127
{formatted_changes}
126-
127-
---
128-
Next steps:
129-
- Check existing <a href="https://github.com/GoogleChrome/lighthouse/tree/master/lighthouse-core/audits">Lighthouse audits</a> for correctness.
130-
- Check existing /web content for correctness. Non-exhaustive list:
131-
{wf_content}
128+
</ul>
129+
130+
<hr>
131+
<p>Next steps:</p>
132+
<ul>
133+
<li>Check existing <a href="https://github.com/GoogleChrome/lighthouse/tree/master/lighthouse-core/audits">Lighthouse audits</a> for correctness.</li>
134+
<li>Check existing /web content for correctness. Non-exhaustive list:
135+
<ul>{wf_content}</ul>
136+
</li>
137+
</ul>
138+
</body></html>
132139
""".format(name=feature.name, id=feature.key().id(), updated=updated_on,
133140
updated_by=feature.updated_by, component_name=component_name,
134-
owners=', '.join(owner_names), milestone=milestone_str,
141+
owners=', '.join([owner.name for owner in owners]), milestone=milestone_str,
135142
status=models.IMPLEMENTATION_STATUS[feature.impl_status_chrome],
136143
formatted_changes=formatted_changes,
137144
wf_content=create_wf_content_list(component_name))
138145

139146
message = mail.EmailMessage(sender='Chromestatus <[email protected]>',
140-
subject='chromestatus update',
141-
to=[owner.email for owner in component.owners])
147+
subject='update',
148+
to=[owner.email for owner in owners])
142149

143150
if is_update:
144151
message.html = update_msg
145-
message.subject = 'chromestatus: updated feature'
152+
message.subject = 'updated feature: %s' % feature.name
146153
else:
147154
message.html = new_msg
148-
message.subject = 'chromestatus: new feature'
155+
message.subject = 'new feature: %s' % feature.name
149156

150157
message.check_initialized()
151158

@@ -223,6 +230,9 @@ def _send_notification_to_feature_subscribers(self, feature, is_update=False):
223230
feature: Feature that was added/modified.
224231
is_update: True if this was an update to the feature. False if it was newly added.
225232
"""
233+
if not settings.SEND_PUSH_NOTIFICATIONS:
234+
return
235+
226236
feature_id = feature.key().id()
227237
topic_id = feature_id if is_update else 'new-feature'
228238

queue.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ queue:
66
task_retry_limit: 3
77
task_age_limit: 2d
88
# bucket_size: 40
9-
# max_concurrent_requests: 10
9+
# max_concurrent_requests: 5
1010
# total_storage_limit: 120M

settings.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
else:
3939
TEMPLATE_CACHE_TIME = 600 # seconds
4040

41-
SEND_EMAIL = False # Flag to turn off email notifications to feature owners.
41+
SEND_EMAIL = True # Flag to turn off email notifications to feature owners.
42+
SEND_PUSH_NOTIFICATIONS = True # Flag to turn off sending push notifications for all users.
4243

4344
FIREBASE_SERVER_KEY = os.environ.get('FIREBASE_SERVER_KEY')

templates/admin/notifications/list.html

+10-10
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,18 @@ <h3>Subscribers breakdown by feature</h3>
9999

100100
function useId(el) {
101101
event.stopPropagation();
102-
event.preventDefault();
102+
// event.preventDefault();
103103
document.getElementById('feature_id').value = el.dataset.id;
104104
}
105105

106106
async function init() {
107+
$('#num_push_tokens').textContent = SUBSCRIPTIONS.length;
108+
document.querySelectorAll('.no-push-notifications').forEach(el => {
109+
el.classList.remove('no-push-notifications');
110+
el.disabled = false;
111+
});
112+
107113
const featureList = await getFeaturesList();
108-
document.body.classList.remove('loading');
109114

110115
// if (!PushNotifier.SUPPORTS_NOTIFICATIONS) {
111116
// return;
@@ -147,26 +152,21 @@ <h3>Subscribers breakdown by feature</h3>
147152
return f;
148153
}).sort((a, b) => a.pushSubscribers - b.pushSubscribers).reverse();
149154

150-
151155
const frag = new DocumentFragment();
152156
sortedFeatures.forEach(f => {
153157
const li = document.createElement('li');
154158
li.textContent = f.name; // escape html entities.
155159
li.innerHTML = `
156160
<span class="num_subscribers">Subscribers: ${f.pushSubscribers}</span>
157-
<a href="#" onclick="useId(this)" data-id="${f.id}">${li.innerHTML}</a>`;
161+
<a href="/feature/${f.id}" onclick="useId(this)" data-id="${f.id}">${li.innerHTML}</a>`;
158162
frag.appendChild(li);
159163
});
160164

165+
document.body.classList.remove('loading');
166+
161167
// Update UI.
162168
const list = document.querySelector('#feature_list');
163169
list.appendChild(frag);
164-
165-
$('#num_push_tokens').textContent = SUBSCRIPTIONS.length;
166-
document.querySelectorAll('.no-push-notifications').forEach(el => {
167-
el.classList.remove('no-push-notifications');
168-
el.disabled = false;
169-
});
170170
}
171171

172172
init();

0 commit comments

Comments
 (0)