class ProductImage(models.Model): product = models.ForeignKey(Product, on_delete=models.DO_NOTHING, db_index=True) created_at = models.DateTimeField('date created', auto_now_add=True) link = models.ImageField(upload_to=upload_to) full = ImageSpecField(source='link', processors=[], format='PNG', options={'quality': 60}) thumbnail = ImageSpecField( source='link', processors=[ResizeToCover(width=370, height=370)], format='PNG', options={'quality': 60}) x600 = ImageSpecField(source='link', processors=[ResizeToCover(width=600, height=600)], format='PNG', options={'quality': 60}) paging = ImageSpecField(source='link', processors=[ResizeToFit(147, 143)], format='PNG', options={'quality': 60}) is_delete = models.BooleanField(default=False, blank=True) def __str__(self): return "{} - {}".format(self.product.pk, self.product.title) class Meta: verbose_name = 'Изображение товара' verbose_name_plural = 'Изображения товаров'
class Musician(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) name = models.CharField(max_length=255, blank=True, null=True) bio = models.TextField() city = models.CharField(max_length=255) latitude = models.FloatField(blank=True, null=True) longitude = models.FloatField(blank=True, null=True) # fields having to do with money cashapp_name = models.CharField(max_length=255, blank=True, null=True) paypal_donation_url = models.CharField(max_length=255, blank=True, null=True) cashapp_qr = models.ImageField(upload_to="images/", null=True, blank=True) paypal_qr = models.ImageField(upload_to="images/", null=True, blank=True) venmo_qr = models.ImageField(upload_to="images/", null=True, blank=True) favorited_by = models.ManyToManyField(User, related_name="favorite_musician", blank=True) # fields having to do with images headshot = models.ImageField(upload_to="images/", null=True, blank=False) thumbnail = ImageSpecField( source="headshot", processors=[Transpose(), ResizeToCover(200, 200), SmartCrop(200, 200)], format="JPEG", options={"quality": 100}, ) full_cover = ImageSpecField( source="headshot", processors=[Transpose(), ResizeToFit(600, 600), SmartCrop(400, 400)], format="JPEG", options={"quality": 100}, ) very_small_thumb = ImageSpecField( source="headshot", processors=[Transpose(), ResizeToCover(100, 100), SmartCrop(100, 100)], format="JPEG", options={"quality": 100}, ) def __str__(self): return f'{self.name}'
class ProductImage(models.Model): product = models.ForeignKey(Product, related_name='images', verbose_name=_('ProductImage|name')) image = ProcessedImageField(upload_to='images', processors=[ ResizeToFit(800, 600), ImageWatermark( 'media/default_images/watermark.png', ) ], format='PNG', default='media/default.png', verbose_name=_('ProductImage|image')) image_preview = ImageSpecField( source='image', processors=[ResizeToCover(300, 400)], format='PNG', ) class Meta: verbose_name = _('ProductImage') verbose_name_plural = _('ProductImage|plural') def __str__(self): return self.product.name
class ImageMixin(models.Model): class Meta: abstract = True upload_image_to = None image_key_attribute = None thumbnail_width = 317 thumbnail_height = 255 thumbnail_upscale = True thumbnail_quality = 90 image = models.ImageField(upload_to='images/', default='images/no_photo.png', blank=False, max_length=512) thumbnail = ImageSpecField(source='image', processors=[ ResizeToCover( width=thumbnail_width, height=thumbnail_height, upscale=thumbnail_upscale, ) ], options={'quality': thumbnail_quality}) @property def has_image(self): return self.image != self.image.field.default
class WorkPriceImage(models.Model): work = models.CharField('Название работы', max_length=100) price = models.PositiveIntegerField('Цена') image = ProcessedImageField( verbose_name='Изображение', upload_to='price_images/', format='JPEG', options={'quality': 85}, processors=[ResizeToCover(width=350, height=350)], ) block_float_roof = models.ForeignKey(BlockFlatRoof, verbose_name='Блок с ценой', on_delete=models.PROTECT, related_name='float_prices', null=True, blank=True) block_slop_roof = models.ForeignKey(BlockSlopRoof, verbose_name='Блок с ценой', on_delete=models.PROTECT, related_name='slop_prices', null=True, blank=True) def __str__(self): return self.work class Meta: verbose_name = 'Работа с ценой' verbose_name_plural = 'Работа с ценой'
class Photo(TimeStampedModel): image = models.ImageField( verbose_name=_("field-image"), height_field="height", width_field="width", upload_to='var/texts', ) image_thumb_detail = ImageSpecField( source='image', format='JPEG', options={'quality': 95}, processors=[ ResizeToCover(600, 600), ], ) height = models.PositiveIntegerField(null=True) width = models.PositiveIntegerField(null=True) description = models.TextField( help_text=_('field-text-help-text'), null=True, blank=True, ) weight = models.PositiveIntegerField(default=0) class Meta: abstract = True verbose_name = _('Photo') verbose_name_plural = _('Photos')
class FindingImage(models.Model): image = models.ImageField(upload_to='finding_images', null=True) image_thumbnail = ImageSpecField(source='image', processors=[ResizeToCover(100, 100)], format='JPEG', options={'quality': 70}) image_small = ImageSpecField(source='image', processors=[ResizeToCover(640, 480)], format='JPEG', options={'quality': 100}) image_medium = ImageSpecField(source='image', processors=[ResizeToCover(800, 600)], format='JPEG', options={'quality': 100}) image_large = ImageSpecField(source='image', processors=[ResizeToCover(1024, 768)], format='JPEG', options={'quality': 100}) def __unicode__(self): return self.image.name
class Illustration(TimeStampedModel): objects = IllustrationManager() image = ImageField( height_field="image_height", upload_to='var/illustration', verbose_name=_("field-drawing-image"), width_field="image_width", ) position = PositiveIntegerField(choices=ILLUSTRATION_POSITION_CHOICES) weight = PositiveIntegerField(default=0) image_thumb_detail = ImageSpecField( source='image', format='JPEG', options={'quality': 95}, processors=[ ResizeToCover(300, 300), ], ) image_thumb_admin = ImageSpecField( source='image', format='JPEG', options={'quality': 95}, processors=[ ResizeToCover(100, 100), ], ) image_height = PositiveIntegerField(null=True) image_width = PositiveIntegerField(null=True) class Meta: abstract = True verbose_name = _('Illustration') verbose_name_plural = _('Illustrations') def image_tag(self): return u'<img src="%s" />' % self.image_thumb_admin.url image_tag.short_description = 'Image' image_tag.allow_tags = True
class Content(models.Model): event = models.OneToOneField(Event, related_name='content') # image = models.ImageField(upload_to=get_image_path, blank=True, null=True) image = ProcessedImageField(upload_to=get_image_path, blank=True, null=True, processors=[ResizeToCover(300, 300) ]) #(width,height) description = models.TextField(max_length=500) addedby = models.ForeignKey(User, null=True, blank=True) def __unicode__(self): return self.event.name
class Image(models.Model): """ Image - примесь для объекта с каринкой image - Картинка upload_image_to - Путь сохранения в папке MEDIA_DIR ('image/goods') image_key_attribute - Атрибут в который переименовывается картинка может быть проперти в том числе По умолчанию image = 'images/no_photo.png' upload_image_to должен иметь вид "path/to/dir". (Нет разделителя ни в конце ни в начале.) """ class Meta: abstract = True def __init__(self, *args, **kwargs): super(Image, self).__init__(*args, **kwargs) upload_image_to = None image_key_attribute = None thumbnail_width = 317 thumbnail_height = 255 thumbnail_upscale = True thumbnail_quality = 85 # Images image = models.ImageField( upload_to=UploadTo, default='images/no_photo.png', verbose_name='Картинка', blank=True, ) thumbnail = ImageSpecField(source='image', processors=[ ResizeToCover( width=thumbnail_width, height=thumbnail_height, upscale=thumbnail_upscale, ) ], options={'quality': thumbnail_quality}) @property def has_image(self): return self.image != self.image.field.default
class Portfolio(models.Model): """Галерея Наши работы""" alt = models.TextField('Описание картинки - АЛЬТ') image = ProcessedImageField( verbose_name='Изображение', upload_to='portfolio/', format='JPEG', options={'quality': 85}, processors=[ResizeToCover(width=800, height=800)], ) def __str__(self): return f'Работа id - {self.id}' class Meta: verbose_name = 'Блок - Наши работы' verbose_name_plural = 'Блок - Наши работы'
class Product(models.Model): TYPES = ((1, _('Product|Insert')), (2, _('Product|Accessory'))) name = models.CharField(max_length=40, verbose_name=_('Product|name')) slug = models.SlugField(unique=True, blank=True, verbose_name=_('Product|slug')) desc = models.TextField(verbose_name=_('Product|desc')) type = models.IntegerField(choices=TYPES, verbose_name=_('Product|type')) price_byn = models.DecimalField(max_digits=5, decimal_places=2, verbose_name=_('Product|BYN')) price_usd = models.DecimalField(max_digits=5, decimal_places=2, verbose_name=_('Product|USD')) base_image = ProcessedImageField(upload_to='images', processors=[ResizeToFit(300, 300)], format='PNG', default='media/default.png', verbose_name=_("Product|base_image")) base_image_thumbnail = ImageSpecField( source='base_image', processors=[ResizeToCover(100, 50)], format='PNG', ) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('Product|created_at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('Product|updated_at')) class Meta: verbose_name = _('Product') verbose_name_plural = _('Product|plural') def __str__(self): return self.name def admin_image_tag(self): return u'<img src="%s" />' % self.base_image_thumbnail.url admin_image_tag.allow_tags = True admin_image_tag.short_description = _('Product|image')
class News(models.Model): body = RichTextField(verbose_name=_('News|body')) carousel = models.BooleanField(default=False, verbose_name=_('News|carousel')) image = ProcessedImageField(upload_to='images/base', processors=[Resize(800, 600, False)], format='JPEG', verbose_name=_('News|image')) cropped_image = ImageSpecField(source='image', processors=[Crop(400, 400)], format='JPEG') image_thumbnail = ImageSpecField( source='image', processors=[ResizeToCover(100, 100)], format='JPEG', ) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('News|created')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('News|updated')) class Meta: verbose_name = _('News') verbose_name_plural = _('News|plural') def __str__(self): return str(self.id) def admin_image_tag(self): return u'<img src="%s" />' % self.image_thumbnail.url def admin_body(self): s = strip_tags(self.body)[:40] if len(strip_tags(self.body)) > 40: s += '...' return s admin_image_tag.allow_tags = True admin_body.allow_tags = True admin_body.short_description = _('News|body') admin_image_tag.short_description = _('News|Image')
class Images(models.Model): image = ProcessedImageField(upload_to='main', processors=[ResizeToCover(500, 500)], format='JPEG', options={'quality': 100}) image_thumbnail = ProcessedImageField(upload_to='thumbnails', processors=[SmartResize(200, 200)], format='JPEG', options={'quality': 100}) # caption = models.CharField(max_length=255) time = models.TimeField(auto_now_add=True) # slug = models.SlugField(max_length=20, null=True) user = models.ForeignKey(User) def __str__(self): return str(self.pk) def upload_image(self, image, user): i = Images.objects.create(image=image, image_thumbnail=image, user=user) return i
class ProductImage(models.Model): product = models.ForeignKey(Product, verbose_name='product', related_name='images', on_delete=models.CASCADE) sort = models.PositiveSmallIntegerField('sorting', default=1) image = models.ImageField('picture', upload_to=upload_to('products')) image_admin = ImageSpecField(source='image', processors=[ResizeToCover(100, 100)], format='JPEG', options={'quality': 90}) image_banner = ImageSpecField(source='image', processors=[ ResizeToFit(250, 250), ResizeCanvas(250, 250, anchor=Anchor.CENTER) ], format='PNG') image_product = ImageSpecField(source='image', processors=[ ResizeToFit(280, 280), ResizeCanvas(280, 280, anchor=Anchor.CENTER) ], format='PNG') image_category = ImageSpecField(source='image', processors=[ ResizeToFit(160, 160), ResizeCanvas(160, 160, anchor=Anchor.CENTER) ], format='PNG') image_list = ImageSpecField(source='image', processors=[ ResizeToFit(160, 160), ResizeCanvas(160, 160, anchor=Anchor.CENTER) ], format='PNG') image_cart = ImageSpecField(source='image', processors=[ ResizeToFit(86, 86), ResizeCanvas(86, 86, anchor=Anchor.CENTER) ], format='PNG') class Meta: verbose_name = 'product image' verbose_name_plural = 'product images' ordering = ('sort', )
class LDPI(ImageSpec): processors = [ResizeToCover(270, 120)] format = 'JPEG' options = {'quality': 100}
class XXXHDPI(ImageSpec): processors = [ResizeToCover(1440, 640)] format = 'JPEG' options = {'quality': 100}
class XHDPI(ImageSpec): processors = [ResizeToCover(720, 320)] format = 'JPEG' options = {'quality': 100}
class Source(models.Model): source_type = _('Base source class') renderers = (RENDERER_JSON, RENDERER_BROWSEABLE_API, RENDERER_XML, RENDERER_YAML, RENDERER_DATAGRID) preview_renderer = RENDERER_DATAGRID support_column_regex = False # Base data name = models.CharField(max_length=128, verbose_name=_('name'), help_text=('Human readable name for this source.')) slug = models.SlugField( unique=True, blank=True, max_length=48, verbose_name=_('slug'), help_text= ('URL friendly description of this source. If none is specified the name will be used.' )) description = models.TextField(blank=True, verbose_name=_('description')) # Images image = models.ImageField(null=True, blank=True, upload_to='images', verbose_name=_('image')) showcase_image = ImageSpecField(source='image', processors=[ResizeToCover(140, 140)], format='PNG', options={'quality': 90}) thumbnail_image = ImageSpecField(source='image', processors=[ResizeToFit(50, 50)], format='PNG', options={'quality': 60}) # Data engine published = models.BooleanField(default=False, verbose_name=_('published')) allowed_groups = models.ManyToManyField(Group, verbose_name=_('allowed groups'), blank=True, null=True) limit = models.PositiveIntegerField( default=DEFAULT_LIMIT, verbose_name=_('limit'), help_text=_( 'Maximum number of items to show when all items are requested.')) origin = models.ForeignKey(Origin, verbose_name=_('origin')) # Scheduling schedule_string = models.CharField(max_length=128, blank=True, verbose_name=_('schedule string'), help_text=_('Use CRON style format.')) schedule_enabled = models.BooleanField( default=False, verbose_name=_('schedule enabled'), help_text=_('Enabled scheduled check for source\' origin updates.')) # Managers objects = InheritanceManager() allowed = SourceAccessManager() def check_source_data(self): logger.info('Checking for new data for source: %s' % self.slug) try: lock_id = u'check_source_data-%d' % self.pk logger.debug('trying to acquire lock: %s' % lock_id) lock = Lock.acquire_lock(lock_id, 60) logger.debug('acquired lock: %s' % lock_id) try: self.check_origin_data() pass except Exception as exception: logger.debug('unhandled exception: %s' % exception) logger.error('Error when checking data for source: %s; %s' % (self.slug, exception)) raise finally: lock.release() except LockError: logger.debug('unable to obtain lock') logger.info( 'Unable to obtain lock to check for new data for source: %s' % self.slug) pass def check_origin_data(self): self.origin_subclass_instance = Origin.objects.get_subclass( pk=self.origin.pk) self.origin_subclass_instance.copy_data() logger.debug('new_hash: %s' % self.origin_subclass_instance.new_hash) try: source_data_version = self.versions.get( checksum=self.origin_subclass_instance.new_hash) except SourceDataVersion.DoesNotExist: logger.info('New origin data version found for source: %s' % self.slug) source_data_version = SourceDataVersion.objects.create( source=self, checksum=self.origin_subclass_instance.new_hash) job = Job(target=self.import_origin_data, args=[source_data_version]) job.submit() logger.debug('launching import job: %s' % job) else: source_data_version.active = True source_data_version.save() def _get_metadata(self): """Source models are responsible for overloading this method""" return '' @transaction.commit_on_success def import_origin_data(self, source_data_version): source_data_version = SourceDataVersion.objects.get( pk=source_data_version.pk) source_data_version.metadata = self._get_metadata() source_data_version.save() if self.support_column_regex: self.get_regex_maps() logger.info('Importing new data for source: %s' % self.slug) row_count = 0 try: for row_id, row in enumerate(self._get_rows(), 1): SourceData.objects.create( source_data_version=source_data_version, row_id=row_id, row=dict(row, **{'_id': row_id})) row_count += 1 except Exception as exception: transaction.rollback() logger.error('Error importing rows; %s' % exception) if getattr(settings, 'DEBUG', False): raise else: return logger.debug('finished importing rows') source_data_version.ready = True source_data_version.active = True source_data_version.elements = row_count source_data_version.save() self.origin_subclass_instance.discard_copy() logger.info('Imported %d rows for source: %s' % (row_count, self.slug)) class AlwaysFalseSearch(object): def search(self, string): return False class AlwaysTrueSearch(object): def search(self, string): return True def get_regex_maps(self): self.skip_regex_map = {} for name, skip_regex in self.columns.values_list('name', 'skip_regex'): if skip_regex: self.skip_regex_map[name] = re.compile(skip_regex) else: self.skip_regex_map[name] = self.__class__.AlwaysFalseSearch() self.import_regex_map = {} for name, import_regex in self.columns.values_list( 'name', 'import_regex'): if import_regex: self.import_regex_map[name] = re.compile(import_regex) else: self.import_regex_map[name] = self.__class__.AlwaysTrueSearch() def process_regex(self, row): skip_result = [ True if self.skip_regex_map[name].search(unicode(value)) else False for name, value in row.items() if name in self.skip_regex_map ] import_result = [ True if self.import_regex_map[name].search(unicode(value)) else False for name, value in row.items() if name in self.import_regex_map ] return all(cell_skip is False for cell_skip in skip_result) and all(import_result) def get_all(self, id=None, parameters=None): logger.debug('parameters: %s' % parameters) initial_datetime = datetime.datetime.now() timestamp, parameters = Source.analyze_request(parameters) logger.debug('timestamp: %s', timestamp) try: if timestamp: source_data_version = self.versions.get(timestamp=timestamp) else: source_data_version = self.versions.get(active=True) except SourceDataVersion.DoesNotExist: return [] self.base_iterator = (item.row for item in SourceData.objects.filter( source_data_version=source_data_version).iterator()) if id: self.base_iterator = islice(self.base_iterator, id - 1, id) results = Query(self).execute(parameters) logger.debug('query elapsed time: %s' % (datetime.datetime.now() - initial_datetime)) return results def get_one(self, id, parameters=None): # ID are all base 1 if id == 0: raise LIBREAPIError('Invalid ID; IDs are base 1') return self.get_all(id, parameters) @staticmethod def analyze_request(parameters=None): kwargs = {} if not parameters: parameters = {} else: for i in parameters: if not i.startswith('_'): kwargs[i] = parameters[i] timestamp = parameters.get('_timestamp', None) return timestamp, parameters def get_functions_map(self): """Calculate the column name to data type conversion map""" return dict([(column, DATA_TYPE_FUNCTIONS[data_type]) for column, data_type in self.columns.values_list( 'name', 'data_type')]) def apply_datatypes(self, properties, functions_map): result = {} for key, value in properties.items(): try: result[key] = functions_map[key](value) except KeyError: # Is not to be converted result[key] = value except ValueError: # Fallback for failed conversion logger.error('Unable to apply data type for field: %s' % key) result[key] = value return result def clear_versions(self): """Delete all the versions of this source""" for version in self.versions.all(): version.delete() def __unicode__(self): return self.name def clean(self): """Validation method, to avoid adding a source without a slug value""" if not self.slug: self.slug = slugify(self.name) @models.permalink def get_absolute_url(self): return ('source_view', [self.slug]) class Meta: verbose_name = _('source') verbose_name_plural = _('sources') ordering = ['name', 'slug']
class HQ(ImageSpec): processors = [ResizeToCover(854, 480)] format = 'JPEG' options = {'quality': 100}
class HD(ImageSpec): processors = [ResizeToCover(1280, 720)] format = 'JPEG' options = {'quality': 100}
class FullHD(ImageSpec): processors = [ResizeToCover(1920, 1080)] format = 'JPEG' options = {'quality': 100}
class Drawing(TimeStampedModel): objects = DrawingManager() name = CharField( max_length=255, verbose_name=_('field-name'), help_text=_('field-name-help-text'), ) size = ForeignKey( 'DrawingSize', related_name='drawings', verbose_name=_('field-size'), ) status = PositiveIntegerField( choices=DRAWING_STATUS_CHOICES, default=DRAWING_STATUS_STORED, verbose_name=_('field-drawing-status'), ) image = ImageField( height_field="image_height", upload_to='var/drawings', verbose_name=_("field-drawing-image"), width_field="image_width", ) image_thumb_detail = ImageSpecField( source='image', format='JPEG', options={'quality': 95}, processors=[ ResizeToCover(600, 600), Watermark( 'web/static/images/watermark-black.png', 0.09, ) ], ) image_thumb_list = ImageSpecField( source='image', format='JPEG', options={'quality': 95}, processors=[ ResizeToCover(300, 300), Watermark( 'web/static/images/watermark-white.png', 0.1, ), ], ) image_height = PositiveIntegerField(null=True) image_width = PositiveIntegerField(null=True) tags = ManyToManyField( 'DrawingTag', verbose_name=_('field-tags'), related_name='drawings', ) class Meta: verbose_name = _('Drawing') verbose_name_plural = _('Drawings') def __str__(self): return '%s (%s)' % (self.name, self.size) def get_active_price_level(self): now = datetime.now() return self.price_levels.filter( (Q(valid_from__isnull=True) | Q(valid_from__gte=now)) & (Q(valid_until__isnull=True) | Q(valid_until__lte=now)), ).order_by('-created').first() def get_price(self): price_level = self.get_active_price_level() return price_level.price if price_level else None def is_price_visible(self): return self.status in DRAWING_AVAILABLE_STATES def is_status_visible(self): return self.status not in DRAWING_AVAILABLE_STATES def mark_as_reserved(self): self.status = DRAWING_STATUS_RESERVED def mark_as_sold(self): self.status = DRAWING_STATUS_SOLD def get_title(self): return '%s %s' % (self.size.name, self.name ) if self.size.standalone_name else self.name
class LQ(ImageSpec): processors = [ResizeToCover(640, 360)] format = 'JPEG' options = {'quality': 100}
class Image(models.Model): source = ProcessedImageField(upload_to='images', processors=[ ResizeToCover( width=settings.GALLERY_MIN_WIDTH, height=settings.GALLERY_MIN_HEIGHT, upscale=False) ], options={'keep_exif': True}) thumbnail = ImageSpecField( source='source', processors=[ ResizeToFit(height=settings.GALLERY_THUMBNAIL_SIZE * settings.GALLERY_HDPI_FACTOR) ], format='JPEG', options={'quality': settings.GALLERY_RESIZE_QUALITY}) preview = ImageSpecField( source='source', processors=[ ResizeToFit( width=settings. GALLERY_PREVIEW_SIZE, #* settings.GALLERY_HDPI_FACTOR, height=settings. GALLERY_PREVIEW_SIZE, #* settings.GALLERY_HDPI_FACTOR ) ], format='JPEG', options={'quality': settings.GALLERY_RESIZE_QUALITY}) date_uploaded = models.DateTimeField(auto_now_add=True) date_taken = models.DateTimeField(null=True, blank=True) @cached_property def size_str(self): if not hasattr(self, 'width'): #try: with pImage.open(self.source.path) as img: self.width = img.width self.height = img.height img.close() #except (ValueError,): # storage/cache seems to have not been created yet, # this happens only on first admin pImage.open access, # ok next time (fixme: sync) # self.width = 0 # self.height = 0 return '%d x %d' % (self.width, self.height) @cached_property def exif(self): """ Retrieve exif data using PIL as a dictionary """ exif_data = {} self.source.open() with pImage.open(self.source) as img: if hasattr(img, '_getexif'): info = img._getexif() if not info: return {} for tag, value in info.items(): decoded = TAGS.get(tag, tag) exif_data[decoded] = value # Process some data for easy rendering in template exif_data['Camera'] = exif_data.get('Model', '') if exif_data.get( 'Make', '' ) not in exif_data['Camera']: # Work around for Canon exif_data['Camera'] = "{0} {1}".format( exif_data['Make'].title(), exif_data['Model']) if 'FNumber' in exif_data: exif_data['Aperture'] = str( exif_data['FNumber'].numerator / exif_data['FNumber'].denominator) if 'ExposureTime' in exif_data: exif_data['Exposure'] = "{0}/{1}".format( exif_data['ExposureTime'].numerator, exif_data['ExposureTime'].denominator) img.close() self.source.close() return exif_data def _date_taken(self): exif_data = piexif.load(self.source.path) taken_bytes = exif_data['Exif'].get( piexif.ExifIFD.DateTimeOriginal) if exif_data.get('Exif') else None if not taken_bytes: return self.mtime try: return datetime.strptime(taken_bytes.decode(), "%Y:%m:%d %H:%M:%S") except Exception: # Fall back to file modification time return self.mtime @cached_property def mtime(self): return datetime.fromtimestamp(os.path.getmtime(self.source.path)) @property def title(self): """ Derive a title from the original filename """ if hasattr(self, '_title'): return self._title name = Path(self.source.name) # remove extension name = name.with_suffix('').name # convert spacing characters to whitespaces return name.translate(str.maketrans('_', ' ')) # Temporary override for album highlights @title.setter def title(self, name): self._title = name def get_absolute_url(self): return reverse('gallery:image_detail', kwargs={'pk': self.pk}) def __str__(self): return self.title def save(self, *args, **kwargs): # The first save commits the uploaded file and creates self.source.file super().save(*args, **kwargs) # Preset date_taken: get exif date if exif exists else file # modified date and save to allow db queries, and admin overrides if not self.date_taken: self.date_taken = self._date_taken() # and re-save. (images are added/saved to db only once per 'lifetime') kwargs.update({'force_insert': False, 'force_update': True}) super().save(*args, **kwargs)
class Picture(models.Model): class Meta: ordering = ['-id'] class NonTrashManager(models.Manager): """ Query only objects which have not been trashed. """ def get_queryset(self): query_set = super(Picture.NonTrashManager, self).get_queryset() return query_set.filter(trashed_time__isnull=True) class TrashManager(models.Manager): """ Query only objects which have been trashed. """ def get_queryset(self): query_set = super(Picture.TrashManager, self).get_queryset() return query_set.filter(trashed_time__isnull=False) id = models.AutoField(primary_key=True) image = models.ImageField(upload_to='uploads/') image_small = ImageSpecField(source='image', processors=[ResizeToCover(450, 450)], format='JPEG', options={'quality': 80}) category = models.ForeignKey('Category', on_delete=models.PROTECT) title = models.CharField(max_length=128) description = models.TextField(max_length=5120, blank=True) upload_time = models.DateField(auto_now_add=True) trashed_time = models.DateTimeField(blank=True, null=True) objects = NonTrashManager() trash = TrashManager() @property def is_narrow(self): return self.image.width / self.image.height < 3 / 4 @property def is_wide(self): return self.image.width / self.image.height > 4 / 3 @property def is_trashed(self): return self.trashed_time is not None def delete(self, trash=True, **kwargs): """ Moves the picture to trash """ if self.is_trashed or not trash: super(Picture, self).delete() return self.trashed_time = datetime.now() self.save() def restore(self, commit=True): """ Restores the picture from the trash """ self.trashed_time = None if commit: self.save() def __str__(self): if self.title: return f'An image #{self.id} with title \'{self.title}\'' if self.description: description = ( self.description[:128] + '...') if len(self.description) > 128 else self.description return f'An image #{self.id} with description \'{description}\'' return f'An image #{self.id}' @staticmethod def clear_obsolete_trash(): """ Permanently deletes all pictures from the trash which were trashed minute ago or later """ minute_ago = datetime.now() - timedelta(minutes=1) Picture.trash.filter(trashed_time__lt=minute_ago).delete()
class Session(models.Model): name = models.CharField(max_length=SESSION_NAME_LEN) description = models.CharField(max_length=SESSION_DESCRIPTION_LEN) #Session size session_type = models.CharField(max_length=3, choices=session_types.SESSION_TYPES, default=session_types.REGIONAL_SESSION) picture = models.ImageField(upload_to='session_pictures/') # Session picture used on front-page to help loading times picture_thumbnail = ImageSpecField(source='picture', processors=[ResizeToCover(400, 400)], format='JPEG', options={'quality': 80}) # Session picture used on session page to help loading times and still acceptable image quality picture_large_fast = ImageSpecField(source='picture', processors=[ResizeToCover(1280, 400)], format='JPEG', options={'quality': 100}) # Session picture author link allows users to credit photographers e.g. for Creative Commons content picture_author = models.CharField(max_length=SESSION_AUTHOR_LEN, blank=True) picture_author_link = models.URLField(blank=True) picture_licence = models.CharField(max_length=SESSION_LICENCE_LEN, blank=True) picture_license_link = models.URLField(blank=True) email = models.EmailField() # The following links will be displayed on the sessions main page if a link is provided resolution_link = models.URLField(blank=True) website_link = models.URLField(blank=True) facebook_link = models.URLField(blank=True) twitter_link = models.URLField(blank=True) country = models.CharField(max_length=2, choices=countries.SESSION_COUNTRIES, default=countries.ALBANIA) #Date Options start_date = models.DateTimeField('start date') end_date = models.DateTimeField('end date') #Setting up statistic types STATISTICS = 'S' CONTENT = 'C' JOINTFORM = 'JF' SPLITFORM = 'SF' RUNNINGORDER = 'R' RUNNINGCONTENT = 'RC' STATISTIC_TYPES = ( (STATISTICS, 'Statistics Only'), (CONTENT, 'Point Content Only'), (JOINTFORM, 'Joint Form Statistics'), (SPLITFORM, 'Split Form Statistics'), (RUNNINGORDER, 'Running Order Statistics'), (RUNNINGCONTENT, 'Running Order Statistics with Point Content') ) session_statistics = models.CharField(max_length=3, choices=STATISTIC_TYPES, default=JOINTFORM) is_visible = models.BooleanField('is visible') voting_enabled = models.BooleanField('session-wide voting enabled', default=True) gender_enabled = models.BooleanField('gender statistics enabled', default=False) max_rounds = models.PositiveSmallIntegerField(default=3) gender_number_female = models.IntegerField(blank=True, null=True) gender_number_male = models.IntegerField(blank=True, null=True) gender_number_other = models.IntegerField(blank=True, null=True) # If the session has had technical problems some data is probably missing. If this is activated a message will be shown to indidate this. has_technical_problems = models.BooleanField('session has technical problems', default=False) #Defining two users for the session. The Admin user who can alter active debates, change points etc. and the #submit user, which will be the login for everyone at any given session who wants to submit a point. admin_user = models.ForeignKey(User, related_name = 'session_admin', blank = True, null = True) submission_user = models.ForeignKey(User, related_name = 'session_submit', blank = True, null = True) def __unicode__(self): return unicode(self.name) def session_ongoing(self): return (self.start_date <= timezone.now() and self.end_date >= timezone.now()) session_ongoing.admin_order_field = 'start_date' session_ongoing.boolean = True session_ongoing.short_description = 'Session Ongoing' def session_latest_activity(self): """ Returns date and time of the latest activity of the session. If there was never any activity, return 1972 as latest activity. """ initialising_datetime = timezone.make_aware(datetime.datetime(1972, 1, 1, 2), timezone.get_default_timezone()) latest_point = initialising_datetime latest_content = initialising_datetime latest_vote = initialising_datetime if Point.objects.filter(session=self): latest_point = Point.objects.filter(session=self).order_by('-timestamp')[0].timestamp if ContentPoint.objects.filter(session=self): latest_content = ContentPoint.objects.filter(session=self).order_by('-timestamp')[0].timestamp if Vote.objects.filter(session=self): latest_vote = Vote.objects.filter(session=self).order_by('-timestamp')[0].timestamp # This sorts the list of datetimes and the latest datetime is the third element of the list, which is saved to latest_activity latest_activity = sorted([latest_vote, latest_point, latest_content])[2] if latest_activity > initialising_datetime: return latest_activity else: return False def minutes_per_point(self): if self.session_statistics != 'C': all_points = Point.objects.filter(session=self).order_by('timestamp') else: all_points = ContentPoint.objects.filter(session=self).order_by('timestamp') if all_points.count() == 0: return 0 total_points = all_points.count() first_point = all_points.first().timestamp latest_point = all_points.last().timestamp time_diff = latest_point - first_point minutes = (time_diff.days * 1440) + (time_diff.seconds / 60) return Decimal(minutes) / Decimal(total_points)