3 # Copyright (C) 2012 FinalsClub Foundation
6 Models for the courses django app.
7 Handles courses, and their related models
8 Courses are the first class object, they contain notes.
9 Courses have a manytoone relation to schools.
14 from django.db import models
15 from django.utils.text import slugify
16 from karmaworld.settings.manual_unique_together import auto_add_check_unique_together
19 class SchoolManager(models.Manager):
20 """ Handle restoring data. """
21 def get_by_natural_key(self, usde_id):
23 Return a School defined by USDE number.
25 return self.get(usde_id=usde_id)
28 class School(models.Model):
29 """ A grouping that contains many courses """
30 objects = SchoolManager()
32 name = models.CharField(max_length=255)
33 slug = models.SlugField(max_length=150, null=True)
34 location = models.CharField(max_length=255, blank=True, null=True)
35 url = models.URLField(max_length=511, blank=True)
36 # Facebook keeps a unique identifier for all schools
37 facebook_id = models.BigIntegerField(blank=True, null=True)
38 # United States Department of Education institution_id
39 usde_id = models.BigIntegerField(blank=True, null=True, unique=True)
40 file_count = models.IntegerField(default=0)
41 priority = models.BooleanField(default=0)
42 alias = models.CharField(max_length=255, null=True, blank=True)
43 hashtag = models.CharField(max_length=16, null=True, blank=True, unique=True, help_text='School abbreviation without #')
46 """ Sort School by file_count descending, name abc=> """
47 ordering = ['-file_count','-priority', 'name']
49 def natural_key(self):
51 A School is uniquely defined by USDE number.
53 Name should be unique, but there are some dupes in the DB.
55 return (self.usde_id,)
57 def __unicode__(self):
58 return u'School {0}: {1}'.format(self.usde_id, self.name)
60 def save(self, *args, **kwargs):
61 """ Save school and generate a slug if one doesn't exist """
63 self.slug = slugify(unicode(self.name))
64 super(School, self).save(*args, **kwargs)
67 def autocomplete_search_fields():
68 return ("name__icontains",)
70 def update_note_count(self):
71 """ Update the School.file_count by summing the
72 contained course.file_count
74 self.file_count = sum([course.file_count for course in self.course_set.all()])
78 class DepartmentManager(models.Manager):
79 """ Handle restoring data. """
80 def get_by_natural_key(self, name, school):
82 Return a Department defined by its name and school.
84 return self.get(name=name, school=school)
87 class Department(models.Model):
88 """ Department within a School. """
89 objects = DepartmentManager()
91 name = models.CharField(max_length=255)
92 school = models.ForeignKey(School) # Should this be optional ever?
93 slug = models.SlugField(max_length=150, null=True)
94 url = models.URLField(max_length=511, blank=True, null=True)
98 The same department name might exist across schools, but only once
101 unique_together = ('name', 'school',)
103 def __unicode__(self):
104 return u'Department: {0} at {1}'.format(self.name, unicode(self.school))
106 def natural_key(self):
108 A Department is uniquely defined by its school and name.
110 return (self.name, self.school.natural_key())
111 # Requires School to be dumped first
112 natural_key.dependencies = ['courses.school']
114 def save(self, *args, **kwargs):
115 """ Save department and generate a slug if one doesn't exist """
117 self.slug = slugify(unicode(self.name))
118 super(Department, self).save(*args, **kwargs)
121 class ProfessorManager(models.Manager):
122 """ Handle restoring data. """
123 def get_by_natural_key(self, name, email):
125 Return a Professor defined by name and email address.
127 return self.get(name=name,email=email)
130 class Professor(models.Model):
132 Track professors for courses.
134 objects = ProfessorManager()
136 name = models.CharField(max_length=255)
137 email = models.EmailField(blank=True, null=True)
141 email should be unique, but some professors have no email address
142 in the database. For those cases, the name must be appended for
145 unique_together = ('name', 'email',)
147 def __unicode__(self):
148 return u'Professor: {0} ({1})'.format(self.name, self.email)
150 def natural_key(self):
152 A Professor is uniquely defined by his/her name and email.
154 return (self.name,self.email)
157 class ProfessorAffiliationManager(models.Manager):
158 """ Handle restoring data. """
159 def get_by_natural_key(self, prof, dept):
161 Return a ProfessorAffiliation defined by prof and department.
163 return self.get(professor=prof,department=dept)
166 class ProfessorAffiliation(models.Model):
168 Track professors for departments. (many-to-many)
170 objects = ProfessorAffiliationManager()
172 professor = models.ForeignKey(Professor)
173 department = models.ForeignKey(Department)
175 def __unicode__(self):
176 return u'Professor {0} working for {1}'.format(unicode(self.professor), unicode(self.department))
180 Many-to-many across both professor and department.
181 However, (prof, dept) as a tuple should only appear once.
183 unique_together = ('professor', 'department',)
185 def natural_key(self):
187 A ProfessorAffiliation is uniquely defined by the prof and department
189 return (self.professor.natural_key(), self.department.natural_key())
190 # Requires dependencies to be dumped first
191 natural_key.dependencies = ['courses.professor','courses.department']
194 class CourseManager(models.Manager):
195 """ Handle restoring data. """
196 def get_by_natural_key(self, name, dept):
198 Return a Course defined by name and department.
200 return self.get(name=name,department=dept)
203 class Course(models.Model):
204 """ First class object that contains many notes.Note objects """
205 objects = CourseManager()
208 name = models.CharField(max_length=255)
209 slug = models.SlugField(max_length=150, null=True)
210 # department should remove nullable when school gets yoinked
211 department = models.ForeignKey(Department, blank=True, null=True)
212 # school is an appendix: the kind that gets swollen and should be removed
214 school = models.ForeignKey(School, null=True, blank=True)
215 file_count = models.IntegerField(default=0)
217 desc = models.TextField(max_length=511, blank=True, null=True)
218 url = models.URLField(max_length=511, blank=True, null=True)
220 # instructor_* is vestigial, replaced by Professor+ProfessorTaught models.
221 instructor_name = models.CharField(max_length=255, blank=True, null=True)
222 instructor_email = models.EmailField(blank=True, null=True)
224 updated_at = models.DateTimeField(default=datetime.datetime.utcnow)
226 created_at = models.DateTimeField(auto_now_add=True)
228 # Number of times this course has been flagged as abusive/spam.
229 flags = models.IntegerField(default=0,null=False)
232 ordering = ['-file_count', 'school', 'name']
233 unique_together = ('name', 'department')
234 verbose_name = 'course'
235 verbose_name_plural = 'courses'
237 def __unicode__(self):
238 return u"Course {0} in {1}".format(self.name, unicode(self.department))
240 def natural_key(self):
242 A Course is uniquely defined by its name and the department it is in.
244 return (self.name, self.department.natural_key())
245 # Requires dependencies to be dumped first
246 natural_key.dependencies = ['courses.department']
248 def get_absolute_url(self):
249 """ return url based on school slug and self slug """
250 return u"/{0}/{1}".format(self.school.slug, self.slug)
252 @reversion.create_revision()
253 def save(self, *args, **kwargs):
254 """ Save school and generate a slug if one doesn't exist """
255 super(Course, self).save(*args, **kwargs) # generate a self.id
257 self.slug = slugify(u"%s %s" % (self.name, self.id))
258 self.save() # Save the slug
260 def get_updated_at_string(self):
261 """ return the formatted style for datetime strings """
262 return self.updated_at.strftime("%I%p // %a %b %d %Y")
265 def autocomplete_search_fields():
266 return ("name__icontains",)
268 @reversion.create_revision()
269 def update_note_count(self):
270 """ Update self.file_count by summing the note_set """
271 self.file_count = self.note_set.count()
274 reversion.register(Course)
276 class ProfessorTaughtManager(models.Manager):
277 """ Handle restoring data. """
278 def get_by_natural_key(self, prof, course):
280 Return a ProfessorTaught defined by professor and course.
282 return self.get(professor=prof, course=course)
285 class ProfessorTaught(models.Model):
287 Track professors teaching courses. (many-to-many)
289 objects = ProfessorTaughtManager()
291 professor = models.ForeignKey(Professor)
292 course = models.ForeignKey(Course)
294 def __unicode__(self):
295 return u'Professor {0} taught {1}'.format(unicode(self.professor), unicode(self.course))
298 # many-to-many across both fields,
299 # but (prof, course) as a tuple should only appear once.
300 unique_together = ('professor', 'course',)
302 def natural_key(self):
304 A ProfessorTaught is uniquely defined by the prof and course.
306 return (self.professor.natural_key(), self.course.natural_key())
307 # Requires dependencies to be dumped first
308 natural_key.dependencies = ['courses.professor','courses.course']
311 # Enforce unique constraints even when we're using a database like
312 # SQLite that doesn't understand them
313 auto_add_check_unique_together(Course)
314 auto_add_check_unique_together(Department)
315 auto_add_check_unique_together(Professor)
316 auto_add_check_unique_together(ProfessorAffiliation)
317 auto_add_check_unique_together(ProfessorTaught)