This repository has been archived by the owner on Aug 26, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 681
/
tasks.py
216 lines (175 loc) · 6.73 KB
/
tasks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import logging
from celery.task import task, group
from django.conf import settings
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.db import transaction
from django.dispatch import receiver
from django.db import connection
from django.core.cache import get_cache
from constance import config
from devmo.utils import MemcacheLock
from .exceptions import StaleDocumentsRenderingInProgress, PageMoveError
from .models import Document, RevisionIP
from .signals import render_done
log = logging.getLogger('k.task')
@task(rate_limit='60/m')
def render_document(pk, cache_control, base_url):
"""Simple task wrapper for the render() method of the Document model"""
document = Document.objects.get(pk=pk)
document.render(cache_control, base_url)
return document.rendered_errors
@task
def render_stale_documents(immediate=False, log=None):
"""Simple task wrapper for rendering stale documents"""
lock = MemcacheLock('render-stale-documents-lock')
if lock.acquired and not immediate:
# fail loudly if this is running already
# may indicate a problem with the schedule of this task
raise StaleDocumentsRenderingInProgress
stale_docs = Document.objects.get_by_stale_rendering()
stale_docs_count = stale_docs.count()
if stale_docs_count == 0:
# not stale documents to render
return
if log is None:
# fetch a logger in case none is given
log = render_stale_documents.get_logger()
log.info("Found %s stale documents" % stale_docs_count)
response = None
if lock.acquire():
try:
subtasks = []
for doc in stale_docs:
if immediate:
doc.render('no-cache', settings.SITE_URL)
log.info("Rendered stale %s" % doc)
else:
subtask = render_document.subtask((doc.pk, 'no-cache',
settings.SITE_URL))
subtasks.append(subtask)
log.info("Deferred rendering for stale %s" % doc)
task_group = group(subtasks)
task_group().get() # calling it since async doesn't work
finally:
lock.release()
return response
@task
def build_json_data_for_document_task(pk, stale):
"""Force-refresh cached JSON data after rendering."""
document = Document.objects.get(pk=pk)
document.get_json_data(stale=stale)
@receiver(render_done)
def build_json_data_handler(sender, instance, **kwargs):
try:
build_json_data_for_document_task.delay(instance.pk, stale=False)
except:
logging.error('JSON metadata build task failed',
exc_info=True)
@task
def move_page(locale, slug, new_slug, email):
with transaction.commit_manually():
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
transaction.rollback()
logging.error('Page move failed: no user with email address %s' %
email)
return
try:
doc = Document.objects.get(locale=locale, slug=slug)
except Document.DoesNotExist:
transaction.rollback()
message = """
Page move failed.
Move was requested for document with slug %(slug)s in locale
%(locale)s, but no such document exists.
""" % {'slug': slug, 'locale': locale}
logging.error(message)
send_mail('Page move failed', message, settings.DEFAULT_FROM_EMAIL,
[user.email])
return
try:
doc._move_tree(new_slug, user=user)
except PageMoveError as e:
transaction.rollback()
message = """
Page move failed.
Move was requested for document with slug %(slug)s in locale
%(locale)s, but could not be completed.
Diagnostic info:
%(message)s
""" % {'slug': slug, 'locale': locale, 'message': e.message}
logging.error(message)
send_mail('Page move failed', message, settings.DEFAULT_FROM_EMAIL,
[user.email])
return
except Exception as e:
transaction.rollback()
message = """
Page move failed.
Move was requested for document with slug %(slug)s in locale %(locale)s,
but could not be completed.
%(info)s
""" % {'slug': slug, 'locale': locale, 'info': e}
logging.error(message)
send_mail('Page move failed', message, settings.DEFAULT_FROM_EMAIL,
[user.email])
return
transaction.commit()
# Now that we know the move succeeded, re-render the whole tree.
for moved_doc in [doc] + doc.get_descendants():
moved_doc.schedule_rendering('max-age=0')
subject = 'Page move completed: ' + slug + ' (' + locale + ')'
full_url = settings.SITE_URL + '/' + locale + '/docs/' + new_slug
message = """
Page move completed.
The move requested for the document with slug %(slug)s in locale
%(locale)s, and all its children, has been completed.
You can now view this document at its new location: %(full_url)s.
""" % {'slug': slug, 'locale': locale, 'full_url': full_url}
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[user.email])
@task
def update_community_stats():
cursor = connection.cursor()
try:
cursor.execute("""
SELECT count(creator_id)
FROM
(SELECT DISTINCT creator_id
FROM wiki_revision
WHERE created >= DATE_SUB(NOW(), INTERVAL 1 YEAR)) AS contributors
""")
contributors = cursor.fetchone()
cursor.execute("""
SELECT count(locale)
FROM
(SELECT DISTINCT wd.locale
FROM wiki_document wd,
wiki_revision wr
WHERE wd.id = wr.document_id
AND wr.created >= DATE_SUB(NOW(), INTERVAL 1 YEAR)) AS locales
""")
locales = cursor.fetchone()
finally:
cursor.close()
community_stats = {}
try:
community_stats['contributors'] = contributors[0]
community_stats['locales'] = locales[0]
except IndexError:
community_stats = None
# storing a None value in cache allows a better check for
# emptiness in the view
if 0 in community_stats.values():
community_stats = None
cache = get_cache('memcache')
cache.set('community_stats', community_stats, 86400)
@task
def delete_old_revision_ips(immediate=False, days=30):
RevisionIP.objects.delete_old(days=days)
@task
def send_first_edit_email(email):
email.to = [config.EMAIL_LIST_FOR_FIRST_EDITS,]
email.send()