Skip to content
Snippets Groups Projects
Commit 3a757923 authored by Ernesto Rico Schmidt's avatar Ernesto Rico Schmidt
Browse files

Remove NoteAdmin, ScheduleAdmin, ShowAdmin and TimeSlotAdmin

parent d9bf050b
No related branches found
No related tags found
No related merge requests found
...@@ -120,484 +120,6 @@ class HostAdmin(admin.ModelAdmin): ...@@ -120,484 +120,6 @@ class HostAdmin(admin.ModelAdmin):
return Host.objects.filter(shows__in=request.user.shows.all()).distinct() return Host.objects.filter(shows__in=request.user.shows.all()).distinct()
class NoteAdmin(admin.ModelAdmin):
date_hierarchy = 'start'
list_display = ('title', 'show', 'start', 'status', 'user')
fields = (('show', 'timeslot'), 'title', 'slug', 'summary', 'content', 'image', 'host', '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):
if request.user.is_superuser:
shows = Show.objects.all()
else:
# Commons 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 = timezone.now() - timedelta(weeks=4)
in_twelve_weeks = timezone.now() + timedelta(weeks=12)
if db_field.name == 'timeslot':
# 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(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
# Common users only see shows they own
if not request.user.is_superuser:
kwargs['queryset'] = Show.objects.filter(pk__in=request.user.shows.all())
if db_field.name == 'host':
# Common users only see hosts of shows they own
if not request.user.is_superuser:
kwargs['queryset'] = Host.objects.filter(shows__in=request.user.shows.all(), is_active=True).distinct()
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
# Try to get direct audio URL from CBA
obj.audio_url = Note.get_audio_url(obj.cba_id)
obj.save()
# Save the note id to table timeslot as well
timeslot = TimeSlot.objects.get(pk=obj.timeslot_id)
timeslot.note_id = obj.id
timeslot.save()
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'), ('tstart', 'tend'), 'dstart', 'until', 'is_repetition',
('add_days_no', 'add_business_days_only'),
'default_playlist_id', 'automation_id',)
list_display = ('get_show_name', 'byweekday', 'rrule', 'tstart', 'tend', 'until')
list_filter = (ActiveSchedulesFilter, 'byweekday', 'rrule', 'is_repetition')
ordering = ('byweekday', 'dstart')
save_on_top = True
search_fields = ('show__name',)
def renew(self, request, queryset):
next_year = date.today().year + 1
until = date(next_year, 12, 31)
renewed = queryset.update(until=until)
if renewed == 1:
message = _("1 schedule was renewed until %s") % until
else:
message = _("%s schedule were renewed until %s") % (renewed, until)
self.message_user(request, message)
renew.short_description = _("Renew selected schedules")
def get_show_name(self, obj):
return obj.show.name
get_show_name.admin_order_field = 'show'
get_show_name.short_description = "Show"
class ScheduleInline(admin.TabularInline):
model = Schedule
ordering = ('pk', '-until', 'byweekday')
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', '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', 'default_playlist_id', 'cba_series_id', 'is_active', 'is_public'
)
class Media:
js = [settings.MEDIA_URL + 'js/calendar/lib/moment.min.js',
settings.MEDIA_URL + 'js/show_change.js', ]
css = {'all': ('/program/styles.css',)}
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)
def get_readonly_fields(self, request, obj=None):
"""Limit field access for common users"""
if not request.user.is_superuser:
# TODO: how to set field 'name' readonly although it's required?
return 'predecessor', 'type', 'hosts', 'owners', 'language', 'category', 'topic', 'musicfocus', 'fundingcategory'
return list()
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
try:
show_id = int(request.get_full_path().split('/')[-2])
except ValueError:
show_id = None
if db_field.name == 'predecessor' and show_id:
kwargs['queryset'] = Show.objects.exclude(pk=show_id)
if db_field.name == 'type':
kwargs['queryset'] = Type.objects.filter(is_active=True)
if db_field.name == 'fundingcategory':
kwargs['queryset'] = FundingCategory.objects.filter(is_active=True)
return super(ShowAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == 'hosts':
kwargs["queryset"] = Host.objects.filter(is_active=True)
if db_field.name == 'language':
kwargs["queryset"] = Language.objects.filter(is_active=True)
if db_field.name == 'category':
kwargs["queryset"] = Category.objects.filter(is_active=True)
if db_field.name == 'topic':
kwargs["queryset"] = Topic.objects.filter(is_active=True)
if db_field.name == 'musicfocus':
kwargs["queryset"] = MusicFocus.objects.filter(is_active=True)
return super(ShowAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)
def save_formset(self, request, form, formset, change):
"""
Is called after the "save show"-form or collision-form were submitted
Saves the show after first submit
If any changes in schedules happened
* added/changed schedules are used to generate new timeslots and
matched against existing ones, which will be displayed in the collision form
If a collision form was submitted
* save the current schedule
* delete/create timeslots and relink notes after confirmation
Each step passes on to response_add or response_change which will
* either display the collision form for the next step
* or redirect to the original show-form if the resolving process has been finished
(= if either max_steps was surpassed or end_reached was True)
"""
self.end_reached = False
schedule_instances = formset.save(commit=False)
# If there are no schedules to save, do nothing
if schedule_instances:
show_id = schedule_instances[0].show.id
else:
self.end_reached = True
schedule = []
timeslots = []
max_steps = int(len(schedule_instances)) if len(schedule_instances) > 0 else 1
step = 1
if request.POST.get('step') is None:
# First save-show submit
# Generate thumbnails
if form.instance.image.name and settings.THUMBNAIL_SIZES:
for size in settings.THUMBNAIL_SIZES:
thumbnail = form.instance.image.crop[size].name
# Save show data only
form.save()
# Delete schedules (as well as related timeslots and notes) if flagged as such
for obj in formset.deleted_objects:
obj.delete()
# If nothing else changed, do nothing and redirect to show-form
if not formset.changed_objects and not formset.new_objects:
self.end_reached = True
else:
# If a collision form was submitted
step = int(request.POST.get('step'))
if request.POST.get('num_inputs') is not None and int(request.POST.get('num_inputs')) > 0:
print("Resolving conflicts...")
# Declare and retrieve variables
# Either datetimes as string (e.g. '2017-01-01 00:00:00 - 2017-01-01 01:00:00') to create
# or ints of colliding timeslots to keep otherwise
resolved_timeslots = []
# IDs of colliding timeslots found in the db. If there's no corresponding collision to the
# same index in create_timeslot, value will be None
collisions = []
# Datetimes as string (e.g. '2017-01-01 00:00:00 - 2017-01-01 01:00:00') for timeslots to create
create_timeslots = []
# IDs of timeslots to delete
delete_timeslots = set()
# Number of timeslots to be generated
num_inputs = int(request.POST.get('num_inputs'))
# Numbers of notes to relink for existing timeslots and newly created ones
# each of them relating to one of these POST vars:
# POST.ntids[idx][id] and POST.ntids[idx][note_id] contain ids of existing timeslots and note_ids to link, while
# POST.ntind[idx][id] and POST.ntind[idx][note_id] contain indices of corresponding elements in create_timeslots
# and note_ids which will be linked after they're created and thus split into two lists beforehand
num_ntids = int(request.POST.get('num_ntids'))
num_ntind = int(request.POST.get('num_ntind'))
# Retrieve POST vars of current schedule
schedule_id = int(request.POST.get('ps_save_id')) if request.POST.get('ps_save_id') != 'None' else None
rrule = RRule.objects.get(pk=int(request.POST.get('ps_save_rrule_id')))
show = Show.objects.get(pk=show_id)
byweekday = int(request.POST.get('ps_save_byweekday'))
tstart = datetime.strptime(request.POST.get('ps_save_tstart'), '%H:%M').time()
tend = datetime.strptime(request.POST.get('ps_save_tend'), '%H:%M').time()
dstart = datetime.strptime(request.POST.get('ps_save_dstart'), '%Y-%m-%d').date()
if dstart < timezone.now().date(): # Create or delete upcoming timeslots only
dstart = timezone.now().date()
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
default_playlist_id = int(request.POST.get('ps_save_default_playlist_id')) if request.POST.get(
'ps_save_default_playlist_id') != 'None' else 0
add_days_no = int(request.POST.get('ps_save_add_days_no')) if request.POST.get(
'ps_save_add_days_no') != 'None' and int(request.POST.get('ps_save_add_days_no')) > 0 else None
add_business_days_only = request.POST.get('ps_save_add_business_days_only')
# Put timeslot POST vars into lists with same indices
for i in range(num_inputs):
resolved_ts = request.POST.get('resolved_timeslots[' + str(i) + ']')
if resolved_ts:
resolved_timeslots.append(resolved_ts)
create_timeslots.append(request.POST.get('create_timeslots[' + str(i) + ']')) # May contain None
collisions.append(request.POST.get('collisions[' + str(i) + ']')) # May contain None
else:
num_inputs -= 1
# Prepare resolved timeslots
# Separate timeslots to delete from those to create
keep_collisions = []
for x in range(num_inputs):
if resolved_timeslots[x] is None or resolved_timeslots[x].isdigit():
# If it's a digit, keep the existing timeslot by preventing the new one from being created
create_timeslots[x] = None
keep_collisions.append(int(collisions[x]))
else:
# Otherwise collect the timeslot ids to be deleted later
if len(collisions[x]) > 0:
delete_timeslots.add(int(collisions[x]))
# Collect IDs of upcoming timeslots of the same schedule to delete except those in keep_collision
if schedule_id:
for ts in TimeSlot.objects.filter(start__gte=dstart, end__lte=until, schedule_id=schedule_id).exclude(
pk__in=keep_collisions).values_list('id', flat=True):
delete_timeslots.add(ts)
# Save schedule
new_schedule = Schedule(pk=schedule_id,
rrule=rrule,
byweekday=byweekday,
show=show,
dstart=dstart,
tstart=tstart,
tend=tend,
until=until,
is_repetition=is_repetition,
automation_id=automation_id,
default_playlist_id=default_playlist_id,
add_days_no=add_days_no,
add_business_days_only=add_business_days_only)
# Only save schedule if any timeslots changed
if len(resolved_timeslots) > 0:
new_schedule.save()
# Relink notes to existing timeslots and prepare those to be linked
# Relink notes with existing timeslot ids
for i in range(num_ntids):
try:
note = Note.objects.get(pk=int(request.POST.get('ntids[' + str(i) + '][note_id]')))
note.timeslot_id = int(request.POST.get('ntids[' + str(i) + '][id]'))
note.save(update_fields=["timeslot_id"])
print("Rewrote note " + str(note.id) + "...to timeslot_id " + str(note.timeslot_id))
except ObjectDoesNotExist:
pass
# Put list indices of yet to be created timeslots and note_ids in corresponding lists to relink them during creation
note_indices = []
note_ids = []
for i in range(num_ntind):
note_indices.append(int(request.POST.get('ntind[' + str(i) + '][id]')))
note_ids.append(int(request.POST.get('ntind[' + str(i) + '][note_id]')))
# Database changes for resolved timeslots and relinked notes for newly created
for idx, ts in enumerate(create_timeslots):
if ts:
start_end = ts.split(' - ')
# Only create upcoming timeslots
if datetime.strptime(start_end[0], "%Y-%m-%d %H:%M:%S") > timezone.now():
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:
note_idx = note_indices.index(idx) # Get the note_id's index...
note_id = note_ids[note_idx] # ...which contains the note_id to relate to
try:
note = Note.objects.get(pk=note_id)
note.timeslot_id = timeslot_created.id
note.save(update_fields=["timeslot_id"])
print("Timeslot " + str(timeslot_created.id) + " linked to note " + str(note_id))
except ObjectDoesNotExist:
pass
# Finally delete discarded timeslots
for timeslot_id in delete_timeslots:
TimeSlot.objects.filter(pk=timeslot_id).delete()
if step > max_steps:
self.end_reached = True
# Everything below here is called when a new collision is loaded before being handed over to the client
# Generate timeslots from current schedule
k = 1
for instance in schedule_instances:
if isinstance(instance, Schedule):
if k == step:
timeslots = Schedule.generate_timeslots(instance)
schedule = instance
break
k += 1
# Get collisions for timeslots
collisions = Schedule.get_collisions(timeslots)
# Get notes of colliding timeslots
notes = []
for id in collisions:
try:
notes.append(Note.objects.get(timeslot_id=id))
except ObjectDoesNotExist:
pass
self.schedule = schedule
self.timeslots = timeslots
self.collisions = collisions
self.num_collisions = len(
[s for s in self.collisions if s != 'None']) # Number of real collisions displayed to the user
self.notes = notes
self.showform = form
self.schedulesform = formset
self.step = step + 1 # Becomes upcoming step
self.max_steps = max_steps
# Pass it on to response_add() or response_change()
return self
def response_add(self, request, obj, post_url_continue=None):
return ShowAdmin.respond(self, request, obj)
def response_change(self, request, obj):
return ShowAdmin.respond(self, request, obj)
def respond(self, request, obj):
"""
Redirects to the show-change-form if no schedules changed or resolving has been finished (or any other form validation error occured)
Displays the collision form for the current schedule otherwise
"""
# Never check for collisions if not superuser
# Common users can't edit the formset, so save_formset() will never be called thus end_reached wasn't set yet
if not request.user.is_superuser:
self.end_reached = True
if self.end_reached:
return super(ShowAdmin, self).response_change(request, obj)
timeslots_to_collisions = list(zip(self.timeslots, self.collisions))
return render(request, 'collisions.html', {'self': self, 'obj': obj, 'request': request,
'timeslots': self.timeslots,
'collisions': self.collisions,
'schedule': self.schedule,
'timeslots_to_collisions': timeslots_to_collisions,
'schedulesform': self.schedulesform,
'showform': self.showform,
'num_inputs': len(self.timeslots),
'step': self.step,
'max_steps': self.max_steps,
'now': timezone.now(),
'num_collisions': self.num_collisions})
admin.site.register(Language, LanguageAdmin) admin.site.register(Language, LanguageAdmin)
admin.site.register(Type, TypeAdmin) admin.site.register(Type, TypeAdmin)
admin.site.register(MusicFocus, MusicFocusAdmin) admin.site.register(MusicFocus, MusicFocusAdmin)
...@@ -605,7 +127,3 @@ admin.site.register(Category, CategoryAdmin) ...@@ -605,7 +127,3 @@ admin.site.register(Category, CategoryAdmin)
admin.site.register(Topic, TopicAdmin) admin.site.register(Topic, TopicAdmin)
admin.site.register(FundingCategory, FundingCategoryAdmin) admin.site.register(FundingCategory, FundingCategoryAdmin)
admin.site.register(Host, HostAdmin) 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)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment