From a67eb0e801483d452657dc789fbec6851a695251 Mon Sep 17 00:00:00 2001
From: ingo <ingo.leindecker@fro.at>
Date: Wed, 13 Dec 2017 20:53:24 +0100
Subject: [PATCH] Moved most of the custom user profile fields to host model.

Shall we still put this in a sub app?
Would still need to figure out how to hook from the sub app into the serializer viewsets (and not vice versa) in order the custom fields get saved.

See #21
---
 profile/migrations/0003_auto_20171213_1737.py | 67 ++++++++++++++
 profile/models.py                             | 20 +----
 profile/serializers.py                        |  8 ++
 profile/urls.py                               |  6 +-
 program/admin.py                              |  2 +-
 program/migrations/0016_auto_20171213_1737.py | 87 +++++++++++++++++++
 program/models.py                             | 26 +++++-
 program/serializers.py                        | 38 ++++----
 program/views.py                              |  1 -
 9 files changed, 215 insertions(+), 40 deletions(-)
 create mode 100644 profile/migrations/0003_auto_20171213_1737.py
 create mode 100644 profile/serializers.py
 create mode 100644 program/migrations/0016_auto_20171213_1737.py

diff --git a/profile/migrations/0003_auto_20171213_1737.py b/profile/migrations/0003_auto_20171213_1737.py
new file mode 100644
index 00000000..1d4de59c
--- /dev/null
+++ b/profile/migrations/0003_auto_20171213_1737.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.3 on 2017-12-13 17:37
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('profile', '0002_auto_20171129_1828'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='profile',
+            name='biography',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='cba_url',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='dorftv_url',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='facebook_url',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='googleplus_url',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='height',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='image',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='linkedin_url',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='ppoi',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='twitter_url',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='website',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='width',
+        ),
+        migrations.RemoveField(
+            model_name='profile',
+            name='youtube_url',
+        ),
+    ]
diff --git a/profile/models.py b/profile/models.py
index bcb5ebd4..0a2d867c 100644
--- a/profile/models.py
+++ b/profile/models.py
@@ -12,21 +12,8 @@ from tinymce import models as tinymce_models
 
 class Profile(models.Model):
     user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile', editable=False)
-    biography = tinymce_models.HTMLField(_("Biography"), blank=True, null=True, help_text=_("Describe yourself and your fields of interest in a few sentences."))
-    website = models.URLField(_("Website"), blank=True, help_text=_("URL to your personal website."))
-    googleplus_url = models.URLField(_("Google+ URL"), blank=True, help_text=_("URL to your Google+ profile."))
-    facebook_url = models.URLField(_("Facebook URL"), blank=True, help_text=_("URL to your Facebook profile."))
-    twitter_url = models.URLField(_("Twitter URL"), blank=True, help_text=_("URL to your Twitter profile."))
-    linkedin_url = models.URLField(_("LinkedIn URL"), blank=True, help_text=_("URL to your LinkedIn profile."))
-    youtube_url = models.URLField(_("Youtube URL"), blank=True, help_text=_("URL to your Youtube channel."))
-    dorftv_url = models.URLField(_("DorfTV URL"), blank=True, help_text=_("URL to your dorfTV channel."))
-    cba_url = models.URLField(_("CBA URL"), blank=True, help_text=_("URL to your CBA profile."))
     cba_username = models.CharField(_("CBA Username"), blank=True, max_length=60, help_text=_("Your username in CBA. This is necessary for uploading files to your account."))
     cba_user_token = models.CharField(_("CBA Token"), blank=True, max_length=255, help_text=_("The CBA upload token for your account. This is NOT your password which you use to log into CBA!"))
-    ppoi = PPOIField('Image PPOI')
-    height = models.PositiveIntegerField('Image Height', blank=True, null=True, editable=False)
-    width = models.PositiveIntegerField('Image Width', blank=True, null=True,editable=False)
-    image = VersatileImageField(_("Profile picture"), blank=True, null=True, upload_to='user_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload a picture of yourself. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save."))
 
     def __str__(self):
         return self.user.username
@@ -35,9 +22,4 @@ class Profile(models.Model):
         db_table = 'profile'
 
     def save(self, *args, **kwargs):
-        super(Profile, self).save(*args, **kwargs)
-
-        # Generate thumbnails
-        if self.image.name and settings.THUMBNAIL_SIZES:
-            for size in settings.THUMBNAIL_SIZES:
-                thumbnail = self.image.crop[size].name
\ No newline at end of file
+        super(Profile, self).save(*args, **kwargs)
\ No newline at end of file
diff --git a/profile/serializers.py b/profile/serializers.py
new file mode 100644
index 00000000..6cee5553
--- /dev/null
+++ b/profile/serializers.py
@@ -0,0 +1,8 @@
+from django.contrib.auth.models import User
+from rest_framework import serializers
+from profile.models import Profile
+
+class ProfileSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = Profile
+        fields = '__all__'
\ No newline at end of file
diff --git a/profile/urls.py b/profile/urls.py
index 7ac3efa3..ef961e39 100644
--- a/profile/urls.py
+++ b/profile/urls.py
@@ -1,5 +1,9 @@
 from django.contrib import admin
+from rest_framework import routers
 
 admin.autodiscover()
 
-urlpatterns = []
\ No newline at end of file
+urlpatterns = []
+
+router = routers.DefaultRouter()
+router.register(r'users', APIUserViewSet)
\ No newline at end of file
diff --git a/program/admin.py b/program/admin.py
index c3760334..9d4bff8f 100644
--- a/program/admin.py
+++ b/program/admin.py
@@ -81,7 +81,7 @@ class RTRCategoryAdmin(admin.ModelAdmin):
 
 
 class HostAdmin(admin.ModelAdmin):
-    list_display = ('name',)
+    list_display = ('name','email',)
     list_filter = (ActiveHostsFilter, 'is_always_visible',)
 
 
diff --git a/program/migrations/0016_auto_20171213_1737.py b/program/migrations/0016_auto_20171213_1737.py
new file mode 100644
index 00000000..c87b746c
--- /dev/null
+++ b/program/migrations/0016_auto_20171213_1737.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.3 on 2017-12-13 17:37
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import tinymce.models
+import versatileimagefield.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('program', '0015_note_audio_url'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='host',
+            name='biography',
+            field=tinymce.models.HTMLField(blank=True, help_text='Describe yourself and your fields of interest in a few sentences.', null=True, verbose_name='Biography'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='cba_url',
+            field=models.URLField(blank=True, help_text='URL to your CBA profile.', verbose_name='CBA URL'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='dorftv_url',
+            field=models.URLField(blank=True, help_text='URL to your dorfTV channel.', verbose_name='DorfTV URL'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='facebook_url',
+            field=models.URLField(blank=True, help_text='URL to your Facebook profile.', verbose_name='Facebook URL'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='googleplus_url',
+            field=models.URLField(blank=True, help_text='URL to your Google+ profile.', verbose_name='Google+ URL'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='height',
+            field=models.PositiveIntegerField(blank=True, editable=False, null=True, verbose_name='Image Height'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='image',
+            field=versatileimagefield.fields.VersatileImageField(blank=True, height_field='height', help_text="Upload a picture of yourself. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save.", null=True, upload_to='user_images', verbose_name='Profile picture', width_field='width'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='linkedin_url',
+            field=models.URLField(blank=True, help_text='URL to your LinkedIn profile.', verbose_name='LinkedIn URL'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='ppoi',
+            field=versatileimagefield.fields.PPOIField(default='0.5x0.5', editable=False, max_length=20, verbose_name='Image PPOI'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='twitter_url',
+            field=models.URLField(blank=True, help_text='URL to your Twitter profile.', verbose_name='Twitter URL'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='width',
+            field=models.PositiveIntegerField(blank=True, editable=False, null=True, verbose_name='Image Width'),
+        ),
+        migrations.AddField(
+            model_name='host',
+            name='youtube_url',
+            field=models.URLField(blank=True, help_text='URL to your Youtube channel.', verbose_name='Youtube URL'),
+        ),
+        migrations.AlterField(
+            model_name='host',
+            name='website',
+            field=models.URLField(blank=True, help_text='URL to your personal website.', verbose_name='Website'),
+        ),
+        migrations.AlterField(
+            model_name='timeslot',
+            name='is_repetition',
+            field=models.BooleanField(default=False, verbose_name='REP'),
+        ),
+    ]
diff --git a/program/models.py b/program/models.py
index bcf6d4b2..570169a5 100644
--- a/program/models.py
+++ b/program/models.py
@@ -243,7 +243,19 @@ class Host(models.Model):
     name = models.CharField(_("Name"), max_length=128)
     is_always_visible = models.BooleanField(_("Is always visible"), default=False)
     email = models.EmailField(_("E-Mail"), blank=True)
-    website = models.URLField(_("Website"), blank=True)
+    website = models.URLField(_("Website"), blank=True, help_text=_("URL to your personal website."))
+    biography = tinymce_models.HTMLField(_("Biography"), blank=True, null=True, help_text=_("Describe yourself and your fields of interest in a few sentences."))
+    googleplus_url = models.URLField(_("Google+ URL"), blank=True, help_text=_("URL to your Google+ profile."))
+    facebook_url = models.URLField(_("Facebook URL"), blank=True, help_text=_("URL to your Facebook profile."))
+    twitter_url = models.URLField(_("Twitter URL"), blank=True, help_text=_("URL to your Twitter profile."))
+    linkedin_url = models.URLField(_("LinkedIn URL"), blank=True, help_text=_("URL to your LinkedIn profile."))
+    youtube_url = models.URLField(_("Youtube URL"), blank=True, help_text=_("URL to your Youtube channel."))
+    dorftv_url = models.URLField(_("DorfTV URL"), blank=True, help_text=_("URL to your dorfTV channel."))
+    cba_url = models.URLField(_("CBA URL"), blank=True, help_text=_("URL to your CBA profile."))
+    ppoi = PPOIField('Image PPOI')
+    height = models.PositiveIntegerField('Image Height', blank=True, null=True, editable=False)
+    width = models.PositiveIntegerField('Image Width', blank=True, null=True,editable=False)
+    image = VersatileImageField(_("Profile picture"), blank=True, null=True, upload_to='user_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload a picture of yourself. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save."))
 
     class Meta:
         ordering = ('name',)
@@ -259,6 +271,14 @@ class Host(models.Model):
     def active_shows(self):
         return self.shows.filter(schedules__until__gt=datetime.today())
 
+    def save(self, *args, **kwargs):
+        super(Host, self).save(*args, **kwargs)
+
+        # Generate thumbnails
+        if self.image.name and settings.THUMBNAIL_SIZES:
+            for size in settings.THUMBNAIL_SIZES:
+                thumbnail = self.image.crop[size].name
+
 
 class Show(models.Model):
     predecessor = models.ForeignKey('self', blank=True, null=True, related_name='successors', verbose_name=_("Predecessor"))
@@ -567,7 +587,7 @@ class TimeSlot(models.Model):
     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)
+    is_repetition = models.BooleanField(_("REP"), default=False)
     playlist_id = models.IntegerField(_("Playlist ID"), null=True)
 
     objects = TimeSlotManager()
@@ -580,7 +600,7 @@ class TimeSlot(models.Model):
     def __str__(self):
         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 ''
+        is_repetition = ' ' + _('REP') if self.schedule.is_repetition is 1 else ''
 
         return '%s - %s  %s (%s)' % (start, end, is_repetition, self.show.name)
 
diff --git a/program/serializers.py b/program/serializers.py
index 2850bb39..47594149 100644
--- a/program/serializers.py
+++ b/program/serializers.py
@@ -4,12 +4,7 @@ from rest_framework import serializers, status
 from rest_framework.response import Response
 from program.models import Show, Schedule, TimeSlot, Category, RTRCategory, Host, Language, Topic, MusicFocus, Note, Type, Language
 from profile.models import Profile
-
-
-class ProfileSerializer(serializers.ModelSerializer):
-    class Meta:
-        model = Profile
-        fields = '__all__'
+from profile.serializers import ProfileSerializer
 
 
 class UserSerializer(serializers.ModelSerializer):
@@ -31,16 +26,8 @@ class UserSerializer(serializers.ModelSerializer):
         instance.last_name = validated_data.get('last_name', instance.last_name)
         instance.email = validated_data.get('email', instance.email)
 
+        # TODO: How to hook into this from ProfileSerializer without having to call it here?
         profile = Profile.objects.get(user=instance.id)
-        profile.biography = validated_data['profile'].get('biography')
-        profile.website = validated_data['profile'].get('website')
-        profile.googleplus_url = validated_data['profile'].get('googleplus_url')
-        profile.facebook_url = validated_data['profile'].get('facebook_url')
-        profile.twitter_url = validated_data['profile'].get('twitter_url')
-        profile.linkedin_url = validated_data['profile'].get('linkedin_url')
-        profile.youtube_url = validated_data['profile'].get('youtube_url')
-        profile.dorftv_url = validated_data['profile'].get('dorftv_url')
-        profile.cba_url = validated_data['profile'].get('cba_url')
         profile.cba_username = validated_data['profile'].get('cba_username')
         profile.cba_user_token = validated_data['profile'].get('cba_user_token')
         profile.save()
@@ -60,6 +47,27 @@ class HostSerializer(serializers.ModelSerializer):
         model = Host
         fields = '__all__'
 
+    def update(self, instance, validated_data):
+        """
+        Update and return an existing Host instance, given the validated data.
+        """
+        instance.name = validated_data.get('name', instance.name)
+        instance.is_always_visible = validated_data.get('is_always_visible', instance.is_always_visible)
+        instance.email = validated_data.get('email', instance.email)
+        instance.website = validated_data.get('website', instance.website)
+        instance.biography = validated_data.get('biography', instance.biography)
+        instance.googleplus_url = validated_data.get('googleplus_url', instance.googleplus_url)
+        instance.facebook_url = validated_data.get('facebook_url', instance.facebook_url)
+        instance.twitter_url = validated_data.get('twitter_url', instance.twitter_url)
+        instance.linkedin_url = validated_data.get('linkedin_url', instance.linkedin_url)
+        instance.youtube_url = validated_data.get('youtube_url', instance.youtube_url)
+        instance.dorftv_url = validated_data.get('dorftv_url', instance.dorftv_url)
+        instance.cba_url = validated_data.get('cba_url', instance.cba_url)
+        instance.image = validated_data.get('image', instance.image)
+
+        instance.save()
+        return instance
+
 
 class LanguageSerializer(serializers.ModelSerializer):
     class Meta:
diff --git a/program/views.py b/program/views.py
index bdd3534b..85f468dc 100644
--- a/program/views.py
+++ b/program/views.py
@@ -18,7 +18,6 @@ from rest_framework.response import Response
 from oauth2_provider.contrib.rest_framework import TokenHasReadWriteScope, TokenHasScope
 
 from program.models import Type, MusicFocus, Language, Note, Show, Category, RTRCategory, Topic, TimeSlot, Host, Schedule
-from profile.models import Profile
 from program.serializers import TypeSerializer, LanguageSerializer, MusicFocusSerializer, NoteSerializer, ShowSerializer, CategorySerializer, RTRCategorySerializer, TopicSerializer, TimeSlotSerializer, HostSerializer, UserSerializer
 from program.utils import tofirstdayinisoweek, get_cached_shows
 
-- 
GitLab