# Path: dwitter/models.py
# class Comment(models.Model):
#     text = models.TextField()
#     posted = models.DateTimeField()
#     reply_to = models.ForeignKey(Dweet, on_delete=models.CASCADE,
#                                  related_name="comments")
#     author = models.ForeignKey(User, on_delete=models.CASCADE)
# 
#     class Meta:
#         ordering = ('posted',)
# 
#     def __str__(self):
#         return ('c/' +
#                 str(self.id) +
#                 ' (' +
#                 self.author.username +
#                 ') to ' +
#                 str(self.reply_to))
# 
# class Dweet(models.Model):
#     code = models.TextField()
#     posted = models.DateTimeField(db_index=True)
#     reply_to = models.ForeignKey("self", on_delete=models.DO_NOTHING,
#                                  related_name="remixes", null=True, blank=True)
# 
#     likes = models.ManyToManyField(User, related_name="liked", blank=True)
#     hotness = models.FloatField(default=1.0, db_index=True)
#     deleted = models.BooleanField(default=False)
# 
#     author = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user),
#                                null=True, blank=True)
# 
#     objects = NotDeletedDweetManager()
#     with_deleted = models.Manager()
# 
#     class Meta:
#         ordering = ('-posted',)
# 
#     def delete(self):
#         self.deleted = True
#         self.save()
# 
#     def save(self, *args, **kwargs):
#         self.calculate_hotness((self.pk is None))
#         super(Dweet, self).save(*args, **kwargs)
# 
#     @cached_property
#     def top_comment(self):
#         """
#         Return the top comment. This is mainly a caching optimization to avoid queries
#         """
#         return self.comments.first()
# 
#     @cached_property
#     def dweet_length(self):
#         return length_of_code(self.code)
# 
#     @cached_property
#     def has_sticky_comment(self):
#         """
#         True when first comment should be stickied (first comment author == dweet author)
#         """
#         if self.top_comment is None:
#             return False
#         return self.top_comment.author == self.author
# 
#     def __str__(self):
#         return 'd/' + str(self.id) + ' (' + self.author.username + ')'
# 
#     def calculate_hotness(self, is_new):
#         """
#         Hotness is inspired by the Hackernews ranking algorithm
#         Read more here:
#         https://medium.com/hacking-and-gonzo/how-hacker-news-ranking-algorithm-works-1d9b0cf2c08d
#         """
#         def epoch_seconds(date):
#             epoch = datetime(2015, 5, 5)  # arbitrary start date before Dwitter existed
#             naive = date.replace(tzinfo=None)
#             td = naive - epoch
#             return td.days * 86400 + td.seconds + (float(td.microseconds) / 1000000)
# 
#         likes = 1
#         if not is_new:
#             likes = max(self.likes.count(), 1)
# 
#         order = log(likes, 2)
#         # 86400 seconds = 24 hours.
#         # So for every log(2) likes on a dweet, its effective
#         # "posted time" moves 24 forward
#         # In other words, it takes log2(likes) * 24hrs before
#         # a dweet with a single like beat yours
#         self.hotness = round(order + epoch_seconds(self.posted)/86400, 7)
# 
# Path: dwitter/templatetags/to_gravatar_url.py
# @register.filter
# def to_gravatar_url(email):
#     return ('https://gravatar.com/avatar/%s?d=retro' %
#             hashlib.md5((email or '').strip().lower().encode('utf-8')).hexdigest())


from rest_framework import serializers
from dwitter.models import Comment, Dweet
from dwitter.templatetags.to_gravatar_url import to_gravatar_url
from django.contrib.auth.models import User
from rest_framework.exceptions import ValidationError
from django.contrib.auth.password_validation import validate_password
        errors = dict()
        try:
            # Note: 'password2' gets removed from the data here with pop
            if data['password'] != data.pop('password2'):
                errors['password'] = 'Passwords do not match'
            else:
                validate_password(data['password'])
        except ValidationError as e:
            errors['password'] = list(e.messages)

        # Any checks here only applies to the registration of new users
        if(data['username'] == '.' or data['username'] == '..'):
            # Since . and .. in urls can have special semantics in some cases (up directory)
            # best avoid them in usernames for new users
            errors['username'] = 'Invalid username'

        if errors:
            raise serializers.ValidationError(errors)

        return super(UserSerializer, self).validate(data)

    def create(self, validated_data):
        user = User.objects.create_user(
            username=validated_data['username'],
            email=validated_data['email'],
            password=validated_data['password'],
        )
        return user

    def get_avatar(self, obj):

# Path: dwitter/models.py
# class Comment(models.Model):
#     text = models.TextField()
#     posted = models.DateTimeField()
#     reply_to = models.ForeignKey(Dweet, on_delete=models.CASCADE,
#                                  related_name="comments")
#     author = models.ForeignKey(User, on_delete=models.CASCADE)
# 
#     class Meta:
#         ordering = ('posted',)
# 
#     def __str__(self):
#         return ('c/' +
#                 str(self.id) +
#                 ' (' +
#                 self.author.username +
#                 ') to ' +
#                 str(self.reply_to))
# 
# class Dweet(models.Model):
#     code = models.TextField()
#     posted = models.DateTimeField(db_index=True)
#     reply_to = models.ForeignKey("self", on_delete=models.DO_NOTHING,
#                                  related_name="remixes", null=True, blank=True)
# 
#     likes = models.ManyToManyField(User, related_name="liked", blank=True)
#     hotness = models.FloatField(default=1.0, db_index=True)
#     deleted = models.BooleanField(default=False)
# 
#     author = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user),
#                                null=True, blank=True)
# 
#     objects = NotDeletedDweetManager()
#     with_deleted = models.Manager()
# 
#     class Meta:
#         ordering = ('-posted',)
# 
#     def delete(self):
#         self.deleted = True
#         self.save()
# 
#     def save(self, *args, **kwargs):
#         self.calculate_hotness((self.pk is None))
#         super(Dweet, self).save(*args, **kwargs)
# 
#     @cached_property
#     def top_comment(self):
#         """
#         Return the top comment. This is mainly a caching optimization to avoid queries
#         """
#         return self.comments.first()
# 
#     @cached_property
#     def dweet_length(self):
#         return length_of_code(self.code)
# 
#     @cached_property
#     def has_sticky_comment(self):
#         """
#         True when first comment should be stickied (first comment author == dweet author)
#         """
#         if self.top_comment is None:
#             return False
#         return self.top_comment.author == self.author
# 
#     def __str__(self):
#         return 'd/' + str(self.id) + ' (' + self.author.username + ')'
# 
#     def calculate_hotness(self, is_new):
#         """
#         Hotness is inspired by the Hackernews ranking algorithm
#         Read more here:
#         https://medium.com/hacking-and-gonzo/how-hacker-news-ranking-algorithm-works-1d9b0cf2c08d
#         """
#         def epoch_seconds(date):
#             epoch = datetime(2015, 5, 5)  # arbitrary start date before Dwitter existed
#             naive = date.replace(tzinfo=None)
#             td = naive - epoch
#             return td.days * 86400 + td.seconds + (float(td.microseconds) / 1000000)
# 
#         likes = 1
#         if not is_new:
#             likes = max(self.likes.count(), 1)
# 
#         order = log(likes, 2)
#         # 86400 seconds = 24 hours.
#         # So for every log(2) likes on a dweet, its effective
#         # "posted time" moves 24 forward
#         # In other words, it takes log2(likes) * 24hrs before
#         # a dweet with a single like beat yours
#         self.hotness = round(order + epoch_seconds(self.posted)/86400, 7)
# 
# Path: dwitter/templatetags/to_gravatar_url.py
# @register.filter
# def to_gravatar_url(email):
#     return ('https://gravatar.com/avatar/%s?d=retro' %
#             hashlib.md5((email or '').strip().lower().encode('utf-8')).hexdigest())


# Path: dwitter/serializers_v2.py
from rest_framework import serializers
from dwitter.models import Comment, Dweet
from dwitter.templatetags.to_gravatar_url import to_gravatar_url
from django.contrib.auth.models import User
from rest_framework.exceptions import ValidationError
from django.contrib.auth.password_validation import validate_password

        errors = dict()
        try:
            # Note: 'password2' gets removed from the data here with pop
            if data['password'] != data.pop('password2'):
                errors['password'] = 'Passwords do not match'
            else:
                validate_password(data['password'])
        except ValidationError as e:
            errors['password'] = list(e.messages)

        # Any checks here only applies to the registration of new users
        if(data['username'] == '.' or data['username'] == '..'):
            # Since . and .. in urls can have special semantics in some cases (up directory)
            # best avoid them in usernames for new users
            errors['username'] = 'Invalid username'

        if errors:
            raise serializers.ValidationError(errors)

        return super(UserSerializer, self).validate(data)

    def create(self, validated_data):
        user = User.objects.create_user(
            username=validated_data['username'],
            email=validated_data['email'],
            password=validated_data['password'],
        )
        return user

    def get_avatar(self, obj):

        return to_gravatar_url(obj.email)