Пример #1
0
class AdvisorVisitCategory(models.Model):
    """
    Allow each unit to manage the categories which are now included in a visit.
    """
    unit = models.ForeignKey(Unit,
                             null=False,
                             blank=False,
                             on_delete=models.PROTECT)
    label = models.CharField(null=False, blank=False, max_length=50)
    description = models.CharField(null=True, blank=True, max_length=500)
    hidden = models.BooleanField(null=False,
                                 blank=False,
                                 default=False,
                                 editable=False)
    config = JSONField(null=False, blank=False, default=dict,
                       editable=False)  # addition configuration stuff:

    def autoslug(self):
        return make_slug(self.unit.slug + '-' + self.label)

    slug = AutoSlugField(populate_from='autoslug',
                         null=False,
                         editable=False,
                         unique=True)

    def __str__(self):
        return self.label

    objects = AdvisorVisitCategoryQuerySet.as_manager()

    def delete(self):
        # As usual, only hide stuff, don't delete it.
        self.hidden = True
        self.save()
Пример #2
0
class Announcement(models.Model):
    title = models.CharField(max_length=100)
    message = models.TextField(blank=False, null=False)
    created_at = models.DateTimeField(default=datetime.datetime.now)
    author = models.ForeignKey(Person, related_name='posted_by', on_delete=models.PROTECT,
                                help_text='The user who created the news item',
                                editable=False)
    hidden = models.BooleanField(null=False, db_index=True, default=False)
    unit = models.ForeignKey(Unit, help_text='Academic unit who owns the note', null=False, blank=False,
                             on_delete=models.PROTECT)

    config = JSONField(null=False, blank=False, default=dict)

    markup = config_property('markup', 'plain')
    math = config_property('math', False)

    def __str__(self):
        return "%s" % (self.title)

    class Meta:
       ordering = ('-created_at',)

    def delete(self, *args, **kwargs):
        raise NotImplementedError("This object cannot be deleted, set the hidden flag instead.")
    
    def html_content(self):
        return markup_to_html(self.message, self.markup, restricted=False)
Пример #3
0
class FacultyMemberInfo(models.Model):
    #person = models.ForeignKey(Person, unique=True, related_name='+')
    person = models.OneToOneField(Person, related_name='+')
    title = models.CharField(max_length=50)
    birthday = models.DateField(verbose_name="Birthdate",
                                null=True,
                                blank=True)
    office_number = models.CharField('Office',
                                     max_length=20,
                                     null=True,
                                     blank=True)
    phone_number = models.CharField('Local Phone Number',
                                    max_length=20,
                                    null=True,
                                    blank=True)
    emergency_contact = models.TextField('Emergency Contact Information',
                                         blank=True)
    config = JSONField(blank=True, null=True,
                       default={})  # addition configuration

    last_updated = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return u'<FacultyMemberInfo({})>'.format(self.person)

    def get_absolute_url(self):
        return reverse('faculty.views.faculty_member_info',
                       args=[self.person.userid_or_emplid()])
Пример #4
0
class AlertType(models.Model):
    """
    An alert code.

    "GPA < 2.4"
    """
    code = models.CharField(
        help_text="A short (<30 characters) title for the report.",
        max_length=30)
    description = models.TextField(
        help_text=
        "A longer, more in-depth explanation of what this alert is for.",
        null=True,
        blank=True)
    unit = models.ForeignKey(
        Unit,
        help_text="Only people in this unit can manage this Alert",
        null=False)
    hidden = models.BooleanField(null=False, default=False)
    config = JSONField(null=False, blank=False, default=dict)

    def autoslug(self):
        return make_slug(self.code)

    slug = AutoSlugField(populate_from='autoslug',
                         null=False,
                         editable=False,
                         unique=True)
Пример #5
0
class AssetChangeRecord(models.Model):
    asset = models.ForeignKey(Asset,
                              null=False,
                              blank=False,
                              related_name='records',
                              on_delete=models.PROTECT)
    person = models.ForeignKey(Person,
                               null=False,
                               blank=False,
                               on_delete=models.PROTECT)
    qty = models.IntegerField(
        "Quantity adjustment",
        null=False,
        blank=False,
        help_text=
        "The change in quantity.  For removal of item, make it a negative number. "
        "For adding items, make it a positive.  e.g. '-2' if someone removed two of "
        "this item for something")
    date = models.DateField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    last_modified = models.DateTimeField(editable=False,
                                         blank=False,
                                         null=False)
    hidden = models.BooleanField(default=False,
                                 null=False,
                                 blank=False,
                                 editable=False)
    saved_by_userid = models.CharField(max_length=8,
                                       blank=False,
                                       null=False,
                                       editable=False)
    # In case we forgot something, this will make it easier to add something in the future without a migration.
    config = JSONField(null=False, blank=False, default=dict, editable=False)

    def autoslug(self):
        if self.qty > 0:
            change_string = " added "
        else:
            change_string = " removed "
        return make_slug(self.person.userid_or_emplid() + change_string +
                         str(self.qty) + ' ' + str(self.asset))

    slug = AutoSlugField(populate_from='autoslug',
                         null=False,
                         editable=False,
                         unique=True)

    objects = AssetChangeRecordQuerySet.as_manager()

    def save(self, user, *args, **kwargs):
        self.last_modified = timezone.now()
        self.saved_by_userid = user
        super(AssetChangeRecord, self).save(*args, **kwargs)

    def delete(self, user):
        """
        Like most of our objects, never actually delete them, just hide them.
        """
        self.hidden = True
        self.save(user)
Пример #6
0
class Artifact(models.Model):
    name = models.CharField(max_length=140,
                            help_text='The name of the artifact',
                            null=False,
                            blank=False)
    category = models.CharField(max_length=3,
                                choices=ARTIFACT_CATEGORIES,
                                null=False,
                                blank=False)

    def autoslug(self):
        return make_slug(self.unit.label + '-' + self.name)

    slug = AutoSlugField(populate_from=autoslug,
                         null=False,
                         editable=False,
                         unique=True)
    unit = models.ForeignKey(
        Unit,
        help_text='The academic unit that owns this artifact',
        null=False,
        blank=False)
    config = JSONField(null=False, blank=False,
                       default={})  # addition configuration stuff:

    class Meta:
        ordering = ['name']
        unique_together = [('name', 'unit')]

    def __unicode__(self):
        return unicode(self.name) + ' (' + unicode(
            self.get_category_display()) + ')'
Пример #7
0
class Position(models.Model):
    title = models.CharField(max_length=100)
    projected_start_date = models.DateField('Projected Start Date', default=timezone_today)
    unit = models.ForeignKey(Unit, null=False, blank=False, on_delete=models.PROTECT)
    position_number = models.CharField(max_length=8)
    rank = models.CharField(choices=RANK_CHOICES, max_length=50, null=True, blank=True)
    step = models.DecimalField(max_digits=3, decimal_places=1, null=True, blank=True)
    percentage = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True,
                                     help_text='Percentage of this position in the given unit', default=100)
    base_salary = models.DecimalField(decimal_places=2, max_digits=10, null=True, blank=True)
    add_salary = models.DecimalField(decimal_places=2, max_digits=10,  null=True, blank=True)
    add_pay = models.DecimalField(decimal_places=2, max_digits=10, null=True, blank=True)
    config = JSONField(null=False, blank=False, editable=False, default=dict)  # For future fields
    hidden = models.BooleanField(default=False, editable=False)
    any_person = models.ForeignKey(AnyPerson, on_delete=models.SET_NULL, null=True, blank=True)
    degree1 = models.CharField(max_length=12, default='')
    year1 = models.CharField(max_length=5, default='')
    institution1 = models.CharField(max_length=25, default='')
    location1 = models.CharField(max_length=23, default='')
    degree2 = models.CharField(max_length=12, default='')
    year2 = models.CharField(max_length=5, default='')
    institution2 = models.CharField(max_length=25, default='')
    location2 = models.CharField(max_length=23, default='')
    degree3 = models.CharField(max_length=12, default='')
    year3 = models.CharField(max_length=5, default='')
    institution3 = models.CharField(max_length=25, default='')
    location3 = models.CharField(max_length=23, default='')
    teaching_semester_credits = models.DecimalField(decimal_places=0, max_digits=3, null=True, blank=True)

    objects = PositionManager()

    def __str__(self):
        return "%s - %s" % (self.position_number, self.title)

    def hide(self):
        self.hidden = True

    class Meta:
        ordering = ('projected_start_date', 'title')

    def get_load_display(self):
        """
        Called if you're going to insert this in another AnnualTeachingCreditField,
        like when we populate the onboarding wizard with this value.
        """
        if 'teaching_load' in self.config and not self.config['teaching_load'] == 'None':
            return str(Fraction(self.config['teaching_load']))

        else:
            return 0

    def get_load_display_corrected(self):
        """
        Called if you're purely going to display the value, as when displaying the contents of the position.
        """
        if 'teaching_load' in self.config and not self.config['teaching_load'] == 'None':
            return str(Fraction(self.config['teaching_load'])*3)

        else:
            return 0
Пример #8
0
class FormGroup(models.Model):
    """
    A group that owns forms and form submissions.
    """
    unit = models.ForeignKey(Unit)
    name = models.CharField(max_length=60, null=False, blank=False)
    members = models.ManyToManyField(Person, through='FormGroupMember')  #

    def autoslug(self):
        return make_slug(self.unit.label + ' ' + self.name)

    slug = AutoSlugField(populate_from=autoslug,
                         null=False,
                         editable=False,
                         unique=True)
    config = JSONField(null=False, blank=False,
                       default={})  # addition configuration stuff:

    class Meta:
        unique_together = (("unit", "name"), )

    def __unicode__(self):
        return u"%s, %s" % (self.name, self.unit.label)

    def delete(self, *args, **kwargs):
        raise NotImplementedError, "This object cannot be deleted because it is used as a foreign key."

    def notify_emails(self):
        """
        Collection of emails to notify about something in this group
        """
        return [
            FormFiller.form_full_email(m.person)
            for m in self.formgroupmember_set.all() if m.email()
        ]
Пример #9
0
class HardcodedReport(models.Model):
    """
        Represents a report that exists as a python file in 
        courses/reports/reportlib/reports
    """
    report = models.ForeignKey(Report)
    file_location = models.CharField(help_text="The location of this report, on disk.", 
        max_length=80, choices=all_reports(), null=False)

    config = JSONField(null=False, blank=False, default={})
    created_at = models.DateTimeField(auto_now_add=True)

    def run(self, manual=False):
        """ execute the code in this file """ 
        r = Run(report=self.report, name=self.file_location, manual=manual)
        r.save()
        logger = RunLineLogger(r)
        try:
            report_object = report_map(self.file_location, logger)
            report_object.run()
            for artifact in report_object.artifacts:
                artifact.convert_to_unicode()
                try:
                    result = Result(run=r, name=artifact.title, table=artifact.to_dict() )
                except AttributeError:
                    result = Result(run=r, table=artifact.to_dict() ) 
                result.save()
            r.success = True
            r.save()
        except Exception as e:
            logger.log("ERROR: " + str(e) )
            type_, value_, traceback_ = sys.exc_info()
            logger.log( ",".join(traceback.format_tb( traceback_ )) )
        return r
Пример #10
0
class TempGrant(models.Model):
    label = models.CharField(max_length=150, help_text="for identification from FAST import")
    initial = models.DecimalField(verbose_name="initial balance", max_digits=12, decimal_places=2)
    project_code = models.CharField(max_length=32, help_text="The fund and project code, like '13-123456'")
    import_key = models.CharField(null=True, blank=True, max_length=255, help_text="e.g. 'nserc-43517b4fd422423382baab1e916e7f63'")
    creator = models.ForeignKey(Person, blank=True, null=True, on_delete=models.PROTECT)
    created = models.DateTimeField(auto_now_add=True)
    config = JSONField(default=dict) # addition configuration for within the temp grant

    objects = TempGrantManager()

    class Meta:
        unique_together = (('label', 'creator'),)

    def get_convert_url(self):
        return reverse("faculty:convert_grant", args=[self.id])

    def get_delete_url(self):
        return reverse("faculty:delete_grant", args=[self.id])

    def grant_dict(self, **kwargs):
        data = {
            "label": self.label,
            "title": self.label,
            "project_code": self.project_code,
            "initial": self.initial,
            "import_key": self.import_key,
        }
        data.update(**kwargs)
        return data
Пример #11
0
class SemesterPlan(models.Model):
    """
    A semester plan which holds potential planned offerings.
    """
    semester = models.ForeignKey(Semester)
    name = models.CharField(
        max_length=70,
        help_text="A name to help you remeber which plan this is.")
    visibility = models.CharField(max_length=4,
                                  choices=VISIBILITY_CHOICES,
                                  default="ADMI",
                                  help_text="Who can see this plan?")
    slug = AutoSlugField(populate_from='name',
                         null=False,
                         editable=False,
                         unique_with='semester')
    unit = models.ForeignKey(
        Unit, help_text='The academic unit that owns this course plan')
    config = JSONField(null=False, blank=False, default=dict)

    def get_absolute_url(self):
        return reverse('planning.views.view_plan',
                       kwargs={'semester': self.semester.name})

    def save(self, *args, **kwargs):
        super(SemesterPlan, self).save(*args, **kwargs)

    class Meta:
        ordering = ['semester', 'name']
        unique_together = (('semester', 'name'), )
Пример #12
0
class Query(models.Model):
    """ 
        A custom query developed by the user. 
    """
    report = models.ForeignKey(Report, on_delete=models.CASCADE)
    name = models.CharField(max_length=150, null=False)
    query = models.TextField()

    config = JSONField(null=False, blank=False, default=dict)
    created_at = models.DateTimeField(auto_now_add=True)

    def run(self, manual=False):
        r = Run(report=self.report, name=self.name, manual=manual)
        r.save()
        logger = RunLineLogger(r)
        try:
            DB2_Query.set_logger(logger)
            DB2_Query.connect()
            q = DB2_Query()
            q.query = string.Template(self.query)
            artifact = q.result()
            artifact.convert_to_unicode()
            result = Result(run=r, name=self.name, table=artifact.to_dict())
            result.save()
            r.success = True
            r.save()
        except Exception as e:
            logger.log("ERROR: " + str(e))
            type_, value_, traceback_ = sys.exc_info()
            logger.log(",".join(traceback.format_tb(traceback_)))
        return r
Пример #13
0
class CourseDescription(models.Model):
    """
    Description of the work for a TA contract
    """
    unit = models.ForeignKey(Unit, on_delete=models.PROTECT)
    description = models.CharField(
        max_length=60,
        blank=False,
        null=False,
        help_text=
        "Description of the work for a course, as it will appear on the contract. (e.g. 'Office/marking')"
    )
    labtut = models.BooleanField(
        default=False,
        verbose_name="Lab/Tutorial?",
        help_text="Does this description get the %s BU bonus?" % (LAB_BONUS))
    hidden = models.BooleanField(default=False)
    config = JSONField(null=False, blank=False, default=dict)

    def __str__(self):
        return self.description

    def delete(self):
        """Like most of our objects, we don't want to ever really delete it."""
        self.hidden = True
        self.save()
Пример #14
0
class AccessRule(models.Model):
    """
        This person can see this report. 
    """
    report = models.ForeignKey(Report, on_delete=models.CASCADE)
    person = models.ForeignKey(Person, on_delete=models.PROTECT)
    notify = models.BooleanField(
        null=False,
        default=False,
        help_text="Email this person when a report completes.")

    config = JSONField(null=False, blank=False, default=dict)
    created_at = models.DateTimeField(auto_now_add=True)

    def send_notification(self, run):
        n = NewsItem(
            user=self.person,
            source_app='reports',
            title="Completed Run: " + self.report.name + " : " + run.slug,
            url=reverse('reports:view_run',
                        kwargs={
                            'report': self.report.slug,
                            'run': run.slug
                        }),
            content="You have a scheduled report that has completed! \n" +
            self.report.description)
        n.save()
Пример #15
0
class NonSFUFormFiller(models.Model):
    """
    A person without an SFU account that can fill out forms.
    """
    last_name = models.CharField(max_length=32)
    first_name = models.CharField(max_length=32)
    email_address = models.EmailField(max_length=254)
    config = JSONField(null=False, blank=False,
                       default={})  # addition configuration stuff:

    def __unicode__(self):
        return u"%s, %s" % (self.last_name, self.first_name)

    def name(self):
        return u"%s %s" % (self.first_name, self.last_name)

    def sortname(self):
        return u"%s, %s" % (self.last_name, self.first_name)

    def initials(self):
        return u"%s%s" % (self.first_name[0], self.last_name[0])

    def email(self):
        return self.email_address

    def delete(self, *args, **kwargs):
        raise NotImplementedError(
            "This object cannot be deleted because it is used as a foreign key."
        )
Пример #16
0
class ActivityComponentMark(models.Model):
    """
    Marking of one particular component of an activity for one student  
    Stores the mark the student gets for the component
    """
    activity_mark = models.ForeignKey(ActivityMark, null=False, on_delete=models.PROTECT)
    activity_component = models.ForeignKey(ActivityComponent, null=False, on_delete=models.PROTECT)
    value = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='Mark', null=True, blank=True)
    comment = models.TextField(null = True, max_length=1000, blank=True)
    config = JSONField(null=False, blank=False, default=dict)
        # 'display_raw': Whether the comment should be displayed in a <pre> tag instead of using the
        # linebreaks filter.  Useful for comments with blocks of code.

    defaults = {'display_raw': False}
    display_raw, set_display_raw = getter_setter('display_raw')

    def __str__(self):
        # get the student and the activity
        return "Marking for [%s]" %(self.activity_component,)
    def delete(self, *args, **kwargs):
        raise NotImplementedError("This object cannot be deleted because it is used for marking history")
        
    class Meta:
        unique_together = (('activity_mark', 'activity_component'),)
        ordering = ('activity_component',)
Пример #17
0
class ScheduleRule(models.Model):
    """
    Run this Report at this time. 
    """
    report = models.ForeignKey(Report, on_delete=models.CASCADE)
    schedule_type = models.CharField(max_length=3,
                                     choices=SCHEDULE_TYPE_CHOICES,
                                     null=False,
                                     default="ONE")
    last_run = models.DateTimeField(
        null=True)  # the last time this ScheduleRule was run
    next_run = models.DateTimeField()  # the next time to run this ScheduleRule

    config = JSONField(null=False, blank=False, default=dict)
    created_at = models.DateTimeField(auto_now_add=True)

    def set_next_run(self):
        self.last_run = self.next_run
        if self.schedule_type == 'DAI':
            self.next_run = increment_day(self.next_run)
        if self.schedule_type == 'WEE':
            self.next_run = increment_week(self.next_run)
        if self.schedule_type == 'MON':
            self.next_run = increment_month(self.next_run)
        if self.schedule_type == 'YEA':
            self.next_run = increment_year(self.next_run)
        if self.schedule_type == 'ONE':
            self.next_run = None

        # if this doesn't get the run to past the current date, try again.
        if self.next_run < datetime.datetime.now():
            self.set_next_run()
Пример #18
0
class Grant(models.Model):
    STATUS_CHOICES = (
        ("A", "Active"),
        ("D", "Deleted"),
    )
    title = models.CharField(max_length=64, help_text='Label for the grant within this system')
    slug = AutoSlugField(populate_from='title', unique_with=("unit",), null=False, editable=False)
    label = models.CharField(max_length=150, help_text="for identification from FAST import", db_index=True)
    owners = models.ManyToManyField(Person, through='GrantOwner', blank=False, help_text='Who owns/controls this grant?')
    project_code = models.CharField(max_length=32, db_index=True, help_text="The fund and project code, like '13-123456'")
    start_date = models.DateField(null=False, blank=False)
    expiry_date = models.DateField(null=True, blank=True)
    status = models.CharField(max_length=2, choices=STATUS_CHOICES, default='A')
    initial = models.DecimalField(verbose_name="Initial balance", max_digits=12, decimal_places=2)
    overhead = models.DecimalField(verbose_name="Annual overhead", max_digits=12, decimal_places=2, help_text="Annual overhead returned to Faculty budget")
    import_key = models.CharField(null=True, blank=True, max_length=255, help_text="e.g. 'nserc-43517b4fd422423382baab1e916e7f63'")
    unit = models.ForeignKey(Unit, null=False, blank=False, help_text="Unit who owns the grant", on_delete=models.PROTECT)
    config = JSONField(blank=True, null=True, default=dict)  # addition configuration for within the grant

    objects = GrantManager()

    class Meta:
        unique_together = (('label', 'unit'),)
        ordering = ['title']

    def __str__(self):
        return "%s" % self.title

    def get_absolute_url(self):
        return reverse("faculty:view_grant", kwargs={'unit_slug': self.unit.slug, 'grant_slug': self.slug})

    def update_balance(self, balance, spent_this_month, actual, date=datetime.datetime.today()):
        gb = GrantBalance.objects.create(
            date=date,
            grant=self,
            balance=balance,
            actual=actual,
            month=spent_this_month
        )
        return gb

    def get_owners_display(self, units):
        """
        HTML display of the owners list

        (some logic required since we want to link to faculty profiles if exists && permitted)
        """
        from django.utils.html import conditional_escape as escape
        from django.utils.safestring import mark_safe
        res = []
        for o in self.grantowner_set.all():
            p = o.person
            if Role.objects.filter(unit__in=units, role='FAC', person=p).exists():
                url = reverse('faculty:summary', kwargs={'userid': p.userid_or_emplid()})
                res.append('<a href="%s">%s</a>' %(escape(url), escape(o.person.name())))
            else:
                res.append(escape(o.person.name()))

        return mark_safe(', '.join(res))
Пример #19
0
class AdvisorNote(models.Model):
    """
    An academic advisor's note about a student.
    """
    text = models.TextField(blank=False, null=False, verbose_name="Contents",
                            help_text='Note about a student')
    student = models.ForeignKey(Person, related_name='student', on_delete=models.PROTECT,
                                help_text='The student that the note is about',
                                editable=False, null=True)
    nonstudent = models.ForeignKey(NonStudent, editable=False, null=True, on_delete=models.PROTECT,
                                help_text='The non-student that the note is about')
    advisor = models.ForeignKey(Person, related_name='advisor', on_delete=models.PROTECT,
                                help_text='The advisor that created the note',
                                editable=False)
    created_at = models.DateTimeField(default=datetime.datetime.now)
    file_attachment = models.FileField(storage=UploadedFileStorage, null=True,
                      upload_to=attachment_upload_to, blank=True, max_length=500)
    file_mediatype = models.CharField(null=True, blank=True, max_length=200, editable=False)
    unit = models.ForeignKey(Unit, help_text='The academic unit that owns this note', on_delete=models.PROTECT)
    # Set this flag if the note is no longer to be accessible.
    hidden = models.BooleanField(null=False, db_index=True, default=False)
    emailed = models.BooleanField(null=False, default=False)
    config = JSONField(null=False, blank=False, default=dict)  # addition configuration stuff:

    # 'markup': markup language used in reminder content: see courselib/markup.py
    # 'math': page uses MathJax? (boolean)
    markup = config_property('markup', 'plain')
    math = config_property('math', False)

    def __str__(self):
        return str(self.student) + "@" + str(self.created_at)

    def delete(self, *args, **kwargs):
        raise NotImplementedError("This object cannot be deleted, set the hidden flag instead.")

    class Meta:
        ordering = ['student', 'created_at']

    def save(self, *args, **kwargs):
        # make sure one of student and nonstudent is there
        if not self.student and not self.nonstudent:
            raise ValueError("AdvisorNote must have either student or non-student specified.")
        super(AdvisorNote, self).save(*args, **kwargs)

    def attachment_filename(self):
        """
        Return the filename only (no path) for the attachment.
        """
        _, filename = os.path.split(self.file_attachment.name)
        return filename
    
    def unique_tuple(self):
        return ( make_slug(self.text[0:100]), self.created_at.isoformat() )
    
    def __hash__(self):
        return self.unique_tuple().__hash__()

    def html_content(self):
        return markup_to_html(self.text, self.markup, restricted=False)
Пример #20
0
class OutreachEvent(models.Model):
    """
    An outreach event.  These are different than our other events, as they are to be attended by non-users of the
    system.
    """
    title = models.CharField(max_length=60, null=False, blank=False)
    start_date = models.DateTimeField('Start Date and Time', default=timezone_today,
                                      help_text='Event start date and time.  Use 24h format for the time if needed.')
    end_date = models.DateTimeField('End Date and Time', blank=False, null=False,
                                    help_text='Event end date and time')
    location = models.CharField(max_length=400, blank=True, null=True)
    description = models.CharField(max_length=800, blank=True, null=True)
    score = models.DecimalField(max_digits=2, decimal_places=0, max_length=2, null=True, blank=True,
                                help_text='The score according to the event score matrix')
    unit = models.ForeignKey(Unit, blank=False, null=False)
    resources = models.CharField(max_length=400, blank=True, null=True, help_text="Resources needed for this event.")
    cost = models.DecimalField(blank=True, null=True, max_digits=8, decimal_places=2, help_text="Cost of this event")
    hidden = models.BooleanField(default=False, null=False, blank=False, editable=False)
    notes = models.CharField(max_length=400, blank=True, null=True, help_text='Special notes to registrants.  These '
                                                                              '*will* be displayed on the registration '
                                                                              'forms.')
    email = models.EmailField('Contact e-mail', null=True, blank=True,
                              help_text='Contact email.  Address that will be given to registrants on the registration '
                                        'success page in case they have any questions/problems.')
    closed = models.BooleanField('Close Registration', default=False,
                                 help_text='If this box is checked, people will not be able to register for this '
                                           'event even if it is still current.')
    config = JSONField(null=False, blank=False, default=dict)
    # 'extra_questions': additional questions to ask registrants

    extra_questions = config_property('extra_questions', [])

    objects = EventQuerySet.as_manager()

    def autoslug(self):
        return make_slug(self.unit.slug + '-' + self.title + '-' + str(self.start_date.date()))

    slug = AutoSlugField(populate_from='autoslug', null=False, editable=False, unique=True)

    def __unicode__(self):
        return u"%s - %s - %s" % (self.title, self.unit.label, self.start_date)

    def delete(self):
        """Like most of our objects, we don't want to ever really delete it."""
        self.hidden = True
        self.save()

    def current(self):
        """
        Find out if an event is still current.  Otherwise, we shouldn't be able to register for it.
        """
        return self.start_date > timezone_today() or self.end_date >= timezone_today()

    # TODO add copy method to copy from one event to another

    def registration_count(self):
        return OutreachEventRegistration.objects.attended_event(self).count()
Пример #21
0
class NonStudent(models.Model):
    """
    For a person (prospective student) who isn't part of the university
    """
    last_name = models.CharField(max_length=32)
    first_name = models.CharField(max_length=32)
    middle_name = models.CharField(max_length=32, null=True, blank=True)
    pref_first_name = models.CharField(max_length=32, null=True, blank=True)
    email_address = models.EmailField(
        null=True,
        blank=True,
        help_text="Needed only if you want to copy the student on notes")
    high_school = models.CharField(max_length=32, null=True, blank=True)
    college = models.CharField(max_length=32, null=True, blank=True)
    start_year = models.IntegerField(
        null=True, blank=True, help_text="The predicted/potential start year")
    notes = models.TextField(
        help_text="Any general information for the student", blank=True)
    unit = models.ForeignKey(
        Unit,
        help_text='The potential academic unit for the student',
        null=True,
        blank=True,
        on_delete=models.PROTECT)

    def autoslug(self):
        return make_slug(self.first_name + ' ' + self.last_name)

    slug = AutoSlugField(populate_from='autoslug',
                         null=False,
                         editable=False,
                         unique=True)

    config = JSONField(null=False, blank=False,
                       default=dict)  # addition configuration stuff:

    def __str__(self):
        return "%s, %s" % (self.last_name, self.first_name)

    def name(self):
        return "%s %s" % (self.first_name, self.last_name)

    def sortname(self):
        return "%s, %s" % (self.last_name, self.first_name)

    def search_label_value(self):
        return "%s (Prospective %s)" % (self.name(), self.start_year)

    def unique_tuple(self):
        return (self.first_name, self.middle_name, self.last_name,
                self.pref_first_name, self.high_school)

    def __hash__(self):
        return self.unique_tuple().__hash__()

    def email(self):
        return self.email_address
Пример #22
0
class QuestionAnswer(models.Model):
    class AnswerStatusManager(models.Manager):
        def get_queryset(self):
            return super().get_queryset().select_related('question', 'question_version').filter(question__status='V')

    question = models.ForeignKey(Question, on_delete=models.PROTECT)
    question_version = models.ForeignKey(QuestionVersion, on_delete=models.PROTECT)
    # Technically .question is redundant with .question_version.question, but keeping it for convenience
    # and the unique_together.
    student = models.ForeignKey(Member, on_delete=models.PROTECT)
    modified_at = models.DateTimeField(default=datetime.datetime.now, null=False, blank=False)
    # format of .answer determined by the corresponding QuestionHelper
    answer = JSONField(null=False, blank=False, default=dict)
    # .file used for file upload question types; null otherwise
    file = models.FileField(blank=True, null=True, storage=UploadedFileStorage, upload_to=file_upload_to,
                            max_length=500)

    class Meta:
        unique_together = [['question_version', 'student']]

    objects = AnswerStatusManager()

    def save(self, *args, **kwargs):
        assert self.question_id == self.question_version.question_id  # ensure denormalized field stays consistent

        saving_file = False
        if '_file' in self.answer:
            if self.answer['_file'] is None:
                # keep current .file
                pass
            elif self.answer['_file'] is False:
                # user requested "clear"
                self.file = None
            else:
                # actually a file
                self.file = self.answer['_file']
                saving_file = True

            del self.answer['_file']

        if saving_file:
            # Inject the true save path into the .answer. Requires a double .save()
            super().save(*args, **kwargs)
            fn = self.file.name
            self.answer['filepath'] = fn

        return super().save(*args, **kwargs)

    def get_absolute_url(self):
        return resolve_url('offering:quiz:view_submission', course_slug=self.question.quiz.activity.offering.slug,
                           activity_slug=self.question.quiz.activity.slug,
                           userid=self.student.person.userid_or_emplid()) + '#' + self.question.ident()

    def answer_html(self) -> SafeText:
        helper = self.question_version.helper()
        return helper.to_html(self)
Пример #23
0
class EventConfig(models.Model):
    """
    A unit's configuration for a particular event type
    """
    unit = models.ForeignKey(Unit, null=False, blank=False, on_delete=models.PROTECT)
    event_type = models.CharField(max_length=10, null=False, choices=EVENT_TYPE_CHOICES)
    config = JSONField(default=dict)

    class Meta:
        unique_together = ('unit', 'event_type')
Пример #24
0
class SemesterConfig(models.Model):
    """
    A table for department-specific semester config.
    """
    unit = models.ForeignKey(Unit,
                             null=False,
                             blank=False,
                             on_delete=models.PROTECT)
    semester = models.ForeignKey(Semester,
                                 null=False,
                                 blank=False,
                                 on_delete=models.PROTECT)
    config = JSONField(null=False, blank=False,
                       default=dict)  # addition configuration stuff
    defaults = {'start_date': None, 'end_date': None}

    # 'start_date': default first day of contracts that semester, 'YYYY-MM-DD'
    # 'end_date': default last day of contracts that semester, 'YYYY-MM-DD'

    class Meta:
        unique_together = (('unit', 'semester'), )

    @classmethod
    def get_config(cls, units, semester):
        """
        Either get existing SemesterConfig or return a new one.
        """
        configs = SemesterConfig.objects.filter(
            unit__in=units, semester=semester).select_related('semester')
        if configs:
            return configs[0]
        else:
            return SemesterConfig(unit=list(units)[0], semester=semester)

    def start_date(self):
        if 'start_date' in self.config:
            return datetime.datetime.strptime(self.config['start_date'],
                                              '%Y-%m-%d').date()
        else:
            return self.semester.start

    def end_date(self):
        if 'end_date' in self.config:
            return datetime.datetime.strptime(self.config['end_date'],
                                              '%Y-%m-%d').date()
        else:
            return self.semester.end

    def set_start_date(self, date):
        self.config['start_date'] = date.strftime('%Y-%m-%d')

    def set_end_date(self, date):
        self.config['end_date'] = date.strftime('%Y-%m-%d')
Пример #25
0
class UserConfig(models.Model):
    """
    Simple class to hold user preferences.
    """
    user = models.ForeignKey(Person, null=False, on_delete=models.PROTECT)
    key = models.CharField(max_length=20, db_index=True, null=False)
    value = JSONField(null=False, blank=False, default=dict)
    class Meta:
        unique_together = (("user", "key"),)

    def __str__(self):
        return "%s: %s='%s'" % (self.user.userid, self.key, self.value)
Пример #26
0
class AlertEmailTemplate(models.Model):
    """
    An automatic e-mail to send. 
    """
    alerttype = models.ForeignKey(AlertType, null=False)
    threshold = models.IntegerField(default=0, null=False, help_text="This email will only be sent to students who have less than this many emails on file already")
    subject = models.CharField(max_length=50, null=False) 
    content = models.TextField(help_text="I.e. 'This is to confirm {{title}} {{last_name}} ... '")
    created_at = models.DateTimeField(auto_now_add=True)
    created_by = models.CharField(max_length=32, null=False, help_text='Email template created by.')
    hidden = models.BooleanField(default=False)
    config = JSONField(null=False, blank=False, default={})
Пример #27
0
class GrantBalance(models.Model):
    date = models.DateField(default=datetime.date.today)
    grant = models.ForeignKey(Grant, null=False, blank=False, on_delete=models.PROTECT)
    balance = models.DecimalField(verbose_name="grant balance", max_digits=12, decimal_places=2)
    actual = models.DecimalField(verbose_name="YTD actual", max_digits=12, decimal_places=2)
    month = models.DecimalField(verbose_name="current month", max_digits=12, decimal_places=2)
    config = JSONField(blank=True, null=True, default=dict)  # addition configuration within the memo

    def __str__(self):
        return "%s balance as of %s" % (self.grant, self.date)

    class Meta:
        ordering = ['date']
Пример #28
0
class Event(models.Model):
    contact = models.ForeignKey(Contact,
                                null=False,
                                blank=False,
                                on_delete=models.PROTECT)
    event_type = models.CharField(max_length=25, choices=EVENT_CHOICES)
    timestamp = models.DateTimeField(default=datetime.datetime.now,
                                     editable=False)
    last_modified = models.DateTimeField(null=True, blank=True, editable=False)
    last_modified_by = models.ForeignKey(Person,
                                         null=True,
                                         blank=True,
                                         on_delete=models.PROTECT)
    deleted = models.BooleanField(default=False)
    config = JSONField(default=dict)
    slug = AutoSlugField(populate_from='slug_string',
                         unique_with=('contact', ),
                         slugify=make_slug,
                         null=False,
                         editable=False)

    objects = IgnoreEventDeleted()

    @property
    def slug_string(self):
        return '%s-%s' % (self.timestamp.year, self.event_type)

    def save(self, call_from_handler=False, editor=None, *args, **kwargs):
        assert call_from_handler, "A contact event must be saved through the handler."
        self.last_modified = datetime.datetime.now()
        self.last_modified_by = editor
        return super(Event, self).save(*args, **kwargs)

    def get_handler(self):
        # Create and return a handler for ourselves.  If we already created it, use the same one again.
        if not hasattr(self, 'handler_cache'):
            self.handler_cache = EVENT_TYPES.get(self.event_type, None)(self)
        return self.handler_cache

    def get_handler_name(self):
        return self.get_handler().name

    def get_config_value(self, field):
        if field in self.config:
            return self.config.get(field)
        else:
            return None

    def is_text_based(self):
        return self.get_handler().text_content
Пример #29
0
class ActivityComponent(models.Model):
    """    
    Markable Component of a numeric activity   
    """
    numeric_activity = models.ForeignKey(NumericActivity,
                                         null=False,
                                         on_delete=models.PROTECT)
    max_mark = models.DecimalField(max_digits=8, decimal_places=2, null=False)
    title = models.CharField(max_length=30, null=False)
    description = models.TextField(max_length=COMMENT_LENGTH,
                                   null=True,
                                   blank=True)
    position = models.IntegerField(null=True, default=0, blank=True)
    # set this flag if it is deleted by the user
    deleted = models.BooleanField(null=False, db_index=True, default=False)

    def autoslug(self):
        return make_slug(self.title)

    slug = AutoSlugField(populate_from='autoslug',
                         null=False,
                         editable=False,
                         unique_with='numeric_activity')
    config = JSONField(null=False, blank=False, default=dict)

    # .config['quiz-question-id']: a Question.id if this ActivityComponent was generated by the quizzes module

    def __str__(self):
        return self.title

    def delete(self, *args, **kwargs):
        raise NotImplementedError(
            "This object cannot be deleted because it is used as a foreign key."
        )

    class Meta:
        verbose_name_plural = "Activity Marking Components"
        ordering = ['numeric_activity', 'deleted', 'position']

    def save(self, *args, **kwargs):
        self.slug = None  # regerate slug so import format stays in sync
        if self.position == 0:
            others = ActivityComponent.objects.filter(
                numeric_activity=self.numeric_activity).exclude(pk=self.pk)
            maxpos = others.aggregate(models.Max('position'))['position__max']
            if maxpos:
                self.position = maxpos + 1
            else:
                self.position = 1
        super(ActivityComponent, self).save(*args, **kwargs)
Пример #30
0
class PagePermission(models.Model):
    """
    An additional person who has permission to view pages for this offering
    """
    offering = models.ForeignKey(CourseOffering)
    person = models.ForeignKey(Person)
    role = models.CharField(max_length=4, choices=PERMISSION_ACL_CHOICES, default="STUD",
        help_text="What level of access should this person have for the course?")
    config = JSONField(null=False, blank=False, default={}) # addition configuration stuff:

    defaults = {}

    class Meta:
        unique_together = (('offering', 'person'), )