Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion ietf/api/tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright The IETF Trust 2015-2020, All Rights Reserved
# Copyright The IETF Trust 2015-2024, All Rights Reserved
# -*- coding: utf-8 -*-
import base64
import datetime
Expand Down Expand Up @@ -222,6 +222,70 @@ def test_api_set_session_video_url(self):
event = doc.latest_event()
self.assertEqual(event.by, recman)

def test_api_set_meetecho_recording_name(self):
url = urlreverse("ietf.meeting.views.api_set_meetecho_recording_name")
recmanrole = RoleFactory(group__type_id="ietf", name_id="recman")
recman = recmanrole.person
meeting = MeetingFactory(type_id="ietf")
session = SessionFactory(group__type_id="wg", meeting=meeting)
apikey = PersonalApiKey.objects.create(endpoint=url, person=recman)
name = "testname"

# error cases
r = self.client.post(url, {})
self.assertContains(r, "Missing apikey parameter", status_code=400)

badrole = RoleFactory(group__type_id="ietf", name_id="ad")
badapikey = PersonalApiKey.objects.create(endpoint=url, person=badrole.person)
badrole.person.user.last_login = timezone.now()
badrole.person.user.save()
r = self.client.post(url, {"apikey": badapikey.hash()})
self.assertContains(r, "Restricted to role: Recording Manager", status_code=403)

r = self.client.post(url, {"apikey": apikey.hash()})
self.assertContains(r, "Too long since last regular login", status_code=400)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't already, we ought to cover features like this in a unit test of the require_api_key() decorator and trust that. (Not actually asking for a change here unless you're enthusiastic about it, just pointing it out.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I felt the lack-of-reuse pain, but didn't pursue it as I think we might want to rework all of these to use DRF going forward maybe?

recman.user.last_login = timezone.now()
recman.user.save()

r = self.client.get(url, {"apikey": apikey.hash()})
self.assertContains(r, "Method not allowed", status_code=405)

r = self.client.post(url, {"apikey": apikey.hash()})
self.assertContains(r, "Missing session_id parameter", status_code=400)

r = self.client.post(url, {"apikey": apikey.hash(), "session_id": session.pk})
self.assertContains(r, "Missing name parameter", status_code=400)

bad_pk = int(Session.objects.order_by("-pk").first().pk) + 1
r = self.client.post(
url,
{
"apikey": apikey.hash(),
"session_id": bad_pk,
"name": name,
},
)
self.assertContains(r, "Session not found", status_code=400)

r = self.client.post(
url,
{
"apikey": apikey.hash(),
"session_id": "foo",
"name": name,
},
)
self.assertContains(r, "Invalid session_id", status_code=400)

r = self.client.post(
url, {"apikey": apikey.hash(), "session_id": session.pk, "name": name}
)
self.assertContains(r, "Done", status_code=200)

session.refresh_from_db()
self.assertEqual(session.meetecho_recording_name, name)


def test_api_add_session_attendees_deprecated(self):
# Deprecated test - should be removed when we stop accepting a simple list of user PKs in
# the add_session_attendees() view
Expand Down
4 changes: 3 additions & 1 deletion ietf/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright The IETF Trust 2017, All Rights Reserved
# Copyright The IETF Trust 2017-2024, All Rights Reserved

from django.conf import settings
from django.urls import include
Expand Down Expand Up @@ -39,6 +39,8 @@
url(r'^iesg/position', views_ballot.api_set_position),
# Let Meetecho set session video URLs
url(r'^meeting/session/video/url$', meeting_views.api_set_session_video_url),
# Let Meetecho tell us the name of its recordings
url(r'meeting/session/recordingname$', meeting_views.api_set_meetecho_recording_name),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a ^ at the start.

Maybe make it ^meeting/session/recording-name to better match recording_name in the model (and kebab-case that we've used in other URLs, like doc/draft-aliases/ at line 27)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will get the ^.
I can kebob case it, but I don't think we have consistency there.

# Meeting agenda + floorplan data
url(r'^meeting/(?P<num>[A-Za-z0-9._+-]+)/agenda-data$', meeting_views.api_get_agenda_data),
# Meeting session materials
Expand Down
20 changes: 20 additions & 0 deletions ietf/meeting/migrations/0009_session_meetecho_recording_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright The IETF Trust 2024, All Rights Reserved

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("meeting", "0008_remove_schedtimesessassignment_notes"),
]

operations = [
migrations.AddField(
model_name="session",
name="meetecho_recording_name",
field=models.CharField(
blank=True, help_text="Name of the meetecho recording", max_length=64
),
),
]
15 changes: 11 additions & 4 deletions ietf/meeting/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,7 @@ class Session(models.Model):
on_agenda = models.BooleanField(default=True, help_text='Is this session visible on the meeting agenda?')
has_onsite_tool = models.BooleanField(default=False, help_text="Does this session use the officially supported onsite and remote tooling?")
chat_room = models.CharField(blank=True, max_length=32, help_text='Name of Zulip stream, if different from group acronym')
meetecho_recording_name = models.CharField(blank=True, max_length=64, help_text="Name of the meetecho recording")

tombstone_for = models.ForeignKey('Session', blank=True, null=True, help_text="This session is the tombstone for a session that was rescheduled", on_delete=models.CASCADE)

Expand Down Expand Up @@ -1332,17 +1333,23 @@ def onsite_tool_url(self):
return None

def _session_recording_url_label(self):
otsa = self.official_timeslotassignment()
if otsa is None:
return None
if self.meeting.type.slug == "ietf" and self.has_onsite_tool:
session_label = f"IETF{self.meeting.number}-{self.group.acronym.upper()}-{self.official_timeslotassignment().timeslot.time.strftime('%Y%m%d-%H%M')}"
session_label = f"IETF{self.meeting.number}-{self.group.acronym.upper()}-{otsa.timeslot.time.strftime('%Y%m%d-%H%M')}"
else:
session_label = f"IETF-{self.group.acronym.upper()}-{self.official_timeslotassignment().timeslot.time.strftime('%Y%m%d-%H%M')}"
session_label = f"IETF-{self.group.acronym.upper()}-{otsa.timeslot.time.strftime('%Y%m%d-%H%M')}"
return session_label

def session_recording_url(self):
url_formatter = getattr(settings, "MEETECHO_SESSION_RECORDING_URL", "")
url = None
if url_formatter and self.video_stream_url:
url = url_formatter.format(session_label=self._session_recording_url_label())
name = self.meetecho_recording_name
if name is None or name.strip() == "":
name = self._session_recording_url_label()
if url_formatter.strip()!="" and name is not None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: spacing around !=

url = url_formatter.format(session_label=name)
return url


Expand Down
6 changes: 5 additions & 1 deletion ietf/meeting/tests_models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright The IETF Trust 2021, All Rights Reserved
# Copyright The IETF Trust 2021-2024, All Rights Reserved
# -*- coding: utf-8 -*-
"""Tests of models in the Meeting application"""
import datetime
Expand Down Expand Up @@ -172,6 +172,10 @@ def test_session_recording_url(self, mock):
settings.MEETECHO_SESSION_RECORDING_URL = "http://player.example.com?{session_label}"
self.assertEqual(session.session_recording_url(), "http://player.example.com?LABEL")

session.meetecho_recording_name="actualname"
session.save()
self.assertEqual(session.session_recording_url(), "http://player.example.com?actualname")

def test_session_recording_url_label_ietf(self):
session = SessionFactory(
meeting__type_id='ietf',
Expand Down
39 changes: 39 additions & 0 deletions ietf/meeting/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4270,6 +4270,45 @@ class OldUploadRedirect(RedirectView):
def get_redirect_url(self, **kwargs):
return reverse_lazy('ietf.meeting.views.session_details',kwargs=self.kwargs)


@require_api_key
@role_required("Recording Manager")
@csrf_exempt
def api_set_meetecho_recording_name(request):
"""Set name for meetecho recording

parameters:
apikey: the poster's personal API key
session_id: id of the session to update
name: the name to use for the recording at meetecho player
"""
def err(code, text):
return HttpResponse(text, status=code, content_type='text/plain')

if request.method != "POST":
return HttpResponseNotAllowed(
content="Method not allowed", content_type="text/plain", permitted_methods=('POST',)
)

session_id = request.POST.get('session_id', None)
if session_id is None:
return err(400, 'Missing session_id parameter')
name = request.POST.get('name', None)
if name is None:
return err(400, 'Missing name parameter')

try:
session = Session.objects.get(pk=session_id)
except Session.DoesNotExist:
return err(400, f"Session not found with session_id '{session_id}'")
except ValueError:
return err(400, "Invalid session_id: {session_id}")

session.meetecho_recording_name = name
session.save()

return HttpResponse("Done", status=200, content_type='text/plain')

@require_api_key
@role_required('Recording Manager')
@csrf_exempt
Expand Down