models.py 8.07 KB
Newer Older
1
# standard library imports
2
from __future__ import absolute_import, print_function
3
import hashlib
4
from datetime import date
5
# core django imports
Jason D Yeomans's avatar
Jason D Yeomans committed
6
from django.db import models
7
from django.utils import timezone
8
from model_utils.models import TimeStampedModel
9
from django.contrib.auth.models import User
Daniel W Bond's avatar
Daniel W Bond committed
10
from django.core.urlresolvers import reverse
11
from django.utils.text import slugify
12
from django.contrib import messages
13
14
# third party imports
from autoslug import AutoSlugField
Daniel W Bond's avatar
Daniel W Bond committed
15
from randomslugfield import RandomSlugField
16
from multiselectfield import MultiSelectField
17
18
from allauth.socialaccount.models import SocialAccount
# imports from your apps
David Haynes's avatar
David Haynes committed
19
from housing.models import Room
20

Jason D Yeomans's avatar
Jason D Yeomans committed
21

22
class Major(TimeStampedModel):
Daniel W Bond's avatar
Daniel W Bond committed
23
    name = models.CharField(max_length=50)
24
    # I believe the longest is "Government and International Politics"
25

Daniel W Bond's avatar
Daniel W Bond committed
26
27
    slug = AutoSlugField(populate_from='name', unique=True)

Daniel W Bond's avatar
Daniel W Bond committed
28
29
30
    def first_letter(self):
        return self.name and self.name[0] or ''

Daniel W Bond's avatar
Daniel W Bond committed
31
32
    def __str__(self):
        return self.name
Daniel W Bond's avatar
Daniel W Bond committed
33

Daniel W Bond's avatar
Daniel W Bond committed
34
35
36
    def __unicode__(self):
        return unicode(self.name)

Daniel W Bond's avatar
Daniel W Bond committed
37
    def get_absolute_url(self):
38
39
40
41
        return reverse('detail_major', kwargs={
            'slug': self.slug,
            'major': slugify(self.name),
        })
Daniel W Bond's avatar
Daniel W Bond committed
42

Daniel W Bond's avatar
Daniel W Bond committed
43
44
    class Meta:
        ordering = ['name']
Daniel W Bond's avatar
Daniel W Bond committed
45

Daniel W Bond's avatar
Daniel W Bond committed
46

Daniel W Bond's avatar
Daniel W Bond committed
47
48
class StudentQuerySet(models.query.QuerySet):
    def floor(self):
Daniel W Bond's avatar
Daniel W Bond committed
49
        return self.filter(privacy='floor')
Daniel W Bond's avatar
Daniel W Bond committed
50
51

    def building(self):
Daniel W Bond's avatar
Daniel W Bond committed
52
        return self.filter(privacy='building')
Daniel W Bond's avatar
Daniel W Bond committed
53
54

    def students(self):
Daniel W Bond's avatar
Daniel W Bond committed
55
        return self.filter(privacy='students')
Daniel W Bond's avatar
Daniel W Bond committed
56

Daniel W Bond's avatar
Daniel W Bond committed
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    # when a student is not on a floor, but in a building
    def building_students(self):
        building = self.building()
        students = self.students()
        return list(building) + list(set(students) - set(building))

    # when a student is on a floor
    def floor_building_students(self):
        floor = self.floor()
        building = self.building()
        students = self.students()

        # using the function above results in UnboundLocalError excpetion
        building_students = list(building) + list(set(students) - set(building))

        return list(floor) + list(set(building_students) - set(floor))

Daniel W Bond's avatar
Daniel W Bond committed
74

Daniel W Bond's avatar
Daniel W Bond committed
75
class StudentManager(models.Manager):
Daniel W Bond's avatar
Daniel W Bond committed
76
77
78

    # this 'duplication' allows for queryset chaining

Daniel W Bond's avatar
Daniel W Bond committed
79
    def get_queryset(self):
Daniel W Bond's avatar
Daniel W Bond committed
80
81
82
        return StudentQuerySet(self.model, using=self._db)

    def floor(self):
Daniel W Bond's avatar
Daniel W Bond committed
83
        return self.get_queryset().floor()
Daniel W Bond's avatar
Daniel W Bond committed
84
85

    def building(self):
Daniel W Bond's avatar
Daniel W Bond committed
86
        return self.get_queryset().building()
Daniel W Bond's avatar
Daniel W Bond committed
87
88

    def students(self):
Daniel W Bond's avatar
Daniel W Bond committed
89
        return self.get_queryset().students()
Daniel W Bond's avatar
Daniel W Bond committed
90
91

    def building_students(self):
Daniel W Bond's avatar
Daniel W Bond committed
92
        return self.get_queryset().building_students()
Daniel W Bond's avatar
Daniel W Bond committed
93

Daniel W Bond's avatar
Daniel W Bond committed
94
    def floor_building_students(self):
Daniel W Bond's avatar
Daniel W Bond committed
95
        return self.get_queryset().floor_building_students()
Daniel W Bond's avatar
Daniel W Bond committed
96

Daniel W Bond's avatar
Daniel W Bond committed
97

98
99
100
class Student(TimeStampedModel):
    user = models.OneToOneField(User)
    # Django user includes a username, password, email, first name, and last name
Daniel W Bond's avatar
Daniel W Bond committed
101
102
103
104
105
106
107
108
109
110
111

    FLOOR = 'floor'
    BUILDING = 'building'
    STUDENTS = 'students'

    PRIVACY_CHOICES = (
        (FLOOR, 'My Floor'),
        (BUILDING, 'My Building'),
        (STUDENTS, 'All Students'),
    )

Daniel W Bond's avatar
Daniel W Bond committed
112
113
114
    FEMALE = 'female'
    MALE = 'male'
    TRANS = 'trans'
115
116
    INTERSEX = 'intersex'
    GENDERLESS = 'genderless'
Daniel W Bond's avatar
Daniel W Bond committed
117
118
119
120
121
122
    OTHER = 'other'

    GENDER_CHOICES = (
        (FEMALE, 'female'),
        (MALE, 'male'),
        (TRANS, 'trans'),
123
124
        (INTERSEX, 'intersex'),
        (GENDERLESS, 'genderless'),
Daniel W Bond's avatar
Daniel W Bond committed
125
126
127
128
        (OTHER, 'other'),
    )

    # selectmultiple in forms
129
    gender = MultiSelectField(max_length=25, choices=GENDER_CHOICES, blank=True)
130
    show_gender = models.BooleanField(default=False)
Daniel W Bond's avatar
Daniel W Bond committed
131

Daniel W Bond's avatar
Daniel W Bond committed
132
133
    privacy = models.CharField(max_length=100, choices=PRIVACY_CHOICES, default=FLOOR)

134
    room = models.ForeignKey(Room, null=True, blank=True)
135

136
    major = models.ForeignKey('Major', related_name='major', null=True, blank=True)
137

138
    times_changed_room = models.PositiveIntegerField(default=0)
139

140
141
142
    current_year = date.today().year
    graduating_year = models.IntegerField(default=current_year, blank=True)

Daniel W Bond's avatar
Daniel W Bond committed
143
    # from when first logged in through peoplefinder, stored for later
144
145
    original_major = models.ForeignKey('Major', related_name='original_major',
                                       null=True, blank=True)
146
147
    original_first_name = models.CharField(max_length=100, blank=True)
    original_last_name = models.CharField(max_length=100, blank=True)
Daniel W Bond's avatar
Daniel W Bond committed
148

149
150
    # social media accounts

151
    # welcome walkthrough completion
152
153
154
155
    completedName = models.BooleanField(default=False)
    completedPrivacy = models.BooleanField(default=False)
    completedMajor = models.BooleanField(default=False)
    completedSocial = models.BooleanField(default=False)
156

157
158
    slug = AutoSlugField(populate_from='user', unique=True)

Daniel W Bond's avatar
Daniel W Bond committed
159
160
    objects = StudentManager()

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
    # this doesn't take into account superseniors or graduate students or negative values
    # hence private method
    def _get_class(self):
        time_to_graduate = self.graduating_year - self.current_year
        if time_to_graduate >= 4:
            return "freshman"
        elif time_to_graduate == 3:
            return "sophomore"
        elif time_to_graduate == 2:
            return "junior"
        elif time_to_graduate == 1:
            return "freshman"
        else:
            return "magic"

176
    def recent_changes(self):
177
178
179
180
181
182
183
        # part of TimeStampedModel
        now = timezone.now()
        created = self.created

        # could make this more formal with dateutil, but...
        days = (now - created).days

184
185
        # must be int-- floor function
        third_years = (days / (30 * 4)) + 1
186

187
        return (self.times_changed_room / third_years)
188

Daniel W Bond's avatar
Daniel W Bond committed
189
    def get_floor(self):
190
191
192
193
194
        try:
            floor = self.room.floor
            return floor
        except AttributeError:
            return None
Daniel W Bond's avatar
Daniel W Bond committed
195
196

    def get_building(self):
197
198
199
200
201
        try:
            building = self.room.floor.building
            return building
        except AttributeError:
            return None
Daniel W Bond's avatar
Daniel W Bond committed
202

Jason D Yeomans's avatar
Jason D Yeomans committed
203
    def profile_image_url(self):
204
        fb_uid = SocialAccount.objects.filter(user=self.user.id, provider='facebook')
205
        #print("profile_image")
Jason D Yeomans's avatar
Jason D Yeomans committed
206

Daniel W Bond's avatar
Daniel W Bond committed
207
        if len(fb_uid) > 0:
208
            return "http://graph.facebook.com/{}/picture?width=175&height=175".format(fb_uid[0].uid)
Jason D Yeomans's avatar
Jason D Yeomans committed
209

210
        return "http://www.gravatar.com/avatar/{}?s=175&d=mm".format(hashlib.md5(self.user.email).hexdigest())
Jason D Yeomans's avatar
Jason D Yeomans committed
211

Daniel W Bond's avatar
Daniel W Bond committed
212
    def get_absolute_url(self):
Daniel W Bond's avatar
Daniel W Bond committed
213
        return reverse('detail_student', kwargs={'slug': self.slug})
Daniel W Bond's avatar
Daniel W Bond committed
214

215
216
217
    def get_flag_count(self):
        my_flag_num = Confirmation.objects.filter(student=self, lives_there=False).count()
        return my_flag_num
218
    
219
220
    def get_first_name_or_uname(self):
        if not(self.user.get_short_name()):
221
            return self.user.username
222
223
224
        else:
            return self.user.get_short_name()
    
225
226
    def get_last_name_or_uname(self):
        if not(self.user.last_name):
227
            return self.user.username
228
229
230
        else:
            return self.user.last_name
    
231
    def get_full_name_or_uname(self):
232
233
        if not(self.user.get_full_name()):
            return self.user.username
234
        else:
235
            return self.user.get_full_name()
236

Daniel W Bond's avatar
Daniel W Bond committed
237
238
239
    class Meta:
        ordering = ['user']

240
241
    def __str__(self):              # __unicode__ on Python 2
        return self.user.username
Daniel W Bond's avatar
Daniel W Bond committed
242

Daniel W Bond's avatar
Daniel W Bond committed
243
244
    def __unicode__(self):
        return unicode(self.user.username)
245
    
246
    # def save(self, *args, **kwargs):
Daniel W Bond's avatar
Daniel W Bond committed
247
248
249
250
251
        #print('we be savin\'!')
        #from django.db.models.signals import pre_save, post_save
        #for signal in [pre_save, post_save]:
            #print(signal, signal.receivers)
        #super(Student, self).save(*args, **kwargs)
252

Daniel W Bond's avatar
Daniel W Bond committed
253

Daniel W Bond's avatar
Daniel W Bond committed
254
255
256
257
258
259
class Confirmation(TimeStampedModel):

    confirmer = models.ForeignKey(Student, related_name='confirmer_set')
    student = models.ForeignKey(Student, related_name='student_set')

    lives_there = models.BooleanField(default=False)
Daniel W Bond's avatar
Daniel W Bond committed
260
    # is RA? -- for later
Daniel W Bond's avatar
Daniel W Bond committed
261
262
263
264

    slug = RandomSlugField(length=6)

    def __unicode__(self):
Daniel W Bond's avatar
Daniel W Bond committed
265
266
267
268
        if self.lives_there:  # implicitly is True
            return "%s Confirmed %s" % (self.confirmer.user.username, self.student.user.username)
        else:  # implicitly is False
            return "%s Flagged %s" % (self.confirmer.user.username, self.student.user.username)