Reverting half-merged commits b6f336c6a13b2a41b2c29b35884277aea9daca50 and 3d4edeb156...
[oweals/karmaworld.git] / karmaworld / apps / courses / models.py
1 #!/usr/bin/env python
2 # -*- coding:utf8 -*-
3 # Copyright (C) 2012  FinalsClub Foundation
4
5 """
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.
10 """
11 import datetime
12
13 from django.db import models
14 from django.template import defaultfilters
15 from karmaworld.settings.manual_unique_together import auto_add_check_unique_together
16
17
18 class School(models.Model):
19     """ A grouping that contains many courses """
20     name        = models.CharField(max_length=255)
21     slug        = models.SlugField(max_length=150, null=True)
22     location    = models.CharField(max_length=255, blank=True, null=True)
23     url         = models.URLField(max_length=511, blank=True)
24     # Facebook keeps a unique identifier for all schools
25     facebook_id = models.BigIntegerField(blank=True, null=True)
26     # United States Department of Education institution_id
27     usde_id     = models.BigIntegerField(blank=True, null=True)
28     file_count  = models.IntegerField(default=0)
29     priority    = models.BooleanField(default=0)
30     alias       = models.CharField(max_length=255, null=True, blank=True)
31     hashtag     = models.CharField(max_length=16, null=True, blank=True, unique=True, help_text='School abbreviation without #')
32
33     class Meta:
34         """ Sort School by file_count descending, name abc=> """
35         ordering = ['-file_count','-priority', 'name']
36
37     def __unicode__(self):
38         return self.name
39
40     def save(self, *args, **kwargs):
41         """ Save school and generate a slug if one doesn't exist """
42         if not self.slug:
43             self.slug = defaultfilters.slugify(self.name)
44         super(School, self).save(*args, **kwargs)
45
46     @staticmethod
47     def autocomplete_search_fields():
48         return ("name__icontains",)
49
50     def update_note_count(self):
51         """ Update the School.file_count by summing the
52             contained course.file_count
53         """
54         self.file_count = sum([course.file_count for course in self.course_set.all()])
55         self.save()
56
57
58 class Department(models.Model):
59     """ Department within a School. """
60     name        = models.CharField(max_length=255)
61     school      = models.ForeignKey(School) # Should this be optional ever?
62     slug        = models.SlugField(max_length=150, null=True)
63     url         = models.URLField(max_length=511, blank=True, null=True)
64
65     def __unicode__(self):
66         return self.name
67
68     def save(self, *args, **kwargs):
69         """ Save department and generate a slug if one doesn't exist """
70         if not self.slug:
71             self.slug = defaultfilters.slugify(self.name)
72         super(Department, self).save(*args, **kwargs)
73
74
75 class Professor(models.Model):
76     """
77     Track professors for courses.
78     """
79     name = models.CharField(max_length=255)
80     email = models.EmailField(blank=True, null=True)
81
82     def __unicode__(self):
83         return u'Professor: {0}'.format(self.name)
84
85
86 class ProfessorAffiliation(models.Model):
87     """
88     Track professors for departments. (many-to-many)
89     """
90     professor = models.ForeignKey(Professor)
91     department = models.ForeignKey(Department)
92
93     def __unicode__(self):
94         return u'Professor {0} working for {1}'.format(self.professor.name, self.department.name)
95
96     class Meta:
97         # many-to-many across both fields,
98         # but (prof, dept) as a tuple should only appear once.
99         unique_together = ('professor', 'department',)
100
101
102 class Course(models.Model):
103     """ First class object that contains many notes.Note objects """
104     # Core metadata
105     name        = models.CharField(max_length=255)
106     slug        = models.SlugField(max_length=150, null=True)
107     # department should remove nullable when school gets yoinked
108     department  = models.ForeignKey(Department, blank=True, null=True)
109     # school is an appendix: the kind that gets swollen and should be removed
110     # (vistigial)
111     school      = models.ForeignKey(School) 
112     file_count  = models.IntegerField(default=0)
113
114     desc        = models.TextField(max_length=511, blank=True, null=True)
115     url         = models.URLField(max_length=511, blank=True, null=True)
116
117     # instructor_* is vestigial, replaced by Professor+ProfessorTaught models.
118     instructor_name     = models.CharField(max_length=255, blank=True, null=True)
119     instructor_email    = models.EmailField(blank=True, null=True)
120
121     updated_at      = models.DateTimeField(default=datetime.datetime.utcnow)
122
123     created_at      = models.DateTimeField(auto_now_add=True)
124
125     # Number of times this course has been flagged as abusive/spam.
126     flags           = models.IntegerField(default=0,null=False)
127
128
129     class Meta:
130         ordering = ['-file_count', 'school', 'name']
131         unique_together = ('school', 'name', 'instructor_name')
132         verbose_name = 'course'
133         verbose_name_plural = 'courses'
134
135     def __unicode__(self):
136         return u"{0}: {1}".format(self.name, self.school)
137
138     def get_absolute_url(self):
139         """ return url based on school slug and self slug """
140         return u"/{0}/{1}".format(self.school.slug, self.slug)
141
142     def save(self, *args, **kwargs):
143         """ Save school and generate a slug if one doesn't exist """
144         super(Course, self).save(*args, **kwargs) # generate a self.id
145         if not self.slug:
146             self.slug = defaultfilters.slugify("%s %s" % (self.name, self.id))
147             self.save() # Save the slug
148
149     def get_updated_at_string(self):
150         """ return the formatted style for datetime strings """
151         return self.updated_at.strftime("%I%p // %a %b %d %Y")
152
153     @staticmethod
154     def autocomplete_search_fields():
155         return ("name__icontains",)
156
157     def update_note_count(self):
158         """ Update self.file_count by summing the note_set """
159         self.file_count = self.note_set.count()
160         self.save()
161
162
163 class ProfessorTaught(models.Model):
164     """
165     Track professors teaching courses. (many-to-many)
166     """
167     professor = models.ForeignKey(Professor)
168     course = models.ForeignKey(Course)
169
170     def __unicode__(self):
171         return u'Professor {0} taught {1}'.format(self.professor.name, self.course.name)
172
173     class Meta:
174         # many-to-many across both fields,
175         # but (prof, course) as a tuple should only appear once.
176         unique_together = ('professor', 'course',)
177
178
179 # Enforce unique constraints even when we're using a database like
180 # SQLite that doesn't understand them
181 auto_add_check_unique_together(Course)
182 auto_add_check_unique_together(ProfessorAffiliation)
183 auto_add_check_unique_together(ProfessorTaught)