We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Control which picture will be used as main illustration (trek, POI)
- Could be a new attribute in attachment model
- Could be a foreign key in trek/poi/.. i.e. `PicturesMixin`


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `geotrek/common/mixins.py`
Content:
```
1 import os
2 import logging
3 import shutil
4 import datetime
5 
6 from django.conf import settings
7 from django.db.models import Manager as DefaultManager
8 from django.db import models
9 from django.utils.translation import ugettext_lazy as _
10 from django.template.defaultfilters import slugify
11 
12 from easy_thumbnails.alias import aliases
13 from easy_thumbnails.exceptions import InvalidImageFormatError
14 from easy_thumbnails.files import get_thumbnailer
15 
16 from geotrek.common.utils import classproperty
17 
18 
19 logger = logging.getLogger(__name__)
20 
21 
22 class TimeStampedModelMixin(models.Model):
23     # Computed values (managed at DB-level with triggers)
24     date_insert = models.DateTimeField(auto_now_add=True, editable=False, verbose_name=_(u"Insertion date"), db_column='date_insert')
25     date_update = models.DateTimeField(auto_now=True, editable=False, verbose_name=_(u"Update date"), db_column='date_update')
26 
27     class Meta:
28         abstract = True
29 
30     def reload(self, fromdb=None):
31         """Reload fields computed at DB-level (triggers)
32         """
33         if fromdb is None:
34             fromdb = self.__class__.objects.get(pk=self.pk)
35         self.date_insert = fromdb.date_insert
36         self.date_update = fromdb.date_update
37         return self
38 
39 
40 class NoDeleteMixin(models.Model):
41     deleted = models.BooleanField(editable=False, default=False, db_column='supprime', verbose_name=_(u"Deleted"))
42 
43     def delete(self, force=False, using=None, **kwargs):
44         if force:
45             super(NoDeleteMixin, self).delete(using, **kwargs)
46         else:
47             self.deleted = True
48             self.save(using=using)
49 
50     class Meta:
51         abstract = True
52 
53     def reload(self, fromdb=None):
54         """Reload fields computed at DB-level (triggers)
55         """
56         if fromdb is None:
57             fromdb = self.__class__.objects.get(pk=self.pk)
58         self.deleted = fromdb.deleted
59         return self
60 
61     @classmethod
62     def get_manager_cls(cls, parent_mgr_cls=DefaultManager):
63 
64         class NoDeleteManager(parent_mgr_cls):
65             # Use this manager when walking through FK/M2M relationships
66             use_for_related_fields = True
67 
68             # Filter out deleted objects
69             def existing(self):
70                 return self.get_queryset().filter(deleted=False)
71 
72         return NoDeleteManager
73 
74 
75 class PicturesMixin(object):
76     """A common class to share code between Trek and POI regarding
77     attached pictures"""
78 
79     @property
80     def pictures(self):
81         """
82         Find first image among attachments.
83         Since we allow screenshot to be overriden by attachments
84         named 'mapimage', filter it from object pictures.
85         """
86         if hasattr(self, '_pictures'):
87             return self._pictures
88         return [a for a in self.attachments.all() if a.is_image
89                 and a.title != 'mapimage']
90 
91     @pictures.setter
92     def pictures(self, values):
93         self._pictures = values
94 
95     @property
96     def serializable_pictures(self):
97         serialized = []
98         for picture in self.pictures:
99             thumbnailer = get_thumbnailer(picture.attachment_file)
100             try:
101                 thdetail = thumbnailer.get_thumbnail(aliases.get('medium'))
102                 thurl = os.path.join(settings.MEDIA_URL, thdetail.name)
103             except InvalidImageFormatError:
104                 thurl = None
105                 logger.error(_("Image %s invalid or missing from disk.") % picture.attachment_file)
106                 pass
107             serialized.append({
108                 'author': picture.author,
109                 'title': picture.title,
110                 'legend': picture.legend,
111                 'url': thurl
112             })
113         return serialized
114 
115     @property
116     def picture_print(self):
117         for picture in self.pictures:
118             thumbnailer = get_thumbnailer(picture.attachment_file)
119             try:
120                 return thumbnailer.get_thumbnail(aliases.get('print'))
121             except InvalidImageFormatError:
122                 logger.error(_("Image %s invalid or missing from disk.") % picture.attachment_file)
123                 pass
124         return None
125 
126     @property
127     def thumbnail(self):
128         for picture in self.pictures:
129             thumbnailer = get_thumbnailer(picture.attachment_file)
130             try:
131                 return thumbnailer.get_thumbnail(aliases.get('small-square'))
132             except InvalidImageFormatError:
133                 logger.error(_("Image %s invalid or missing from disk.") % picture.attachment_file)
134                 pass
135         return None
136 
137     @classproperty
138     def thumbnail_verbose_name(cls):
139         return _("Thumbnail")
140 
141     @property
142     def thumbnail_display(self):
143         thumbnail = self.thumbnail
144         if thumbnail is None:
145             return _("None")
146         return '<img height="20" width="20" src="%s"/>' % os.path.join(settings.MEDIA_URL, thumbnail.name)
147 
148     @property
149     def thumbnail_csv_display(self):
150         return '' if self.thumbnail is None else os.path.join(settings.MEDIA_URL, self.thumbnail.name)
151 
152     @property
153     def serializable_thumbnail(self):
154         th = self.thumbnail
155         if not th:
156             return None
157         return os.path.join(settings.MEDIA_URL, th.name)
158 
159 
160 class BasePublishableMixin(models.Model):
161     """ Basic fields to control publication of objects.
162 
163     It is used for flat pages and publishable entities.
164     """
165     published = models.BooleanField(verbose_name=_(u"Published"), default=False,
166                                     help_text=_(u"Online"), db_column='public')
167     publication_date = models.DateField(verbose_name=_(u"Publication date"),
168                                         null=True, blank=True, editable=False,
169                                         db_column='date_publication')
170 
171     class Meta:
172         abstract = True
173 
174     def save(self, *args, **kwargs):
175         if self.publication_date is None and self.any_published:
176             self.publication_date = datetime.date.today()
177         if self.publication_date is not None and not self.any_published:
178             self.publication_date = None
179         super(BasePublishableMixin, self).save(*args, **kwargs)
180 
181     @property
182     def any_published(self):
183         """Returns True if the object is published in at least one of the language
184         """
185         if not settings.PUBLISHED_BY_LANG:
186             return self.published
187 
188         for l in settings.MAPENTITY_CONFIG['TRANSLATED_LANGUAGES']:
189             if getattr(self, 'published_%s' % l[0], False):
190                 return True
191         return False
192 
193     @property
194     def published_status(self):
195         """Returns the publication status by language.
196         """
197         status = []
198         for l in settings.MAPENTITY_CONFIG['TRANSLATED_LANGUAGES']:
199             if settings.PUBLISHED_BY_LANG:
200                 published = getattr(self, 'published_%s' % l[0], None) or False
201             else:
202                 published = self.published
203             status.append({
204                 'lang': l[0],
205                 'language': l[1],
206                 'status': published
207             })
208         return status
209 
210 
211 class PublishableMixin(BasePublishableMixin):
212     """A mixin that contains all necessary stuff to publish objects
213     (e.g. on Geotrek-rando).
214 
215     It will only work with MapEntity models.
216 
217     Initially, it was part of the ``trekking.Trek`` class. But now, all kinds of information
218     can be published (c.f. PN Cevennes project).
219     """
220     name = models.CharField(verbose_name=_(u"Name"), max_length=128,
221                             help_text=_(u"Public name (Change carefully)"), db_column='nom')
222 
223     class Meta:
224         abstract = True
225 
226     @property
227     def slug(self):
228         return slugify(self.name)
229 
230     @property
231     def name_display(self):
232         s = u'<a data-pk="%s" href="%s" title="%s">%s</a>' % (self.pk,
233                                                               self.get_detail_url(),
234                                                               self.name,
235                                                               self.name)
236         if self.published:
237             s = u'<span class="badge badge-success" title="%s">&#x2606;</span> ' % _("Published") + s
238         return s
239 
240     @property
241     def name_csv_display(self):
242         return unicode(self.name)
243 
244     @models.permalink
245     def get_document_public_url(self):
246         raise NotImplementedError
247 
248     def is_complete(self):
249         """It should also have a description, etc.
250         """
251         modelname = self.__class__._meta.object_name.lower()
252         mandatory = settings.COMPLETENESS_FIELDS.get(modelname, [])
253         for f in mandatory:
254             if not getattr(self, f):
255                 return False
256         return True
257 
258     def is_publishable(self):
259         return self.is_complete() and self.has_geom_valid()
260 
261     def has_geom_valid(self):
262         return self.geom is not None
263 
264     def prepare_map_image(self, rooturl):
265         """
266         We override the default behaviour of map image preparation :
267         if the object has a attached picture file with *title* ``mapimage``, we use it
268         as a screenshot.
269         TODO: remove this when screenshots are bullet-proof ?
270         """
271         attached = None
272         for picture in [a for a in self.attachments.all() if a.is_image]:
273             if picture.title == 'mapimage':
274                 attached = picture.attachment_file
275                 break
276         if attached is None:
277             super(PublishableMixin, self).prepare_map_image(rooturl)
278         else:
279             # Copy it along other screenshots
280             src = os.path.join(settings.MEDIA_ROOT, attached.name)
281             dst = self.get_map_image_path()
282             shutil.copyfile(src, dst)
283 
284     def get_geom_aspect_ratio(self):
285         """ Force object aspect ratio to fit height and width of
286         image in public document.
287         """
288         modelname = self.__class__._meta.object_name.lower()
289         s = settings.EXPORT_MAP_IMAGE_SIZE[modelname]
290         return float(s[0]) / s[1]
291 
292     def get_attachment_print(self):
293         """
294         Look in attachment if there is document to be used as print version
295         """
296         overriden = self.attachments.filter(title="docprint").get()
297         # Must have OpenOffice document mimetype
298         if overriden.mimetype != ['application', 'vnd.oasis.opendocument.text']:
299             raise overriden.DoesNotExist()
300         return os.path.join(settings.MEDIA_ROOT, overriden.attachment_file.name)
301 
302 
303 class PictogramMixin(models.Model):
304     pictogram = models.FileField(verbose_name=_(u"Pictogram"), upload_to=settings.UPLOAD_DIR,
305                                  db_column='picto', max_length=512, null=True)
306 
307     class Meta:
308         abstract = True
309 
310     def pictogram_img(self):
311         return u'<img src="%s" />' % (self.pictogram.url if self.pictogram else "")
312     pictogram_img.short_description = _("Pictogram")
313     pictogram_img.allow_tags = True
314 
315     def get_pictogram_url(self):
316         return self.pictogram.url if self.pictogram else None
317
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/geotrek/common/mixins.py b/geotrek/common/mixins.py
--- a/geotrek/common/mixins.py
+++ b/geotrek/common/mixins.py
@@ -85,7 +85,8 @@
         """
         if hasattr(self, '_pictures'):
             return self._pictures
-        return [a for a in self.attachments.all() if a.is_image
+        all_attachments = self.attachments.order_by('-starred').all()
+        return [a for a in all_attachments if a.is_image
                 and a.title != 'mapimage']
 
     @pictures.setter

{"golden_diff": "diff --git a/geotrek/common/mixins.py b/geotrek/common/mixins.py\n--- a/geotrek/common/mixins.py\n+++ b/geotrek/common/mixins.py\n@@ -85,7 +85,8 @@\n         \"\"\"\n         if hasattr(self, '_pictures'):\n             return self._pictures\n-        return [a for a in self.attachments.all() if a.is_image\n+        all_attachments = self.attachments.order_by('-starred').all()\n+        return [a for a in all_attachments if a.is_image\n                 and a.title != 'mapimage']\n \n     @pictures.setter\n", "issue": "Control which picture will be used as main illustration (trek, POI)\n- Could be a new attribute in attachment model\n- Could be a foreign key in trek/poi/.. i.e. `PicturesMixin`\n\n", "before_files": [{"content": "import os\nimport logging\nimport shutil\nimport datetime\n\nfrom django.conf import settings\nfrom django.db.models import Manager as DefaultManager\nfrom django.db import models\nfrom django.utils.translation import ugettext_lazy as _\nfrom django.template.defaultfilters import slugify\n\nfrom easy_thumbnails.alias import aliases\nfrom easy_thumbnails.exceptions import InvalidImageFormatError\nfrom easy_thumbnails.files import get_thumbnailer\n\nfrom geotrek.common.utils import classproperty\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass TimeStampedModelMixin(models.Model):\n    # Computed values (managed at DB-level with triggers)\n    date_insert = models.DateTimeField(auto_now_add=True, editable=False, verbose_name=_(u\"Insertion date\"), db_column='date_insert')\n    date_update = models.DateTimeField(auto_now=True, editable=False, verbose_name=_(u\"Update date\"), db_column='date_update')\n\n    class Meta:\n        abstract = True\n\n    def reload(self, fromdb=None):\n        \"\"\"Reload fields computed at DB-level (triggers)\n        \"\"\"\n        if fromdb is None:\n            fromdb = self.__class__.objects.get(pk=self.pk)\n        self.date_insert = fromdb.date_insert\n        self.date_update = fromdb.date_update\n        return self\n\n\nclass NoDeleteMixin(models.Model):\n    deleted = models.BooleanField(editable=False, default=False, db_column='supprime', verbose_name=_(u\"Deleted\"))\n\n    def delete(self, force=False, using=None, **kwargs):\n        if force:\n            super(NoDeleteMixin, self).delete(using, **kwargs)\n        else:\n            self.deleted = True\n            self.save(using=using)\n\n    class Meta:\n        abstract = True\n\n    def reload(self, fromdb=None):\n        \"\"\"Reload fields computed at DB-level (triggers)\n        \"\"\"\n        if fromdb is None:\n            fromdb = self.__class__.objects.get(pk=self.pk)\n        self.deleted = fromdb.deleted\n        return self\n\n    @classmethod\n    def get_manager_cls(cls, parent_mgr_cls=DefaultManager):\n\n        class NoDeleteManager(parent_mgr_cls):\n            # Use this manager when walking through FK/M2M relationships\n            use_for_related_fields = True\n\n            # Filter out deleted objects\n            def existing(self):\n                return self.get_queryset().filter(deleted=False)\n\n        return NoDeleteManager\n\n\nclass PicturesMixin(object):\n    \"\"\"A common class to share code between Trek and POI regarding\n    attached pictures\"\"\"\n\n    @property\n    def pictures(self):\n        \"\"\"\n        Find first image among attachments.\n        Since we allow screenshot to be overriden by attachments\n        named 'mapimage', filter it from object pictures.\n        \"\"\"\n        if hasattr(self, '_pictures'):\n            return self._pictures\n        return [a for a in self.attachments.all() if a.is_image\n                and a.title != 'mapimage']\n\n    @pictures.setter\n    def pictures(self, values):\n        self._pictures = values\n\n    @property\n    def serializable_pictures(self):\n        serialized = []\n        for picture in self.pictures:\n            thumbnailer = get_thumbnailer(picture.attachment_file)\n            try:\n                thdetail = thumbnailer.get_thumbnail(aliases.get('medium'))\n                thurl = os.path.join(settings.MEDIA_URL, thdetail.name)\n            except InvalidImageFormatError:\n                thurl = None\n                logger.error(_(\"Image %s invalid or missing from disk.\") % picture.attachment_file)\n                pass\n            serialized.append({\n                'author': picture.author,\n                'title': picture.title,\n                'legend': picture.legend,\n                'url': thurl\n            })\n        return serialized\n\n    @property\n    def picture_print(self):\n        for picture in self.pictures:\n            thumbnailer = get_thumbnailer(picture.attachment_file)\n            try:\n                return thumbnailer.get_thumbnail(aliases.get('print'))\n            except InvalidImageFormatError:\n                logger.error(_(\"Image %s invalid or missing from disk.\") % picture.attachment_file)\n                pass\n        return None\n\n    @property\n    def thumbnail(self):\n        for picture in self.pictures:\n            thumbnailer = get_thumbnailer(picture.attachment_file)\n            try:\n                return thumbnailer.get_thumbnail(aliases.get('small-square'))\n            except InvalidImageFormatError:\n                logger.error(_(\"Image %s invalid or missing from disk.\") % picture.attachment_file)\n                pass\n        return None\n\n    @classproperty\n    def thumbnail_verbose_name(cls):\n        return _(\"Thumbnail\")\n\n    @property\n    def thumbnail_display(self):\n        thumbnail = self.thumbnail\n        if thumbnail is None:\n            return _(\"None\")\n        return '<img height=\"20\" width=\"20\" src=\"%s\"/>' % os.path.join(settings.MEDIA_URL, thumbnail.name)\n\n    @property\n    def thumbnail_csv_display(self):\n        return '' if self.thumbnail is None else os.path.join(settings.MEDIA_URL, self.thumbnail.name)\n\n    @property\n    def serializable_thumbnail(self):\n        th = self.thumbnail\n        if not th:\n            return None\n        return os.path.join(settings.MEDIA_URL, th.name)\n\n\nclass BasePublishableMixin(models.Model):\n    \"\"\" Basic fields to control publication of objects.\n\n    It is used for flat pages and publishable entities.\n    \"\"\"\n    published = models.BooleanField(verbose_name=_(u\"Published\"), default=False,\n                                    help_text=_(u\"Online\"), db_column='public')\n    publication_date = models.DateField(verbose_name=_(u\"Publication date\"),\n                                        null=True, blank=True, editable=False,\n                                        db_column='date_publication')\n\n    class Meta:\n        abstract = True\n\n    def save(self, *args, **kwargs):\n        if self.publication_date is None and self.any_published:\n            self.publication_date = datetime.date.today()\n        if self.publication_date is not None and not self.any_published:\n            self.publication_date = None\n        super(BasePublishableMixin, self).save(*args, **kwargs)\n\n    @property\n    def any_published(self):\n        \"\"\"Returns True if the object is published in at least one of the language\n        \"\"\"\n        if not settings.PUBLISHED_BY_LANG:\n            return self.published\n\n        for l in settings.MAPENTITY_CONFIG['TRANSLATED_LANGUAGES']:\n            if getattr(self, 'published_%s' % l[0], False):\n                return True\n        return False\n\n    @property\n    def published_status(self):\n        \"\"\"Returns the publication status by language.\n        \"\"\"\n        status = []\n        for l in settings.MAPENTITY_CONFIG['TRANSLATED_LANGUAGES']:\n            if settings.PUBLISHED_BY_LANG:\n                published = getattr(self, 'published_%s' % l[0], None) or False\n            else:\n                published = self.published\n            status.append({\n                'lang': l[0],\n                'language': l[1],\n                'status': published\n            })\n        return status\n\n\nclass PublishableMixin(BasePublishableMixin):\n    \"\"\"A mixin that contains all necessary stuff to publish objects\n    (e.g. on Geotrek-rando).\n\n    It will only work with MapEntity models.\n\n    Initially, it was part of the ``trekking.Trek`` class. But now, all kinds of information\n    can be published (c.f. PN Cevennes project).\n    \"\"\"\n    name = models.CharField(verbose_name=_(u\"Name\"), max_length=128,\n                            help_text=_(u\"Public name (Change carefully)\"), db_column='nom')\n\n    class Meta:\n        abstract = True\n\n    @property\n    def slug(self):\n        return slugify(self.name)\n\n    @property\n    def name_display(self):\n        s = u'<a data-pk=\"%s\" href=\"%s\" title=\"%s\">%s</a>' % (self.pk,\n                                                              self.get_detail_url(),\n                                                              self.name,\n                                                              self.name)\n        if self.published:\n            s = u'<span class=\"badge badge-success\" title=\"%s\">&#x2606;</span> ' % _(\"Published\") + s\n        return s\n\n    @property\n    def name_csv_display(self):\n        return unicode(self.name)\n\n    @models.permalink\n    def get_document_public_url(self):\n        raise NotImplementedError\n\n    def is_complete(self):\n        \"\"\"It should also have a description, etc.\n        \"\"\"\n        modelname = self.__class__._meta.object_name.lower()\n        mandatory = settings.COMPLETENESS_FIELDS.get(modelname, [])\n        for f in mandatory:\n            if not getattr(self, f):\n                return False\n        return True\n\n    def is_publishable(self):\n        return self.is_complete() and self.has_geom_valid()\n\n    def has_geom_valid(self):\n        return self.geom is not None\n\n    def prepare_map_image(self, rooturl):\n        \"\"\"\n        We override the default behaviour of map image preparation :\n        if the object has a attached picture file with *title* ``mapimage``, we use it\n        as a screenshot.\n        TODO: remove this when screenshots are bullet-proof ?\n        \"\"\"\n        attached = None\n        for picture in [a for a in self.attachments.all() if a.is_image]:\n            if picture.title == 'mapimage':\n                attached = picture.attachment_file\n                break\n        if attached is None:\n            super(PublishableMixin, self).prepare_map_image(rooturl)\n        else:\n            # Copy it along other screenshots\n            src = os.path.join(settings.MEDIA_ROOT, attached.name)\n            dst = self.get_map_image_path()\n            shutil.copyfile(src, dst)\n\n    def get_geom_aspect_ratio(self):\n        \"\"\" Force object aspect ratio to fit height and width of\n        image in public document.\n        \"\"\"\n        modelname = self.__class__._meta.object_name.lower()\n        s = settings.EXPORT_MAP_IMAGE_SIZE[modelname]\n        return float(s[0]) / s[1]\n\n    def get_attachment_print(self):\n        \"\"\"\n        Look in attachment if there is document to be used as print version\n        \"\"\"\n        overriden = self.attachments.filter(title=\"docprint\").get()\n        # Must have OpenOffice document mimetype\n        if overriden.mimetype != ['application', 'vnd.oasis.opendocument.text']:\n            raise overriden.DoesNotExist()\n        return os.path.join(settings.MEDIA_ROOT, overriden.attachment_file.name)\n\n\nclass PictogramMixin(models.Model):\n    pictogram = models.FileField(verbose_name=_(u\"Pictogram\"), upload_to=settings.UPLOAD_DIR,\n                                 db_column='picto', max_length=512, null=True)\n\n    class Meta:\n        abstract = True\n\n    def pictogram_img(self):\n        return u'<img src=\"%s\" />' % (self.pictogram.url if self.pictogram else \"\")\n    pictogram_img.short_description = _(\"Pictogram\")\n    pictogram_img.allow_tags = True\n\n    def get_pictogram_url(self):\n        return self.pictogram.url if self.pictogram else None\n", "path": "geotrek/common/mixins.py"}], "after_files": [{"content": "import os\nimport logging\nimport shutil\nimport datetime\n\nfrom django.conf import settings\nfrom django.db.models import Manager as DefaultManager\nfrom django.db import models\nfrom django.utils.translation import ugettext_lazy as _\nfrom django.template.defaultfilters import slugify\n\nfrom easy_thumbnails.alias import aliases\nfrom easy_thumbnails.exceptions import InvalidImageFormatError\nfrom easy_thumbnails.files import get_thumbnailer\n\nfrom geotrek.common.utils import classproperty\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass TimeStampedModelMixin(models.Model):\n    # Computed values (managed at DB-level with triggers)\n    date_insert = models.DateTimeField(auto_now_add=True, editable=False, verbose_name=_(u\"Insertion date\"), db_column='date_insert')\n    date_update = models.DateTimeField(auto_now=True, editable=False, verbose_name=_(u\"Update date\"), db_column='date_update')\n\n    class Meta:\n        abstract = True\n\n    def reload(self, fromdb=None):\n        \"\"\"Reload fields computed at DB-level (triggers)\n        \"\"\"\n        if fromdb is None:\n            fromdb = self.__class__.objects.get(pk=self.pk)\n        self.date_insert = fromdb.date_insert\n        self.date_update = fromdb.date_update\n        return self\n\n\nclass NoDeleteMixin(models.Model):\n    deleted = models.BooleanField(editable=False, default=False, db_column='supprime', verbose_name=_(u\"Deleted\"))\n\n    def delete(self, force=False, using=None, **kwargs):\n        if force:\n            super(NoDeleteMixin, self).delete(using, **kwargs)\n        else:\n            self.deleted = True\n            self.save(using=using)\n\n    class Meta:\n        abstract = True\n\n    def reload(self, fromdb=None):\n        \"\"\"Reload fields computed at DB-level (triggers)\n        \"\"\"\n        if fromdb is None:\n            fromdb = self.__class__.objects.get(pk=self.pk)\n        self.deleted = fromdb.deleted\n        return self\n\n    @classmethod\n    def get_manager_cls(cls, parent_mgr_cls=DefaultManager):\n\n        class NoDeleteManager(parent_mgr_cls):\n            # Use this manager when walking through FK/M2M relationships\n            use_for_related_fields = True\n\n            # Filter out deleted objects\n            def existing(self):\n                return self.get_queryset().filter(deleted=False)\n\n        return NoDeleteManager\n\n\nclass PicturesMixin(object):\n    \"\"\"A common class to share code between Trek and POI regarding\n    attached pictures\"\"\"\n\n    @property\n    def pictures(self):\n        \"\"\"\n        Find first image among attachments.\n        Since we allow screenshot to be overriden by attachments\n        named 'mapimage', filter it from object pictures.\n        \"\"\"\n        if hasattr(self, '_pictures'):\n            return self._pictures\n        all_attachments = self.attachments.order_by('-starred').all()\n        return [a for a in all_attachments if a.is_image\n                and a.title != 'mapimage']\n\n    @pictures.setter\n    def pictures(self, values):\n        self._pictures = values\n\n    @property\n    def serializable_pictures(self):\n        serialized = []\n        for picture in self.pictures:\n            thumbnailer = get_thumbnailer(picture.attachment_file)\n            try:\n                thdetail = thumbnailer.get_thumbnail(aliases.get('medium'))\n                thurl = os.path.join(settings.MEDIA_URL, thdetail.name)\n            except InvalidImageFormatError:\n                thurl = None\n                logger.error(_(\"Image %s invalid or missing from disk.\") % picture.attachment_file)\n                pass\n            serialized.append({\n                'author': picture.author,\n                'title': picture.title,\n                'legend': picture.legend,\n                'url': thurl\n            })\n        return serialized\n\n    @property\n    def picture_print(self):\n        for picture in self.pictures:\n            thumbnailer = get_thumbnailer(picture.attachment_file)\n            try:\n                return thumbnailer.get_thumbnail(aliases.get('print'))\n            except InvalidImageFormatError:\n                logger.error(_(\"Image %s invalid or missing from disk.\") % picture.attachment_file)\n                pass\n        return None\n\n    @property\n    def thumbnail(self):\n        for picture in self.pictures:\n            thumbnailer = get_thumbnailer(picture.attachment_file)\n            try:\n                return thumbnailer.get_thumbnail(aliases.get('small-square'))\n            except InvalidImageFormatError:\n                logger.error(_(\"Image %s invalid or missing from disk.\") % picture.attachment_file)\n                pass\n        return None\n\n    @classproperty\n    def thumbnail_verbose_name(cls):\n        return _(\"Thumbnail\")\n\n    @property\n    def thumbnail_display(self):\n        thumbnail = self.thumbnail\n        if thumbnail is None:\n            return _(\"None\")\n        return '<img height=\"20\" width=\"20\" src=\"%s\"/>' % os.path.join(settings.MEDIA_URL, thumbnail.name)\n\n    @property\n    def thumbnail_csv_display(self):\n        return '' if self.thumbnail is None else os.path.join(settings.MEDIA_URL, self.thumbnail.name)\n\n    @property\n    def serializable_thumbnail(self):\n        th = self.thumbnail\n        if not th:\n            return None\n        return os.path.join(settings.MEDIA_URL, th.name)\n\n\nclass BasePublishableMixin(models.Model):\n    \"\"\" Basic fields to control publication of objects.\n\n    It is used for flat pages and publishable entities.\n    \"\"\"\n    published = models.BooleanField(verbose_name=_(u\"Published\"), default=False,\n                                    help_text=_(u\"Online\"), db_column='public')\n    publication_date = models.DateField(verbose_name=_(u\"Publication date\"),\n                                        null=True, blank=True, editable=False,\n                                        db_column='date_publication')\n\n    class Meta:\n        abstract = True\n\n    def save(self, *args, **kwargs):\n        if self.publication_date is None and self.any_published:\n            self.publication_date = datetime.date.today()\n        if self.publication_date is not None and not self.any_published:\n            self.publication_date = None\n        super(BasePublishableMixin, self).save(*args, **kwargs)\n\n    @property\n    def any_published(self):\n        \"\"\"Returns True if the object is published in at least one of the language\n        \"\"\"\n        if not settings.PUBLISHED_BY_LANG:\n            return self.published\n\n        for l in settings.MAPENTITY_CONFIG['TRANSLATED_LANGUAGES']:\n            if getattr(self, 'published_%s' % l[0], False):\n                return True\n        return False\n\n    @property\n    def published_status(self):\n        \"\"\"Returns the publication status by language.\n        \"\"\"\n        status = []\n        for l in settings.MAPENTITY_CONFIG['TRANSLATED_LANGUAGES']:\n            if settings.PUBLISHED_BY_LANG:\n                published = getattr(self, 'published_%s' % l[0], None) or False\n            else:\n                published = self.published\n            status.append({\n                'lang': l[0],\n                'language': l[1],\n                'status': published\n            })\n        return status\n\n\nclass PublishableMixin(BasePublishableMixin):\n    \"\"\"A mixin that contains all necessary stuff to publish objects\n    (e.g. on Geotrek-rando).\n\n    It will only work with MapEntity models.\n\n    Initially, it was part of the ``trekking.Trek`` class. But now, all kinds of information\n    can be published (c.f. PN Cevennes project).\n    \"\"\"\n    name = models.CharField(verbose_name=_(u\"Name\"), max_length=128,\n                            help_text=_(u\"Public name (Change carefully)\"), db_column='nom')\n\n    class Meta:\n        abstract = True\n\n    @property\n    def slug(self):\n        return slugify(self.name)\n\n    @property\n    def name_display(self):\n        s = u'<a data-pk=\"%s\" href=\"%s\" title=\"%s\">%s</a>' % (self.pk,\n                                                              self.get_detail_url(),\n                                                              self.name,\n                                                              self.name)\n        if self.published:\n            s = u'<span class=\"badge badge-success\" title=\"%s\">&#x2606;</span> ' % _(\"Published\") + s\n        return s\n\n    @property\n    def name_csv_display(self):\n        return unicode(self.name)\n\n    @models.permalink\n    def get_document_public_url(self):\n        raise NotImplementedError\n\n    def is_complete(self):\n        \"\"\"It should also have a description, etc.\n        \"\"\"\n        modelname = self.__class__._meta.object_name.lower()\n        mandatory = settings.COMPLETENESS_FIELDS.get(modelname, [])\n        for f in mandatory:\n            if not getattr(self, f):\n                return False\n        return True\n\n    def is_publishable(self):\n        return self.is_complete() and self.has_geom_valid()\n\n    def has_geom_valid(self):\n        return self.geom is not None\n\n    def prepare_map_image(self, rooturl):\n        \"\"\"\n        We override the default behaviour of map image preparation :\n        if the object has a attached picture file with *title* ``mapimage``, we use it\n        as a screenshot.\n        TODO: remove this when screenshots are bullet-proof ?\n        \"\"\"\n        attached = None\n        for picture in [a for a in self.attachments.all() if a.is_image]:\n            if picture.title == 'mapimage':\n                attached = picture.attachment_file\n                break\n        if attached is None:\n            super(PublishableMixin, self).prepare_map_image(rooturl)\n        else:\n            # Copy it along other screenshots\n            src = os.path.join(settings.MEDIA_ROOT, attached.name)\n            dst = self.get_map_image_path()\n            shutil.copyfile(src, dst)\n\n    def get_geom_aspect_ratio(self):\n        \"\"\" Force object aspect ratio to fit height and width of\n        image in public document.\n        \"\"\"\n        modelname = self.__class__._meta.object_name.lower()\n        s = settings.EXPORT_MAP_IMAGE_SIZE[modelname]\n        return float(s[0]) / s[1]\n\n    def get_attachment_print(self):\n        \"\"\"\n        Look in attachment if there is document to be used as print version\n        \"\"\"\n        overriden = self.attachments.filter(title=\"docprint\").get()\n        # Must have OpenOffice document mimetype\n        if overriden.mimetype != ['application', 'vnd.oasis.opendocument.text']:\n            raise overriden.DoesNotExist()\n        return os.path.join(settings.MEDIA_ROOT, overriden.attachment_file.name)\n\n\nclass PictogramMixin(models.Model):\n    pictogram = models.FileField(verbose_name=_(u\"Pictogram\"), upload_to=settings.UPLOAD_DIR,\n                                 db_column='picto', max_length=512, null=True)\n\n    class Meta:\n        abstract = True\n\n    def pictogram_img(self):\n        return u'<img src=\"%s\" />' % (self.pictogram.url if self.pictogram else \"\")\n    pictogram_img.short_description = _(\"Pictogram\")\n    pictogram_img.allow_tags = True\n\n    def get_pictogram_url(self):\n        return self.pictogram.url if self.pictogram else None\n", "path": "geotrek/common/mixins.py"}]}