Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 299-delete-planed-assemblies
  • 302-habitat-info
  • 607-schedule-versions
  • 720-schedule_source
  • add-django-ninja
  • andi/develop
  • andi/schedule-api
  • andi/speaker_import
  • assembly-import
  • camp23-prod
  • chore/backoffice-list
  • chore/conference-singleton
  • chore/event-views
  • chore/update-rooms
  • cyroxx/add_edit_links
  • cyroxx/bulletin_description
  • develop
  • editMail
  • feat/dynamic-link-forms
  • feat/unit-integration-tests
  • feature/RegisterSpeaker
  • feature/audit_log
  • feature/bg-eyecandy
  • feature/conference-query-set
  • feature/event_import_slugs_of_serial_event
  • feature/mqtt
  • feature/parallax-css-testpage
  • feature/pypy
  • feature/scheduleimport_skippedrooms
  • feature/show_vods
  • production
  • room-docu
  • stable-38c3
  • camp23-prod
  • camp23-prod-archive
  • prod-2024-10-14_22-49
  • prod-2024-10-20_23-27
  • prod-2024-10-29_09-45
  • prod-2024-10-31_13-17
  • prod-2024-11-01_11-14
  • prod-2024-11-02_21-16
  • prod-2024-11-03_01-42
  • prod-2024-11-03_12-10
  • prod-2024-11-16_03-41
  • prod-2024-12-04_00-57
  • prod-2024-12-05_00-48
  • prod-2024-12-05_10-09
  • prod-2024-12-10_00-17
  • prod-2024-12-10_07-23
  • prod-2024-12-10_23-04
  • prod-2024-12-14_03-24
  • prod-2024-12-16_02-27
  • prod-2024-12-17_15-05
  • prod-2024-12-19_02-32
  • prod-2024-12-20_12-25
  • prod-2024-12-21_10-44
  • prod-2024-12-21_13-42
  • prod-2024-12-22_00-55
  • prod-2024-12-22_01-34
  • prod-2024-12-22_17-25
  • prod-2024-12-22_21-12
  • prod-2024-12-23_23-39
  • prod-2024-12-24_14-48
  • prod-2024-12-25_01-29
  • prod-2024-12-25_15-54
  • prod-2024-12-25_21-04
  • prod-2024-12-26_00-21
  • prod-2024-12-26_13-12
  • prod-2024-12-26_21-45
  • prod-2024-12-27_00-34
  • prod-2024-12-27_13-29
  • prod-2024-12-27_16-01
  • prod-2024-12-27_16-37
  • prod-2024-12-27_20-15
  • prod-2024-12-27_21-15
  • prod-2024-12-28_02-32
  • prod-2024-12-28_12-24
  • prod-2024-12-28_18-32
  • prod-2024-12-29_02-25
  • prod-2024-12-29_02-55
  • prod-2024-12-29_03-20
  • prod-2024-12-29_03-32
  • prod-2024-12-29_20-35
  • prod-2024-12-30_03-16
  • prod-2024-12-30_12-40
  • prod-2024-12-31_09-54
  • prod-2025-01-07_13-15
  • prod-2025-01-20_00-20
  • prod-2025-01-21_22-00
  • prod-2025-01-21_22-46
  • prod-2025-04-18_22-42
91 results

Target

Select target project
  • hub/hub
  • cyroxx/hub
  • myigel/hub
  • thomasdotwtf/hub
4 results
Select Git revision
  • 299-delete-planed-assemblies
  • 302-habitat-info
  • 445-schedule-redirects
  • 511-schedule-foo-fixed
  • 607-schedule-versions
  • 623-wiki-im-baustellenmodus-sollte-mal-als-wiki-admin-trotzdem-seiten-anlegen-bearbeiten-konnen
  • 720-schedule_source
  • andi/develop
  • andi/schedule-api
  • andi/speaker_import
  • badge-redeem-404
  • camp23-prod
  • chore/event-views
  • cyroxx/add_edit_links
  • cyroxx/bulletin_description
  • deploy/curl-verbose
  • develop
  • editMail
  • feat/dynamic-link-forms
  • feat/unit-integration-tests
  • feature/568-habitatmanagement
  • feature/RegisterSpeaker
  • feature/audit_log
  • feature/bg-eyecandy
  • feature/conference-query-set
  • feature/mqtt
  • feature/parallax-css-testpage
  • feature/pypy
  • feature/scheduleimport_skippedrooms
  • fix/index
  • fix/public-badge-access-rights
  • fix/registration_mail_subject
  • ical-export
  • production
  • room-docu
  • camp23-prod
  • camp23-prod-archive
  • prod-2024-10-14_22-49
  • prod-2024-10-20_23-27
  • prod-2024-10-29_09-45
  • prod-2024-10-31_13-17
  • prod-2024-11-01_11-14
  • prod-2024-11-02_21-16
  • prod-2024-11-03_01-42
  • prod-2024-11-03_12-10
  • prod-2024-11-16_03-41
  • prod-2024-12-04_00-57
  • prod-2024-12-05_00-48
  • prod-2024-12-05_10-09
  • prod-2024-12-10_00-17
  • prod-2024-12-10_07-23
  • prod-2024-12-10_23-04
  • prod-2024-12-14_03-24
  • prod-2024-12-16_02-27
  • prod-2024-12-17_15-05
  • prod-2024-12-19_02-32
  • prod-2024-12-20_12-25
  • prod-2024-12-21_10-44
  • prod-2024-12-21_13-42
  • prod-2024-12-22_00-55
  • prod-2024-12-22_01-34
  • prod-2024-12-22_17-25
  • prod-2024-12-22_21-12
  • prod-2024-12-23_23-39
  • prod-2024-12-24_14-48
  • prod-2024-12-25_01-29
  • prod-2024-12-25_15-54
  • prod-2024-12-25_21-04
  • prod-2024-12-26_00-21
  • prod-2024-12-26_13-12
  • prod-2024-12-26_21-45
  • prod-2024-12-27_00-34
  • prod-2024-12-27_13-29
  • prod-2024-12-27_16-01
  • prod-2024-12-27_16-37
  • prod-2024-12-27_20-15
76 results
Show changes
Showing
with 793 additions and 214 deletions
# Generated by Django 3.1.2 on 2020-10-31 14:30
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('core', '0005_remove_children_and_fsk'),
]
operations = [
migrations.CreateModel(
name='StickerToken',
fields=[
('token', models.CharField(max_length=50, primary_key=True, serialize=False)),
('permanent', models.BooleanField(default=False)),
('issued', models.DateTimeField(auto_now_add=True)),
('sticker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='core.sticker')),
],
),
migrations.CreateModel(
name='RoomLinkUserId',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('roomlink', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='core.roomlink')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
],
),
]
# Generated by Django 3.1.2 on 2020-11-09 10:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0007_rename_stickers_to_badges'),
('api', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='StickerToken',
name='sticker',
field=models.ForeignKey(on_delete=models.deletion.CASCADE, related_name='+', to='core.badge'),
),
migrations.RenameModel(old_name='StickerToken', new_name='BadgeToken'),
migrations.RenameField(model_name='BadgeToken', old_name='sticker', new_name='badge'),
]
# Generated by Django 3.1.4 on 2020-12-25 21:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('api', '0002_rename_stickers_to_badges'),
]
operations = [
migrations.DeleteModel(
name='BadgeToken',
),
]
# Generated by Django 3.2.10 on 2022-01-26 21:50
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('api', '0003_delete_badgetoken'),
]
database_operations = [
migrations.AlterModelTable('RoomLinkUserId', 'core_roomlinkuserid')
]
state_operations = [
migrations.DeleteModel('RoomLinkUserId')
]
operations = [
migrations.SeparateDatabaseAndState(
database_operations=database_operations,
state_operations=state_operations)
]
......@@ -74,43 +74,63 @@ class IsConferenceService(ConferencePermission):
class AssemblyPermission(ConferencePermission):
_assembly: Assembly
def get_assembly(self, *, view, obj=None) -> Assembly:
if hasattr(self, '_assembly'):
return self._assembly
if hasattr(view, 'assembly'):
assembly = view.assembly
elif slug := view.kwargs.get('assembly', None):
assembly = Assembly.objects.get(slug=slug)
elif isinstance(obj, Assembly):
assembly = obj
elif hasattr(obj, 'assembly'):
elif obj and hasattr(obj, 'assembly'):
assembly = obj.assembly
elif obj and hasattr(obj, 'issuing_assembly'):
assembly = obj.issuing_assembly
else:
raise ObjectDoesNotExist('Assembly for this view not found')
raise Assembly.DoesNotExist
self._assembly = assembly
return assembly
class IsPublicAssemblyReadOnly(AssemblyPermission):
def has_permission(self, request, view):
try:
self.get_assembly(view=view)
return self.has_object_permission(request, view)
except Assembly.DoesNotExist:
return bool(getattr(view, 'fallback_to_object', False))
def has_object_permission(self, request, view, obj=None):
try:
assembly = self.get_assembly(view=view)
except ObjectDoesNotExist:
assembly = self.get_assembly(view=view, obj=obj)
except Assembly.DoesNotExist:
return False
return request.method in permissions.SAFE_METHODS and assembly.is_public
class IsAssemblyService(AssemblyPermission):
def has_permission(self, request, view):
try:
self.get_assembly(view=view)
return self.has_object_permission(request, view)
except Assembly.DoesNotExist:
return bool(getattr(view, 'fallback_to_object', False))
def has_object_permission(self, request, view, obj=None):
assembly = self.get_assembly(view=view, obj=obj)
return assembly.technical_user == request.user
class IsAssemblyManager(AssemblyPermission):
def has_permission(self, request, view):
try:
self.get_assembly(view=view)
return self.has_object_permission(request, view)
except Assembly.DoesNotExist:
return bool(getattr(view, 'fallback_to_object', False))
def has_object_permission(self, request, view, obj=None):
user = request.user
......@@ -120,20 +140,23 @@ class IsAssemblyManager(AssemblyPermission):
return False
try:
assembly = self.get_assembly(view=view)
assembly = self.get_assembly(view=view, obj=obj)
except ObjectDoesNotExist:
return False
query_set = Assembly.objects.filter(pk=assembly.id)
return query_set.filter(members__member=user, members__can_manage_assembly=True).exists()
return request.user.has_perm('core.change_assembly', assembly)
class HasIssuingToken(AssemblyPermission):
def has_permission(self, request, view):
try:
self.get_assembly(view=view)
return self.has_object_permission(request, view)
except Assembly.DoesNotExist:
return bool(getattr(view, 'fallback_to_object', False))
def has_object_permission(self, request, view, obj=None):
try:
assembly = self.get_assembly(view=view)
assembly = self.get_assembly(view=view, obj=obj)
except ObjectDoesNotExist:
return False
if not (issuing_token := view.kwargs.get('issuing_token', None)):
......
......@@ -4,7 +4,7 @@ import logging
import re
from collections import OrderedDict
from datetime import datetime, timedelta
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from uuid import UUID
from lxml import etree as ET
......@@ -18,6 +18,7 @@ from core.models.conference import Conference, ConferenceMember
from core.models.events import Event, EventParticipant
from core.models.rooms import Room
from core.models.users import PlatformUser
from core.markdown import normalize_markdown
if TYPE_CHECKING:
from django.db.models import QuerySet
......@@ -90,6 +91,12 @@ class RoomDay:
class ScheduleEncoder(json.JSONEncoder):
tz = None
conference: Conference
def __init__(self, *args, conference: Conference = None, **kwargs):
assert conference is not None
self.conference = conference
super().__init__(*args, **kwargs)
def encode_duration(self, duration: timedelta | None) -> str | None:
"""converts a python `timedelta` to the schedule xml timedelta string that represents this timedelta. ([d:]HH:mm)"""
......@@ -118,7 +125,7 @@ class ScheduleEncoder(json.JSONEncoder):
'name': name,
'public_name': name,
'avatar': p.avatar_url,
'biography': member.description if member else None,
'biography': normalize_markdown(self.conference, member.description) if member and member.description else None,
# 'links': p.links, # TODO
'url': p.get_absolute_url(),
}
......@@ -132,7 +139,7 @@ class ScheduleEncoder(json.JSONEncoder):
'name': name,
'public_name': name,
'avatar': p.participant.avatar_url,
'biography': member.description if member else '',
'biography': normalize_markdown(self.conference, member.description) if member and member.description else '',
# 'links': p.participant.links, # TODO
'url': p.participant.get_absolute_url(),
}
......@@ -163,7 +170,6 @@ class ScheduleEncoder(json.JSONEncoder):
start = event.schedule_start.astimezone(tz or self.tz) if event.schedule_start is not None else None
additional_data = event.additional_data or {}
legacy_id = additional_data.get('id') or int(re.sub('[^0-9]+', '', str(event.id))[0:6])
slug = f'{event.conference.slug}-{event.slug}'
if event.streaming == Event.Streaming.NO:
additional_data['do_not_stream'] = True
......@@ -178,9 +184,9 @@ class ScheduleEncoder(json.JSONEncoder):
**additional_data,
# ATTENTION: if the description also exists in additional_data it is overwritten
'abstract': event.abstract,
'description': event.description,
'description': normalize_markdown(self.conference, event.description) if event.description else event.description,
'persons': self.collect_persons(event),
'slug': slug,
'slug': event.voc_slug,
'url': event.get_absolute_url(),
'guid': event.id,
'date': start.isoformat() if start is not None else None,
......@@ -216,8 +222,8 @@ class ScheduleEncoder(json.JSONEncoder):
'type': obj.room_type,
'stream_id': obj.voc_stream_id,
'capacity': obj.capacity,
'description_en': obj.description_en,
'description_de': obj.description_de,
'description_en': normalize_markdown(self.conference, obj.description_en) if obj.description_en else obj.description_en,
'description_de': normalize_markdown(self.conference, obj.description_de) if obj.description_de else obj.description_de,
'features': features,
'assembly': obj.assembly if obj.assembly else None,
# Future TODO 'url': obj.get_absolute_url(),
......@@ -252,9 +258,9 @@ class ScheduleEncoder(json.JSONEncoder):
return str(obj)
return None
def default(self, obj):
r = self.transform(obj)
return json.JSONEncoder.default(self, obj) if r is None else r
def default(self, o: Any):
r = self.transform(o)
return json.JSONEncoder.default(self, o) if r is None else r
class Schedule:
......@@ -357,15 +363,15 @@ class Schedule:
raise Warning(' illegal start time: ' + start_time.isoformat())
def __str__(self):
return json.dumps(self, indent=2, cls=ScheduleEncoder)
return json.dumps(self, indent=2, cls=ScheduleEncoder, conference=self.conference)
def json(self):
return json.dumps(self, cls=ScheduleEncoder)
return json.dumps(self, cls=ScheduleEncoder, conference=self.conference)
# dict_to_etree from http://stackoverflow.com/a/10076823
def xml(self):
root_node = None
encoder = ScheduleEncoder()
encoder = ScheduleEncoder(conference=self.conference)
def _set_attrib(tag, k, v):
if isinstance(v, str):
......
......@@ -16,6 +16,7 @@ from core.models.messages import DirectMessage
from core.models.metanavi import MetaNavItem
from core.models.rooms import Room
from core.models.users import PlatformUser, UserTimelineEntry
from core.templatetags.hub_absolute import hub_absolute
class ParameterisedHyperlinkedIdentityField(HyperlinkedIdentityField):
......@@ -81,8 +82,6 @@ class HubHyperlinkedIdentityField(HyperlinkedIdentityField):
attr = getattr(attr, field)
kwargs[url_param] = attr
from core.templatetags.hub_absolute import hub_absolute # pylint: disable=import-outside-toplevel
# TODO: add request=request, format=format,
return hub_absolute(view_name, **kwargs, i18n=False)
......@@ -102,15 +101,15 @@ class LinkRelatedField(serializers.RelatedField):
class ValidatingModelSerializer(serializers.ModelSerializer):
def validate(self, data):
instance = self.Meta.model(**{field: value for field, value in data.items() if field in self.Meta.model._meta.fields})
for f, v in {field: value for field, value in data.items() if field in self.Meta.model._meta.fields}.items():
def validate(self, attrs):
instance = self.Meta.model(**{field: value for field, value in attrs.items() if field in self.Meta.model._meta.fields})
for f, v in {field: value for field, value in attrs.items() if field in self.Meta.model._meta.fields}.items():
getattr(instance, f).set(v)
try:
instance.clean()
except ValidationError as err:
raise serializers.ValidationError(err.args[0])
return data
return attrs
class HubModelSerializer(ValidatingModelSerializer):
......@@ -288,10 +287,9 @@ class RoomSerializer(HubModelSerializer):
'links',
'assembly',
'links',
'backend_link',
'public_url',
]
staff_only_fields = ['blocked', 'backend_link']
staff_only_fields = ['blocked']
class EventSerializer(HubModelSerializer):
......@@ -430,3 +428,34 @@ class DirectMessageSendSerializer(HubModelSerializer):
write_only_fields = [f for f in fields if f != 'id']
recipient = PlatformUserByUsernameFieldSerializer()
class BadgeRewardSerializer(serializers.Serializer):
"""
A serializer for awarding a badge to a user.
The abstract methods are unused, this class is only to show the expected fields.
"""
username = serializers.CharField()
dect = serializers.CharField()
class DectNumberSerializer(serializers.Serializer):
"""
A serializer for checking if a DECT number exists.
The abstract methods are unused, this class is only to show the expected fields.
"""
dect = serializers.IntegerField()
class DectVerificationTokenSerializer(serializers.Serializer):
"""
A serializer for verifying a DECT number.
The abstract methods are unused, this class is only to show the expected fields.
"""
token = serializers.CharField()
from .assemblies import * # noqa: F401, F403
from .badges import * # noqa: F401, F403
from .bbb import * # noqa: F401, F403
from .engelsystem import * # noqa: F401, F403
from .events import * # noqa: F401, F403
from .map import * # noqa: F401, F403
from .messages import * # noqa: F401, F403
from .metrics import * # noqa: F401, F403
from .rooms import * # noqa: F401, F403
from .schedule import * # noqa: F401, F403
__all__ = ('*',) # noqa: F405
import uuid
from datetime import datetime, timedelta
from http import HTTPStatus
from zoneinfo import ZoneInfo
from pytz import UTC
from rest_framework.authtoken.models import Token
from django.test import Client, TestCase, override_settings
from django.urls import reverse
from core.models import Assembly, AssemblyMember, Badge, Conference, ConferenceMember, Event, PlatformUser
TEST_CONF_ID = uuid.uuid4()
@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID)
class AssembliesTestCase(TestCase):
def setUp(self):
self.conference1 = Conference(id=TEST_CONF_ID, slug='foo', name='Foo Conference', is_public=True)
self.conference1.save()
self.human_user = PlatformUser(username='bernd', user_type=PlatformUser.Type.HUMAN)
self.human_user.save()
self.human_cm = ConferenceMember(conference=self.conference1, user=self.human_user)
self.human_cm.save()
self.assembly = Assembly(conference=self.conference1, name='DUMMY', slug='dummy', state=Assembly.State.ACCEPTED, is_official=True)
self.assembly.save()
self.event = Event(
conference=self.conference1,
slug='event1',
assembly=self.assembly,
name='Event1_1',
is_public=True,
schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
schedule_duration=timedelta(minutes=45),
kind=Event.Kind.OFFICIAL,
location='Some Room',
)
self.event.save()
def test_ConferenceAssemblyEventsICal(self):
self.maxDiff = None
c = Client()
resp = c.get(reverse('api:assembly-events-ical', kwargs={'assembly': 'non-existing'}))
self.assertEqual(resp.status_code, 404)
resp = c.get(reverse('api:assembly-events-ical', kwargs={'assembly': self.assembly.slug}))
self.assertEqual(resp.status_code, 200)
# remove DTSTAMP and URL attributes as they are differing between tests & replace \n\r with \n
self.assertEqual(
'\n'.join([line for line in resp.content.decode('utf-8').splitlines() if not line.startswith('DTSTAMP') and not line.startswith('URL')]),
f"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Foo Conference//DUMMY's events
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:DUMMY's events
BEGIN:VEVENT
SUMMARY:Event1_1
DTSTART:20200102T000000Z
DTEND:20200102T004500Z
UID:{self.event.id!s}
CATEGORIES:official
DESCRIPTION:
LOCATION:Some Room
END:VEVENT
END:VCALENDAR""",
)
@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID)
class AssembliesExportTests(TestCase):
def setUp(self):
tz = ZoneInfo('CET')
self.conf = Conference(
id=TEST_CONF_ID,
slug='conf',
name='TestConf',
is_public=True,
start=datetime(2020, 12, 27, 1, 23, 45, tzinfo=tz),
end=datetime(2020, 12, 30, 23, 45, 00, tzinfo=tz),
)
self.conf.save()
self.human_user1 = PlatformUser(username='bernd', user_type=PlatformUser.Type.HUMAN)
self.human_user1.save()
self.human_user2 = PlatformUser(username='brot', user_type=PlatformUser.Type.HUMAN)
self.human_user2.save()
self.human_cm = ConferenceMember(conference=self.conf, user=self.human_user1)
self.human_cm.save()
self.assembly1 = Assembly(name='TestAssembly', slug='asmbly', conference=self.conf, state=Assembly.State.PLACED)
self.assembly1.save()
self.assembly2 = Assembly(name='TestAssembly2', slug='asmbly2', conference=self.conf, state=Assembly.State.PLACED)
self.assembly2.save()
self.am = AssemblyMember(assembly=self.assembly1, is_representative=True, can_manage_assembly=True, member=self.human_user1)
self.am.save()
self.badge = Badge(
conference=self.conf,
issuing_assembly=self.assembly1,
name='Test Badge',
)
self.badge.save()
self.token = Token(user=self.human_user1)
self.token.save()
self.event = Event(
conference=self.conf,
assembly=self.assembly1,
name='export test event without start time',
)
self.event.save()
def test_ConferenceAssemblyExport(self):
# not authenticated
url = reverse('api:assembly-export', kwargs={'assembly': self.assembly1.slug})
resp = self.client.get(url, {})
self.assertEqual(resp.status_code, HTTPStatus.FORBIDDEN)
self.client.force_login(self.human_user2)
# valid slug and missing permissions
url = reverse('api:assembly-export', kwargs={'assembly': self.assembly1.slug})
resp = self.client.get(url, {})
self.assertEqual(resp.status_code, HTTPStatus.NOT_FOUND)
self.client.force_login(self.human_user1)
# missing permissions for assembly
url = reverse('api:assembly-export', kwargs={'assembly': self.assembly2.slug})
resp = self.client.get(url, {})
self.assertEqual(resp.status_code, HTTPStatus.NOT_FOUND)
# invalid slug
url = reverse('api:assembly-export', kwargs={'assembly': 'not-exists'})
resp = self.client.get(url, {})
self.assertEqual(resp.status_code, HTTPStatus.NOT_FOUND)
# valid slug and has permissions
url = reverse('api:assembly-export', kwargs={'assembly': self.assembly1.slug})
resp = self.client.get(url, {})
self.assertEqual(resp.status_code, HTTPStatus.OK)
from api.tests.badges.create_redeem_token import * # noqa: F401, F403
from api.tests.badges.award import AwardTestCase
from api.tests.badges.create_redeem_token import CreateRedeemTokenTests
__all__ = (
'AwardTestCase',
'CreateRedeemTokenTests',
)
import json
import uuid
from rest_framework.authtoken.models import Token
from django.test import Client, TestCase, override_settings
from django.urls import reverse
from core.models import (
Assembly,
AssemblyMember,
Badge,
Conference,
ConferenceMember,
PlatformUser,
UserBadge,
UserCommunicationChannel,
)
TEST_CONF_ID = uuid.uuid4()
@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID)
class AwardTestCase(TestCase):
dummy_pswd = 'Spagh3ttiMon$ter'
def setUp(self):
self.conference1 = Conference(id=TEST_CONF_ID, slug='foo', name='Foo Conference', is_public=True)
self.conference1.save()
self.user = PlatformUser.objects.create(username='alice')
self.user_cm = ConferenceMember.objects.create(conference=self.conference1, user=self.user)
UserCommunicationChannel.objects.create(user=self.user, channel=UserCommunicationChannel.Channel.DECT, address='1337')
self.human_user_name = 'bernd'
self.human_user_pswd = 'Spagh3ttiMon$ter'
self.human_user = PlatformUser.objects.create(username=self.human_user_name, user_type=PlatformUser.Type.HUMAN)
self.human_user.set_password(self.human_user_pswd)
self.human_user.save()
self.human_cm = ConferenceMember.objects.create(conference=self.conference1, user=self.human_user)
self.assembly = Assembly.objects.create(conference=self.conference1, slug='bar', name='Bar Assembly')
AssemblyMember.objects.create(assembly=self.assembly, member=self.human_user, can_manage_assembly=True)
self.technical_user = self.assembly.create_technical_user_if_necessary()
self.technical_token = Token.objects.create(user=self.technical_user)
self.badge = Badge.objects.create(issuing_assembly=self.assembly, name='foo', state=Badge.State.PUBLIC)
def test_award_user_trough_dect_by_user(self):
c = Client()
token_response = c.post(reverse('api:auth-get-token'), {'username': self.human_user_name, 'password': self.human_user_pswd})
self.assertEqual(token_response.status_code, 200)
token = token_response.json()['token']
response = c.post(
reverse('api:badge-award', kwargs={'pk': self.badge.pk}),
json.dumps({'dect': '1337'}),
content_type='application/json',
headers={'Authorization': f'Token {token}', 'Content-Type': 'application/json'},
)
self.assertEqual(response.status_code, 200)
self.assertTrue(UserBadge.objects.filter(user=self.user, badge=self.badge).exists())
def test_award_user_trough_dect_by_technical_user(self):
c = Client()
response = c.post(
reverse('api:badge-award', kwargs={'pk': self.badge.pk}),
json.dumps({'dect': '1337'}),
content_type='application/json',
headers={'Authorization': f'Token {self.technical_token}'},
)
self.assertEqual(response.status_code, 200)
self.assertTrue(UserBadge.objects.filter(user=self.user, badge=self.badge).exists())
import uuid
from uuid import uuid4
from django.test import TestCase, override_settings
from django.urls import reverse
from core.models import Assembly, Conference, Room
TEST_CONF_ID = uuid.uuid4()
@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID, INTEGRATIONS_BBB=True)
class BBBTest(TestCase):
def test_MeetingEnded(self):
conf = Conference(id=TEST_CONF_ID, slug='conf', name='TestConf')
conf.save()
assembly = Assembly(name='TestAssembly', slug='asmbly', conference=conf)
assembly.save()
room = Room(
conference=conf,
assembly=assembly,
name='Room 1',
room_type=Room.RoomType.BIGBLUEBUTTON,
backend_status=Room.BackendStatus.ACTIVE,
backend_link=str(uuid4()),
backend_data={'close_secret': 'asdf'},
)
room.save()
self.client.get(
reverse('api:bbb_meeting_end'),
{
'meetingID': room.backend_link,
'close_secret': 'invalid',
},
)
room.refresh_from_db()
self.assertEqual(room.backend_status, Room.BackendStatus.ACTIVE)
self.client.get(
reverse('api:bbb_meeting_end'),
{
'meetingID': room.backend_link,
'close_secret': 'asdf',
},
)
room.refresh_from_db()
self.assertEqual(room.backend_status, Room.BackendStatus.INACTIVE)
......@@ -34,6 +34,7 @@ class EventsTestCase(TestCase):
schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
schedule_duration=timedelta(minutes=45),
kind=Event.Kind.OFFICIAL,
location='Some Room',
)
self.event.save()
......@@ -60,14 +61,38 @@ class EventsTestCase(TestCase):
resp = c.put(reverse('api:my-events-set', kwargs={'pk': str(self.event.pk)}))
self.assertEqual(list(self.human_user.favorite_events.all()), [self.event])
def test_EventMyFavoritesSet_delete(self):
def test_EventUserFavoritesICal(self):
c = Client()
resp = c.delete(reverse('api:my-events-set', kwargs={'pk': str(uuid.uuid4())}))
self.assertEqual(resp.status_code, 403)
c.force_login(self.human_user)
resp = c.get(reverse('api:user-events-ical', kwargs={'user_id': str(self.human_user.pk), 'ical_secret': 'invalid', 'filename_ignored': 'asdf.ics'}))
self.assertEqual(resp.status_code, 404)
resp = c.get(
reverse(
'api:user-events-ical',
kwargs={'user_id': str(self.human_user.pk), 'ical_secret': self.human_user.secret_ical_token, 'filename_ignored': 'asdf.ics'},
)
)
self.assertEqual(resp.status_code, 200)
self.assertEqual(
resp.content.decode('utf-8'),
"""BEGIN:VCALENDAR\r
VERSION:2.0\r
PRODID:-//Foo Conference//bernd's favorites\r
CALSCALE:GREGORIAN\r
METHOD:PUBLISH\r
X-WR-CALNAME:bernd's favorites\r
END:VCALENDAR\r\n""",
)
self.human_user.favorite_events.add(self.event)
c.force_login(self.human_user)
self.assertEqual(list(self.human_user.favorite_events.all()), [self.event])
resp = c.delete(reverse('api:my-events-set', kwargs={'pk': str(self.event.pk)}))
self.assertEqual(list(self.human_user.favorite_events.all()), [])
resp = c.get(
reverse(
'api:user-events-ical',
kwargs={'user_id': str(self.human_user.pk), 'ical_secret': self.human_user.secret_ical_token, 'filename_ignored': 'asdf.ics'},
)
)
self.assertEqual(resp.status_code, 200)
self.assertIn('SUMMARY:Event1_1', resp.content.decode('utf-8'))
from uuid import uuid4
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from django.urls import reverse
......@@ -41,10 +39,7 @@ class MetricsTest(TestCase):
conference=conf,
assembly=assembly,
name='Room 1',
room_type=Room.RoomType.BIGBLUEBUTTON,
backend_status=Room.BackendStatus.ACTIVE,
backend_link=str(uuid4()),
backend_data={'close_secret': 'asdf'},
room_type=Room.RoomType.STAGE,
)
room.save()
user = get_user_model()(username='testuser')
......@@ -70,12 +65,12 @@ class MetricsTest(TestCase):
self.assertContains(resp, 'hub_conference_tickets{conference="conf"} 0')
# Anzahl Assemblies: registriert, angenommen, abgelehnt
self.assertContains(resp, 'hub_conference_assemblies{conference="conf",state="planned"} 1')
self.assertContains(resp, 'hub_conference_assemblies{conference="conf",state="planned",location_state="none"} 1')
# Anzahl Events
self.assertContains(resp, 'hub_conference_events{conference="conf",kind="official"} 0')
self.assertContains(resp, 'hub_conference_events{conference="conf",kind="assembly"} 0')
self.assertContains(resp, 'hub_conference_events{conference="conf",kind="sos"} 0')
# Anzahl Räume nach Typen: stage, bbb, ..
self.assertContains(resp, 'hub_conference_rooms{conference="conf",room_type="bbb"} 1')
# Anzahl Räume nach Typen: stage, ..
self.assertContains(resp, 'hub_conference_rooms{conference="conf",room_type="stage"} 1')
import uuid
from datetime import datetime, timedelta
from pytz import UTC
from django.test import Client, TestCase, override_settings
from django.urls import reverse
from core.models import Assembly, Conference, ConferenceMember, Event, PlatformUser
from core.models.rooms import Room
TEST_CONF_ID = uuid.uuid4()
@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID)
class RoomsTestCase(TestCase):
def setUp(self):
self.conference1 = Conference(id=TEST_CONF_ID, slug='foo', name='Foo Conference', is_public=True)
self.conference1.save()
self.human_user = PlatformUser(username='bernd', user_type=PlatformUser.Type.HUMAN)
self.human_user.save()
self.human_cm = ConferenceMember(conference=self.conference1, user=self.human_user)
self.human_cm.save()
self.assembly = Assembly(conference=self.conference1, name='DUMMY', slug='dummy', state=Assembly.State.ACCEPTED, is_official=True)
self.assembly.save()
self.room = Room(conference=self.conference1, assembly=self.assembly, name='Foo Room', slug='foo-room', room_type=Room.RoomType.STAGE)
self.room.save()
self.event = Event(
conference=self.conference1,
slug='event1',
assembly=self.assembly,
name='Event1_1',
is_public=True,
schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
schedule_duration=timedelta(minutes=45),
kind=Event.Kind.OFFICIAL,
room=self.room,
)
self.event.save()
def test_ConferenceRoomEventsICal(self):
c = Client()
resp = c.get(reverse('api:room-events-ical', kwargs={'room': 'non-existing'}))
self.assertEqual(resp.status_code, 404)
resp = c.get(reverse('api:room-events-ical', kwargs={'room': self.room.slug}))
self.assertEqual(resp.status_code, 200)
# remove DTSTAMP and URL attributes as they are differing between tests & replace \n\r with \n
self.assertEqual(
'\n'.join([line for line in resp.content.decode('utf-8').splitlines() if not line.startswith('DTSTAMP') and not line.startswith('URL')]),
f"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Foo Conference//events in Foo Room
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:events in Foo Room
BEGIN:VEVENT
SUMMARY:Event1_1
DTSTART:20200102T000000Z
DTEND:20200102T004500Z
UID:{self.event.id!s}
CATEGORIES:official
DESCRIPTION:
LOCATION:Foo Room
END:VEVENT
END:VCALENDAR""",
)
from api.tests.user.dect import (
CheckTestCase,
VerifyTestCase,
)
__all__ = [
'CheckTestCase',
'VerifyTestCase',
]
import json
import uuid
from rest_framework.authtoken.models import Token
from django.test import Client, TestCase, override_settings
from django.urls import reverse
from core.models import (
Assembly,
AssemblyMember,
Badge,
Conference,
ConferenceMember,
PlatformUser,
UserCommunicationChannel,
)
TEST_CONF_ID = uuid.uuid4()
@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID)
class CheckTestCase(TestCase):
dummy_pswd = 'Spagh3ttiMon$ter'
def setUp(self):
self.conference = Conference(id=TEST_CONF_ID, slug='foo', name='Foo Conference', is_public=True)
self.conference.save()
self.user = PlatformUser.objects.create(username='alice')
self.user_cm = ConferenceMember.objects.create(conference=self.conference, user=self.user)
UserCommunicationChannel.objects.create(user=self.user, channel=UserCommunicationChannel.Channel.DECT, address='1337', is_verified=True)
self.user_2 = PlatformUser.objects.create(username='user_2')
self.user_2_cm = ConferenceMember.objects.create(conference=self.conference, user=self.user_2)
UserCommunicationChannel.objects.create(user=self.user_2, channel=UserCommunicationChannel.Channel.DECT, address='1338', is_verified=True)
self.user_3 = PlatformUser.objects.create(username='user_3')
self.user_3_cm = ConferenceMember.objects.create(conference=self.conference, user=self.user_3)
UserCommunicationChannel.objects.create(user=self.user_3, channel=UserCommunicationChannel.Channel.DECT, address='1338', is_verified=True)
self.user_4 = PlatformUser.objects.create(username='user_4')
self.user_4_cm = ConferenceMember.objects.create(conference=self.conference, user=self.user_4)
UserCommunicationChannel.objects.create(user=self.user_4, channel=UserCommunicationChannel.Channel.DECT, address='1339')
self.human_user_name = 'bernd'
self.human_user_pswd = 'Spagh3ttiMon$ter'
self.human_user = PlatformUser.objects.create(username=self.human_user_name, user_type=PlatformUser.Type.HUMAN)
self.human_user.set_password(self.human_user_pswd)
self.human_user.save()
self.human_cm = ConferenceMember.objects.create(conference=self.conference, user=self.human_user)
self.assembly = Assembly.objects.create(conference=self.conference, slug='bar', name='Bar Assembly')
AssemblyMember.objects.create(assembly=self.assembly, member=self.human_user, can_manage_assembly=True)
self.technical_user = self.assembly.create_technical_user_if_necessary()
self.service_user = Token.objects.create(user=self.technical_user)
self.badge = Badge.objects.create(issuing_assembly=self.assembly, name='foo', state=Badge.State.PUBLIC)
def test_check_dect_as_human_user_token(self):
c = Client()
token_response = c.post(reverse('api:auth-get-token'), {'username': self.human_user_name, 'password': self.human_user_pswd})
self.assertEqual(token_response.status_code, 200)
token = token_response.json()['token']
response = c.post(
reverse('api:dect-check', kwargs={'dect': 1337}),
content_type='application/json',
headers={'Authorization': f'Token {token}', 'Content-Type': 'application/json'},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {'exists': True, 'multiple': False})
def test_check_dect_as_human_user(self):
c = Client()
c.force_login(self.human_user)
response = c.post(
reverse('api:dect-check', kwargs={'dect': 1337}),
content_type='application/json',
headers={'Content-Type': 'application/json'},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {'exists': True, 'multiple': False})
def test_check_dect_as_technical_user(self):
c = Client()
response = c.post(
reverse('api:dect-check', kwargs={'dect': 1337}),
content_type='application/json',
headers={'Authorization': f'Token {self.service_user}', 'Content-Type': 'application/json'},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {'exists': True, 'multiple': False})
def test_check_dect_not_found(self):
c = Client()
c.force_login(self.human_user)
response = c.post(
reverse('api:dect-check', kwargs={'dect': 1336}),
content_type='application/json',
headers={'Content-Type': 'application/json'},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {'exists': False, 'multiple': False})
def test_check_dect_multiple_found(self):
c = Client()
c.force_login(self.human_user)
response = c.post(
reverse('api:dect-check', kwargs={'dect': 1338}),
content_type='application/json',
headers={'Content-Type': 'application/json'},
)
self.assertEqual(response.status_code, 300)
self.assertEqual(response.json(), {'exists': True, 'multiple': True})
def test_check_unverified_not_found(self):
c = Client()
c.force_login(self.human_user)
response = c.post(
reverse('api:dect-check', kwargs={'dect': 1339}),
content_type='application/json',
headers={'Content-Type': 'application/json'},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {'exists': False, 'multiple': False})
@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID)
class VerifyTestCase(TestCase):
def setUp(self):
self.conference = Conference.objects.create(id=TEST_CONF_ID, slug='foo', name='Foo Conference', is_public=True)
self.user = PlatformUser.objects.create(username='alice')
self.user_cm = ConferenceMember.objects.create(conference=self.conference, user=self.user)
self.ucc = UserCommunicationChannel.objects.create(user=self.user, channel=UserCommunicationChannel.Channel.DECT, address='1337')
self.dect_verification_token = self.ucc.get_verification_token()
self.user_2 = PlatformUser.objects.create(username='user_2')
self.user_2_cm = ConferenceMember.objects.create(conference=self.conference, user=self.user_2)
self.ucc_2 = UserCommunicationChannel.objects.create(user=self.user_2, channel=UserCommunicationChannel.Channel.DECT, address='1338')
self.user_3 = PlatformUser.objects.create(username='user_3')
self.user_3_cm = ConferenceMember.objects.create(conference=self.conference, user=self.user_3)
self.ucc_3 = UserCommunicationChannel.objects.create(user=self.user_3, channel=UserCommunicationChannel.Channel.DECT, address='1338')
self.human_user = PlatformUser.objects.create(username='bernd', user_type=PlatformUser.Type.HUMAN)
self.human_cm = ConferenceMember.objects.create(conference=self.conference, user=self.human_user)
self.service_user = PlatformUser(username='poc', user_type=PlatformUser.Type.SERVICE)
self.service_user.save()
ConferenceMember(conference=self.conference, user=self.service_user, roles=['poc']).save()
def test_verify_dect_as_human_user(self):
c = Client()
c.force_login(self.human_user)
response = c.post(
reverse('api:dect-verify', kwargs={'dect': 1337}),
data=json.dumps({'token': self.dect_verification_token}),
content_type='application/json',
)
self.assertEqual(response.status_code, 403)
self.assertFalse(UserCommunicationChannel.objects.filter(pk=self.ucc.pk, is_verified=True).exists())
def test_verify_dect_as_service_user(self):
c = Client()
c.force_login(self.service_user)
response = c.post(
reverse('api:dect-verify', kwargs={'dect': 1337}),
data=json.dumps({'token': self.dect_verification_token}),
content_type='application/json',
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {'verified': True})
self.ucc.refresh_from_db()
self.assertTrue(self.ucc.is_verified)
def test_failed_verification(self):
c = Client()
c.force_login(self.service_user)
response = c.post(
reverse('api:dect-verify', kwargs={'dect': 1337}),
data=json.dumps({'token': '1' * 13}),
content_type='application/json',
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {'verified': False})
self.ucc.refresh_from_db()
self.assertFalse(self.ucc.is_verified)
def test_dect_not_found_verification(self):
c = Client()
c.force_login(self.service_user)
response = c.post(
reverse('api:dect-verify', kwargs={'dect': 1336}),
data=json.dumps({'token': self.dect_verification_token}),
content_type='application/json',
)
self.assertEqual(response.status_code, 404)
self.ucc.refresh_from_db()
self.assertFalse(self.ucc.is_verified)
def test_multiple_dect_one_token_match(self):
c = Client()
self.ucc_2.renew_verification_token()
self.ucc_3.renew_verification_token()
c.force_login(self.service_user)
response = c.post(
reverse('api:dect-verify', kwargs={'dect': 1338}),
data=json.dumps({'token': self.ucc_2.verification_token}),
content_type='application/json',
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {'verified': True})
self.ucc_2.refresh_from_db()
self.ucc_3.refresh_from_db()
self.assertTrue(self.ucc_2.is_verified)
self.assertFalse(self.ucc_3.is_verified)
def test_multiple_dect_found(self):
c = Client()
self.ucc_2.verification_token = '1' * 12
self.ucc_2.save()
self.ucc_3.verification_token = '1' * 12
self.ucc_3.save()
c.force_login(self.service_user)
response = c.post(
reverse('api:dect-verify', kwargs={'dect': 1338}),
data=json.dumps({'token': '1' * 12}),
content_type='application/json',
)
self.assertEqual(response.status_code, 300)
self.ucc_2.refresh_from_db()
self.ucc_3.refresh_from_db()
self.assertFalse(self.ucc_2.is_verified)
self.assertFalse(self.ucc_3.is_verified)
def test_multiple_dect_not_found(self):
c = Client()
self.ucc_2.renew_verification_token()
self.ucc_3.renew_verification_token()
c.force_login(self.service_user)
response = c.post(
reverse('api:dect-verify', kwargs={'dect': 1338}),
data=json.dumps({'token': '1' * 12}),
content_type='application/json',
)
self.assertEqual(response.status_code, 404)
self.ucc_2.refresh_from_db()
self.ucc_3.refresh_from_db()
self.assertFalse(self.ucc_2.is_verified)
self.assertFalse(self.ucc_3.is_verified)
from rest_framework.authtoken import views as authtoken_views
from rest_framework.urlpatterns import format_suffix_patterns
from django.conf import settings
from django.urls import path
from api.views import api_root, assemblies, badges, bbb, conferencemember, conferences, events, maps, messages, metanav, rooms, schedule, users
from api.views import api_root, assemblies, badges, conferencemember, conferences, events, maps, messages, metanav, rooms, schedule, users
app_name = 'api'
urlpatterns = [
......@@ -23,6 +22,9 @@ urlpatterns = [
path('me/delete-message/<uuid:pk>', messages.DirectMessageDelete.as_view(), name='my-delete-message'),
path('me/friends', users.friends, name='friends'),
path('me/timeline', users.UserTimelineList.as_view(), name='timeline-list'),
path('user/dect/check', users.CheckDectExists.as_view()),
path('user/dect/<int:dect>/', users.CheckDectExists.as_view(), name='dect-check'),
path('user/dect/<int:dect>/verify', users.VerifyDect.as_view(), name='dect-verify'),
# conference-specific views
path('info', conferences.ConferenceDetail.as_view(), name='conference-detail'),
path('metanav', metanav.MetaNavView.as_view(), name='conference-metanav'),
......@@ -31,6 +33,7 @@ urlpatterns = [
path('track/<slug:track>/', conferences.ConferenceTrackDetail.as_view(), name='conferencetrack-detail'),
path('assemblies', assemblies.ConferenceAssemblyList.as_view(), name='assembly-list'),
path('assembly/<slug:assembly>/', assemblies.ConferenceAssemblyDetail.as_view(), name='assembly-detail'),
path('assembly/<slug:assembly>/export', assemblies.ConferenceAssemblyExport.as_view(), name='assembly-export'),
path('assembly/<slug:assembly>/events', assemblies.ConferenceAssemblyEventList.as_view(), name='assembly-events'),
path('assembly/<slug:assembly>/rooms', assemblies.ConferenceAssemblyRoomList.as_view(), name='assembly-rooms'),
path('assembly/<slug:assembly>/schedule', schedule.AssemblySchedule.as_view(), name='assembly-schedule'),
......@@ -41,25 +44,26 @@ urlpatterns = [
path('map/assemblies/poi.json', maps.AssembliesPoiExportView.as_view(), name='map-assemblies-poi'),
path('map/assemblies/areas.json', maps.AssembliesAreasExportView.as_view(), name='map-assemblies-areas'),
path('badges/redeem_token', badges.redeem_badge_token, name='badge-redeem'),
path('badge/<uuid:pk>/award/', badges.RewardBadgeToUser.as_view(), name='badge-award'),
path('badge/<uuid:pk>/award/user', badges.RewardBadgeToUser.as_view()),
path('badge/<uuid:pk>/award/dect', badges.RewardBadgeToUser.as_view()),
path('rooms', rooms.ConferenceRoomList.as_view(), name='room-list'),
path('room/<uuid:pk>/', rooms.ConferenceRoomDetail.as_view(), name='room-detail'),
path('room/<uuid:pk>/schedule', schedule.RoomSchedule.as_view(), name='room-schedule'),
path('events', events.EventList.as_view(), name='event-list'),
path('event/<uuid:pk>/', events.EventDetail.as_view(), name='event-detail'),
path('event/<uuid:pk>/schedule', schedule.EventSchedule.as_view(), name='event-schedule'),
path('ics/user/<int:user_id>/<str:ical_secret>/<str:filename_ignored>', events.EventUserFavoritesICal.as_view(), name='user-events-ical'),
path('ics/assembly/<slug:assembly>/events', assemblies.ConferenceAssemblyEventsICal.as_view(), name='assembly-events-ical'),
path('ics/room/<slug:room>/events', rooms.ConferenceRoomEventsICal.as_view(), name='room-events-ical'),
# integration with other components
path('integration/c3nav', maps.C3NavExportView.as_view(), name='c3nav'),
path('integration/is_angel/<str:username>', conferencemember.AngelView.as_view(), name='user-angel'),
]
if settings.INTEGRATIONS_BBB:
urlpatterns += [
# BBB meeting ended callback
path('integration/bbb/meeting_end', bbb.MeetingEnded.as_view(), name='bbb_meeting_end'),
]
urlpatterns = format_suffix_patterns(urlpatterns)
urlpatterns += [
path('auth/get-token', authtoken_views.obtain_auth_token),
path('auth/get-token', authtoken_views.obtain_auth_token, name='auth-get-token'),
]
from rest_framework import generics
from typing import Any
from rest_framework import generics, permissions
from rest_framework.views import APIView
from django.core.exceptions import PermissionDenied
from django.http import HttpRequest, HttpResponse
from django.views.generic import View
from core.export import Exporter
from core.models.assemblies import Assembly
from core.models.events import Event
from core.models.rooms import Room
from api.ical import EventICalFeed
from api.serializers import AssemblySerializer, EventSerializer, RoomSerializer
from api.views.mixins import ConferenceSlugAssemblyMixin, ConferenceSlugMixin
from api.views.mixins import ConferenceSlugAssemblyMixin, ConferenceSlugAssemblyMixinNonAuth, ConferenceSlugMixin
class ConferenceAssemblyList(ConferenceSlugMixin, generics.ListAPIView):
......@@ -26,7 +35,7 @@ class ConferenceAssemblyEventList(ConferenceSlugAssemblyMixin, generics.ListAPIV
serializer_class = EventSerializer
def get_queryset(self, **kwargs):
return Event.objects.conference_accessible(conference=self.conference).filter(room__assembly=self.assembly).order_by('name')
return Event.objects.conference_accessible(conference=self.conference).filter(assembly=self.assembly).order_by('name')
class ConferenceAssemblyRoomList(ConferenceSlugAssemblyMixin, generics.ListAPIView):
......@@ -34,3 +43,30 @@ class ConferenceAssemblyRoomList(ConferenceSlugAssemblyMixin, generics.ListAPIVi
def get_queryset(self, **kwargs):
return Room.objects.conference_accessible(self.conference).filter(assembly=self.assembly).order_by('name')
class ConferenceAssemblyEventsICal(ConferenceSlugAssemblyMixinNonAuth, View):
def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
events = Event.objects.conference_accessible(conference=self.conference).filter(assembly=self.assembly).select_related('room', 'track')
feed = EventICalFeed(events, name=f"{self.assembly.name}'s events", conference=self.conference)
return feed(request, args, kwargs)
class ConferenceAssemblyExport(ConferenceSlugAssemblyMixin, APIView):
permission_classes = [permissions.IsAuthenticated]
def get(self, request, *args, **kwargs):
if not self.assembly.user_can_manage(request.user):
raise PermissionDenied
exporter = Exporter(
self.conference,
assembly=self.assembly,
events=self.assembly.events.all(),
projects=self.assembly.projects.all(),
rooms=self.assembly.rooms.all(),
badges=self.assembly.badges.all(),
)
response = HttpResponse(exporter.zip_archive(), content_type='archive/zip')
response['Content-Disposition'] = f'attachment; filename="{self.conference.slug}-{self.assembly.slug}.zip"'
return response
......@@ -2,18 +2,18 @@ import logging
from rest_framework.decorators import api_view
from rest_framework.exceptions import NotFound
from rest_framework.generics import ListCreateAPIView
from rest_framework.generics import GenericAPIView, ListCreateAPIView
from rest_framework.response import Response
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from core.models.badges import Badge, BadgeToken
from core.models.badges import Badge, BadgeToken, UserBadge
from core.models.conference import Conference
from core.models.users import PlatformUser
from core.models.users import PlatformUser, UserCommunicationChannel
from api.permissions import HasIssuingToken, IsAssemblyManager, IsSuperUser
from api.serializers import BadgeSerializer, BadgeTokenSerializer
from api.permissions import HasIssuingToken, IsAssemblyManager, IsAssemblyService, IsSuperUser
from api.serializers import BadgeRewardSerializer, BadgeSerializer, BadgeTokenSerializer
from api.views.mixins import ConferenceSlugAssemblyMixin
logger = logging.getLogger(__name__)
......@@ -79,3 +79,28 @@ def redeem_badge_token(request, conference, **kwargs):
badge_token.redeem(user, False)
return Response({'badge': f'{badge_token.badge}', 'user': f'{user.username}'})
class RewardBadgeToUser(GenericAPIView):
permission_classes = [IsAssemblyService | IsAssemblyManager | IsSuperUser]
serializer_class = BadgeRewardSerializer
queryset = Badge.objects.all()
fallback_to_object = True
def get_user(self) -> PlatformUser:
if user := self.request.data.get('user', None):
return get_object_or_404(PlatformUser, username__iexact=user, is_active=True)
if dect := self.request.data.get('dect', None):
return get_object_or_404(UserCommunicationChannel, address=dect).user
raise ValueError('No valid data provided')
def post(self, *args, **kwargs):
badge = self.get_object()
try:
user = self.get_user()
except ValueError as e:
return Response({'error': str(e)}, status=400)
except UserCommunicationChannel.MultipleObjectsReturned:
return Response({'error': 'Unique user cannot be found through dect'}, status=400)
UserBadge.objects.redeem_badge(user=user, badge=badge, issuer=self.request.user)
return Response({'badge': f'{badge.name}', 'rewarded': True})