source: asadb/forms/models.py @ 6aa3517

space-accessstablestage
Last change on this file since 6aa3517 was 26826c9, checked in by Alex Dehnert <adehnert@…>, 13 years ago

Sort imports more

My current style is to sort as follows:

  • Group the imports by type
    • Python stdlib
    • Django stdlib (including django.contrib, but not random eggs)
    • Django add-ons
    • ASA-specific code
  • Sort by name within the type
  • Property mode set to 100644
File size: 10.7 KB
Line 
1import datetime
2import os, errno
3
4from django.conf import settings
5from django.db import models
6
7import groups.models
8from util.misc import log_and_ignore_failures, mkdir_p
9import util.previews
10
11class FYSM(models.Model):
12    group = models.ForeignKey(groups.models.Group, db_index=True, )
13    display_name = models.CharField(max_length=50, help_text="""Form of your name suitable for display (for example, don't end your name with ", MIT")""")
14    year = models.IntegerField(db_index=True, )
15    website = models.URLField()
16    join_url = models.URLField(verbose_name="recruiting URL", help_text="""<p>If you have a specific web page for recruiting new members of your group, you can link to it here. It will be used as the destination for most links about your group (join link on the main listing page and when clicking on the slide, but not the "website" link on the slide page). If you do not have such a page, use your main website's URL.</p>""")
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 no more than 400 characters (including spaces), what your group does and why incoming students should get involved.")
19    logo = models.ImageField(upload_to='fysm/logos', blank=True, help_text="Upload a logo (JPG, GIF, or PNG) to display on the main FYSM page as well as the group detail page. This will be scaled to be 100px wide.")
20    slide = models.ImageField(upload_to='fysm/slides', default="", help_text="Upload a slide (JPG, GIF, or PNG) to display on the group detail page. This will be scaled to be at most 600x600 pixels. We recommend making it exactly that size.")
21    tags = models.CharField(max_length=100, blank=True, help_text="Specify some free-form, comma-delimited tags for your group", )
22    categories = models.ManyToManyField('FYSMCategory', blank=True, help_text="Put your group into whichever of our categories seem applicable.", )
23    join_preview = models.ForeignKey('PagePreview', null=True, )
24
25    def save(self, *args, **kwargs):
26        if self.join_preview is None or self.join_url != self.join_preview.url:
27            self.join_preview = PagePreview.allocate_page_preview(
28                filename='fysm/%d/group%d'%(self.year, self.group.pk, ),
29                url=self.join_url,
30            )
31        super(FYSM, self).save(*args, **kwargs) # Call the "real" save() method.
32
33    def __str__(self, ):
34        return "%s (%d)" % (self.display_name, self.year, )
35
36    class Meta:
37        verbose_name = "FYSM submission"
38
39class FYSMCategory(models.Model):
40    name = models.CharField(max_length=25)
41    slug = models.SlugField(unique=True, )
42    blurb = models.TextField()
43
44    def __str__(self, ):
45        return self.name
46
47    class Meta:
48        verbose_name = "FYSM category"
49        verbose_name_plural = "FYSM categories"
50        ordering = ['name', ]
51
52class FYSMView(models.Model):
53    when = models.DateTimeField(default=datetime.datetime.now)
54    fysm = models.ForeignKey(FYSM, null=True, blank=True, )
55    year = models.IntegerField(null=True, blank=True, )
56    page = models.CharField(max_length=20, blank=True, )
57    referer = models.URLField(verify_exists=False, null=True, )
58    user_agent = models.CharField(max_length=255)
59    source_ip = models.IPAddressField()
60    source_user = models.CharField(max_length=30, blank=True, )
61
62    @staticmethod
63    @log_and_ignore_failures(logfile=settings.LOGFILE)
64    def record_metric(request, fysm=None, year=None, page=None, ):
65        record = FYSMView()
66        record.fysm = fysm
67        record.year = year
68        record.page = page
69        if 'HTTP_REFERER' in request.META:
70            record.referer = request.META['HTTP_REFERER']
71        record.user_agent = request.META['HTTP_USER_AGENT']
72        record.source_ip = request.META['REMOTE_ADDR']
73        record.source_user = request.user.username
74        record.save()
75
76class PagePreview(models.Model):
77    update_time = models.DateTimeField(default=datetime.datetime.utcfromtimestamp(0))
78    url = models.URLField()
79    image = models.ImageField(upload_to='page-previews', blank=True, )
80
81    never_updated = datetime.datetime.utcfromtimestamp(0) # Never updated
82    update_interval = datetime.timedelta(hours=23)
83
84    def image_filename(self, ):
85        return os.path.join(settings.MEDIA_ROOT, self.image.name)
86
87
88    @classmethod
89    def allocate_page_preview(cls, filename, url, ):
90        preview = PagePreview()
91        preview.update_time = cls.never_updated
92        preview.url = url
93        preview.image = 'page-previews/%s.jpg' % (filename, )
94        image_filename = preview.image_filename()
95        mkdir_p(os.path.dirname(image_filename))
96        try:
97            os.symlink('no-preview.jpg', image_filename)
98        except OSError as exc:
99            if exc.errno == errno.EEXIST:
100                pass
101            else: raise
102        preview.save()
103        return preview
104
105    def update_preview(self, ):
106        self.update_time = datetime.datetime.now()
107        self.save()
108        failure = util.previews.generate_webpage_preview(self.url, self.image_filename(), )
109        if failure:
110            self.update_time = self.never_updated
111            self.save()
112
113    @classmethod
114    def previews_needing_updates(cls, interval=None, ):
115        if interval is None:
116            interval = cls.update_interval
117        before = datetime.datetime.now() - interval
118        return cls.objects.filter(update_time__lte=before)
119
120    @classmethod
121    def update_outdated_previews(cls, interval=None, ):
122        previews = cls.previews_needing_updates(interval)
123        now = datetime.datetime.now()
124        update_list = []
125        previews_dict = {}
126        for preview in previews:
127            update_list.append((preview.url, preview.image_filename(), ))
128            previews_dict[preview.url] = preview
129            preview.update_time = now
130            preview.save()
131        failures = util.previews.generate_webpage_previews(update_list)
132        for url, msg in failures:
133            print "%s: %s" % (url, msg, )
134            preview = previews_dict[url]
135            preview.update_time = cls.never_updated
136            preview.save()
137
138
139class GroupConfirmationCycle(models.Model):
140    name = models.CharField(max_length=30)
141    slug = models.SlugField(unique=True, )
142    create_date = models.DateTimeField(default=datetime.datetime.now)
143    deadlines = models.TextField(blank=True)
144
145    def __unicode__(self, ):
146        return u"GroupConfirmationCycle %d: %s" % (self.id, self.name, )
147
148    @classmethod
149    def latest(cls, ):
150        return cls.objects.order_by('-create_date')[0]
151
152
153class GroupMembershipUpdate(models.Model):
154    update_time = models.DateTimeField(default=datetime.datetime.utcfromtimestamp(0))
155    updater_name = models.CharField(max_length=30)
156    updater_title = models.CharField(max_length=30, help_text="You need not hold any particular title in the group, but we like to know who is completing the form.")
157   
158    cycle = models.ForeignKey(GroupConfirmationCycle)
159    group = models.ForeignKey(groups.models.Group, help_text="If your group does not appear in the list above, then please email asa-exec@mit.edu.", db_index=True, )
160    group_email = models.EmailField(help_text="The text of the law will be automatically distributed to your members via this list, in order to comply with the law.")
161    officer_email = models.EmailField()
162
163    membership_definition = models.TextField()
164    num_undergrads = models.IntegerField()
165    num_grads = models.IntegerField()
166    num_alum = models.IntegerField()
167    num_other_affiliate = models.IntegerField(verbose_name="Num other MIT affiliates")
168    num_other = models.IntegerField(verbose_name="Num non-MIT")
169
170    membership_list = models.TextField(help_text="Member emails on separate lines (Athena usernames where applicable)")
171
172    email_preface = models.TextField(blank=True, help_text="If you would like, you may add text here that will preface the text of the policies when it is sent out to the group membership list provided above.")
173
174    hazing_statement = "By checking this, I hereby affirm that I have read and understand Chapter 269: Sections 17, 18, and 19 of Massachusetts Law. I furthermore attest that I have provided the appropriate address or will otherwise distribute to group members, pledges, and/or applicants, copies of Massachusetts Law 269: 17, 18, 19 and that our organization, group, or team agrees to comply with the provisions of that law. (See below for text.)"
175    no_hazing = models.BooleanField(help_text=hazing_statement)
176
177    discrimination_statement = "By checking this, I hereby affirm that I have read and understand the MIT Non-Discrimination Policy.  I furthermore attest that our organization, group, or team agrees to not discriminate against individuals on the basis of race, color, sex, sexual orientation, gender identity, religion, disability, age, genetic information, veteran status, ancestry, or national or ethnic origin."
178    no_discrimination = models.BooleanField(help_text=discrimination_statement)
179
180    def __unicode__(self, ):
181        return "GroupMembershipUpdate for %s" % (self.group, )
182
183
184VALID_UNSET         = 0
185VALID_AUTOVALIDATED = 10
186VALID_OVERRIDDEN    = 20    # confirmed by an admin
187VALID_AUTOREJECTED      = -10
188VALID_HANDREJECTED      = -20
189VALID_CHOICES = (
190    (VALID_UNSET,           "unvalidated"),
191    (VALID_AUTOVALIDATED,   "autovalidated"),
192    (VALID_OVERRIDDEN,      "hand-validated"),
193    (VALID_AUTOREJECTED,    "autorejected"),
194    (VALID_HANDREJECTED,    "hand-rejected"),
195)
196
197class PersonMembershipUpdate(models.Model):
198    update_time = models.DateTimeField(default=datetime.datetime.utcfromtimestamp(0))
199    username = models.CharField(max_length=30)
200    cycle = models.ForeignKey(GroupConfirmationCycle, db_index=True, )
201    deleted = models.DateTimeField(default=None, null=True, blank=True, )
202    valid = models.IntegerField(choices=VALID_CHOICES, default=VALID_UNSET)
203    groups = models.ManyToManyField(groups.models.Group, help_text="By selecting a group here, you indicate that you are an active member of the group in question.<br>If your group does not appear in the list above, then please email asa-exec@mit.edu.<br>")
204
205    def __unicode__(self, ):
206        return "PersonMembershipUpdate for %s" % (self.username, )
207
208
209
210##########
211# MIDWAY #
212##########
213
214
215class Midway(models.Model):
216    name = models.CharField(max_length=50)
217    slug = models.SlugField()
218    date = models.DateTimeField()
219    table_map = models.ImageField(upload_to='midway/maps')
220
221    def __str__(self, ):
222        return "%s" % (self.name, )
223
224class MidwayAssignment(models.Model):
225    midway = models.ForeignKey(Midway)
226    location = models.CharField(max_length=20)
227    group = models.ForeignKey(groups.models.Group)
228
229    def __str__(self, ):
230        return "<MidwayAssignment: %s at %s at %s>" % (self.group, self.location, self.midway, )
Note: See TracBrowser for help on using the repository browser.