[80982de] | 1 | from django.db import models |
---|
| 2 | |
---|
| 3 | import datetime |
---|
[f3dcf5a] | 4 | import os, errno |
---|
[80982de] | 5 | |
---|
[9fd60d4] | 6 | import settings |
---|
[80982de] | 7 | import groups.models |
---|
[f3dcf5a] | 8 | from util.misc import log_and_ignore_failures, mkdir_p |
---|
[0265c6d] | 9 | import util.previews |
---|
[80982de] | 10 | |
---|
| 11 | class FYSM(models.Model): |
---|
| 12 | group = models.ForeignKey(groups.models.Group) |
---|
[dafa3c8] | 13 | display_name = models.CharField(max_length=50) |
---|
[80982de] | 14 | year = models.IntegerField() |
---|
| 15 | website = models.URLField() |
---|
[d67c1c1] | 16 | join_url = models.URLField(help_text="<p>If you have a specific web page for students interested in joining your group, you can link to it here. If you want, this can basically be little more than a single image, similar to slides in previous years. However, you can also make it a video, interactive content, or whatever other creative material you can think of.</p><p>An image of the page will be snapshotted daily, and displayed on your group's FYSM detail page. It should first update sometime in the fifteen minutes after you first submit.</p>") |
---|
[80194d8] | 17 | contact_email = models.EmailField(help_text="Give an address for students interested in joining the group to email (e.g., an officers list)") |
---|
| 18 | description = models.TextField(help_text="Explain in about three or four sentences what your group does and why incoming freshmen should get involved.") |
---|
[1c853ea] | 19 | logo = models.ImageField(upload_to='fysm/logos', blank=True, ) |
---|
[57f8ffa] | 20 | tags = models.CharField(max_length=100, blank=True, help_text="Specify some free-form, comma-delimited tags for your group", ) |
---|
| 21 | categories = models.ManyToManyField('FYSMCategory', blank=True, help_text="Put your group into whichever of our categories seem applicable.", ) |
---|
[f3dcf5a] | 22 | join_preview = models.ForeignKey('PagePreview', null=True, ) |
---|
| 23 | |
---|
| 24 | def save(self, *args, **kwargs): |
---|
| 25 | if self.join_preview is None or self.join_url != self.join_preview.url: |
---|
| 26 | self.join_preview = PagePreview.allocate_page_preview( |
---|
| 27 | filename='fysm/%d/group%d'%(self.year, self.group.pk, ), |
---|
| 28 | url=self.join_url, |
---|
| 29 | ) |
---|
| 30 | super(FYSM, self).save(*args, **kwargs) # Call the "real" save() method. |
---|
[80982de] | 31 | |
---|
| 32 | class Meta: |
---|
| 33 | verbose_name = "FYSM submission" |
---|
| 34 | |
---|
[c27da9e] | 35 | class FYSMCategory(models.Model): |
---|
[c278cf0] | 36 | name = models.CharField(max_length=25) |
---|
[dafa3c8] | 37 | slug = models.SlugField() |
---|
[80982de] | 38 | blurb = models.TextField() |
---|
[aba463b] | 39 | |
---|
| 40 | def __str__(self, ): |
---|
| 41 | return self.name |
---|
[dafa3c8] | 42 | |
---|
| 43 | class Meta: |
---|
[c27da9e] | 44 | verbose_name = "FYSM category" |
---|
| 45 | verbose_name_plural = "FYSM categories" |
---|
[9fd60d4] | 46 | |
---|
| 47 | class FYSMView(models.Model): |
---|
[f55ccfd] | 48 | when = models.DateTimeField(default=datetime.datetime.now) |
---|
| 49 | fysm = models.ForeignKey(FYSM, null=True, blank=True, ) |
---|
[9fd60d4] | 50 | year = models.IntegerField(null=True, blank=True, ) |
---|
| 51 | page = models.CharField(max_length=20, blank=True, ) |
---|
[f55ccfd] | 52 | referer = models.URLField(verify_exists=False, null=True, ) |
---|
[9fd60d4] | 53 | user_agent = models.CharField(max_length=255) |
---|
| 54 | source_ip = models.IPAddressField() |
---|
| 55 | source_user = models.CharField(max_length=30, blank=True, ) |
---|
| 56 | |
---|
| 57 | @staticmethod |
---|
| 58 | @log_and_ignore_failures(logfile=settings.LOGFILE) |
---|
| 59 | def record_metric(request, fysm=None, year=None, page=None, ): |
---|
| 60 | record = FYSMView() |
---|
| 61 | record.fysm = fysm |
---|
| 62 | record.year = year |
---|
| 63 | record.page = page |
---|
[f55ccfd] | 64 | if 'HTTP_REFERER' in request.META: |
---|
| 65 | record.referer = request.META['HTTP_REFERER'] |
---|
[9fd60d4] | 66 | record.user_agent = request.META['HTTP_USER_AGENT'] |
---|
| 67 | record.source_ip = request.META['REMOTE_ADDR'] |
---|
| 68 | record.source_user = request.user.username |
---|
| 69 | record.save() |
---|
[f3dcf5a] | 70 | |
---|
| 71 | class PagePreview(models.Model): |
---|
| 72 | update_time = models.DateTimeField(default=datetime.datetime.utcfromtimestamp(0)) |
---|
| 73 | url = models.URLField() |
---|
| 74 | image = models.ImageField(upload_to='page-previews', blank=True, ) |
---|
| 75 | |
---|
[0265c6d] | 76 | never_updated = datetime.datetime.utcfromtimestamp(0) # Never updated |
---|
| 77 | update_interval = datetime.timedelta(hours=23) |
---|
| 78 | |
---|
| 79 | def image_filename(self, ): |
---|
| 80 | return os.path.join(settings.MEDIA_ROOT, self.image.name) |
---|
| 81 | |
---|
| 82 | |
---|
[f3dcf5a] | 83 | @classmethod |
---|
| 84 | def allocate_page_preview(cls, filename, url, ): |
---|
| 85 | preview = PagePreview() |
---|
[0265c6d] | 86 | preview.update_time = cls.never_updated |
---|
[f3dcf5a] | 87 | preview.url = url |
---|
[0265c6d] | 88 | preview.image = 'page-previews/%s.jpg' % (filename, ) |
---|
| 89 | image_filename = preview.image_filename() |
---|
[f3dcf5a] | 90 | mkdir_p(os.path.dirname(image_filename)) |
---|
| 91 | try: |
---|
[0265c6d] | 92 | os.symlink('no-preview.jpg', image_filename) |
---|
[f3dcf5a] | 93 | except OSError as exc: |
---|
| 94 | if exc.errno == errno.EEXIST: |
---|
| 95 | pass |
---|
| 96 | else: raise |
---|
| 97 | preview.save() |
---|
| 98 | return preview |
---|
| 99 | |
---|
| 100 | def update_preview(self, ): |
---|
[0265c6d] | 101 | self.update_time = datetime.datetime.now() |
---|
| 102 | self.save() |
---|
| 103 | failure = util.previews.generate_webpage_preview(self.url, self.image_filename(), ) |
---|
| 104 | if failure: |
---|
| 105 | self.update_time = self.never_updated |
---|
| 106 | self.save() |
---|
[f3dcf5a] | 107 | |
---|
| 108 | @classmethod |
---|
| 109 | def previews_needing_updates(cls, interval=None, ): |
---|
| 110 | if interval is None: |
---|
[0265c6d] | 111 | interval = cls.update_interval |
---|
[f3dcf5a] | 112 | before = datetime.datetime.now() - interval |
---|
[0265c6d] | 113 | return cls.objects.filter(update_time__lte=before) |
---|
| 114 | |
---|
| 115 | @classmethod |
---|
| 116 | def update_outdated_previews(cls, interval=None, ): |
---|
| 117 | previews = cls.previews_needing_updates(interval) |
---|
| 118 | now = datetime.datetime.now() |
---|
| 119 | update_list = [] |
---|
| 120 | previews_dict = {} |
---|
| 121 | for preview in previews: |
---|
| 122 | update_list.append((preview.url, preview.image_filename(), )) |
---|
| 123 | previews_dict[preview.url] = preview |
---|
| 124 | preview.update_time = now |
---|
| 125 | preview.save() |
---|
| 126 | failures = util.previews.generate_webpage_previews(update_list) |
---|
| 127 | for url, msg in failures: |
---|
| 128 | print "%s: %s" % (url, msg, ) |
---|
| 129 | preview = previews_dict[url] |
---|
| 130 | preview.update_time = cls.never_updated |
---|
| 131 | preview.save() |
---|