diff --git a/profile/admin.py b/profile/admin.py index 281aa6eee7e0e65ce99675fc9d3ef07aeb60f34a..6fffd8e3fef000b0a7401ceecc7a75964ed0049a 100644 --- a/profile/admin.py +++ b/profile/admin.py @@ -10,13 +10,20 @@ class ProfileInline(admin.StackedInline): verbose_name_plural = 'Profile' fk_name = 'user' -class CustomUserAdmin(UserAdmin): +class ProfileUserAdmin(UserAdmin): inlines = (ProfileInline, ) + def get_queryset(self, request): + # Users can only edit their own profile + if not request.user.is_superuser: + return super(UserAdmin, self).get_queryset(request).filter(pk=request.user.id) + + return super(UserAdmin, self).get_queryset(request) + def get_inline_instances(self, request, obj=None): if not obj: return list() - return super(CustomUserAdmin, self).get_inline_instances(request, obj) + return super(ProfileUserAdmin, self).get_inline_instances(request, obj) admin.site.unregister(User) -admin.site.register(User, CustomUserAdmin) \ No newline at end of file +admin.site.register(User, ProfileUserAdmin) \ No newline at end of file diff --git a/program/admin.py b/program/admin.py index 251eff1c650682854e524b51e7eed2438bd5d176..ab7f53b791225fe14763be7c2a63e6e56f2e8b22 100644 --- a/program/admin.py +++ b/program/admin.py @@ -9,7 +9,6 @@ from .forms import MusicFocusForm, CollisionForm from datetime import date, datetime, time, timedelta - class ActivityFilter(admin.SimpleListFilter): title = _("Activity") @@ -89,32 +88,61 @@ class HostAdmin(admin.ModelAdmin): class NoteAdmin(admin.ModelAdmin): date_hierarchy = 'start' list_display = ('title', 'show', 'start', 'status') + fields = (( 'show', 'timeslot'), 'title', 'slug', 'summary', 'content', 'image', 'status', 'cba_id') prepopulated_fields = {'slug': ('title',)} list_filter = ('status',) ordering = ('timeslot',) save_as = True + class Media: + js = [ settings.MEDIA_URL + 'js/calendar/lib/moment.min.js', + settings.MEDIA_URL + 'js/note_change.js', ] + def get_queryset(self, request): - shows = request.user.shows.all() + if request.user.is_superuser: + # Superusers see notes of all shows + shows = Show.objects.all() + else: + # Users only see notes of shows they own + shows = request.user.shows.all() + return super(NoteAdmin, self).get_queryset(request).filter(show__in=shows) def formfield_for_foreignkey(self, db_field, request=None, **kwargs): four_weeks_ago = datetime.now() - timedelta(weeks=4) - in_twelf_weeks = datetime.now() + timedelta(weeks=12) + in_twelve_weeks = datetime.now() + timedelta(weeks=12) if db_field.name == 'timeslot': - try: - timeslot_id = int(request.get_full_path().split('/')[-2]) - except ValueError: - shows = request.user.shows.all() - kwargs['queryset'] = TimeSlot.objects.filter(show__in=shows, start__gt=four_weeks_ago, - start__lt=in_twelf_weeks) # note__isnull=True + # Adding/Editing a note: load timeslots of the user's shows into the dropdown + + # TODO: Don't show any timeslot in the select by default. + # User should first choose show, then timeslots are loaded into the select via ajax. + # + # How to do this while not constraining the queryset? + # Saving won't be possible otherwise, if queryset doesn't contain the selectable elements beforehand + #kwargs['queryset'] = TimeSlot.objects.filter(show=-1) + + # Superusers see every timeslot for every show + if request.user.is_superuser: + kwargs['queryset'] = TimeSlot.objects.filter(start__gt=four_weeks_ago, + start__lt=in_twelve_weeks) # note__isnull=True + # Users see timeslots of shows they own else: - kwargs['queryset'] = TimeSlot.objects.filter(note=timeslot_id) + kwargs['queryset'] = TimeSlot.objects.filter(show__in=request.user.shows.all(), start__gt=four_weeks_ago, + start__lt=in_twelve_weeks) # note__isnull=True + + if db_field.name == 'show': + # Adding/Editing a note: load user's shows into the dropdown + + # Superusers see all shows + if not request.user.is_superuser: + kwargs['queryset'] = Show.objects.filter(pk__in=request.user.shows.all()) return super(NoteAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) def save_model(self, request, obj, form, change): + + # Save the creator when adding a note if not change: obj.user = request.user obj.save() @@ -124,11 +152,14 @@ class TimeSlotInline(admin.TabularInline): model = TimeSlot ordering = ('-end',) +class TimeSlotAdmin(admin.ModelAdmin): + model = TimeSlot + class ScheduleAdmin(admin.ModelAdmin): actions = ('renew',) inlines = (TimeSlotInline,) - fields = (('rrule', 'byweekday'), ('dstart', 'tstart', 'tend'), 'until', 'is_repetition', 'automation_id', 'fallback_playlist') + fields = (('rrule', 'byweekday'), ('dstart', 'tstart', 'tend'), 'until', 'is_repetition', 'automation_id', 'fallback_playlist_id') list_display = ('get_show_name', 'byweekday', 'rrule', 'tstart', 'tend', 'until') list_filter = (ActiveSchedulesFilter, 'byweekday', 'rrule', 'is_repetition') ordering = ('byweekday', 'dstart') @@ -171,10 +202,19 @@ class ShowAdmin(admin.ModelAdmin): 'musicfocus', 'fallback_pool', 'cba_series_id', ) + def get_queryset(self, request): + if request.user.is_superuser: + # Superusers see all shows + shows = Show.objects.all() + else: + # Users only see shows they own + shows = request.user.shows.all() + + return super(ShowAdmin, self).get_queryset(request).filter(pk__in=shows) + class Media: - js = [ settings.MEDIA_URL + 'js/show_change.js', - settings.MEDIA_URL + 'js/calendar/lib/moment.min.js', - ] + js = [ settings.MEDIA_URL + 'js/calendar/lib/moment.min.js', + settings.MEDIA_URL + 'js/show_change.js', ] css = { 'all': ('/program/styles.css',) } @@ -294,6 +334,7 @@ class ShowAdmin(admin.ModelAdmin): until = datetime.strptime(request.POST.get('ps_save_until'), '%Y-%m-%d').date() is_repetition = request.POST.get('ps_save_is_repetition') automation_id = int(request.POST.get('ps_save_automation_id')) if request.POST.get('ps_save_automation_id') != 'None' else 0 + fallback_playlist_id = int(request.POST.get('fallback_playlist_id')) if request.POST.get('ps_save_fallback_playlist_id') != 'None' else 0 # Put timeslot POST vars into lists with same indices for i in range(num_inputs): @@ -337,7 +378,8 @@ class ShowAdmin(admin.ModelAdmin): tend=tend, until=until, is_repetition=is_repetition, - automation_id=automation_id) + automation_id=automation_id, + fallback_playlist_id=fallback_playlist_id) # Only save schedule if any timeslots changed if len(resolved_timeslots) > 0: @@ -371,7 +413,7 @@ class ShowAdmin(admin.ModelAdmin): start_end = ts.split(' - ') # Only create upcoming timeslots if datetime.strptime(start_end[0], "%Y-%m-%d %H:%M:%S") > datetime.today(): - timeslot_created = TimeSlot.objects.create(schedule=new_schedule, start=start_end[0], end=start_end[1]) + timeslot_created = TimeSlot.objects.create(schedule=new_schedule, is_repetition=new_schedule.is_repetition, start=start_end[0], end=start_end[1]) # Link a note to the new timeslot if idx in note_indices: @@ -478,5 +520,5 @@ admin.site.register(RTRCategory, RTRCategoryAdmin) admin.site.register(Host, HostAdmin) admin.site.register(Note, NoteAdmin) #admin.site.register(Schedule, ScheduleAdmin) -#admin.site.register(TimeSlot, TimeSlotAdmin) +admin.site.register(TimeSlot, TimeSlotAdmin) admin.site.register(Show, ShowAdmin) \ No newline at end of file diff --git a/program/fixtures/group_permissions.yaml b/program/fixtures/group_permissions.yaml new file mode 100644 index 0000000000000000000000000000000000000000..20fa7567d6c35ae28963b3eb5cedc31593bf69a2 --- /dev/null +++ b/program/fixtures/group_permissions.yaml @@ -0,0 +1,36 @@ +- model: auth.group_permissions + pk: 1 + fields: + id: 1 + group_id: 1 + permission_id: 35 +- model: auth.group_permissions + pk: 2 + fields: + id: 2 + group_id: 1 + permission_id: 28 +- model: auth.group_permissions + pk: 3 + fields: + id: 3 + group_id: 1 + permission_id: 29 +- model: auth.group_permissions + pk: 4 + fields: + id: 4 + group_id: 1 + permission_id: 30 +- model: auth.group_permissions + pk: 5 + fields: + id: 5 + group_id: 1 + permission_id: 8 +- model: auth.group_permissions + pk: 6 + fields: + id: 6 + group_id: 1 + permission_id: 68 \ No newline at end of file diff --git a/program/fixtures/groups.yaml b/program/fixtures/groups.yaml new file mode 100644 index 0000000000000000000000000000000000000000..da482957143e7beec7daf47d2edfee6aa8dec217 --- /dev/null +++ b/program/fixtures/groups.yaml @@ -0,0 +1,5 @@ +- model: auth.group + pk: 1 + fields: + id: 1 + name: ProgrammmacherInnen \ No newline at end of file diff --git a/program/fixtures/user_groups.yaml b/program/fixtures/user_groups.yaml new file mode 100644 index 0000000000000000000000000000000000000000..82ce3739ec635d392758dcc485f5e9a4f2abddd3 --- /dev/null +++ b/program/fixtures/user_groups.yaml @@ -0,0 +1,6 @@ +- model: auth.user_groups + pk: 1 + fields: + id: 1 + user_id: 2 + group_id: 1 \ No newline at end of file diff --git a/program/fixtures/users.yaml b/program/fixtures/users.yaml new file mode 100644 index 0000000000000000000000000000000000000000..671dfa5c85b34c1bc7c647e7c88a71c8303090db --- /dev/null +++ b/program/fixtures/users.yaml @@ -0,0 +1,11 @@ +- model: auth.user + pk: 2 + fields: + id: 2 + password: pbkdf2_sha256$36000$uMBrNYea7vcc$6599RKSDzRkpoO8lcOjOQI/O1ufPXphIYh10VNqZcYU= + last_login: NULL + is_superuser: 0 + username: pm1 + is_staff: 1 + is_active: 1 + date_joined: 2017-11-22 00:00:00 \ No newline at end of file diff --git a/program/migrations/0012_auto_20171109_1843.py b/program/migrations/0012_auto_20171122_1718.py similarity index 89% rename from program/migrations/0012_auto_20171109_1843.py rename to program/migrations/0012_auto_20171122_1718.py index 27c9511380358f52d1e00163fae86b05ba9fb2dc..3141a578f5162e2d4a1f7f2880dbf6863d15b641 100644 --- a/program/migrations/0012_auto_20171109_1843.py +++ b/program/migrations/0012_auto_20171122_1718.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.3 on 2017-11-09 18:43 +# Generated by Django 1.11.3 on 2017-11-22 17:18 from __future__ import unicode_literals from django.conf import settings @@ -34,6 +34,18 @@ class Migration(migrations.Migration): 'ordering': ('category',), }, ), + migrations.CreateModel( + name='Language', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=32, verbose_name='Language')), + ], + options={ + 'verbose_name': 'Language', + 'verbose_name_plural': 'Languages', + 'ordering': ('language',), + }, + ), migrations.CreateModel( name='RTRCategory', fields=[ @@ -58,14 +70,14 @@ class Migration(migrations.Migration): ('tend', models.TimeField(verbose_name='End time')), ('until', models.DateField(verbose_name='Last date')), ('is_repetition', models.BooleanField(default=False, verbose_name='Is repetition')), - ('fallback_playlist', models.IntegerField(blank=True, null=True, verbose_name='Fallback Playlist')), + ('fallback_playlist_id', models.IntegerField(blank=True, null=True, verbose_name='Fallback Playlist ID')), ('automation_id', models.IntegerField(blank=True, null=True, verbose_name='Automation ID')), ('created', models.DateTimeField(auto_now_add=True, null=True)), ('last_updated', models.DateTimeField(auto_now=True, null=True)), ], options={ - 'verbose_name': 'Program slot', - 'verbose_name_plural': 'Program slots', + 'verbose_name': 'Schedule', + 'verbose_name_plural': 'Schedules', 'ordering': ('dstart', 'tstart'), }, ), @@ -209,6 +221,16 @@ class Migration(migrations.Migration): name='width', field=models.PositiveIntegerField(blank=True, editable=False, null=True, verbose_name='Image Width'), ), + migrations.AddField( + model_name='timeslot', + name='is_repetition', + field=models.BooleanField(default=False, verbose_name='WH'), + ), + migrations.AddField( + model_name='timeslot', + name='memo', + field=models.TextField(blank=True, verbose_name='Memo'), + ), migrations.AlterField( model_name='musicfocus', name='big_button', @@ -224,6 +246,11 @@ class Migration(migrations.Migration): name='button_hover', field=models.ImageField(blank=True, null=True, upload_to='buttons', verbose_name='Button image (hover)'), ), + migrations.AlterField( + model_name='note', + name='show', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='program.Show'), + ), migrations.AlterField( model_name='show', name='image', @@ -261,6 +288,11 @@ class Migration(migrations.Migration): name='category', field=models.ManyToManyField(blank=True, related_name='shows', to='program.Category', verbose_name='Category'), ), + migrations.AddField( + model_name='show', + name='language', + field=models.ManyToManyField(blank=True, related_name='language', to='program.Language', verbose_name='Language'), + ), migrations.AddField( model_name='show', name='rtrcategory', diff --git a/program/migrations/0013_auto_20171109_1942.py b/program/migrations/0013_auto_20171109_1942.py deleted file mode 100644 index 9e6723c42415478eab5586d1bc366423c65e02b7..0000000000000000000000000000000000000000 --- a/program/migrations/0013_auto_20171109_1942.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.3 on 2017-11-09 19:42 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('program', '0012_auto_20171109_1843'), - ] - - operations = [ - migrations.CreateModel( - name='Language', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=32, verbose_name='Language')), - ], - options={ - 'verbose_name': 'Language', - 'verbose_name_plural': 'Languages', - 'ordering': ('language',), - }, - ), - migrations.AlterModelOptions( - name='schedule', - options={'ordering': ('dstart', 'tstart'), 'verbose_name': 'Schedule', 'verbose_name_plural': 'Schedules'}, - ), - migrations.AddField( - model_name='show', - name='language', - field=models.ManyToManyField(blank=True, related_name='language', to='program.Language', verbose_name='Language'), - ), - ] diff --git a/program/models.py b/program/models.py index c24cde20251d6e18e0c20df01fd142450954c929..9da8178c1a26e2c824dc839640e1a6bd271fcffa 100644 --- a/program/models.py +++ b/program/models.py @@ -351,16 +351,17 @@ class Schedule(models.Model): tend = models.TimeField(_("End time")) until = models.DateField(_("Last date")) is_repetition = models.BooleanField(_("Is repetition"), default=False) - fallback_playlist = models.IntegerField(_("Fallback Playlist"), blank=True, null=True) + fallback_playlist_id = models.IntegerField(_("Fallback Playlist ID"), blank=True, null=True) automation_id = models.IntegerField(_("Automation ID"), blank=True, null=True, choices=get_automation_id_choices()) # Deprecated created = models.DateTimeField(auto_now_add=True, editable=False, null=True) #-> both see https://stackoverflow.com/questions/1737017/django-auto-now-and-auto-now-add last_updated = models.DateTimeField(auto_now=True, editable=False, null=True) class Meta: ordering = ('dstart', 'tstart') - # Produces error when adding several schedules at the same time. - # Do this test in another way, since it is quite unspecific anyway - #unique_together = ('rrule', 'byweekday', 'dstart', 'tstart') + # DEPRECATED + # Produces error when adding several schedules at the same time + # get_collisions() covers this case and checks for interfering times too + # unique_together = ('rrule', 'byweekday', 'dstart', 'tstart') verbose_name = _("Schedule") verbose_name_plural = _("Schedules") @@ -369,6 +370,7 @@ class Schedule(models.Model): tend = self.tend.strftime('%H:%M') dstart = self.dstart.strftime('%d. %b %Y') tstart = self.tstart.strftime('%H:%M') + #is_repetition = self.is_repetition if self.rrule.freq == 0: return '%s %s, %s - %s' % (self.rrule, dstart, tstart, tend) @@ -380,7 +382,7 @@ class Schedule(models.Model): def generate_timeslots(schedule): """ Returns a list of timeslot objects based on a schedule and its rrule - Returns past timeslots as well starting from dstart (not today) + Returns past timeslots as well, starting from dstart (not today) """ byweekno = None @@ -562,6 +564,8 @@ class TimeSlot(models.Model): start = models.DateTimeField(_("Start time")) # Removed 'unique=True' because new Timeslots need to be created before deleting the old ones (otherwise linked notes get deleted first) end = models.DateTimeField(_("End time")) show = models.ForeignKey(Show, editable=False, related_name='timeslots') + memo = models.TextField(_("Memo"), blank=True) + is_repetition = models.BooleanField(_("WH"), default=False) objects = TimeSlotManager() @@ -571,10 +575,11 @@ class TimeSlot(models.Model): verbose_name_plural = _("Time slots") def __str__(self): - start = self.start.strftime('%d.%m.%Y %H:%M') + start = self.start.strftime('%a, %d.%m.%Y %H:%M') end = self.end.strftime('%H:%M') + is_repetition = ' ' + _('WH') if self.schedule.is_repetition is 1 else '' - return '%s - %s | %s' % (start, end, self.show.name) + return '%s - %s %s (%s)' % (start, end, is_repetition, self.show.name) def save(self, *args, **kwargs): self.show = self.schedule.show @@ -607,7 +612,7 @@ class Note(models.Model): image = VersatileImageField(_("Featured image"), blank=True, null=True, upload_to='note_images', width_field='width', height_field='height', ppoi_field='ppoi') status = models.IntegerField(_("Status"), choices=STATUS_CHOICES, default=1) start = models.DateTimeField(editable=False) - show = models.ForeignKey(Show, editable=False, related_name='notes') + show = models.ForeignKey(Show, related_name='notes', editable=True) # User chooses the show. Timeslots are loaded via ajax. cba_id = models.IntegerField(_("CBA ID"), blank=True, null=True) created = models.DateTimeField(auto_now_add=True, editable=False) last_updated = models.DateTimeField(auto_now=True, editable=False) diff --git a/program/templates/calendar.html b/program/templates/calendar.html index 2acfbaa92064e26a2434fb341d1addb2de5a9854..318f052ebb5cf638b0bd3aba685ece1f0eab590a 100644 --- a/program/templates/calendar.html +++ b/program/templates/calendar.html @@ -88,6 +88,11 @@ <div id="timeslot-id"></div> <div id="timeslot-start"></div> <div id="timeslot-end"></div> + <div id="show-name"></div> + <div id="show-id"></div> + <div id="show-hosts"></div> + <div id="is-repetition"></div> + <div id="fallback-playlist-id"></div> <div id="response-message"></div> </div> </div> @@ -138,7 +143,7 @@ .done(function( data ) { // Remove element from DOM jQuery('#calendar').fullCalendar('removeEvents', event._id ); - notify( 'Episode gelöscht.' ); + notify( 'Time slot deleted.' ); console.log(data.result); }) .fail(function( data ) { @@ -170,6 +175,12 @@ jQuery("#timeslot-id").html(timeslot.id); jQuery("#timeslot-start").html(timeslot.start); jQuery("#timeslot-end").html(timeslot.end); + jQuery("#show-name").html(timeslot.show_name); + jQuery("#show-id").html(timeslot.show_id); + jQuery("#show-hosts").html(timeslot.show_hosts); + jQuery("#is-repetition").html(timeslot.is_repetition); + jQuery("#fallback-playlist-id").html(timeslot.fallback_playlist_id); + jQuery("#memo").html(timeslot.memo); jQuery("#response-message").html("Success"); }, error: function() { @@ -200,6 +211,7 @@ }); }); +/* function updateTimeslot( event ) { var id = event.id; @@ -213,7 +225,7 @@ jQuery.post( ajaxurl, { 'action': 'update_timeslot', 'id': id, 'date': date, 'start': start, 'stop' : stop } ) .done(function( data ) { - notify( 'Änderungen gespeichert.' ); + notify( 'Changes saved.' ); console.log(data.result); }) .fail(function( data ) { @@ -221,7 +233,7 @@ }); } - +*/ </script> </body> diff --git a/program/templates/collisions.html b/program/templates/collisions.html index e891b8dc88e5b8343745cae22a1e2215871deb71..84de2053f59fdfd16e25f1e660acb9f6778156f5 100644 --- a/program/templates/collisions.html +++ b/program/templates/collisions.html @@ -136,6 +136,7 @@ <input type="hidden" name="ps_save_until" value="{{ schedule.until|date:"Y-m-d" }}" /> <input type="hidden" name="ps_save_is_repetition" value="{{ schedule.is_repetition }}" /> <input type="hidden" name="ps_save_automation_id" value="{{ schedule.automation_id }}" /> + <input type="hidden" name="ps_save_fallback_playlist_id" value="{{ schedule.fallback_playlist_id }}" /> <input type="hidden" name="ps_save_show_id" value="{{ schedule.show_id }} " /> <input type="hidden" name="num_inputs" value="{{ num_inputs }}" /> <input type="hidden" name="step" value="{{ step }}" /> @@ -154,6 +155,7 @@ <p class="deletelink-box"><a href="/admin/program/show/{{ obj.id }}/change" class="deletelink">Abbrechen und Änderungen verwerfen</a></p> </div> + <!-- TODO: Problemo: If form validation failed, submit will be prevented -> only include the fields necessary --> <div style="display:none;"> {{ schedulesform.as_ul }} {{ showform.as_ul }} diff --git a/program/views.py b/program/views.py index 8258d6bd834a94f8f0bbad7c48d82032df1b50a7..70471c00ab6595414f753b86da50767c38561b9b 100644 --- a/program/views.py +++ b/program/views.py @@ -11,7 +11,6 @@ from django.views.generic.detail import DetailView from django.views.generic.list import ListView from .models import Type, MusicFocus, Note, Show, Category, Topic, TimeSlot, Host - from program.utils import tofirstdayinisoweek, get_cached_shows @@ -227,6 +226,7 @@ def json_week_schedule(request): """ Called by calendar to get all timeslots for a week. Expects GET variable 'start' (date), otherwise start will be today + Returns all timeslots of the next 7 days """ start = request.GET.get('start') @@ -240,15 +240,28 @@ def json_week_schedule(request): schedule = [] for ts in timeslots: - # TODO: Will be a field of timeslots in the future - is_repetition = ' (WH)' if ts.schedule.is_repetition == 1 else '' + is_repetition = ' ' + _('WH') if ts.schedule.is_repetition is 1 else '' + + hosts = '' + + for host in ts.show.hosts.all(): + hosts = host.name + ', ' + hosts entry = { 'start': ts.start.strftime('%Y-%m-%dT%H:%M:%S'), 'end': ts.end.strftime('%Y-%m-%dT%H:%M:%S'), 'title': ts.show.name + is_repetition, 'id': ts.id, #show.id, - 'automation-id': -1 + 'automation-id': -1, + 'schedule_id': ts.schedule.id, + 'show_id': ts.show.id, + 'show_name': ts.show.name, + 'show_hosts': hosts, + 'is_repetition': ts.is_repetition, + 'fallback_playlist_id': ts.schedule.fallback_playlist_id, # the schedule's fallback playlist + 'show_fallback_pool': ts.show.fallback_pool, # the show's fallback + # TODO + #'station_fallback_pool': # the station's global fallback (might change suddenly) } if ts.schedule.automation_id: @@ -287,8 +300,56 @@ def json_timeslots_specials(request): def json_get_timeslot(request): + if not request.user.is_authenticated(): + return JsonResponse(_('Permission denied.')) + if request.method == 'GET': try: - return JsonResponse( model_to_dict(TimeSlot.objects.select_related('schedule').select_related('show').get(pk=int(request.GET.get('timeslot_id'))))) + timeslot = TimeSlot.objects.get(pk=int(request.GET.get('timeslot_id'))) + + returnvar = { 'id': timeslot.id, 'start': timeslot.start, 'end': timeslot.end, + 'schedule_id': timeslot.schedule.id, 'show_name': timeslot.show.name, + 'is_repetition': timeslot.schedule.is_repetition, + 'fallback_playlist_id': timeslot.schedule.fallback_playlist_id, + 'memo': timeslot.memo } + return JsonResponse( returnvar, safe=False ) except ObjectDoesNotExist: - return JsonResponse( list('Error') ); \ No newline at end of file + return JsonResponse( _('Error') ); + + +def json_get_timeslots_by_show(request): + ''' + Returns a JSON object of timeslots of a given show from 4 weeks ago until 12 weeks in the future + Called by /export/get_timeslot_by_show/?show_id=1 to populate a timeslot-select for being assigned to a note + ''' + + if not request.user.is_authenticated(): + return JsonResponse(_('Permission denied.')) + + if request.method == 'GET' and int(request.GET.get('show_id')): + + four_weeks_ago = datetime.now() - timedelta(weeks=4) + in_twelve_weeks = datetime.now() + timedelta(weeks=12) + + timeslots = [] + saved_timeslot_id = int(request.GET.get('timeslot_id')) + + # If the saved timeslot is part of the currently selected show, + # include it as the first select-option in order not to lose it if it's past + if saved_timeslot_id > 0: + try: + saved_timeslot = TimeSlot.objects.get(pk=int(request.GET.get('timeslot_id')),show=int(request.GET.get('show_id'))) + timeslots.append( { 'timeslot': str(saved_timeslot), 'timeslot_id': saved_timeslot.id, 'start': saved_timeslot.start, 'end': saved_timeslot.end } ) + except ObjectDoesNotExist: + pass + + for timeslot in TimeSlot.objects.filter(show=int(request.GET.get('show_id')), + start__gt=four_weeks_ago, + start__lt=in_twelve_weeks).exclude(pk=saved_timeslot_id): + + timeslots.append( { 'timeslot': str(timeslot), 'timeslot_id' : timeslot.id, 'start': timeslot.start, 'end': timeslot.end } ) + + return JsonResponse( timeslots, safe=False ) + + else: + return JsonResponse( _('No show_id given.'), safe=False ) \ No newline at end of file diff --git a/pv/site_media/js/note_change.js b/pv/site_media/js/note_change.js new file mode 100644 index 0000000000000000000000000000000000000000..7a397d57041d5e35cc57ad8c39fcec21545413fe --- /dev/null +++ b/pv/site_media/js/note_change.js @@ -0,0 +1,46 @@ +django.jQuery(document).ready( function() { + + /* Get the already saved timeslot_id to preserve if past */ + var selected_timeslot_id = django.jQuery('select#id_timeslot option:selected').val() || 0; + + /* If a show is selected load its timeslots into the corresponding select */ + django.jQuery("select#id_show").on("change", function() { + + /* Get selected show */ + var show_id = django.jQuery("select#id_show option:selected").val(); + if( show_id == '' ) { + django.jQuery('select#id_timeslot').html( new Option( '', '' ) ); + return; + } + + django.jQuery('select#id_timeslot').fadeOut(); + + /* Call ajax function and retrieve array containing objects */ + django.jQuery.ajax({ + url: '/export/get_timeslots_by_show', + type: 'GET', + data: { + 'show_id': show_id, + 'timeslot_id': selected_timeslot_id, + 'csrfmiddlewaretoken': django.jQuery('input[name="csrfmiddlewartetoken"]').val() + }, + success: function(timeslots) { + /* Populate timeslot select */ + var options = new Array(); + + for( var i=0; i < timeslots.length; i++ ) { + options[i] = new Option( timeslots[i].timeslot, parseInt(timeslots[i].timeslot_id) ); //+ " " + moment.utc( timeslots[i].start ).format('dddd, D.M. YYYY HH:mm') + ' - ' + moment.utc( timeslots[i].end ).format('HH:mm'), timeslots[i].timeslot_id ); + } + + django.jQuery('select#id_timeslot').html( options ).fadeIn(); + + }, + error: function() { + alert("Couldn't load timeslots."); + } + + }); + + }); + +}); \ No newline at end of file diff --git a/pv/site_media/js/show_change.js b/pv/site_media/js/show_change.js index f3db2ec526663e8ba6a4cbe057a4ff20be1e5eb3..5e3f8f2c0e6ad0c8a63a4fcf047173d759432c76 100644 --- a/pv/site_media/js/show_change.js +++ b/pv/site_media/js/show_change.js @@ -14,9 +14,16 @@ function toggleSelects( select_elm ) { if( option_val < 4 ) { weekday_select.fadeOut().val(0); // Although it'll be ignored, select Monday in order the form can be validated - if( option_val == 1 ) + if( option_val == 1 ) { // If once, hide the until-date too and try to set it to dstart - until_select.val(dstart.val()).fadeOut().next('.datetimeshortcuts').fadeOut(); + if( until_select.val() == '') + until_select.val(dstart.val()); + + until_select.fadeOut().next('.datetimeshortcuts').fadeOut(); + } else { + until_select.fadeIn().next('.datetimeshortcuts').fadeIn(); + } + } else { weekday_select.fadeIn(); diff --git a/pv/urls.py b/pv/urls.py index 8135910840fbc39413baf192d2ede128a89eb2b3..69ca4947c67d2cc2c1829d1d40418d7794814176 100644 --- a/pv/urls.py +++ b/pv/urls.py @@ -3,8 +3,7 @@ from django.conf.urls import url, include from django.contrib import admin from django.views.static import serve -from program.views import json_day_schedule, json_week_schedule, json_timeslots_specials, json_get_timeslot - +from program.views import json_day_schedule, json_week_schedule, json_timeslots_specials, json_get_timeslot, json_get_timeslots_by_show admin.autodiscover() @@ -17,6 +16,7 @@ urlpatterns = [ url(r'^export/week_schedule$', json_week_schedule), url(r'^export/timeslots_specials.json$', json_timeslots_specials), url(r'^export/get_timeslot$', json_get_timeslot, name='get-timeslot'), + url(r'^export/get_timeslots_by_show$', json_get_timeslots_by_show, name='get-timeslots-by-show'), ] if settings.DEBUG: