#!/usr/bin/env python # -*- coding: utf-8 -*- ''' (Data)models which describe the structure of data to be saved into database. ''' import os import uuid from django.db import models from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver from django.core.validators import MaxValueValidator, MinValueValidator from easy_thumbnails.fields import ThumbnailerImageField from taggit.managers import TaggableManager # Create your models here. class Explorer(models.Model): """ Profile that is linked to the a User. Every user has a profile. """ user = models.OneToOneField( User, on_delete=models.CASCADE, related_name='explorer' ) def __str__(self): return self.user.username @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: Explorer.objects.create(user=instance) @receiver(post_save, sender=User) def save_user_profile(sender, instance, **kwargs): instance.explorer.save() class Voucher(models.Model): """ Vouchers are authorization tokens to allow the registration of new users. A voucher has a code, a creation and a deletion date, which are all positional. Creation date is being set automatically during voucher creation. """ code = models.CharField(unique=True, max_length=30) created_when = models.DateTimeField(auto_now_add=True) expires_when = models.DateTimeField() def __str__(self): return "Voucher " + str(self.code) class Place (models.Model): """ Place defines a lost place (location, name, description etc.). """ name = models.CharField(max_length=50) submitted_when = models.DateTimeField(auto_now_add=True, null=True) submitted_by = models.ForeignKey( Explorer, on_delete=models.SET_NULL, null=True, blank=True, related_name='places' ) location = models.CharField(max_length=50) latitude = models.FloatField( validators=[ MinValueValidator(-90), MaxValueValidator(90) ] ) longitude = models.FloatField( validators=[ MinValueValidator(-180), MaxValueValidator(180) ] ) description = models.TextField() tags = TaggableManager(blank=True) def get_absolute_url(self): return reverse('place_detail', kwargs={'pk': self.pk}) @classmethod # Get center position of LP-geocoordinates. def average_latlon(cls, place_list): amount = len(place_list) # Init fill values to prevent None longitude = 0 latitude = 0 if amount > 0: for place in place_list: longitude += place.longitude latitude += place.latitude return {'latitude':latitude / amount, 'longitude': longitude / amount} return {'latitude': latitude, 'longitude': longitude} def __str__(self): return self.name def generate_image_upload_path(instance, filename): """ Callback for generating path for uploaded images. """ return 'places/' + str(uuid.uuid4())+'.'+filename.split('.')[-1] class PlaceImage (models.Model): """ PlaceImage defines an image file object that points to a file in uploads/. Intermediate image sizes are generated as defined in SIZES. PlaceImage references a Place to which it belongs. """ description = models.TextField(blank=True) filename = ThumbnailerImageField(upload_to=generate_image_upload_path) place = models.ForeignKey( Place, on_delete=models.CASCADE, related_name='images' ) submitted_when = models.DateTimeField(auto_now_add=True, null=True) submitted_by = models.ForeignKey( Explorer, on_delete=models.SET_NULL, null=True, blank=True, related_name='images' ) def __str__(self): """ Returning the name of the corresponding place + id of this image as textual represntation of this instance """ return ' '.join([self.place.name, str(self.pk)]) # These two auto-delete files from filesystem when they are unneeded: @receiver(models.signals.post_delete, sender=PlaceImage) def auto_delete_file_on_delete(sender, instance, **kwargs): """ Deletes file from filesystem when corresponding `PlaceImage` object is deleted. """ if instance.filename: if os.path.isfile(instance.filename.path): os.remove(instance.filename.path) @receiver(models.signals.pre_save, sender=PlaceImage) def auto_delete_file_on_change(sender, instance, **kwargs): """ Deletes old file from filesystem when corresponding `PlaceImage` object is updated with new file. """ if not instance.pk: return False try: old_file = PlaceImage.objects.get(pk=instance.pk).filename except PlaceImage.DoesNotExist: return False new_file = instance.filename if not old_file == new_file: if os.path.isfile(old_file.path): os.remove(old_file.path) class ExternalLink(models.Model): url = models.URLField(max_length=200) label = models.CharField(max_length=100) submitted_by = models.ForeignKey( Explorer, on_delete=models.SET_NULL, null=True, blank=True, related_name='external_links' ) submitted_when = models.DateTimeField(auto_now_add=True, null=True) class PhotoAlbum(ExternalLink): place = models.ForeignKey( Place, on_delete=models.CASCADE, related_name='photo_albums', null=True )