class PictureArea(models.Model): picture = models.ForeignKey('picture.Picture', related_name='areas') area = jsonfield.JSONField(_('area'), default={}, editable=False) kind = models.CharField( _('kind'), max_length=10, blank=False, null=False, db_index=True, choices=(('thing', _('thing')), ('theme', _('theme')))) objects = models.Manager() tagged = managers.ModelTaggedItemManager(catalogue.models.Tag) tags = managers.TagDescriptor(catalogue.models.Tag) tag_relations = GenericRelation(catalogue.models.Tag.intermediary_table_model) short_html_url_name = 'picture_area_short' @classmethod def rectangle(cls, picture, kind, coords): pa = PictureArea() pa.picture = picture pa.kind = kind pa.area = coords return pa def flush_includes(self, languages=True): if not languages: return if languages is True: languages = [lc for (lc, _ln) in settings.LANGUAGES] flush_ssi_includes([ template % (self.pk, lang) for template in [ '/katalog/pa/%d/short.%s.html', ] for lang in languages ])
class Fragment(models.Model): """Represents a themed fragment of a book.""" text = models.TextField() short_text = models.TextField(editable=False) anchor = models.CharField(max_length=120) book = models.ForeignKey('Book', related_name='fragments') objects = models.Manager() tagged = managers.ModelTaggedItemManager(Tag) tags = managers.TagDescriptor(Tag) class Meta: ordering = ( 'book', 'anchor', ) verbose_name = _('fragment') verbose_name_plural = _('fragments') app_label = 'catalogue' def get_absolute_url(self): return '%s#m%s' % (reverse('book_text', args=[self.book.slug ]), self.anchor) def reset_short_html(self): if self.id is None: return cache_key = "Fragment.short_html/%d/%s" for lang, langname in settings.LANGUAGES: permanent_cache.delete(cache_key % (self.id, lang)) def get_short_text(self): """Returns short version of the fragment.""" return self.short_text if self.short_text else self.text def short_html(self): if self.id: cache_key = "Fragment.short_html/%d/%s" % (self.id, get_language()) short_html = permanent_cache.get(cache_key) else: short_html = None if short_html is not None: return mark_safe(short_html) else: short_html = unicode( render_to_string('catalogue/fragment_short.html', {'fragment': self})) if self.id: permanent_cache.set(cache_key, short_html) return mark_safe(short_html)
class Fragment(models.Model): """Represents a themed fragment of a book.""" text = models.TextField() short_text = models.TextField(editable=False) anchor = models.CharField(max_length=120) book = models.ForeignKey('Book', related_name='fragments') objects = models.Manager() tagged = managers.ModelTaggedItemManager(Tag) tags = managers.TagDescriptor(Tag) tag_relations = GenericRelation(Tag.intermediary_table_model) short_html_url_name = 'catalogue_fragment_short' class Meta: ordering = ( 'book', 'anchor', ) verbose_name = _('fragment') verbose_name_plural = _('fragments') app_label = 'catalogue' def get_absolute_url(self): return '%s#m%s' % (reverse('book_text', args=[self.book.slug ]), self.anchor) def get_short_text(self): """Returns short version of the fragment.""" return self.short_text if self.short_text else self.text def flush_includes(self, languages=True): if not languages: return if languages is True: languages = [lc for (lc, _ln) in settings.LANGUAGES] flush_ssi_includes([ template % (self.pk, lang) for template in [ '/katalog/f/%d/short.%s.html', '/api/include/fragment/%d.%s.json', '/api/include/fragment/%d.%s.xml', ] for lang in languages ])
class Book(models.Model): """Represents a book imported from WL-XML.""" title = models.CharField(_('title'), max_length=32767) sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False) sort_key_author = models.CharField( _('sort key by author'), max_length=120, db_index=True, editable=False, default=u'') slug = models.SlugField(_('slug'), max_length=120, db_index=True, unique=True) common_slug = models.SlugField(_('slug'), max_length=120, db_index=True) language = models.CharField(_('language code'), max_length=3, db_index=True, default=app_settings.DEFAULT_LANGUAGE) description = models.TextField(_('description'), blank=True) created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True) changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True) parent_number = models.IntegerField(_('parent number'), default=0) extra_info = jsonfield.JSONField(_('extra information'), default={}) gazeta_link = models.CharField(blank=True, max_length=240) wiki_link = models.CharField(blank=True, max_length=240) # files generated during publication cover = EbookField( 'cover', _('cover'), null=True, blank=True, upload_to=_cover_upload_to, storage=bofh_storage, max_length=255) # Cleaner version of cover for thumbs cover_thumb = EbookField( 'cover_thumb', _('cover thumbnail'), null=True, blank=True, upload_to=_cover_thumb_upload_to, max_length=255) ebook_formats = constants.EBOOK_FORMATS formats = ebook_formats + ['html', 'xml'] parent = models.ForeignKey('self', blank=True, null=True, related_name='children') ancestor = models.ManyToManyField('self', blank=True, editable=False, related_name='descendant', symmetrical=False) objects = models.Manager() tagged = managers.ModelTaggedItemManager(Tag) tags = managers.TagDescriptor(Tag) tag_relations = GenericRelation(Tag.intermediary_table_model) html_built = django.dispatch.Signal() published = django.dispatch.Signal() short_html_url_name = 'catalogue_book_short' class AlreadyExists(Exception): pass class Meta: ordering = ('sort_key_author', 'sort_key') verbose_name = _('book') verbose_name_plural = _('books') app_label = 'catalogue' def __unicode__(self): return self.title def get_initial(self): try: return re.search(r'\w', self.title, re.U).group(0) except AttributeError: return '' def authors(self): return self.tags.filter(category='author') def tag_unicode(self, category): relations = prefetched_relations(self, category) if relations: return ', '.join(rel.tag.name for rel in relations) else: return ', '.join(self.tags.filter(category=category).values_list('name', flat=True)) def author_unicode(self): return self.tag_unicode('author') def save(self, force_insert=False, force_update=False, **kwargs): from sortify import sortify self.sort_key = sortify(self.title)[:120] self.title = unicode(self.title) # ??? try: author = self.authors().first().sort_key except AttributeError: author = u'' self.sort_key_author = author ret = super(Book, self).save(force_insert, force_update, **kwargs) return ret @permalink def get_absolute_url(self): return 'catalogue.views.book_detail', [self.slug] @staticmethod @permalink def create_url(slug): return 'catalogue.views.book_detail', [slug] def gallery_path(self): return gallery_path(self.slug) def gallery_url(self): return gallery_url(self.slug) @property def name(self): return self.title def language_code(self): return constants.LANGUAGES_3TO2.get(self.language, self.language) def language_name(self): return dict(settings.LANGUAGES).get(self.language_code(), "") def is_foreign(self): return self.language_code() != settings.LANGUAGE_CODE def has_media(self, type_): if type_ in Book.formats: return bool(getattr(self, "%s_file" % type_)) else: return self.media.filter(type=type_).exists() def get_media(self, type_): if self.has_media(type_): if type_ in Book.formats: return getattr(self, "%s_file" % type_) else: return self.media.filter(type=type_) else: return None def get_mp3(self): return self.get_media("mp3") def get_odt(self): return self.get_media("odt") def get_ogg(self): return self.get_media("ogg") def get_daisy(self): return self.get_media("daisy") def has_description(self): return len(self.description) > 0 has_description.short_description = _('description') has_description.boolean = True # ugly ugly ugly def has_mp3_file(self): return bool(self.has_media("mp3")) has_mp3_file.short_description = 'MP3' has_mp3_file.boolean = True def has_ogg_file(self): return bool(self.has_media("ogg")) has_ogg_file.short_description = 'OGG' has_ogg_file.boolean = True def has_daisy_file(self): return bool(self.has_media("daisy")) has_daisy_file.short_description = 'DAISY' has_daisy_file.boolean = True def wldocument(self, parse_dublincore=True, inherit=True): from catalogue.import_utils import ORMDocProvider from librarian.parser import WLDocument if inherit and self.parent: meta_fallbacks = self.parent.cover_info() else: meta_fallbacks = None return WLDocument.from_file( self.xml_file.path, provider=ORMDocProvider(self), parse_dublincore=parse_dublincore, meta_fallbacks=meta_fallbacks) @staticmethod def zip_format(format_): def pretty_file_name(book): return "%s/%s.%s" % ( book.extra_info['author'], book.slug, format_) field_name = "%s_file" % format_ books = Book.objects.filter(parent=None).exclude(**{field_name: ""}) paths = [(pretty_file_name(b), getattr(b, field_name).path) for b in books.iterator()] return create_zip(paths, app_settings.FORMAT_ZIPS[format_]) def zip_audiobooks(self, format_): bm = BookMedia.objects.filter(book=self, type=format_) paths = map(lambda bm: (None, bm.file.path), bm) return create_zip(paths, "%s_%s" % (self.slug, format_)) def search_index(self, book_info=None, index=None, index_tags=True, commit=True): if index is None: from search.index import Index index = Index() try: index.index_book(self, book_info) if index_tags: index.index_tags() if commit: index.index.commit() except Exception, e: index.index.rollback() raise e
class Book(models.Model): """Represents a book imported from WL-XML.""" title = models.CharField(_('title'), max_length=120) sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False) slug = models.SlugField(_('slug'), max_length=120, db_index=True, unique=True) common_slug = models.SlugField(_('slug'), max_length=120, db_index=True) language = models.CharField(_('language code'), max_length=3, db_index=True, default=app_settings.DEFAULT_LANGUAGE) description = models.TextField(_('description'), blank=True) created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True) changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True) parent_number = models.IntegerField(_('parent number'), default=0) extra_info = jsonfield.JSONField(_('extra information'), default='{}') gazeta_link = models.CharField(blank=True, max_length=240) wiki_link = models.CharField(blank=True, max_length=240) # files generated during publication cover = EbookField('cover', _('cover'), upload_to=book_upload_path('jpg'), null=True, blank=True) ebook_formats = constants.EBOOK_FORMATS formats = ebook_formats + ['html', 'xml'] parent = models.ForeignKey('self', blank=True, null=True, related_name='children') _related_info = jsonfield.JSONField(blank=True, null=True, editable=False) objects = models.Manager() tagged = managers.ModelTaggedItemManager(Tag) tags = managers.TagDescriptor(Tag) html_built = django.dispatch.Signal() published = django.dispatch.Signal() class AlreadyExists(Exception): pass class Meta: ordering = ('sort_key',) verbose_name = _('book') verbose_name_plural = _('books') app_label = 'catalogue' def __unicode__(self): return self.title def save(self, force_insert=False, force_update=False, reset_short_html=True, **kwargs): from sortify import sortify self.sort_key = sortify(self.title) ret = super(Book, self).save(force_insert, force_update) if reset_short_html: self.reset_short_html() return ret @permalink def get_absolute_url(self): return ('catalogue.views.book_detail', [self.slug]) @staticmethod @permalink def create_url(slug): return ('catalogue.views.book_detail', [slug]) @property def name(self): return self.title def book_tag_slug(self): return ('l-' + self.slug)[:120] def book_tag(self): slug = self.book_tag_slug() book_tag, created = Tag.objects.get_or_create(slug=slug, category='book') if created: book_tag.name = self.title[:50] book_tag.sort_key = self.title.lower() book_tag.save() return book_tag def has_media(self, type_): if type_ in Book.formats: return bool(getattr(self, "%s_file" % type_)) else: return self.media.filter(type=type_).exists() def get_media(self, type_): if self.has_media(type_): if type_ in Book.formats: return getattr(self, "%s_file" % type_) else: return self.media.filter(type=type_) else: return None def get_mp3(self): return self.get_media("mp3") def get_odt(self): return self.get_media("odt") def get_ogg(self): return self.get_media("ogg") def get_daisy(self): return self.get_media("daisy") def reset_short_html(self): if self.id is None: return type(self).objects.filter(pk=self.pk).update(_related_info=None) # Fragment.short_html relies on book's tags, so reset it here too for fragm in self.fragments.all().iterator(): fragm.reset_short_html() def has_description(self): return len(self.description) > 0 has_description.short_description = _('description') has_description.boolean = True # ugly ugly ugly def has_mp3_file(self): return bool(self.has_media("mp3")) has_mp3_file.short_description = 'MP3' has_mp3_file.boolean = True def has_ogg_file(self): return bool(self.has_media("ogg")) has_ogg_file.short_description = 'OGG' has_ogg_file.boolean = True def has_daisy_file(self): return bool(self.has_media("daisy")) has_daisy_file.short_description = 'DAISY' has_daisy_file.boolean = True def wldocument(self, parse_dublincore=True, inherit=True): from catalogue.import_utils import ORMDocProvider from librarian.parser import WLDocument if inherit and self.parent: meta_fallbacks = self.parent.cover_info() else: meta_fallbacks = None return WLDocument.from_file(self.xml_file.path, provider=ORMDocProvider(self), parse_dublincore=parse_dublincore, meta_fallbacks=meta_fallbacks) @staticmethod def zip_format(format_): def pretty_file_name(book): return "%s/%s.%s" % ( book.extra_info['author'], book.slug, format_) field_name = "%s_file" % format_ books = Book.objects.filter(parent=None).exclude(**{field_name: ""}) paths = [(pretty_file_name(b), getattr(b, field_name).path) for b in books.iterator()] return create_zip(paths, app_settings.FORMAT_ZIPS[format_]) def zip_audiobooks(self, format_): bm = BookMedia.objects.filter(book=self, type=format_) paths = map(lambda bm: (None, bm.file.path), bm) return create_zip(paths, "%s_%s" % (self.slug, format_)) def search_index(self, book_info=None, index=None, index_tags=True, commit=True): import search if index is None: index = search.Index() try: index.index_book(self, book_info) if index_tags: index.index_tags() if commit: index.index.commit() except Exception, e: index.index.rollback() raise e
class Picture(models.Model): """ Picture resource. """ title = models.CharField(_('title'), max_length=120) slug = models.SlugField(_('slug'), max_length=120, db_index=True, unique=True) sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False) created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True) changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True) xml_file = models.FileField('xml_file', upload_to="xml", storage=picture_storage) image_file = ImageField(_('image_file'), upload_to="images", storage=picture_storage) objects = models.Manager() tagged = managers.ModelTaggedItemManager(catalogue.models.Tag) tags = managers.TagDescriptor(catalogue.models.Tag) class AlreadyExists(Exception): pass class Meta: ordering = ('sort_key', ) verbose_name = _('picture') verbose_name_plural = _('pictures') def save(self, force_insert=False, force_update=False, reset_short_html=True, **kwargs): from sortify import sortify self.sort_key = sortify(self.title) ret = super(Picture, self).save(force_insert, force_update) if reset_short_html: self.reset_short_html() return ret def __unicode__(self): return self.title @permalink def get_absolute_url(self): return ('picture.views.picture_detail', [self.slug]) @classmethod def from_xml_file(cls, xml_file, image_file=None, overwrite=False): """ Import xml and it's accompanying image file. If image file is missing, it will be fetched by librarian.picture.ImageStore which looks for an image file in the same directory the xml is, with extension matching its mime type. """ from sortify import sortify from django.core.files import File from librarian.picture import WLPicture, ImageStore close_xml_file = False close_image_file = False # class SimpleImageStore(object): # def path(self_, slug, mime_type): # """Returns the image file. Ignores slug ad mime_type.""" # return image_file if image_file is not None and not isinstance(image_file, File): image_file = File(open(image_file)) close_image_file = True if not isinstance(xml_file, File): xml_file = File(open(xml_file)) close_xml_file = True try: # use librarian to parse meta-data picture_xml = WLPicture.from_file( xml_file, image_store=ImageStore(picture_storage.path('images'))) # image_store=SimpleImageStore picture, created = Picture.objects.get_or_create( slug=picture_xml.slug) if not created and not overwrite: raise Picture.AlreadyExists('Picture %s already exists' % picture_xml.slug) picture.title = picture_xml.picture_info.title motif_tags = set() for part in picture_xml.partiter(): for motif in part['themes']: tag, created = catalogue.models.Tag.objects.get_or_create( slug=slughifi(motif), category='theme') if created: tag.name = motif tag.sort_key = sortify(tag.name) tag.save() motif_tags.add(tag) picture.tags = catalogue.models.Tag.tags_from_info(picture_xml.picture_info) + \ list(motif_tags) if image_file is not None: img = image_file else: img = picture_xml.image_file() # FIXME: hardcoded extension picture.image_file.save(path.basename(picture_xml.image_path), File(img)) picture.xml_file.save("%s.xml" % picture.slug, File(xml_file)) picture.save() finally: if close_xml_file: xml_file.close() if close_image_file: image_file.close() return picture @classmethod def picture_list(cls, filter=None): """Generates a hierarchical listing of all pictures Pictures are optionally filtered with a test function. """ pics = cls.objects.all().order_by('sort_key')\ .only('title', 'slug', 'image_file') if filter: pics = pics.filter(filter).distinct() pics_by_author = SortedDict() orphans = [] for tag in catalogue.models.Tag.objects.filter(category='author'): pics_by_author[tag] = [] for pic in pics.iterator(): authors = list(pic.tags.filter(category='author')) if authors: for author in authors: pics_by_author[author].append(pic) else: orphans.append(pic) return pics_by_author, orphans @property def info(self): if not hasattr(self, '_info'): from librarian import dcparser from librarian import picture info = dcparser.parse(self.xml_file.path, picture.PictureInfo) self._info = info return self._info def reset_short_html(self): if self.id is None: return cache_key = "Picture.short_html/%d" % (self.id) get_cache('permanent').delete(cache_key) def short_html(self): if self.id: cache_key = "Picture.short_html/%d" % (self.id) short_html = get_cache('permanent').get(cache_key) else: short_html = None if short_html is not None: return mark_safe(short_html) else: tags = self.tags.filter(category__in=('author', 'kind', 'epoch')) tags = split_tags(tags) short_html = unicode( render_to_string('picture/picture_short.html', { 'picture': self, 'tags': tags })) if self.id: get_cache('permanent').set(cache_key, short_html) return mark_safe(short_html)
class Picture(models.Model): """ Picture resource. """ title = models.CharField(_('title'), max_length=32767) slug = models.SlugField(_('slug'), max_length=120, db_index=True, unique=True) sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False) sort_key_author = models.CharField( _('sort key by author'), max_length=120, db_index=True, editable=False, default=u'') created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True) changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True) xml_file = models.FileField(_('xml file'), upload_to="xml", storage=picture_storage) image_file = ImageField(_('image file'), upload_to="images", storage=picture_storage) html_file = models.FileField(_('html file'), upload_to="html", storage=picture_storage) areas_json = jsonfield.JSONField(_('picture areas JSON'), default={}, editable=False) extra_info = jsonfield.JSONField(_('extra information'), default={}) culturepl_link = models.CharField(blank=True, max_length=240) wiki_link = models.CharField(blank=True, max_length=240) width = models.IntegerField(null=True) height = models.IntegerField(null=True) objects = models.Manager() tagged = managers.ModelTaggedItemManager(catalogue.models.Tag) tags = managers.TagDescriptor(catalogue.models.Tag) tag_relations = GenericRelation(catalogue.models.Tag.intermediary_table_model) short_html_url_name = 'picture_short' class AlreadyExists(Exception): pass class Meta: ordering = ('sort_key_author', 'sort_key') verbose_name = _('picture') verbose_name_plural = _('pictures') def save(self, force_insert=False, force_update=False, **kwargs): from sortify import sortify self.sort_key = sortify(self.title)[:120] try: author = self.authors().first().sort_key except AttributeError: author = u'' self.sort_key_author = author ret = super(Picture, self).save(force_insert, force_update) return ret def __unicode__(self): return self.title def authors(self): return self.tags.filter(category='author') def tag_unicode(self, category): relations = prefetched_relations(self, category) if relations: return ', '.join(rel.tag.name for rel in relations) else: return ', '.join(self.tags.filter(category=category).values_list('name', flat=True)) def author_unicode(self): return self.tag_unicode('author') @permalink def get_absolute_url(self): return 'picture.views.picture_detail', [self.slug] def get_initial(self): try: return re.search(r'\w', self.title, re.U).group(0) except AttributeError: return '' def get_next(self): try: return type(self).objects.filter(sort_key__gt=self.sort_key)[0] except IndexError: return None def get_previous(self): try: return type(self).objects.filter(sort_key__lt=self.sort_key).order_by('-sort_key')[0] except IndexError: return None @classmethod def from_xml_file(cls, xml_file, image_file=None, image_store=None, overwrite=False): """ Import xml and it's accompanying image file. If image file is missing, it will be fetched by librarian.picture.ImageStore which looks for an image file in the same directory the xml is, with extension matching its mime type. """ from sortify import sortify from django.core.files import File from librarian.picture import WLPicture, ImageStore close_xml_file = False close_image_file = False if image_file is not None and not isinstance(image_file, File): image_file = File(open(image_file)) close_image_file = True if not isinstance(xml_file, File): xml_file = File(open(xml_file)) close_xml_file = True with transaction.atomic(): # use librarian to parse meta-data if image_store is None: image_store = ImageStore(picture_storage.path('images')) picture_xml = WLPicture.from_file(xml_file, image_store=image_store) picture, created = Picture.objects.get_or_create(slug=picture_xml.slug[:120]) if not created and not overwrite: raise Picture.AlreadyExists('Picture %s already exists' % picture_xml.slug) picture.areas.all().delete() picture.title = unicode(picture_xml.picture_info.title) picture.extra_info = picture_xml.picture_info.to_dict() picture_tags = set(catalogue.models.Tag.tags_from_info(picture_xml.picture_info)) motif_tags = set() thing_tags = set() area_data = {'themes': {}, 'things': {}} # Treat all names in picture XML as in default language. lang = settings.LANGUAGE_CODE for part in picture_xml.partiter(): if picture_xml.frame: c = picture_xml.frame[0] part['coords'] = [[p[0] - c[0], p[1] - c[1]] for p in part['coords']] if part.get('object', None) is not None: _tags = set() for objname in part['object'].split(','): objname = objname.strip().capitalize() tag, created = catalogue.models.Tag.objects.get_or_create( slug=slughifi(objname), category='thing') if created: tag.name = objname setattr(tag, 'name_%s' % lang, tag.name) tag.sort_key = sortify(tag.name) tag.save() # thing_tags.add(tag) area_data['things'][tag.slug] = { 'object': objname, 'coords': part['coords'], } _tags.add(tag) area = PictureArea.rectangle(picture, 'thing', part['coords']) area.save() area.tags = _tags else: _tags = set() for motifs in part['themes']: for motif in motifs.split(','): tag, created = catalogue.models.Tag.objects.get_or_create( slug=slughifi(motif), category='theme') if created: tag.name = motif tag.sort_key = sortify(tag.name) tag.save() # motif_tags.add(tag) _tags.add(tag) area_data['themes'][tag.slug] = { 'theme': motif, 'coords': part['coords'] } logging.debug("coords for theme: %s" % part['coords']) area = PictureArea.rectangle(picture, 'theme', part['coords']) area.save() area.tags = _tags.union(picture_tags) picture.tags = picture_tags.union(motif_tags).union(thing_tags) picture.areas_json = area_data if image_file is not None: img = image_file else: img = picture_xml.image_file() modified = cls.crop_to_frame(picture_xml, img) modified = cls.add_source_note(picture_xml, modified) picture.width, picture.height = modified.size modified_file = StringIO() modified.save(modified_file, format='JPEG', quality=95) # FIXME: hardcoded extension - detect from DC format or orginal filename picture.image_file.save(path.basename(picture_xml.image_path), File(modified_file)) picture.xml_file.save("%s.xml" % picture.slug, File(xml_file)) picture.save() tasks.generate_picture_html(picture.id) if close_xml_file: xml_file.close() if close_image_file: image_file.close() return picture @classmethod def crop_to_frame(cls, wlpic, image_file): img = Image.open(image_file) if wlpic.frame is None or wlpic.frame == [[0, 0], [-1, -1]]: return img img = img.crop(itertools.chain(*wlpic.frame)) return img @staticmethod def add_source_note(wlpic, img): from PIL import ImageDraw, ImageFont from librarian import get_resource annotated = Image.new(img.mode, (img.size[0], img.size[1] + 40), (255, 255, 255)) annotated.paste(img, (0, 0)) annotation = Image.new('RGB', (img.size[0] * 3, 120), (255, 255, 255)) ImageDraw.Draw(annotation).text( (30, 15), wlpic.picture_info.source_name, (0, 0, 0), font=ImageFont.truetype(get_resource("fonts/DejaVuSerif.ttf"), 75) ) annotated.paste(annotation.resize((img.size[0], 40), Image.ANTIALIAS), (0, img.size[1])) return annotated # WTF/unused @classmethod def picture_list(cls, filter=None): """Generates a hierarchical listing of all pictures Pictures are optionally filtered with a test function. """ pics = cls.objects.all().order_by('sort_key').only('title', 'slug', 'image_file') if filter: pics = pics.filter(filter).distinct() pics_by_author = SortedDict() orphans = [] for tag in catalogue.models.Tag.objects.filter(category='author'): pics_by_author[tag] = [] for pic in pics.iterator(): authors = list(pic.authors().only('pk')) if authors: for author in authors: pics_by_author[author].append(pic) else: orphans.append(pic) return pics_by_author, orphans @property def info(self): if not hasattr(self, '_info'): from librarian import dcparser from librarian import picture info = dcparser.parse(self.xml_file.path, picture.PictureInfo) self._info = info return self._info def pretty_title(self, html_links=False): names = [(tag.name, tag.get_absolute_url()) for tag in self.authors().only('name', 'category', 'slug')] names.append((self.title, self.get_absolute_url())) if html_links: names = ['<a href="%s">%s</a>' % (tag[1], tag[0]) for tag in names] else: names = [tag[0] for tag in names] return ', '.join(names) def related_themes(self): return catalogue.models.Tag.objects.usage_for_queryset( self.areas.all(), counts=True).filter(category__in=('theme', 'thing')) def flush_includes(self, languages=True): if not languages: return if languages is True: languages = [lc for (lc, _ln) in settings.LANGUAGES] flush_ssi_includes([ template % (self.pk, lang) for template in [ '/katalog/p/%d/short.%s.html', '/katalog/p/%d/mini.%s.html', ] for lang in languages ])