Beispiel #1
0
class Department(models.Model):
    """Represents a department. Provides UI organization capabilities to drill-down courses by department."""
    name = models.CharField(max_length=200, blank=True, default='')
    code = models.CharField(max_length=50, unique=True)
    semesters = models.ManyToManyField(Semester,
                                       through='SemesterDepartment',
                                       related_name='departments')

    objects = managers.QuerySetManager(managers.SemesterBasedQuerySet)

    class Meta:
        ordering = ['code']

    def __unicode__(self):
        if self.name:
            return u"%s (%s)" % (self.name, self.code)
        return self.code

    def unknown(self):
        return self.courses.filter(status="unknown")

    def noexam(self):
        return self.courses.filter(status="noexam")

    def hasexam(self):
        return self.courses.filter(status="hasexam")

    def toJSON(self, select_related=()):
        json = {
            'name': self.name,
            'code': self.code,
        }
        if has_model(select_related, Semester):
            json['semesters'] = self.semesters.all().toJSON(select_related)
        return json
Beispiel #2
0
class SectionPeriod(models.Model):
    "M2M model of sections and periods"
    period = models.ForeignKey('Period', related_name='section_times')
    section = models.ForeignKey('Section', related_name='section_times')

    # we could do M2M here, but the other data here is related, and it's just easier have one link
    # per semeter...
    semester = models.ForeignKey('Semester', related_name='section_times')
    instructor = models.CharField(max_length=150, blank=True)
    location = models.CharField(max_length=150, blank=True)
    kind = models.CharField(
        max_length=75,
        help_text=
        "The kind of meeting time (eg - lab, recitation, lecture, etc.)")

    objects = managers.QuerySetManager(managers.SectionPeriodQuerySet)

    def __eq__(self, other):
        return isinstance(other, self.__class__) and self.semester == other.semester \
            and self.period == other.period and self.instructor == other.instructor \
            and self.location == other.location and self.kind == other.kind

    def __hash__(self):
        s = ":".join([
            str(self.semester),
            str(self.period), self.instructor, self.location, self.kind
        ])
        return s.__hash__()

    class Meta:
        unique_together = (('period', 'section', 'semester'), )
        verbose_name = 'Section Period'
        verbose_name_plural = 'Section Periods'

    def __unicode__(self):
        return "%s holds %s during %r at %s for section %s" % (
            self.instructor, self.kind, self.period, self.location,
            self.section)

    def toJSON(self, select_related=()):
        json = {
            'id': self.id,
            'instructor': self.instructor,
            'location': self.location,
            'kind': self.kind,
            #'semester_id': self.semester.id,
        }
        json.update(self.period.toJSON())
        return json

    def conflicts_with(self, section_period):
        "Returns True if times conflict with the given section period."
        return self.period.conflicts_with(section_period.period)
Beispiel #3
0
class Semester(models.Model):
    """Represents the semester / quarter for a college. Courses may not be offered every semester.
    """
    year = models.IntegerField(help_text="The year the semester takes place")
    month = models.IntegerField(help_text="The starting month of the semester")
    name = models.CharField(
        max_length=100, help_text="An human-readable display of the semester")
    ref = models.CharField(
        max_length=150,
        help_text="Internally used by bridge module to refer to a semester.",
        unique=True)
    date_updated = models.DateTimeField(auto_now=True)
    date_created = models.DateTimeField(auto_now_add=True)
    visible = models.BooleanField(
        default=True, help_text="Should this semester be publicly visible?")

    objects = managers.QuerySetManager(managers.SerializableQuerySet)
    visible_objects = managers.PublicSemestersQuerySetManager(
        managers.SerializableQuerySet)

    class Meta:
        unique_together = (('year', 'month'), )
        ordering = ['-year', '-month']

    def __unicode__(self):
        return self.name

    def __repr__(self):
        return "<Semester: %d-%d @ %r>" % (self.year, self.month, self.ref)

    def toJSON(self, select_related=()):
        json = {
            'id': self.id,
            'year': self.year,
            'month': self.month,
            'name': self.name,
            'date_updated': self.date_updated,
        }
        if has_model(select_related, Department):
            json['departments'] = self.departments.all().toJSON(select_related)
        return json

    def __cmp__(self, other):
        return cmp(self.year, other.year) or cmp(self.month, other.month)
Beispiel #4
0
class Course(models.Model):
    """A course offered."""
    name = models.CharField(max_length=200)
    number = models.IntegerField()

    department = models.ForeignKey(Department, related_name='courses')
    semesters = models.ManyToManyField(Semester,
                                       through='OfferedFor',
                                       related_name='courses')

    description = models.TextField(default="")
    min_credits = models.IntegerField('Min Credits')
    max_credits = models.IntegerField('Max Credits')

    grade_type = models.CharField(max_length=150, blank=True, default='')
    prereqs = models.TextField(default="")
    is_comm_intense = models.BooleanField('Communication Intensive')
    objects = managers.QuerySetManager(managers.CourseQuerySet)

    status = models.CharField(max_length=100, default='unknown')

    class Meta:
        ordering = ['department__code', 'number']

    def __unicode__(self):
        return '%s (%s %d)' % (self.name, self.department.code, self.number)

    def __hash__(self):
        return hash(self.id)

    def conflicts_with(self, course):
        "Returns True if the provided course conflicts with this one on time periods."
        sections = course.sections.all()
        for section1 in self.sections.all():
            for section2 in sections:
                if section1.conflicts_with(section2):
                    return True
        return False

    def toJSON(self, select_related=()):
        values = {
            'id': self.pk,
            'name': self.name,
            'number': self.number,
            'min_credits': self.min_credits,
            'max_credits': self.max_credits,
            'description': self.description,
            'prereqs': self.prereqs,
            'is_comm_intense': self.is_comm_intense,
        }
        if has_model(select_related, Department):
            values['department'] = self.department.toJSON(select_related)
        if has_prefetched(self, 'semesters'):
            values['semesters'] = [s.id for s in self.semesters.all()]
        if has_prefetched(self, 'sections'):
            values['sections'] = [s.toJSON() for s in self.sections.all()]
        return values

    @property
    def code(self):
        "Returns the department code and course number as a string."
        return '%s %s' % (self.department.code, self.number)

    @property
    def credits_display(self):
        "Returns the number of credits the course for those needy humans."
        if self.min_credits == self.max_credits:
            return "%d credit%s" % (self.min_credits,
                                    '' if self.min_credits == 1 else 's')
        return "%d - %d credits" % (self.min_credits, self.max_credits)

    @property
    def available_sections(self):
        return self.sections.by_availability()

    #def sections_by_semester(self, semester):
    #    return self.sections.filter(semesters__contains=semester)

    #def available_sections_by_semester(self, semester):
    #    return self.available_sections.filter(semesters__contains=semester)

    # TODO: These few properties should be moved into a manager for query optimization
    @property
    def section_periods(self):
        if not hasattr(self, 'all_section_periods'):
            return SectionPeriod.objects.by_course(course=self)
        return self.all_section_periods

    # TODO: RPI specific... remove
    @property
    def notes(self):
        def _process(notes):
            lines = set([line for note in notes for line in note.split('\n')])
            return lines

        return _process(set(sp.section.notes for sp in self.section_times))

    @property
    def section_ids(self):
        notify_if_missing_prefetch(self, 'sections')
        return set(section.id for section in self.sections.all())

    @property
    def full_section_ids(self):
        notify_if_missing_prefetch(self, 'sections')
        return set(s.id for s in self.sections.all()
                   if s.seats_taken >= s.seats_total)

    @property
    def crns(self):
        notify_if_missing_prefetch(self, 'sections')
        return set(section.crn for section in self.sections.all())

    @property
    def full_crns(self):
        notify_if_missing_prefetch(self, 'sections')
        return set(s.crn for s in self.sections.all()
                   if s.seats_taken >= s.seats_total)

    @property
    def section_times(self):
        notify_if_missing_prefetch(self, 'sections')
        section_periods = []
        for section in self.sections.all():
            notify_if_missing_prefetch(section, 'section_times')
            section_periods.extend(section.section_times.all())
        return set(section_periods)

    @property
    def instructors(self):
        return set(sp.instructor for sp in self.section_times)

    @property
    def kinds(self):
        return set(st.kind for st in self.section_times)
Beispiel #5
0
class Section(models.Model):
    """Represents a particular course a student can sign up for."""
    STUDY_ABROAD = -1
    OFF_CAMPUS = -2
    number = models.CharField(max_length=5)

    crn = models.IntegerField(unique=True)
    course = models.ForeignKey('Course', related_name='sections')
    semester = models.ForeignKey(Semester, related_name='sections')
    periods = models.ManyToManyField(Period,
                                     through='SectionPeriod',
                                     related_name='sections')
    visited = models.IntegerField(default=0,
                                  choices=((0, "unvisited"), (1, "visited"),
                                           (2, "moved")))

    seats_taken = models.IntegerField('Seats Taken')
    seats_total = models.IntegerField('Seats Total')
    # TODO: RPI Specific; provide alternative
    notes = models.TextField(blank=True)

    inv_preferences = models.CharField("Day Preferences",
                                       default='[]',
                                       max_length=50)

    objects = managers.QuerySetManager(managers.SectionQuerySet)

    class Meta:
        ordering = ['number']

    #class Meta:
    #    unique_together = ('number', 'course')

    def __unicode__(self):
        return "%s (%s) Seats: %d / %d" % (self.number, self.crn,
                                           self.seats_taken, self.seats_total)

    def __hash__(self):
        return hash(self.id)

    def toJSON(self, select_related=()):
        values = {
            'number': self.number,
            'crn': self.crn,
            'seats_taken': self.seats_taken,
            'seats_total': self.seats_total,
            'seats_left': self.seats_left,
            'notes': list(set(n for n in self.notes.split('\n') if n)),
        }
        if has_model(select_related, Course):
            values['course'] = self.course.toJSON(select_related)
        values['periods'] = [sp.toJSON() for sp in self.get_section_times()]
        return values

    @property
    def is_study_abroad(self):
        return self.number == self.STUDY_ABROAD

    @property
    def is_full(self):
        return self.seats_left <= 0

    @property
    def seats_left(self):
        return max(self.seats_total - self.seats_taken, 0)

    @property
    def days_of_week(self):
        dows = set()
        for period in self.get_periods():
            dows.update(period.days_of_week)
        return sorted_daysofweek(dows)

    @property
    def instructors(self):
        return set([ps.instructor for ps in self.get_section_times()])

    def get_section_times(self):
        if WARN_EXTRA_QUERIES and not has_prefetched(self, 'section_times'):
            print "WARN: DB query call for 'section_times'. You should probably use prefetch_related."
        return self.section_times.all()

    def get_periods(self):
        if WARN_EXTRA_QUERIES and not has_prefetched(self, 'periods'):
            print "WARN: DB query for 'periods'. You should probably use prefetch_related."
        return self.periods.all()

    def conflicts_with(self, section):
        "Returns True if the given section conflicts with another provided section."
        # always conflicts with itself...
        if self == section:
            return True
        # START --- this should really be a proxy in scheduler.models.SectionProxy
        # but there seems to be a django bug with Proxy models causing tests to fail.
        # self.conflicts has to be set by the view....
        if hasattr(self, 'conflicts'):
            return section.id in self.conflicts
        # END ---
        for period1, period2 in product(self.get_periods(),
                                        section.get_periods()):
            if period1 == period2 or period1.conflicts_with(period2):
                return True
        return False
Beispiel #6
0
class Course(models.Model):
    """A course offered."""
    name = models.CharField(max_length=200, db_index=True)
    number = models.IntegerField(db_index=True)

    department = models.ForeignKey(Department, related_name='courses')
    semesters = models.ManyToManyField(Semester,
                                       through='OfferedFor',
                                       related_name='courses')

    description = models.TextField(default="")
    min_credits = models.IntegerField('Min Credits')
    max_credits = models.IntegerField('Max Credits')

    grade_type = models.CharField(max_length=150, blank=True, default='')
    prereqs = models.TextField(default="")
    is_comm_intense = models.BooleanField('Communication Intensive',
                                          default=False)

    objects = managers.QuerySetManager(managers.CourseQuerySet)
    tags = tagged_manager()

    class Meta:
        ordering = ['department__code', 'number']

    def __unicode__(self):
        return '%d - %s (%s %d)' % (self.id, self.name, self.department.code,
                                    self.number)

    def __hash__(self):
        return hash(self.id)

    def conflicts_with(self, course):
        "Returns True if the provided course conflicts with this one on time periods."
        sections = course.sections.all()
        for section1 in self.sections.all():
            for section2 in sections:
                if section1.conflicts_with(section2):
                    return True
        return False

    def toJSON(self, select_related=()):
        values = {
            'id': self.pk,
            'name': self.name,
            'number': self.number,
            'min_credits': self.min_credits,
            'max_credits': self.max_credits,
            'description': self.description,
            'prereqs': self.prereqs,
            'is_comm_intense': self.is_comm_intense,
        }
        if has_model(select_related, Department):
            values['department'] = self.department.toJSON(select_related)
        if has_prefetched(self, 'semesters'):
            values['semesters'] = [s.id for s in self.semesters.all()]
        if has_prefetched(self, 'sections'):
            values['sections'] = [s.toJSON() for s in self.sections.all()]
        return values

    @property
    def code(self):
        "Returns the department code and course number as a string."
        return '%s %s' % (self.department.code, self.number)

    @property
    def credits_display(self):
        "Returns the number of credits the course for those needy humans."
        if self.min_credits == self.max_credits:
            return "%d credit%s" % (self.min_credits,
                                    '' if self.min_credits == 1 else 's')
        return "%d - %d credits" % (self.min_credits, self.max_credits)

    @property
    def num_sections_display(self):
        section_count = self.sections.count()
        return "%d section%s" % (section_count,
                                 '' if section_count == 1 else 's')

    @property
    def seats_left_display(self):
        seats_left = self.seats_left
        return "%d seat%s" % (seats_left, '' if seats_left == 1 else 's')

    @property
    def tags(self):
        tags = []
        section_types = {
            'LEC': {
                'name': 'Lecture',
                'title':
                'This course has lecture, where the instructor teaches the course.',
                'sort_order': 0
            },
            'TES': {
                'name': 'Testing',
                'title':
                'This course has a testing period outside normal lecture or recitation.',
                'sort_order': 5
            },
            'REC': {
                'name': 'Recitation',
                'title':
                'This course has recitation, where problemsets and quizzes generally occur.',
                'sort_order': 2
            },
            'LAB': {
                'name': 'Lab',
                'title':
                'This course has lab, where hands-on activities occur.',
                'sort_order': 1
            },
            'STU': {
                'name': 'Studio',
                'title': 'This course has studio',
                'sort_order': 4
            }
        }
        assigned_tags = set([])
        for section_period in self.section_periods:
            tag = section_types.get(section_period.kind)
            if tag and tag['name'] not in assigned_tags:
                tags.append(tag)
                assigned_tags = assigned_tags.union([tag['name']])
        if self.is_comm_intense:
            tags.append({
                'name': 'Comm Intensive',
                'title':
                'This course counts as a communication intensive course.',
                'classes': 'satisfies-requirement',
                'sort_order': 10
            })
        if self.grade_type == 'Satisfactory/Unsatisfactory':
            tags.append({
                'name': 'Pass/Fail',
                'title':
                "This course's final grade is pass or fail instead of a GPA.",
                'classes': 'pass_or_fail',
                'sort_order': 11
            })
        return tags

    @property
    def available_sections(self):
        return self.sections.by_availability()

    # TODO: These few properties should be moved into a manager for query optimization
    @property
    def section_periods(self):
        if not hasattr(self, 'all_section_periods'):
            return SectionPeriod.objects.by_course(course=self)
        return self.all_section_periods

    @property
    def notes(self):
        def _process(notes):
            lines = set([line for note in notes for line in note.split('\n')])
            return lines

        return _process(set(sp.section.notes for sp in self.section_times))

    @property
    def section_ids(self):
        notify_if_missing_prefetch(self, 'sections')
        return set(section.id for section in self.sections.all())

    @property
    def full_section_ids(self):
        notify_if_missing_prefetch(self, 'sections')
        return set(s.id for s in self.sections.all()
                   if s.seats_taken >= s.seats_total)

    @property
    def crns(self):
        notify_if_missing_prefetch(self, 'sections')
        return set(section.crn for section in self.sections.all())

    @property
    def full_crns(self):
        notify_if_missing_prefetch(self, 'sections')
        return set(s.crn for s in self.sections.all()
                   if s.seats_taken >= s.seats_total)

    @property
    def seats_left(self):
        notify_if_missing_prefetch(self, 'sections')
        return sum(s.seats_total - s.seats_taken for s in self.sections.all())

    @property
    def section_times(self):
        notify_if_missing_prefetch(self, 'sections')
        section_periods = []
        for section in self.sections.all():
            notify_if_missing_prefetch(section, 'section_times')
            section_periods.extend(section.section_times.all())
        return set(section_periods)

    @property
    def instructors(self):
        return set(sp.instructor for sp in self.section_times)

    @property
    def kinds(self):
        return set(st.kind for st in self.section_times)