-
Notifications
You must be signed in to change notification settings - Fork 354
Expand file tree
/
Copy pathviews.py
More file actions
334 lines (298 loc) · 12.5 KB
/
views.py
File metadata and controls
334 lines (298 loc) · 12.5 KB
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
from rest_framework import status as http_status
import logging
from flask import request
import waffle
from django.db import transaction, connection
from django.contrib.contenttypes.models import ContentType
from framework.auth import get_or_create_user
from framework.exceptions import HTTPError, ServiceDiscontinuedError
from framework.flask import redirect
from framework.transactions.handlers import no_auto_transaction
from osf import features
from osf.models import AbstractNode, Node, Conference, OSFUser
from website import settings
from website.conferences import utils
from website.conferences.message import ConferenceMessage, ConferenceError
from website.external_web_app.decorators import ember_flag_is_active
from website.mails import CONFERENCE_SUBMITTED, CONFERENCE_INACTIVE, CONFERENCE_FAILED, CONFERENCE_DEPRECATION
from website.mails import send_mail
from website.util import web_url_for
from website.util.metrics import CampaignSourceTags
logger = logging.getLogger(__name__)
@no_auto_transaction
def meeting_hook():
"""View function for email conference submission.
"""
message = ConferenceMessage()
if waffle.flag_is_active(request, features.DISABLE_MEETINGS):
send_mail(
message.sender_email,
CONFERENCE_DEPRECATION,
fullname=message.sender_display,
support_email=settings.OSF_SUPPORT_EMAIL,
can_change_preferences=False,
logo=settings.OSF_MEETINGS_LOGO,
)
raise ServiceDiscontinuedError()
try:
message.verify()
except ConferenceError as error:
logger.error(error)
raise HTTPError(http_status.HTTP_406_NOT_ACCEPTABLE)
try:
conference = Conference.get_by_endpoint(message.conference_name, active=False)
except ConferenceError as error:
logger.error(error)
raise HTTPError(http_status.HTTP_406_NOT_ACCEPTABLE)
if not conference.active:
send_mail(
message.sender_email,
CONFERENCE_INACTIVE,
fullname=message.sender_display,
presentations_url=web_url_for('conference_view', _absolute=True),
can_change_preferences=False,
logo=settings.OSF_MEETINGS_LOGO,
)
raise HTTPError(http_status.HTTP_406_NOT_ACCEPTABLE)
add_poster_by_email(conference=conference, message=message)
def add_poster_by_email(conference, message):
"""
:param Conference conference:
:param ConferenceMessage message:
"""
# Fail if no attachments
if not message.attachments:
return send_mail(
message.sender_email,
CONFERENCE_FAILED,
fullname=message.sender_display,
can_change_preferences=False,
logo=settings.OSF_MEETINGS_LOGO
)
with transaction.atomic():
user, user_created = get_or_create_user(
message.sender_display,
message.sender_email,
is_spam=message.is_spam,
)
if user_created:
if utils.is_valid_email(user.fullname):
user.fullname = user._id # Users cannot use an email as their full name
user.save() # need to save in order to access m2m fields (e.g. tags)
user.add_system_tag(CampaignSourceTags.Osf4m.value)
user.update_date_last_login()
user.save()
# must save the user first before accessing user._id
set_password_url = web_url_for(
'reset_password_get',
uid=user._id,
token=user.verification_key_v2['token'],
_absolute=True,
)
else:
set_password_url = None
# Always create a new meeting node
node = Node.objects.create(
title=message.subject,
creator=user
)
node.add_system_tag(CampaignSourceTags.Osf4m.value)
node.save()
utils.provision_node(conference, message, node, user)
created_user = user if user_created else None
utils.record_message(message, node, created_user)
# Prevent circular import error
from framework.auth import signals as auth_signals
if user_created:
auth_signals.user_confirmed.send(user)
utils.upload_attachments(user, node, message.attachments)
download_url = node.web_url_for(
'addon_view_or_download_file',
path=message.attachments[0].filename,
provider='osfstorage',
action='download',
_absolute=True,
)
# Send confirmation email
send_mail(
message.sender_email,
CONFERENCE_SUBMITTED,
conf_full_name=conference.name,
conf_view_url=web_url_for(
'conference_results',
meeting=message.conference_name,
_absolute=True,
),
fullname=message.sender_display,
user_created=user_created,
set_password_url=set_password_url,
profile_url=user.absolute_url,
node_url=node.absolute_url,
file_url=download_url,
presentation_type=message.conference_category.lower(),
is_spam=message.is_spam,
can_change_preferences=False,
logo=settings.OSF_MEETINGS_LOGO
)
def conference_data(meeting):
try:
conf = Conference.objects.get(endpoint__iexact=meeting)
except Conference.DoesNotExist:
raise HTTPError(http_status.HTTP_404_NOT_FOUND)
return conference_submissions_sql(conf)
def conference_submissions_sql(conf):
"""
Serializes all meeting submissions to a conference (returns array of dictionaries)
:param obj conf: Conference object.
"""
submission1_name = conf.field_names['submission1']
submission2_name = conf.field_names['submission2']
conference_url = web_url_for('conference_results', meeting=conf.endpoint)
abstract_node_content_type_id = ContentType.objects.get_for_model(AbstractNode).id
osf_user_content_type_id = ContentType.objects.get_for_model(OSFUser).id
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT json_build_object(
'id', ROW_NUMBER() OVER (ORDER BY 1),
'title', osf_abstractnode.title,
'nodeUrl', '/' || GUID._id || '/',
'author', CASE WHEN AUTHOR.family_name != '' THEN AUTHOR.family_name ELSE AUTHOR.fullname END,
'authorUrl', '/' || AUTHOR_GUID._id || '/',
'category', CASE WHEN %s = ANY(TAGS_LIST.tag_list) THEN %s ELSE %s END,
'download', COALESCE(DOWNLOAD_COUNT, 0),
'downloadUrl', COALESCE('/project/' || GUID._id || '/files/osfstorage/' || FILE._id || '/?action=download', ''),
'dateCreated', osf_abstractnode.created,
'confName', %s,
'confUrl', %s,
'tags', array_to_string(TAGS_LIST.tag_list, ' ')
)
FROM osf_abstractnode
INNER JOIN osf_conference_submissions ON (osf_conference_submissions.abstractnode_id = osf_abstractnode.id)
LEFT JOIN LATERAL(
SELECT array_agg(osf_tag.name) AS tag_list
FROM osf_tag
INNER JOIN osf_abstractnode_tags ON (osf_tag.id = osf_abstractnode_tags.tag_id)
WHERE (osf_tag.system = FALSE AND osf_abstractnode_tags.abstractnode_id = osf_abstractnode.id)
) AS TAGS_LIST ON TRUE -- Concatenates tag names with space in between
LEFT JOIN LATERAL (
SELECT osf_osfuser.*
FROM osf_osfuser
INNER JOIN osf_contributor ON (osf_contributor.user_id = osf_osfuser.id)
WHERE (osf_contributor.node_id = osf_abstractnode.id AND osf_contributor.visible = TRUE)
ORDER BY osf_contributor._order ASC
LIMIT 1
) AUTHOR ON TRUE -- Returns first visible contributor
LEFT JOIN LATERAL (
SELECT osf_guid._id, osf_guid.id
FROM osf_guid
WHERE (osf_guid.object_id = osf_abstractnode.id AND osf_guid.content_type_id = %s) -- Content type for AbstractNode
ORDER BY osf_guid.created DESC
LIMIT 1 -- Returns node guid
) GUID ON TRUE
LEFT JOIN LATERAL (
SELECT osf_guid._id
FROM osf_guid
WHERE (osf_guid.object_id = AUTHOR.id AND osf_guid.content_type_id = %s) -- Content type for OSFUser
LIMIT 1
) AUTHOR_GUID ON TRUE -- Returns author_guid
LEFT JOIN LATERAL (
SELECT osf_basefilenode.*
FROM osf_basefilenode
WHERE (osf_basefilenode.type = 'osf.osfstoragefile'
AND osf_basefilenode.provider = 'osfstorage'
AND osf_basefilenode.target_content_type_id = %s -- Content type for AbstractNode
AND osf_basefilenode.target_object_id = osf_abstractnode.id)
ORDER BY osf_basefilenode.id ASC
LIMIT 1 -- Joins file
) FILE ON TRUE
LEFT JOIN LATERAL (
SELECT P.total AS DOWNLOAD_COUNT
FROM osf_pagecounter AS P
WHERE P.action = 'download'
AND P.resource_id = GUID.id
AND P.file_id = FILE.id
LIMIT 1
) DOWNLOAD_COUNT ON TRUE
-- Get all the nodes for a specific meeting
WHERE (osf_conference_submissions.conference_id = %s
AND osf_abstractnode.is_deleted = FALSE
AND osf_abstractnode.is_public = TRUE
AND AUTHOR_GUID IS NOT NULL)
ORDER BY osf_abstractnode.created DESC;
""", [
submission1_name,
submission1_name,
submission2_name,
conf.name,
conference_url,
abstract_node_content_type_id,
osf_user_content_type_id,
abstract_node_content_type_id,
conf.id,
]
)
rows = cursor.fetchall()
return [row[0] for row in rows]
def redirect_to_meetings(**kwargs):
return redirect('/meetings/')
def serialize_conference(conf):
return {
'active': conf.active,
'admins': list(conf.admins.all().values_list('guids___id', flat=True)),
'end_date': conf.end_date,
'endpoint': conf.endpoint,
'field_names': conf.field_names,
'info_url': conf.info_url,
'is_meeting': conf.is_meeting,
'location': conf.location,
'logo_url': conf.logo_url,
'name': conf.name,
'num_submissions': conf.valid_submissions.count(),
'poster': conf.poster,
'public_projects': conf.public_projects,
'start_date': conf.start_date,
'talk': conf.talk,
}
@ember_flag_is_active(features.EMBER_MEETING_DETAIL)
def conference_results(meeting):
"""Return the data for the grid view for a conference.
:param str meeting: Endpoint name for a conference.
"""
try:
conf = Conference.objects.get(endpoint__iexact=meeting)
except Conference.DoesNotExist:
raise HTTPError(http_status.HTTP_404_NOT_FOUND)
data = conference_data(meeting)
return {
'data': data,
'label': meeting,
'meeting': serialize_conference(conf),
# Needed in order to use base.mako namespace
'settings': settings,
}
def redirect_to_conference_results(meeting):
return redirect(f'/meetings/{meeting}')
def conference_submissions(**kwargs):
"""
This view previously cached submissions count on num_submissions field.
"""
return {'success': True}
@ember_flag_is_active(features.EMBER_MEETINGS)
def conference_view(**kwargs):
meetings = []
for conf in Conference.objects.all():
if len(conf.valid_submissions) < settings.CONFERENCE_MIN_COUNT:
continue
if (hasattr(conf, 'is_meeting') and (conf.is_meeting is False)):
continue
meetings.append({
'name': conf.name,
'location': conf.location,
'end_date': conf.end_date.strftime('%b %d, %Y') if conf.end_date else None,
'start_date': conf.start_date.strftime('%b %d, %Y') if conf.start_date else None,
'url': web_url_for('conference_results', meeting=conf.endpoint),
'count': conf.valid_submissions.count(),
})
meetings.sort(key=lambda meeting: meeting['count'], reverse=True)
return {'meetings': meetings}