diff --git a/manage.sh b/manage.sh new file mode 100755 index 0000000000000000000000000000000000000000..3ab4863692d4880f6e2f1302d824dc5726e27782 --- /dev/null +++ b/manage.sh @@ -0,0 +1,4 @@ +#!/bin/bash +BASE_D=$(realpath "${BASH_SOURCE%/*}/") + +exec sudo docker run --rm -it -u $UID:$GID -p 127.0.0.1:8000:8000 -v "$BASE_D":/srv aura/pv /srv/manage.py $@ diff --git a/program/admin.py b/program/admin.py index 9db5ccc8699871a6202631ae83bbf73adf49fdf2..f69de36fce98db1eaf58d494a5e4fe29a9080061 100644 --- a/program/admin.py +++ b/program/admin.py @@ -224,14 +224,14 @@ class ShowAdmin(admin.ModelAdmin): filter_horizontal = ('hosts', 'owners', 'musicfocus', 'category', 'topic', 'language') inlines = (ScheduleInline,) list_display = ('name', 'short_description') - list_filter = (ActiveShowsFilter, 'type', 'category', 'topic', 'musicfocus', 'language', 'fundingcategory') + list_filter = (ActiveShowsFilter, 'type', 'category', 'topic', 'musicfocus', 'language', 'fundingcategory', 'is_public') ordering = ('slug',) prepopulated_fields = {'slug': ('name',)} search_fields = ('name', 'short_description', 'description') fields = ( 'predecessor', 'type', 'name', 'slug', 'image', 'logo', 'short_description', 'description', 'email', 'website', 'hosts', 'owners', 'language', 'category', 'fundingcategory', 'topic', - 'musicfocus', 'fallback_id', 'cba_series_id', 'is_active' + 'musicfocus', 'fallback_id', 'cba_series_id', 'is_active', 'is_public' ) @@ -598,4 +598,4 @@ admin.site.register(Host, HostAdmin) admin.site.register(Note, NoteAdmin) admin.site.register(Schedule, ScheduleAdmin) admin.site.register(TimeSlot, TimeSlotAdmin) -admin.site.register(Show, ShowAdmin) \ No newline at end of file +admin.site.register(Show, ShowAdmin) diff --git a/program/management/commands/addnote.py b/program/management/commands/addnote.py index 5c0147eb27451207b803144c6a7f89df1bd0eff3..fafb33fcc3deb637bdfd1ec6d0430100dab8be9a 100644 --- a/program/management/commands/addnote.py +++ b/program/management/commands/addnote.py @@ -64,4 +64,4 @@ class Command(BaseCommand): raise CommandError(ve.messages[0]) else: note.save() - print 'added note "%s" to "%s"' % (title, timeslot) + self.stdout.write(self.style.SUCCESS, f'added note "{title}" to "{timeslot}"') diff --git a/program/management/commands/check_automation_ids.py b/program/management/commands/check_automation_ids.py index 8bfe164391306f3b726dfbad648335e8bb7c1e22..825149ea988aad3ddb0f811aa61b5337ae7798e8 100644 --- a/program/management/commands/check_automation_ids.py +++ b/program/management/commands/check_automation_ids.py @@ -35,10 +35,10 @@ class Command(NoArgsCommand): multi_id = rd_ids[automation_id]['multi']['id'] if automation_id not in pv_ids and multi_id not in pv_ids: if multi_id < 0: - print '+ %d' % (automation_id) + self.stdout.write(self.style.NOTICE, f'+ {automation_id}') else: - print '+ %d (%d)' % (automation_id, multi_id) + self.stdout.write(self.style.NOTICE, f'+ {automation_id} ({multi_id})') for automation_id in sorted(pv_ids): if automation_id not in rd_ids: - print '-', automation_id \ No newline at end of file + self.stdout.write(self.style.NOTICE, f'- {automation_id}') diff --git a/program/management/commands/createuser.py b/program/management/commands/createuser.py index a78c1014e0182cdfc7d6b55c0638d2f07376addc..422c4bff8ebdb76f3e567ba148d862119a74df39 100644 --- a/program/management/commands/createuser.py +++ b/program/management/commands/createuser.py @@ -21,6 +21,6 @@ class Command(BaseCommand): User.objects.get(username=username) except User.DoesNotExist: User.objects.create_user(username=username, email=email) - print 'user created successfully.' + self.stdout.write(self.style.SUCCESS, 'user created successfully.') else: - print 'User already exists, no need to create.' + self.stdout.write(self.style.NOTICE, 'User already exists, no need to create.') diff --git a/program/management/commands/deleteuser.py b/program/management/commands/deleteuser.py index db2cb60b6e5f79654c2f9a894d3cb91ba4e3bce1..a8bb2308ec7c401c92722e85ddc4750e00469bdc 100644 --- a/program/management/commands/deleteuser.py +++ b/program/management/commands/deleteuser.py @@ -20,4 +20,4 @@ class Command(BaseCommand): except User.DoesNotExist: raise 'user does not exist.' else: - print 'user deleted succesfuly.' + self.stdout.write(self.style.SUCCESS, 'user deleted succesfuly.') diff --git a/program/management/commands/export_showlog.py b/program/management/commands/export_showlog.py index 0e26c011941df2978747289caf7d6664eda9a079..41c202178d8e3a0b1e3df0ba30ca8bfe868c3be9 100644 --- a/program/management/commands/export_showlog.py +++ b/program/management/commands/export_showlog.py @@ -2,7 +2,7 @@ import codecs import sys -from datetime import date, datetime, time, timedelta +from datetime import datetime from django.core.management.base import BaseCommand, CommandError from program.models import TimeSlot @@ -23,21 +23,21 @@ class Command(BaseCommand): else: raise CommandError('you must provide the year') - print "# Radio Helsinki Sendungslog %d" % year + self.stdout.write(self.style.NOTICE, f"# Radio Helsinki Sendungslog {year}") start = datetime.strptime('%d__01__01__00__00' % (year), '%Y__%m__%d__%H__%M') end = datetime.strptime('%d__01__01__00__00' % (year+1), '%Y__%m__%d__%H__%M') currentDate = None for ts in TimeSlot.objects.filter(end__gt=start, start__lt=end).select_related('schedule').select_related('show'): - if currentDate == None or currentDate < ts.start.date(): + if currentDate is None or currentDate < ts.start.date(): if currentDate: - print "\n" + self.stdout.write('\n') currentDate = ts.start.date() - print currentDate.strftime("## %a %d.%m.%Y:\n") + self.stdout.write(self.style.NOTICE, currentDate.strftime("## %a %d.%m.%Y:\n")) title = ts.show.name if ts.schedule.is_repetition: title += " (WH)" - print " * **%s - %s**: %s" % (ts.start.strftime("%H:%M:%S"), ts.end.strftime("%H:%M:%S"), title) \ No newline at end of file + self.stdout.write(self.style.NOTICE, f' * **{ts.start.strftime("%H:%M:%S")} - {ts.end.strftime("%H:%M:%S")}**: {title}') diff --git a/program/management/commands/importhosts.py b/program/management/commands/importhosts.py index 31f4a3f61be828408214258c3246d168803c3318..a0b92923d7f722aaa888edddd8528706dcf82b1e 100644 --- a/program/management/commands/importhosts.py +++ b/program/management/commands/importhosts.py @@ -32,4 +32,4 @@ WHERE letzter_termin > current_date AND macher != '' AND titel NOT LIKE 'Musikpr cursor.close() connection.close() - print '%i hosts imported' % counter + self.stdout.write(self.style.SUCCESS, F'{counter} hosts imported') diff --git a/program/management/commands/importnotes.py b/program/management/commands/importnotes.py index f835e37ed085a64b3c7f28ca2a81640acdc5e7e7..91476e6f9e094fedda2a5bd1f092ab57aae06531 100644 --- a/program/management/commands/importnotes.py +++ b/program/management/commands/importnotes.py @@ -36,30 +36,30 @@ WHERE n.sendung_id in (SELECT id FROM sendungen WHERE letzter_termin > current_d try: show = Show.objects.get(name=stitel) except ObjectDoesNotExist: - print 'show with name "%s" not found' % stitel + self.stdout.write(self.style.WARNING, f'show with name "{stitel}" not found') else: try: timeslot = TimeSlot.objects.get(schedule__show=show, start__year=year, start__month=month, start__day=day) except ObjectDoesNotExist: - print 'no timeslot found for sendung "%s" and datum "%s"' % (stitel, datum) + self.stdout.write(self.style.WARNING, f'no timeslot found for sendung "{stitel}" and datum "{datum}"') except MultipleObjectsReturned: - print 'multiple timeslots found for sendung "%s" and datum "%s"' % (stitel, datum) + self.stdout.write(self.style.WARNING, f'multiple timeslots found for sendung "{stitel}" and datum "{datum}"') else: note = Note(timeslot=timeslot, title=ntitel, content=notiz) try: note.validate_unique() except ValidationError: - print 'note already imported for show "%s" and datum "%s"' % (stitel, datum) + self.stdout.write(self.style.WARNING, f'note already imported for show "{stitel}" and datum "{datum}"') else: try: note.save() except: - print 'could not save note "%s" for show "%s" and datum "%s"' % (ntitel, stitel, datum) + self.stdout.write(self.style.ERROR, f'could not save note "{ntitel}" for show "{stitel}" and datum "{datum}"') else: counter += 1 cursor.close() connection.close() - print '%i notes imported' % counter \ No newline at end of file + self.stdout.write(self.style.SUCCESS, f'{counter} notes imported') diff --git a/program/management/commands/importprogramslots.py b/program/management/commands/importprogramslots.py index b6e542279b1cdd7f87df155181e93a31959b0912..8ce8a2bdd80084202bc75e37feedfff5098fea47 100644 --- a/program/management/commands/importprogramslots.py +++ b/program/management/commands/importprogramslots.py @@ -48,17 +48,17 @@ WHERE letzter_termin > current_date AND titel NOT LIKE 'Musikprogramm' AND titel try: show = Show.objects.get(name=titel) except ObjectDoesNotExist: - print 'show with name "%s" not found' % titel + self.stdout.write(self.style.NOTICE, f'show with name "{titel}" not found') else: schedule = Schedule(rrule=rrule, byweekday=termin, show=show, dstart=erster_termin, - tstart=tstart, tend=tend, until=letzter_termin) + tstart=tstart, tend=tend, until=letzter_termin) try: schedule.save() counter += 1 except: pass except KeyError: - print 'rhythmus "%i" is not supported for sendung "%s"' % (rhytmus, titel) + self.stdout.write(self.style.NOTICE, f'rhythmus "{rhytmus}" is not supported for sendung "{titel}"') cursor.execute("""SELECT titel, beginn, ende, erster_termin, letzter_termin, rhytmus, termin FROM sendungen @@ -80,19 +80,19 @@ WHERE letzter_termin > current_date AND titel LIKE '%%(Wiederholung)'""") try: show = Show.objects.get(name=titel) except ObjectDoesNotExist: - print 'show with name "%s" not found' % titel + self.stdout.write(self.style.WARNING, f'show with name "{titel}" not found') else: schedule = Schedule(rrule=rrule, byweekday=termin, show=show, dstart=erster_termin, - tstart=tstart, tend=tend, until=letzter_termin, is_repetition=True) + tstart=tstart, tend=tend, until=letzter_termin, is_repetition=True) try: schedule.save() counter += 1 except: pass except KeyError: - print 'rhythmus "%i" is not supported for sendung "%s"' % (rhytmus, titel) + self.stdout.write(self.style.WARNING, f'rhythmus "{rhytmus}" is not supported for sendung "{titel}"') cursor.close() connection.close() - print '%i schedules imported' % counter \ No newline at end of file + self.stdout.write(self.style.SUCCESS, f'{counter} schedules imported') diff --git a/program/management/commands/importshows.py b/program/management/commands/importshows.py index f1353b8a88cd2d9e39a586cddb309fb848d5f8db..cff3d434d8b29ab7692c1da57e2cb93292020e27 100644 --- a/program/management/commands/importshows.py +++ b/program/management/commands/importshows.py @@ -41,15 +41,15 @@ ORDER BY titel, beginn, ende""") try: host = Host.objects.get(name=macher) except MultipleObjectsReturned: - print 'multiple hosts with name "%s" found' % macher + self.stdout.write(self.style.NOTICE, f'multiple hosts with name "{macher}" found') except ObjectDoesNotExist: - print 'host with name "%s" not found' % macher + self.stdout.write(self.style.NOTICE, f'host with name "{macher}" not found') else: hosts.append(host) try: show = Show.objects.get(name=titel) - print 'sendung "%s" already imported as show "%s"' % (titel, show) + self.stdout.write(self.style.NOTICE, f'sendung "{titel}" already imported as show "{show}"') except ObjectDoesNotExist: show = Show(broadcastformat=TALK, name=titel, slug=slug, short_description='FIXME', description=beschreibung) @@ -57,7 +57,7 @@ ORDER BY titel, beginn, ende""") show.save() counter += 1 except: - print 'sendung "%s" could not be imported' % titel + self.stdout.write(self.style.NOTICE, f'sendung "{titel}" could not be imported') else: for h in hosts: show.hosts.add(h) @@ -66,4 +66,4 @@ ORDER BY titel, beginn, ende""") cursor.close() connection.close() - print '%i shows imported' % counter + self.stdout.write(self.style.SUCCESS, f'{counter} shows imported') diff --git a/program/migrations/0022_show_is_public.py b/program/migrations/0022_show_is_public.py new file mode 100644 index 0000000000000000000000000000000000000000..51136dd18633f0b41edaf94c37f6f39862f31954 --- /dev/null +++ b/program/migrations/0022_show_is_public.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.3 on 2019-09-18 12:48 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('program', '0021_show_is_active'), + ] + + operations = [ + migrations.AddField( + model_name='show', + name='is_public', + field=models.BooleanField(default=False, help_text='Files and Playlists of Public Shows can only be changed by owners but may be used by everyone.', verbose_name='Is Public?'), + ), + ] diff --git a/program/models.py b/program/models.py index c56e6708361ce74f0c4565fa3bb2aa5b45645c90..ae330a3d9bb28b5b2ecc3321a44434eb8efc3678 100644 --- a/program/models.py +++ b/program/models.py @@ -325,6 +325,7 @@ class Show(models.Model): created = models.DateTimeField(auto_now_add=True, editable=False) last_updated = models.DateTimeField(auto_now=True, editable=False) is_active = models.BooleanField(_("Is active?"), default=True) + is_public = models.BooleanField(_("Is Public?"), default=False, help_text=_("Files and Playlists of Public Shows can only be changed by owners but may be used by everyone.")) class Meta: ordering = ('slug',) diff --git a/program/serializers.py b/program/serializers.py index 779316f4100e78f65b42f3cbd7ea7367c2ab7509..6f6c5c3a88e1cb27016c0b4771e81553034e27e2 100644 --- a/program/serializers.py +++ b/program/serializers.py @@ -256,7 +256,7 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer): fields = ('id', 'name', 'slug', 'image', 'ppoi', 'logo', 'short_description', 'description', 'email', 'website', 'created', 'last_updated', 'type', 'fundingcategory', 'predecessor', 'cba_series_id', 'fallback_id', 'category', 'hosts', - 'owners', 'language', 'topic', 'musicfocus', 'thumbnails', 'is_active') + 'owners', 'language', 'topic', 'musicfocus', 'thumbnails', 'is_active', 'is_public') def create(self, validated_data): @@ -316,6 +316,7 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer): instance.fundingcategory = validated_data.get('fundingcategory', instance.fundingcategory) instance.predecessor = validated_data.get('predecessor', instance.predecessor) instance.is_active = validated_data.get('is_active', instance.is_active) + instance.is_public = validated_data.get('is_public', instance.is_public) instance.save() return instance diff --git a/program/views.py b/program/views.py index 4b139d16d36d654812dc352a0958c584c84e70b0..48a7c45c132a5f1472ea0db42a22fbc4eb5ba3ab 100644 --- a/program/views.py +++ b/program/views.py @@ -443,6 +443,8 @@ class APIShowViewSet(viewsets.ModelViewSet): /api/v1/shows/ Returns all shows (GET, POST) /api/v1/shows/?active=true Returns all active shows (= currently running) (GET) /api/v1/shows/?active=false Returns all inactive shows (= past or upcoming) (GET) + /api/v1/shows/?public=true Returns all public shows (GET) + /api/v1/shows/?public=false Returns all non-public shows (GET) /api/v1/shows/?host=1 Returns shows assigned to a given host (GET) /api/v1/shows/?owner=1 Returns shows of a given owner (GET) /api/v1/shows/1 Used for retrieving a single show or update (if owned) (GET, PUT) - DELETE is not allowed via API. Set is_active to False instead. @@ -489,6 +491,14 @@ class APIShowViewSet(viewsets.ModelViewSet): '''Return all shows except those which are running''' shows = Show.objects.exclude(id__in=show_ids,is_active=True) + if self.request.GET.get('public') == 'true': + '''Return all public shows''' + shows = shows.filter(is_public=True) + + if self.request.GET.get('public') == 'false': + '''Return all public shows''' + shows = shows.filter(is_public=False) + if self.request.GET.get('owner') != None: '''Filter shows by owner''' shows = shows.filter(owners__in=[int(self.request.GET.get('owner'))]) diff --git a/pv/oidc_provider_settings.py b/pv/oidc_provider_settings.py index e1b96998b883d69f3925736daa66325963297471..a3e2465b3ee6607f9153dc4574058938d15147b7 100644 --- a/pv/oidc_provider_settings.py +++ b/pv/oidc_provider_settings.py @@ -11,7 +11,9 @@ class AuraScopeClaims(ScopeClaims): def scope_username(self): dic = { - 'username': self.user.username + 'username': self.user.username, + #'privileged': (self.user.is_staff or self.user.is_superuser) + 'privileged': (self.user.is_superuser) } return dic @@ -22,9 +24,14 @@ class AuraScopeClaims(ScopeClaims): ) def scope_aura_shows(self): + from program.models import Show + + # TODO: should add filter `is_active=True` ? + public_show_slugs = list(Show.objects.filter(is_public=True).values_list('slug', flat=True)) show_slugs = list(self.user.shows.all().values_list('slug', flat=True)) dic = { - 'shows': show_slugs + 'shows': show_slugs, + 'public-shows': public_show_slugs } return dic