Exemple #1
0
class CommentLabelHistory(models.Model):
    grader              = ForeignKey(User)       
    ctime               = DateTimeField(default=datetime.now())
    grade               = IntegerField()
    category            = ForeignKey(LabelCategory) #so we can grade different dimensions of a post. 
    comment             = ForeignKey(Comment)
Exemple #2
0
class CourseOverview(TimeStampedModel):
    """
    Model for storing and caching basic information about a course.

    This model contains basic course metadata such as an ID, display name,
    image URL, and any other information that would be necessary to display
    a course as part of:
        user dashboard (enrolled courses)
        course catalog (courses to enroll in)
        course about (meta data about the course)

    .. no_pii:
    """
    class Meta(object):
        app_label = 'course_overviews'

    # IMPORTANT: Bump this whenever you modify this model and/or add a migration.
    VERSION = 12  # this one goes to thirteen

    # Cache entry versioning.
    version = IntegerField()

    # Course identification
    id = CourseKeyField(db_index=True, primary_key=True, max_length=255)
    _location = UsageKeyField(max_length=255)
    org = TextField(max_length=255, default=u'outdated_entry')
    display_name = TextField(null=True)
    display_number_with_default = TextField()
    display_org_with_default = TextField()

    # Start/end dates
    # TODO Remove 'start' & 'end' in removing field in column renaming, DE-1822
    start = DateTimeField(null=True)
    end = DateTimeField(null=True)
    start_date = DateTimeField(null=True)
    end_date = DateTimeField(null=True)
    advertised_start = TextField(null=True)
    announcement = DateTimeField(null=True)

    # URLs
    # Not allowing null per django convention; not sure why many TextFields in this model do allow null
    banner_image_url = TextField()
    course_image_url = TextField()
    social_sharing_url = TextField(null=True)
    end_of_course_survey_url = TextField(null=True)

    # Certification data
    certificates_display_behavior = TextField(null=True)
    certificates_show_before_end = BooleanField(default=False)
    cert_html_view_enabled = BooleanField(default=False)
    has_any_active_web_certificate = BooleanField(default=False)
    cert_name_short = TextField()
    cert_name_long = TextField()
    certificate_available_date = DateTimeField(default=None, null=True)

    # Grading
    lowest_passing_grade = DecimalField(max_digits=5,
                                        decimal_places=2,
                                        null=True)

    # Access parameters
    days_early_for_beta = FloatField(null=True)
    mobile_available = BooleanField(default=False)
    visible_to_staff_only = BooleanField(default=False)
    _pre_requisite_courses_json = TextField(
    )  # JSON representation of list of CourseKey strings

    # Enrollment details
    enrollment_start = DateTimeField(null=True)
    enrollment_end = DateTimeField(null=True)
    enrollment_domain = TextField(null=True)
    invitation_only = BooleanField(default=False)
    max_student_enrollments_allowed = IntegerField(null=True)

    # Catalog information
    catalog_visibility = TextField(null=True)
    short_description = TextField(null=True)
    course_video_url = TextField(null=True)
    effort = TextField(null=True)
    self_paced = BooleanField(default=False)
    marketing_url = TextField(null=True)
    eligible_for_financial_aid = BooleanField(default=True)

    language = TextField(null=True)

    history = HistoricalRecords()

    @classmethod
    def _create_or_update(cls, course):  # lint-amnesty, pylint: disable=too-many-statements
        """
        Creates or updates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: created or updated overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from openedx.core.lib.courses import course_image_url

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        display_name = course.display_name
        start = course.start
        end = course.end
        max_student_enrollments_allowed = course.max_student_enrollments_allowed
        if isinstance(course.id, CCXLocator):
            from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator
            ccx = get_ccx_from_ccx_locator(course.id)
            display_name = ccx.display_name
            start = ccx.start
            end = ccx.due
            max_student_enrollments_allowed = ccx.max_student_enrollments_allowed

        course_overview = cls.objects.filter(id=course.id)
        if course_overview.exists():
            log.info(u'Updating course overview for %s.',
                     six.text_type(course.id))
            course_overview = course_overview.first()
            # MySQL ignores casing, but CourseKey doesn't. To prevent multiple
            # courses with different cased keys from overriding each other, we'll
            # check for equality here in python.
            if course_overview.id != course.id:
                raise CourseOverviewCaseMismatchException(
                    course_overview.id, course.id)
        else:
            log.info(u'Creating course overview for %s.',
                     six.text_type(course.id))
            course_overview = cls()

        course_overview.version = cls.VERSION
        course_overview.id = course.id
        course_overview._location = course.location  # lint-amnesty, pylint: disable=protected-access
        course_overview.org = course.location.org
        course_overview.display_name = display_name
        course_overview.display_number_with_default = course.display_number_with_default
        course_overview.display_org_with_default = course.display_org_with_default

        course_overview.start = start
        # Add writes to new fields 'start_date' & 'end_date'.
        course_overview.start_date = start
        course_overview.end = end
        course_overview.end_date = end
        course_overview.advertised_start = course.advertised_start
        course_overview.announcement = course.announcement

        course_overview.banner_image_url = course_image_url(
            course, 'banner_image')
        course_overview.course_image_url = course_image_url(course)
        course_overview.social_sharing_url = course.social_sharing_url

        course_overview.certificates_display_behavior = course.certificates_display_behavior
        course_overview.certificates_show_before_end = course.certificates_show_before_end
        course_overview.cert_html_view_enabled = course.cert_html_view_enabled
        course_overview.has_any_active_web_certificate = (
            get_active_web_certificate(course) is not None)
        course_overview.cert_name_short = course.cert_name_short
        course_overview.cert_name_long = course.cert_name_long
        course_overview.certificate_available_date = course.certificate_available_date
        course_overview.lowest_passing_grade = lowest_passing_grade
        course_overview.end_of_course_survey_url = course.end_of_course_survey_url

        course_overview.days_early_for_beta = course.days_early_for_beta
        course_overview.mobile_available = course.mobile_available
        course_overview.visible_to_staff_only = course.visible_to_staff_only
        course_overview._pre_requisite_courses_json = json.dumps(
            course.pre_requisite_courses)  # lint-amnesty, pylint: disable=protected-access

        course_overview.enrollment_start = course.enrollment_start
        course_overview.enrollment_end = course.enrollment_end
        course_overview.enrollment_domain = course.enrollment_domain
        course_overview.invitation_only = course.invitation_only
        course_overview.max_student_enrollments_allowed = max_student_enrollments_allowed

        course_overview.catalog_visibility = course.catalog_visibility
        course_overview.short_description = CourseDetails.fetch_about_attribute(
            course.id, 'short_description')
        course_overview.effort = CourseDetails.fetch_about_attribute(
            course.id, 'effort')
        course_overview.course_video_url = CourseDetails.fetch_video_url(
            course.id)
        course_overview.self_paced = course.self_paced

        if not CatalogIntegration.is_enabled():
            course_overview.language = course.language

        return course_overview

    @classmethod
    def load_from_module_store(cls, course_id):
        """
        Load a CourseDescriptor, create or update a CourseOverview from it, cache the
        overview, and return it.

        Arguments:
            course_id (CourseKey): the ID of the course overview to be loaded.

        Returns:
            CourseOverview: overview of the requested course.

        Raises:
            - CourseOverview.DoesNotExist if the course specified by course_id
                was not found.
            - IOError if some other error occurs while trying to load the
                course from the module store.
        """
        log.info(
            "Attempting to load CourseOverview for course %s from modulestore.",
            course_id,
        )
        store = modulestore()
        with store.bulk_operations(course_id):
            course = store.get_course(course_id)
            if isinstance(course, CourseDescriptor):
                try:
                    course_overview = cls._create_or_update(course)
                    with transaction.atomic():
                        course_overview.save()
                        # Remove and recreate all the course tabs
                        CourseOverviewTab.objects.filter(
                            course_overview=course_overview).delete()
                        CourseOverviewTab.objects.bulk_create([
                            CourseOverviewTab(
                                tab_id=tab.tab_id,
                                type=tab.type,
                                name=tab.name,
                                course_staff_only=tab.course_staff_only,
                                url_slug=tab.get('url_slug'),
                                link=tab.get('link'),
                                is_hidden=tab.get('is_hidden', False),
                                course_overview=course_overview)
                            for tab in course.tabs
                        ])
                        # Remove and recreate course images
                        CourseOverviewImageSet.objects.filter(
                            course_overview=course_overview).delete()
                        CourseOverviewImageSet.create(course_overview, course)

                except IntegrityError:
                    # There is a rare race condition that will occur if
                    # CourseOverview.get_from_id is called while a
                    # another identical overview is already in the process
                    # of being created.
                    # One of the overviews will be saved normally, while the
                    # other one will cause an IntegrityError because it tries
                    # to save a duplicate.
                    # (see: https://openedx.atlassian.net/browse/TNL-2854).
                    log.info(
                        "Multiple CourseOverviews for course %s requested "
                        "simultaneously; will only save one.",
                        course_id,
                    )
                except Exception:
                    log.exception(
                        "Saving CourseOverview for course %s failed with "
                        "unexpected exception!",
                        course_id,
                    )
                    raise

                return course_overview
            elif course is not None:
                raise IOError(  # lint-amnesty, pylint: disable=raising-format-tuple
                    "Error while loading CourseOverview for course {} "
                    "from the module store: {}",
                    six.text_type(course_id), course.error_msg if isinstance(
                        course, ErrorBlock) else six.text_type(course))
            else:
                log.info(
                    "Could not create CourseOverview for non-existent course: %s",
                    course_id,
                )
                raise cls.DoesNotExist()

    @classmethod
    def course_exists(cls, course_id):
        """
        Check whether a course run exists (in CourseOverviews _or_ modulestore).

        Checks the CourseOverview table first.
        If it is not there, check the modulestore.
        Equivalent to, but more efficient than:
            bool(CourseOverview.get_from_id(course_id))

        Arguments:
            course_id (CourseKey)

        Returns: bool
        """
        if cls.objects.filter(id=course_id).exists():
            return True
        return modulestore().has_course(course_id)

    @classmethod
    @request_cached('course_overview')
    def get_from_id(cls, course_id):
        """
        Load a CourseOverview object for a given course ID.

        First, we try to load the CourseOverview from the database. If it
        doesn't exist, we load the entire course from the modulestore, create a
        CourseOverview object from it, and then cache it in the database for
        future use.

        Arguments:
            course_id (CourseKey): the ID of the course overview to be loaded.

        Returns:
            CourseOverview: overview of the requested course.

        Raises:
            - CourseOverview.DoesNotExist if the course specified by course_id
                was not found.
            - IOError if some other error occurs while trying to load the
                course from the module store.
        """
        try:
            course_overview = cls.objects.select_related('image_set').get(
                id=course_id)
            if course_overview.version < cls.VERSION:
                # Reload the overview from the modulestore to update the version
                course_overview = cls.load_from_module_store(course_id)
        except cls.DoesNotExist:
            course_overview = None

        # Regenerate the thumbnail images if they're missing (either because
        # they were never generated, or because they were flushed out after
        # a change to CourseOverviewImageConfig.
        if course_overview:
            if hasattr(course_overview, 'image_set'):
                image_set = course_overview.image_set
                if not image_set.small_url or not image_set.large_url:
                    CourseOverviewImageSet.objects.filter(
                        course_overview=course_overview).delete()
                    CourseOverviewImageSet.create(course_overview)
            else:
                CourseOverviewImageSet.create(course_overview)

        return course_overview or cls.load_from_module_store(course_id)

    @classmethod
    def get_from_ids(cls, course_ids):
        """
        Return a dict mapping course_ids to CourseOverviews.

        Tries to select all CourseOverviews in one query,
        then fetches remaining (uncached) overviews from the modulestore.

        Course IDs for non-existant courses will map to None.

        Arguments:
            course_ids (iterable[CourseKey])

        Returns: dict[CourseKey, CourseOverview|None]
        """
        overviews = {
            overview.id: overview
            for overview in cls.objects.select_related('image_set').filter(
                id__in=course_ids, version__gte=cls.VERSION)
        }
        for course_id in course_ids:
            if course_id not in overviews:
                try:
                    overviews[course_id] = cls.load_from_module_store(
                        course_id)
                except CourseOverview.DoesNotExist:
                    overviews[course_id] = None
        return overviews

    def clean_id(self, padding_char='='):
        """
        Returns a unique deterministic base32-encoded ID for the course.

        Arguments:
            padding_char (str): Character used for padding at end of base-32
                                -encoded string, defaulting to '='
        """
        return course_metadata_utils.clean_course_key(self.location.course_key,
                                                      padding_char)

    @property
    def location(self):
        """
        Returns the UsageKey of this course.

        UsageKeyField has a strange behavior where it fails to parse the "run"
        of a course out of the serialized form of a Mongo Draft UsageKey. This
        method is a wrapper around _location attribute that fixes the problem
        by calling map_into_course, which restores the run attribute.
        """
        if self._location.run is None:
            self._location = self._location.map_into_course(self.id)
        return self._location

    @property
    def number(self):
        """
        Returns this course's number.

        This is a "number" in the sense of the "course numbers" that you see at
        lots of universities. For example, given a course
        "Intro to Computer Science" with the course key "edX/CS-101/2014", the
        course number would be "CS-101"
        """
        return course_metadata_utils.number_for_course_location(self.location)

    @property
    def url_name(self):
        """
        Returns this course's URL name.
        """
        return block_metadata_utils.url_name_for_block(self)

    @property
    def display_name_with_default(self):
        """
        Return reasonable display name for the course.
        """
        return block_metadata_utils.display_name_with_default(self)

    @property
    def display_name_with_default_escaped(self):
        """
        DEPRECATED: use display_name_with_default

        Return html escaped reasonable display name for the course.

        Note: This newly introduced method should not be used.  It was only
        introduced to enable a quick search/replace and the ability to slowly
        migrate and test switching to display_name_with_default, which is no
        longer escaped.
        """
        # pylint: disable=line-too-long
        return block_metadata_utils.display_name_with_default_escaped(
            self)  # xss-lint: disable=python-deprecated-display-name

    @property
    def dashboard_start_display(self):
        """
         Return start date to diplay on learner's dashboard, preferably `Course Advertised Start`
        """
        return self.advertised_start or self.start

    def has_started(self):
        """
        Returns whether the the course has started.
        """
        return course_metadata_utils.has_course_started(self.start)

    def has_ended(self):
        """
        Returns whether the course has ended.
        """
        return course_metadata_utils.has_course_ended(self.end)

    def has_marketing_url(self):
        """
        Returns whether the course has marketing url.
        """
        return settings.FEATURES.get('ENABLE_MKTG_SITE') and bool(
            self.marketing_url)

    def has_social_sharing_url(self):
        """
        Returns whether the course has social sharing url.
        """
        is_social_sharing_enabled = getattr(settings,
                                            'SOCIAL_SHARING_SETTINGS',
                                            {}).get('CUSTOM_COURSE_URLS')
        return is_social_sharing_enabled and bool(self.social_sharing_url)

    def starts_within(self, days):
        """
        Returns True if the course starts with-in given number of days otherwise returns False.
        """
        return course_metadata_utils.course_starts_within(self.start, days)

    @property
    def start_date_is_still_default(self):
        """
        Checks if the start date set for the course is still default, i.e.
        .start has not been modified, and .advertised_start has not been set.
        """
        return course_metadata_utils.course_start_date_is_default(
            self.start,
            self.advertised_start,
        )

    @property
    def sorting_score(self):
        """
        Returns a tuple that can be used to sort the courses according
        the how "new" they are. The "newness" score is computed using a
        heuristic that takes into account the announcement and
        (advertised) start dates of the course if available.

        The lower the number the "newer" the course.
        """
        return course_metadata_utils.sorting_score(self.start,
                                                   self.advertised_start,
                                                   self.announcement)

    @property
    def start_type(self):
        """
        Returns the type of the course's 'start' field.
        """
        if self.advertised_start:
            return u'string'
        elif self.start != DEFAULT_START_DATE:
            return u'timestamp'
        else:
            return u'empty'

    @property
    def start_display(self):
        """
        Returns the display value for the course's start date.
        """
        if self.advertised_start:
            return self.advertised_start
        elif self.start != DEFAULT_START_DATE:
            return defaultfilters.date(self.start, "DATE_FORMAT")
        else:
            return None

    def may_certify(self):
        """
        Returns whether it is acceptable to show the student a certificate
        download link.
        """
        return course_metadata_utils.may_certify_for_course(
            self.certificates_display_behavior,
            self.certificates_show_before_end, self.has_ended(),
            self.certificate_available_date, self.self_paced)

    @property
    def pre_requisite_courses(self):
        """
        Returns a list of ID strings for this course's prerequisite courses.
        """
        return json.loads(self._pre_requisite_courses_json)

    @pre_requisite_courses.setter
    def pre_requisite_courses(self, value):
        """
        Django requires there be a setter for this, but it is not
        necessary for the way we currently use it. Due to the way
        CourseOverviews are constructed raising errors here will
        cause a lot of issues. These should not be mutable after
        construction, so for now we just eat this.
        """
        pass  # lint-amnesty, pylint: disable=unnecessary-pass

    @classmethod
    def update_select_courses(cls, course_keys, force_update=False):
        """
        A side-effecting method that updates CourseOverview objects for
        the given course_keys.

        Arguments:
            course_keys (list[CourseKey]): Identifies for which courses to
                return CourseOverview objects.
            force_update (boolean): Optional parameter that indicates
                whether the requested CourseOverview objects should be
                forcefully updated (i.e., re-synched with the modulestore).
        """
        log.info(u'Generating course overview for %d courses.',
                 len(course_keys))
        log.debug(
            u'Generating course overview(s) for the following courses: %s',
            course_keys)

        action = CourseOverview.load_from_module_store if force_update else CourseOverview.get_from_id

        for course_key in course_keys:
            try:
                action(course_key)
            except Exception as ex:  # pylint: disable=broad-except
                log.exception(
                    u'An error occurred while generating course overview for %s: %s',
                    six.text_type(course_key),
                    text_type(ex),
                )

        log.info('Finished generating course overviews.')

    @classmethod
    def get_all_courses(cls, orgs=None, filter_=None):
        """
        Return a queryset containing all CourseOverview objects in the database.

        Arguments:
            orgs (list[string]): Optional parameter that allows case-insensitive
                filtering by organization.
            filter_ (dict): Optional parameter that allows custom filtering.
        """
        # Note: If a newly created course is not returned in this QueryList,
        # make sure the "publish" signal was emitted when the course was
        # created. For tests using CourseFactory, use emit_signals=True.
        course_overviews = CourseOverview.objects.all()

        if orgs:
            # In rare cases, courses belonging to the same org may be accidentally assigned
            # an org code with a different casing (e.g., Harvardx as opposed to HarvardX).
            # Case-insensitive matching allows us to deal with this kind of dirty data.
            org_filter = Q(
            )  # Avoiding the `reduce()` for more readability, so a no-op filter starter is needed.
            for org in orgs:
                org_filter |= Q(org__iexact=org)
            course_overviews = course_overviews.filter(org_filter)

        if filter_:
            course_overviews = course_overviews.filter(**filter_)

        return course_overviews

    @classmethod
    def get_all_course_keys(cls):
        """
        Returns all course keys from course overviews.
        """
        return CourseOverview.objects.values_list('id', flat=True)

    def is_discussion_tab_enabled(self):
        """
        Returns True if course has discussion tab and is enabled
        """
        tabs = self.tab_set.all()
        # creates circular import; hence explicitly referenced is_discussion_enabled
        for tab in tabs:
            if tab.tab_id == "discussion" and django_comment_client.utils.is_discussion_enabled(
                    self.id):
                return True
        return False

    @property
    def tabs(self):
        """
        Returns an iterator of CourseTabs.
        """
        for tab_dict in self.tab_set.all().values():
            tab = CourseTab.from_json(tab_dict)
            if tab is None:
                log.warning("Can't instantiate CourseTab from %r", tab_dict)
            else:
                yield tab

    @property
    def image_urls(self):
        """
        Return a dict with all known URLs for this course image.

        Current resolutions are:
          raw = original upload from the user
          small = thumbnail with dimensions CourseOverviewImageConfig.current().small
          large = thumbnail with dimensions CourseOverviewImageConfig.current().large

        If no thumbnails exist, the raw (originally uploaded) image will be
        returned for all resolutions.
        """
        # This is either the raw image that the course team uploaded, or the
        # settings.DEFAULT_COURSE_ABOUT_IMAGE_URL if they didn't specify one.
        raw_image_url = self.course_image_url

        # Default all sizes to return the raw image if there is no
        # CourseOverviewImageSet associated with this CourseOverview. This can
        # happen because we're disabled via CourseOverviewImageConfig.
        urls = {
            'raw': raw_image_url,
            'small': raw_image_url,
            'large': raw_image_url,
        }

        # If we do have a CourseOverviewImageSet, we still default to the raw
        # images if our thumbnails are blank (might indicate that there was a
        # processing error of some sort while trying to generate thumbnails).
        if hasattr(
                self,
                'image_set') and CourseOverviewImageConfig.current().enabled:
            urls['small'] = self.image_set.small_url or raw_image_url
            urls['large'] = self.image_set.large_url or raw_image_url

        return self.apply_cdn_to_urls(urls)

    @property
    def pacing(self):
        """ Returns the pacing for the course.

        Potential values:
            self: Self-paced courses
            instructor: Instructor-led courses
        """
        return 'self' if self.self_paced else 'instructor'

    @property
    def closest_released_language(self):
        """
        Returns the language code that most closely matches this course' language and is fully
        supported by the LMS, or None if there are no fully supported languages that
        match the target.
        """
        return get_closest_released_language(
            self.language) if self.language else None

    def apply_cdn_to_url(self, image_url):
        """
        Applies a new CDN/base URL to the given URLs if CDN configuration is
        enabled.

        If CDN does not exist or is disabled, just returns the original. The
        URL that we store in CourseOverviewImageSet is already top level path,
        so we don't need to go through the /static remapping magic that happens
        with other course assets. We just need to add the CDN server if appropriate.
        """
        cdn_config = AssetBaseUrlConfig.current()
        if not cdn_config.enabled:
            return image_url

        return self._apply_cdn_to_url(image_url, cdn_config.base_url)

    def apply_cdn_to_urls(self, image_urls):
        """
        Given a dict of resolutions -> urls, return a copy with CDN applied.

        If CDN does not exist or is disabled, just returns the original. The
        URLs that we store in CourseOverviewImageSet are all already top level
        paths, so we don't need to go through the /static remapping magic that
        happens with other course assets. We just need to add the CDN server if
        appropriate.
        """
        return {
            resolution: self.apply_cdn_to_url(url)
            for resolution, url in image_urls.items()
        }

    def _apply_cdn_to_url(self, url, base_url):
        """
        Applies a new CDN/base URL to the given URL.

        If a URL is absolute, we skip switching the host since it could
        be a hostname that isn't behind our CDN, and we could unintentionally
        break the URL overall.
        """

        # The URL can't be empty.
        if not url:
            return url

        _, netloc, path, params, query, fragment = urlparse(url)

        # If this is an absolute URL, just return it as is.  It could be a domain
        # that isn't ours, and thus CDNing it would actually break it.
        if netloc:
            return url

        return urlunparse(('', base_url, path, params, query, fragment))

    @cached_property
    def _original_course(self):
        """
        Returns the course from the modulestore.
        """
        return modulestore().get_course(self.id)

    @property
    def allow_public_wiki_access(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.allow_public_wiki_access

    @property
    def textbooks(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.textbooks

    @property
    def pdf_textbooks(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.pdf_textbooks

    @property
    def html_textbooks(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.html_textbooks

    @property
    def hide_progress_tab(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.hide_progress_tab

    @property
    def edxnotes(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.edxnotes

    @property
    def enable_ccx(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.enable_ccx

    @property
    def course_visibility(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.course_visibility

    @property
    def teams_enabled(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.teams_enabled

    @property
    def show_calculator(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.show_calculator

    @property
    def edxnotes_visibility(self):
        """
        TODO: move this to the model.
        """
        return self._original_course.edxnotes_visibility

    def __str__(self):
        """Represent ourselves with the course key."""
        return six.text_type(self.id)
Exemple #3
0
 def test_IntegerField(self):
     lazy_func = lazy(lambda: 1, int)
     self.assertIsInstance(IntegerField().get_prep_value(lazy_func()), int)
Exemple #4
0
class FokusGruppe(models.Model):
    """
    Randomiseret liste over Elever. Gentagelser af samme Elev vil forekomme i længere forløb.
    En hel Klasse tilføjes ad gangen. 
    Rækkefølgen af Elever i Klassen skifter for hver `klasse.fokus_runde`, og bestemmes af `self.rand_rank`.
    Læreren tildeler sig til hvert Modul (hver "time") et antal elever fra klassen. Læreren giver hver elev i denne gruppe Adfærdsobservation.
    Reificerer relation mellem Elev og Modul.

    Sammenlagt FokusGruppe og Adfærd 1/8 2021
    """
    # Fields
    #elev_fg_runde_id = AutoField(primary_key=True, verbose_name='Fokusgruppens Elev-løbenummer')
    id = AutoField(
        primary_key=True,
        verbose_name='Fokusgruppe-kandidatens elev+modul-løbenummer')
    """Klasse (og dermed `Elev.Klasse.fokus_runde`), samt Elev gives af denne relation."""
    elev = models.ForeignKey('Elev', on_delete=models.RESTRICT, null=True)
    oprettet = models.DateTimeField(
        #default=timezone.now()
        auto_now_add=
        True,  # https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.DateField.auto_now_add
    )
    opdateret = models.DateTimeField(  # NB: Dato odateres ved Model.save() ikke ved QuerySet.update(), se dokumenation!
        #default=timezone.now(),
        auto_now=
        True,  # https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.DateField.auto_now
    )
    """
        Modul udfyldes ikke ved generering af del-liste, 
        først når læreren udvælger hvor mange, der skal observeres i det pågældende Modul.
        Dato (og tid) gives af denne relation.
    """
    """
        Modul giver Forløb og knytter Elev til modulets fokusgruppe, når tildelt.
        Eleven i en instantiering (række) præsenteres i liste over Fokusgruppe-kandidater,
        hvis bedømt=False eller =Null. Sættes til =True, når observation registreres.
    """
    modul = models.ForeignKey(
        'Modul',
        models.SET_NULL,
        blank=True,
        null=True,
    )

    #@property eller overflødig?
    bedømt = models.BooleanField(null=True, default='')
    """True, hvis Elev var til stede i Modul? Null indtil Elev er bedømt"""
    tilstede = BooleanField(null=True, default='')
    """
        Tilfældig værdi mellem 0 og 1, der tildeles ved oprettelse.
        Sorteringsværdi (indenfor Elev.Klasse.fokus_runde). 
    """
    rand_rank = models.FloatField(
        validators=[MinValueValidator(0.0),
                    MaxValueValidator(1.0)],
        # https://docs.djangoproject.com/en/3.2/ref/models/database-functions/#random
        # maybe also: https://realpython.com/python-random/
        default=Random(),  # Introduced in Django 3.2
        editable=False,
        null=False)
    """
    De to sociale performance indicators er intenderet til at stimulere/måle elevernes 'Decency Quotient'. 
    Den faglige er til at stimulere/måle 'Intelligenskvitient'.
    Scores kan alene registreres (not Null), hvis `tilstede`=True (observation ikke mulig af fraværende elever).
    """
    def get_max_score():
        return 4

    spørg = IntegerField(
        blank=True,
        null=True,
        validators=[MinValueValidator(1),
                    MaxValueValidator(get_max_score())],
        help_text=
        'Score for elevens evne til at søge hjælp på fagligt spørgsmål')
    hjælp = IntegerField(
        blank=True,
        null=True,
        validators=[MinValueValidator(1),
                    MaxValueValidator(4)],
        help_text=
        'Score for elevens evne til at yde hjælp til faglig problemløsning')
    faglig = IntegerField(
        blank=True,
        null=True,
        validators=[MinValueValidator(1),
                    MaxValueValidator(4)],
        help_text='Score for elevens evne til at bidrage til en faglig samtale'
    )
    stikord = CharField(blank=True,
                        null=True,
                        max_length=30,
                        help_text='Lærerens observationer i "tre" ord')
    reaktion = CharField(blank=True,
                         null=True,
                         max_length=30,
                         help_text='Elevens bemærkning')

    class Meta:
        ordering = ['id', 'elev']
        verbose_name = 'fokusgruppe til adfærdsobservation'
        verbose_name_plural = 'fokusgrupper til adfærdsobservation'
        # Suggested by @bdbd https://stackoverflow.com/q/68872046/888033
        # https://docs.djangoproject.com/en/3.2/ref/models/constraints/#uniqueconstraint
        constraints = [
            UniqueConstraint(fields=['elev', 'modul'], name='unik_modul_elev')
        ]

    # Methods
    """Giver 'baglæns' URL-kodning mening for denne Model?"""

    def get_absolute_url(self):
        """Returnerer URL, der tilgår en bestemt instantiering af klassen Klasse (et bestemt hold)."""
        return reverse('fokusgruppe', args=[str(self.id)])

    def __str__(self):
        """Streng, som repræsenterer Elev (på Admin siden etc.)."""
        tmp_forløb = tmp_modul = '-'
        if self.modul:
            tmp_forløb = self.modul.forløb.titel
            if self.modul.afholdt:
                tmp_modul = self.modul.afholdt
            else:
                tmp_modul = 'NA'
            self.modul.forløb.titel
            return f"{self.elev.fornavn} {self.elev.efternavn}, d. {tmp_modul} om '{tmp_forløb}' (runde {self.klasse.fokus_runde} i {self.klasse.kortnavn})"
        else:
            tmp_forløb = 'Ukendt'
        return f"{self.elev.fornavn} {self.elev.efternavn} ikke tildelt (runde {self.klasse.fokus_runde} i {self.klasse.kortnavn})"

    @property  # Getter method - avoiding reduncant data entry
    # Frit efter https://www.geeksforgeeks.org/python-property-decorator-property/
    def runde(self):
        """Runde af observation, fra Elev.Klasse.fokus_runde (redundant)."""
        return self.elev.klasse.fokus_runde

    @property
    def klasse(self):
        return self.elev.klasse

        #def clean(self,modul):
        """
Exemple #5
0
 def output_field(self):
     return IntegerField()
Exemple #6
0
class RBACSync(Model):
    """A row in this table denotes a change that requires information RBAC
    micro-service to be updated.

    Typically this will be populated by a trigger within the database. A
    listeners in regiond will be notified and consult the un-synced records
    in this table. This way we can consistently publish RBAC information to the
    RBAC service in an HA environment.
    """
    class Meta(DefaultMeta):
        """Default meta."""

    objects = RBACSyncManager()

    action = CharField(
        editable=False,
        max_length=6,
        null=False,
        blank=True,
        choices=RBAC_ACTION_CHOICES,
        default=RBAC_ACTION.FULL,
        help_text="Action that should occur on the RBAC service.",
    )

    # An '' string is used when action is 'full'.
    resource_type = CharField(
        editable=False,
        max_length=255,
        null=False,
        blank=True,
        help_text="Resource type that as been added/updated/removed.",
    )

    # A `None` is used when action is 'full'.
    resource_id = IntegerField(
        editable=False,
        null=True,
        blank=True,
        help_text="Resource ID that has been added/updated/removed.",
    )

    # A '' string is used when action is 'full'.
    resource_name = CharField(
        editable=False,
        max_length=255,
        null=False,
        blank=True,
        help_text="Resource name that has been added/updated/removed.",
    )

    # This field is informational.
    created = DateTimeField(editable=False,
                            null=False,
                            auto_now=False,
                            auto_now_add=True)

    # This field is informational.
    source = CharField(
        editable=False,
        max_length=255,
        null=False,
        blank=True,
        help_text="A brief explanation what changed.",
    )
Exemple #7
0
class Extract(TimezoneMixin, Transform):
    lookup_name = None
    output_field = IntegerField()

    def __init__(self, expression, lookup_name=None, tzinfo=None, **extra):
        if self.lookup_name is None:
            self.lookup_name = lookup_name
        if self.lookup_name is None:
            raise ValueError('lookup_name must be provided')
        self.tzinfo = tzinfo
        super().__init__(expression, **extra)

    def as_sql(self, compiler, connection):
        sql, params = compiler.compile(self.lhs)
        lhs_output_field = self.lhs.output_field
        if isinstance(lhs_output_field, DateTimeField):
            tzname = self.get_tzname()
            sql = connection.ops.datetime_extract_sql(self.lookup_name, sql,
                                                      tzname)
        elif isinstance(lhs_output_field, DateField):
            sql = connection.ops.date_extract_sql(self.lookup_name, sql)
        elif isinstance(lhs_output_field, TimeField):
            sql = connection.ops.time_extract_sql(self.lookup_name, sql)
        elif isinstance(lhs_output_field, DurationField):
            if not connection.features.has_native_duration_field:
                raise ValueError(
                    'Extract requires native DurationField database support.')
            sql = connection.ops.time_extract_sql(self.lookup_name, sql)
        else:
            # resolve_expression has already validated the output_field so this
            # assert should never be hit.
            assert False, "Tried to Extract from an invalid type."
        return sql, params

    def resolve_expression(self,
                           query=None,
                           allow_joins=True,
                           reuse=None,
                           summarize=False,
                           for_save=False):
        copy = super().resolve_expression(query, allow_joins, reuse, summarize,
                                          for_save)
        field = copy.lhs.output_field
        if not isinstance(
                field, (DateField, DateTimeField, TimeField, DurationField)):
            raise ValueError(
                'Extract input expression must be DateField, DateTimeField, '
                'TimeField, or DurationField.')
        # Passing dates to functions expecting datetimes is most likely a mistake.
        if type(field) == DateField and copy.lookup_name in ('hour', 'minute',
                                                             'second'):
            raise ValueError(
                "Cannot extract time component '%s' from DateField '%s'. " %
                (copy.lookup_name, field.name))
        if (isinstance(field, DurationField) and copy.lookup_name
                in ('year', 'iso_year', 'month', 'week', 'week_day',
                    'iso_week_day', 'quarter')):
            raise ValueError(
                "Cannot extract component '%s' from DurationField '%s'." %
                (copy.lookup_name, field.name))
        return copy
Exemple #8
0
    def filter_list_queryset(self, request, queryset, view):
        label_filter = request.query_params.get('filter', None)
        if label_filter != 'payments':
            queryset = queryset.exclude(archived=True)
        queryset = queryset.filter(user__in=get_user_model().objects.all())

        if label_filter in ['running', 'my-tasks', 'payments']:
            if label_filter == 'running':
                queryset = queryset.filter(closed=False)
            elif label_filter == 'payments':
                queryset = queryset.filter(closed=True).order_by(
                    'paid', 'pay_distributed', '-created_at')
            if label_filter != 'payments' or not request.user.is_admin:
                queryset = queryset.filter(
                    Q(user=request.user)
                    | (Q(participation__user=request.user)
                       & (Q(participation__accepted=True)
                          | Q(participation__responded=False))))
        elif label_filter in ['new-projects', 'estimates', 'quotes']:
            queryset = queryset.exclude(scope=TASK_SCOPE_TASK)
            if label_filter == 'new-projects':
                queryset = queryset.filter(pm__isnull=True)
            elif label_filter in ['estimates', 'quotes']:
                if request.user.is_project_manager:
                    queryset = queryset.filter(pm=request.user)
                if label_filter == 'estimates':
                    queryset = queryset.exclude(
                        estimate__status=STATUS_ACCEPTED)
                if label_filter == 'quotes':
                    queryset = queryset.filter(
                        estimate__status=STATUS_ACCEPTED).exclude(
                            quote__status=STATUS_ACCEPTED)
        elif label_filter == 'skills':
            try:
                user_skills = request.user.userprofile.skills.all()
                queryset = queryset.filter(skills__in=user_skills)
                when = []
                for skill in user_skills:
                    new_when = When(skills=skill, then=1)
                    when.append(new_when)
                queryset = queryset.annotate(matches=Sum(
                    Case(*when, default=0,
                         output_field=IntegerField()))).order_by(
                             '-matches', '-created_at')
            except (ObjectDoesNotExist, UserProfile.DoesNotExist):
                return queryset.none()
        elif label_filter in ['my-clients', 'project-owners']:
            queryset = queryset.filter(
                (Q(user__connections_initiated__to_user=request.user)
                 & Q(user__connections_initiated__accepted=True))
                | (Q(user__connection_requests__from_user=request.user)
                   & Q(user__connection_requests__accepted=True)))

        if request.user.is_authenticated():
            if request.user.is_staff or request.user.is_superuser:
                return queryset
            if request.user.is_project_owner:
                queryset = queryset.filter(
                    Q(user=request.user) | Q(taskaccess__user=request.user))
            elif request.user.is_developer:
                return queryset.exclude(approved=False).filter(
                    Q(scope=TASK_SCOPE_TASK)
                    | (Q(scope=TASK_SCOPE_PROJECT) & Q(pm_required=False)
                       & ~Q(source=TASK_SOURCE_NEW_USER))
                    | Q(quote__status=STATUS_ACCEPTED)
                ).filter(
                    Q(user=request.user) | Q(participation__user=request.user)
                    | (Q(visibility=VISIBILITY_DEVELOPER) |
                       (Q(visibility=VISIBILITY_MY_TEAM) &
                        ((Q(user__connections_initiated__to_user=request.user)
                          & Q(user__connections_initiated__accepted=True)) |
                         (Q(user__connection_requests__from_user=request.user)
                          & Q(user__connection_requests__accepted=True)))))
                ).distinct()
            elif request.user.is_project_manager:
                queryset = queryset.filter(
                    Q(user=request.user) | Q(taskaccess__user=request.user) |
                    (Q(scope=TASK_SCOPE_ONGOING) |
                     (Q(scope=TASK_SCOPE_PROJECT) &
                      (Q(pm_required=True) | Q(source=TASK_SOURCE_NEW_USER)))))
            else:
                return queryset.none()
        else:
            return queryset.none()
        return queryset
    def test_alter_field_change_null_with_single_index(self):
        """
        Tests altering nullability of field with single index
        """
        with DatabaseSchemaEditor(self.connection) as schema_editor:
            schema_editor.execute = mock.MagicMock()

            def delete_index_sql(*args, **kwargs):
                # Overriding Statement creation with sql string.
                return "DROP INDEX num_unique"

            def create_index_sql(*args, **kwargs):
                # Overriding Statement creation with sql string.
                return "CREATE INDEX tests_author ON tests_author (author_num)"

            def constraint_names(*args, **kwargs):
                return ["num_unique"]

            schema_editor._delete_index_sql = delete_index_sql
            schema_editor._create_index_sql = create_index_sql
            schema_editor._constraint_names = constraint_names
            old_field = IntegerField(null=True, db_index=True)
            old_field.set_attributes_from_name("num")
            new_field = IntegerField(db_index=True)
            new_field.set_attributes_from_name("author_num")
            schema_editor.alter_field(Author, old_field, new_field)

            calls = [
                mock.call("DROP INDEX num_unique"),
                mock.call(
                    "ALTER TABLE tests_author RENAME COLUMN num TO author_num"
                ),
                mock.call(
                    "ALTER TABLE tests_author ALTER COLUMN author_num INT64 NOT NULL",
                    [],
                ),
                mock.call(
                    "CREATE INDEX tests_author ON tests_author (author_num)"),
            ]
            schema_editor.execute.assert_has_calls(calls)
        if HAS_OPENTELEMETRY_INSTALLED:
            span_list = self.ot_exporter.get_finished_spans()
            self.assertEqual(len(span_list), 3)
            self.assertSpanAttributes(
                "CloudSpannerDjango.alter_field.delete_index",
                attributes=dict(
                    BASE_ATTRIBUTES,
                    model_name="tests_author",
                    index_name="num_unique",
                    alter_field="num",
                ),
                span=span_list[0],
            )
            self.assertSpanAttributes(
                "CloudSpannerDjango.alter_field",
                attributes=dict(
                    BASE_ATTRIBUTES,
                    model_name="tests_author",
                    alter_field="num",
                ),
                span=span_list[1],
            )
            self.assertSpanAttributes(
                "CloudSpannerDjango.alter_field.recreate_index",
                attributes=dict(
                    BASE_ATTRIBUTES,
                    model_name="tests_author",
                    alter_field="author_num",
                ),
                span=span_list[2],
            )
Exemple #10
0
class LabelCategoryCaption(models.Model):
    category = ForeignKey(LabelCategory)
    grade = IntegerField()
    caption = CharField(max_length=127)
Exemple #11
0
"""
Classes to represent the default SQL aggregate functions
"""
import copy

from django.db.models.fields import IntegerField, FloatField

# Fake fields used to identify aggregate types in data-conversion operations.
ordinal_aggregate_field = IntegerField()
computed_aggregate_field = FloatField()


class Aggregate(object):
    """
    Default SQL Aggregate.
    """
    is_ordinal = False
    is_computed = False
    sql_template = '%(function)s(%(field)s)'

    def __init__(self, col, source=None, is_summary=False, **extra):
        """Instantiate an SQL aggregate

         * col is a column reference describing the subject field
           of the aggregate. It can be an alias, or a tuple describing
           a table and column name.
         * source is the underlying field or aggregate definition for
           the column reference. If the aggregate is not an ordinal or
           computed type, this reference is used to determine the coerced
           output type of the aggregate.
         * extra is a dictionary of additional data to provide for the
Exemple #12
0
class Tag(models.Model):
    TYPES = ((1, "Individual"), )
    type = IntegerField(choices=TYPES)
    individual = ForeignKey(User, null=True)
    comment = ForeignKey(Comment)
    last_reminder = DateTimeField(null=True)
Exemple #13
0
class HTML5Location(models.Model):
    location = OneToOneField(Location)
    path1 = CharField(max_length=2048, blank=True, null=True)
    path2 = CharField(max_length=2048, blank=True, null=True)
    offset1 = IntegerField()
    offset2 = IntegerField()
Exemple #14
0
def shopmanJsonList(request):
    '''
    환경설정 > 매장관리  : 리스트 데이터 Json
    '''
    userAuth = request.user.userAuth_id  # 사용자 권한 코드

    if userAuth in ["S0001M", "S0001C", "S0001A"]:  # 시스템관리자, 대표, 총괄만 가능
        # 검색조건(Parameter)
        sUseYn = request.POST.get("sUseYn")
        sShopNm = request.POST.get("sShopNm")

        # Query
        qry = Q()
        ####################
        # 검색 조건
        ####################
        if not is_empty(sUseYn):  # 사용여부
            qry &= Q(useYn__exact=sUseYn)

        if not is_empty(sShopNm):  # 매장명
            qry = Q(shopNm__exact=sShopNm)

        shopInfos = SysShop.objects.for_company(
            companyId=request.user.orgShopId.companyId).filter(qry).annotate(
                staffCnt=Count("r_system_sysuser_org_shop_id"),
                staffCntUseY=Count(
                    Case(
                        When(r_system_sysuser_org_shop_id__useYn__exact=True,
                             then=1),
                        output_field=IntegerField(),
                    )),
                staffCntUseN=Count(
                    Case(
                        When(r_system_sysuser_org_shop_id__useYn__exact=False,
                             then=1),
                        output_field=IntegerField(),
                    )),
            ).order_by("-useYn", ).values(
                "shopId",
                "shopNm",
                "zipCd",
                "addr1",
                "addr2",
                "useYn",
                "cellNo1",
                "cellNo2",
                "cellNo3",
                "telNo1",
                "telNo2",
                "telNo3",
                "faxNo1",
                "faxNo2",
                "faxNo3",
                "mainYn",
                "staffCnt",
                "staffCntUseY",
                "staffCntUseN",
                "regDt",
                "regId",
                "modDt",
                "modId",
            )

        return HttpResponse(
            makeJsonDump(resultData=list(shopInfos),
                         maskYn=is_masked_data(request.user),
                         maskFields={
                             "cellNo2": "T",
                         }),
            content_type="application/json",
        )
    else:
        raise PermissionDenied()
Exemple #15
0
class MaterialFeature(models.Model):
    material_id = CharField(max_length=64, null=True)
    weight = IntegerField(default=1)
Exemple #16
0
class CourseOverview(TimeStampedModel):
    """
    Model for storing and caching basic information about a course.

    This model contains basic course metadata such as an ID, display name,
    image URL, and any other information that would be necessary to display
    a course as part of a user dashboard or enrollment API.
    """

    # IMPORTANT: Bump this whenever you modify this model and/or add a migration.
    VERSION = 2

    # Cache entry versioning.
    version = IntegerField()

    # Course identification
    id = CourseKeyField(db_index=True, primary_key=True, max_length=255)  # pylint: disable=invalid-name
    _location = UsageKeyField(max_length=255)
    display_name = TextField(null=True)
    display_number_with_default = TextField()
    display_org_with_default = TextField()

    # Start/end dates
    start = DateTimeField(null=True)
    end = DateTimeField(null=True)
    advertised_start = TextField(null=True)

    # URLs
    course_image_url = TextField()
    facebook_url = TextField(null=True)
    social_sharing_url = TextField(null=True)
    end_of_course_survey_url = TextField(null=True)

    # Certification data
    certificates_display_behavior = TextField(null=True)
    certificates_show_before_end = BooleanField()
    cert_html_view_enabled = BooleanField()
    has_any_active_web_certificate = BooleanField()
    cert_name_short = TextField()
    cert_name_long = TextField()

    # Grading
    lowest_passing_grade = DecimalField(max_digits=5,
                                        decimal_places=2,
                                        null=True)

    # Access parameters
    days_early_for_beta = FloatField(null=True)
    mobile_available = BooleanField()
    visible_to_staff_only = BooleanField()
    _pre_requisite_courses_json = TextField(
    )  # JSON representation of list of CourseKey strings

    # Enrollment details
    enrollment_start = DateTimeField(null=True)
    enrollment_end = DateTimeField(null=True)
    enrollment_domain = TextField(null=True)
    invitation_only = BooleanField(default=False)
    max_student_enrollments_allowed = IntegerField(null=True)

    @classmethod
    def _create_from_course(cls, course):
        """
        Creates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from lms.djangoapps.courseware.courses import course_image_url

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        display_name = course.display_name
        start = course.start
        end = course.end
        if isinstance(course.id, CCXLocator):
            from ccx.utils import get_ccx_from_ccx_locator  # pylint: disable=import-error
            ccx = get_ccx_from_ccx_locator(course.id)
            display_name = ccx.display_name
            start = ccx.start
            end = ccx.due

        return cls(
            version=cls.VERSION,
            id=course.id,
            _location=course.location,
            display_name=display_name,
            display_number_with_default=course.display_number_with_default,
            display_org_with_default=course.display_org_with_default,
            start=start,
            end=end,
            advertised_start=course.advertised_start,
            course_image_url=course_image_url(course),
            facebook_url=course.facebook_url,
            social_sharing_url=course.social_sharing_url,
            certificates_display_behavior=course.certificates_display_behavior,
            certificates_show_before_end=course.certificates_show_before_end,
            cert_html_view_enabled=course.cert_html_view_enabled,
            has_any_active_web_certificate=(get_active_web_certificate(course)
                                            is not None),
            cert_name_short=course.cert_name_short,
            cert_name_long=course.cert_name_long,
            lowest_passing_grade=lowest_passing_grade,
            end_of_course_survey_url=course.end_of_course_survey_url,
            days_early_for_beta=course.days_early_for_beta,
            mobile_available=course.mobile_available,
            visible_to_staff_only=course.visible_to_staff_only,
            _pre_requisite_courses_json=json.dumps(
                course.pre_requisite_courses),
            enrollment_start=course.enrollment_start,
            enrollment_end=course.enrollment_end,
            enrollment_domain=course.enrollment_domain,
            invitation_only=course.invitation_only,
            max_student_enrollments_allowed=course.
            max_student_enrollments_allowed,
        )

    @classmethod
    def load_from_module_store(cls, course_id):
        """
        Load a CourseDescriptor, create a new CourseOverview from it, cache the
        overview, and return it.

        Arguments:
            course_id (CourseKey): the ID of the course overview to be loaded.

        Returns:
            CourseOverview: overview of the requested course.

        Raises:
            - CourseOverview.DoesNotExist if the course specified by course_id
                was not found.
            - IOError if some other error occurs while trying to load the
                course from the module store.
        """
        store = modulestore()
        with store.bulk_operations(course_id):
            course = store.get_course(course_id)
            if isinstance(course, CourseDescriptor):
                course_overview = cls._create_from_course(course)
                try:
                    course_overview.save()
                    CourseOverviewTab.objects.bulk_create([
                        CourseOverviewTab(tab_id=tab.tab_id,
                                          course_overview=course_overview)
                        for tab in course.tabs
                    ])
                except IntegrityError:
                    # There is a rare race condition that will occur if
                    # CourseOverview.get_from_id is called while a
                    # another identical overview is already in the process
                    # of being created.
                    # One of the overviews will be saved normally, while the
                    # other one will cause an IntegrityError because it tries
                    # to save a duplicate.
                    # (see: https://openedx.atlassian.net/browse/TNL-2854).
                    pass
                return course_overview
            elif course is not None:
                raise IOError(
                    "Error while loading course {} from the module store: {}",
                    unicode(course_id), course.error_msg if isinstance(
                        course, ErrorDescriptor) else unicode(course))
            else:
                raise cls.DoesNotExist()

    @classmethod
    def get_from_id(cls, course_id):
        """
        Load a CourseOverview object for a given course ID.

        First, we try to load the CourseOverview from the database. If it
        doesn't exist, we load the entire course from the modulestore, create a
        CourseOverview object from it, and then cache it in the database for
        future use.

        Arguments:
            course_id (CourseKey): the ID of the course overview to be loaded.

        Returns:
            CourseOverview: overview of the requested course.

        Raises:
            - CourseOverview.DoesNotExist if the course specified by course_id
                was not found.
            - IOError if some other error occurs while trying to load the
                course from the module store.
        """
        try:
            course_overview = cls.objects.get(id=course_id)
            if course_overview.version != cls.VERSION:
                # Throw away old versions of CourseOverview, as they might contain stale data.
                course_overview.delete()
                course_overview = None
        except cls.DoesNotExist:
            course_overview = None
        return course_overview or cls.load_from_module_store(course_id)

    def clean_id(self, padding_char='='):
        """
        Returns a unique deterministic base32-encoded ID for the course.

        Arguments:
            padding_char (str): Character used for padding at end of base-32
                                -encoded string, defaulting to '='
        """
        return course_metadata_utils.clean_course_key(self.location.course_key,
                                                      padding_char)

    @property
    def location(self):
        """
        Returns the UsageKey of this course.

        UsageKeyField has a strange behavior where it fails to parse the "run"
        of a course out of the serialized form of a Mongo Draft UsageKey. This
        method is a wrapper around _location attribute that fixes the problem
        by calling map_into_course, which restores the run attribute.
        """
        if self._location.run is None:
            self._location = self._location.map_into_course(self.id)
        return self._location

    @property
    def number(self):
        """
        Returns this course's number.

        This is a "number" in the sense of the "course numbers" that you see at
        lots of universities. For example, given a course
        "Intro to Computer Science" with the course key "edX/CS-101/2014", the
        course number would be "CS-101"
        """
        return course_metadata_utils.number_for_course_location(self.location)

    @property
    def url_name(self):
        """
        Returns this course's URL name.
        """
        return course_metadata_utils.url_name_for_course_location(
            self.location)

    @property
    def display_name_with_default(self):
        """
        Return reasonable display name for the course.
        """
        return course_metadata_utils.display_name_with_default(self)

    def has_started(self):
        """
        Returns whether the the course has started.
        """
        return course_metadata_utils.has_course_started(self.start)

    def has_ended(self):
        """
        Returns whether the course has ended.
        """
        return course_metadata_utils.has_course_ended(self.end)

    def starts_within(self, days):
        """
        Returns True if the course starts with-in given number of days otherwise returns False.
        """

        return course_metadata_utils.course_starts_within(self.start, days)

    def start_datetime_text(self, format_string="SHORT_DATE"):
        """
        Returns the desired text corresponding the course's start date and
        time in UTC.  Prefers .advertised_start, then falls back to .start.
        """
        return course_metadata_utils.course_start_datetime_text(
            self.start, self.advertised_start, format_string, ugettext,
            strftime_localized)

    @property
    def start_date_is_still_default(self):
        """
        Checks if the start date set for the course is still default, i.e.
        .start has not been modified, and .advertised_start has not been set.
        """
        return course_metadata_utils.course_start_date_is_default(
            self.start,
            self.advertised_start,
        )

    def end_datetime_text(self, format_string="SHORT_DATE"):
        """
        Returns the end date or datetime for the course formatted as a string.
        """
        return course_metadata_utils.course_end_datetime_text(
            self.end, format_string, strftime_localized)

    def may_certify(self):
        """
        Returns whether it is acceptable to show the student a certificate
        download link.
        """
        return course_metadata_utils.may_certify_for_course(
            self.certificates_display_behavior,
            self.certificates_show_before_end, self.has_ended())

    @property
    def pre_requisite_courses(self):
        """
        Returns a list of ID strings for this course's prerequisite courses.
        """
        return json.loads(self._pre_requisite_courses_json)

    @classmethod
    def get_all_course_keys(cls):
        """
        Returns all course keys from course overviews.
        """
        return [
            CourseKey.from_string(course_overview['id'])
            for course_overview in CourseOverview.objects.values('id')
        ]

    def is_discussion_tab_enabled(self):
        """
        Returns True if course has discussion tab and is enabled
        """
        tabs = self.tabs.all()  # pylint: disable=E1101
        # creates circular import; hence explicitly referenced is_discussion_enabled
        for tab in tabs:
            if tab.tab_id == "discussion" and django_comment_client.utils.is_discussion_enabled(
                    self.id):
                return True
        return False
Exemple #17
0
class FeaturedAuthor(models.Model):
    author_id = IntegerField(null=False)
    weight = IntegerField(default=1)
Exemple #18
0
class Reminder(models.Model):
    project_id = CharField(null=False, blank=False, max_length=264)
    day = IntegerField(null=True, blank=True)
    message = CharField(max_length=160)
    reminder_mode = CharField(null=False,
                              blank=False,
                              max_length=20,
                              default=ReminderMode.BEFORE_DEADLINE)
    organization = ForeignKey(Organization)

    def to_dict(self):
        return {
            'day': self.day,
            'message': self.message,
            'reminder_mode': self.reminder_mode,
            'id': self.id
        }

    def void(self, void=True):
        self.voided = void
        self.save()

    def should_be_send_on(self, deadline, on_date):
        assert isinstance(on_date, date)
        deadline_date = self._get_applicapable_deadline_date(deadline, on_date)
        return on_date == deadline_date + timedelta(days=self._delta())

    def get_sender_list(self, project, on_date, dbm):
        if not project.reminder_and_deadline['should_send_reminder_to_all_ds']:
            deadline_date = self._get_applicapable_deadline_date(
                project.deadline(), on_date)
            return project.get_data_senders_without_submissions_for(
                deadline_date, dbm)
        return project.get_data_senders(dbm)

    def _delta(self):
        if self.reminder_mode == ReminderMode.ON_DEADLINE:
            return 0
        if self.reminder_mode == ReminderMode.BEFORE_DEADLINE:
            return -self.day
        if self.reminder_mode == ReminderMode.AFTER_DEADLINE:
            return self.day

    def _get_applicapable_deadline_date(self, deadline, on_date):
        if self.reminder_mode == ReminderMode.BEFORE_DEADLINE:
            return deadline.next_deadline(on_date)
        else:
            return deadline.current_deadline(on_date)

    def log(self,
            dbm,
            project_id,
            date,
            to_number,
            sent_status='sent',
            number_of_sms=0):
        log = ReminderLog(dbm=dbm,
                          reminder=self,
                          project_id=project_id,
                          date=date,
                          sent_status=sent_status,
                          number_of_sms=number_of_sms,
                          to_number=to_number)
        log.save()
        return log
Exemple #19
0
class Notification(AbstractNotification):
    object_id = IntegerField(max_length=125, blank=True, null=True)
    type = IntegerField(max_length=125, blank=True, null=True)
Exemple #20
0
class DefaultSetting(models.Model):
    name = CharField(max_length=1023)
    description = TextField(blank=True, null=True)
    value = IntegerField()
Exemple #21
0
 def test_IntegerField(self):
     self.assertIsInstance(IntegerField().get_prep_value(1), int)
Exemple #22
0
class SettingLabel(models.Model):
    setting = ForeignKey(DefaultSetting)
    value = IntegerField()
    label = TextField()
Exemple #23
0
 def rel_db_type(self, connection):
     return IntegerField().db_type(connection=connection)
Exemple #24
0
class UserSetting(models.Model):
    user = ForeignKey(User)
    setting = ForeignKey(DefaultSetting)
    value = IntegerField()
    ctime = DateTimeField(default=datetime.now)
Exemple #25
0
class CourseOverview(TimeStampedModel):
    """
    Model for storing and caching basic information about a course.

    This model contains basic course metadata such as an ID, display name,
    image URL, and any other information that would be necessary to display
    a course as part of:
        user dashboard (enrolled courses)
        course catalog (courses to enroll in)
        course about (meta data about the course)
    """

    class Meta(object):
        app_label = 'course_overviews'

    # IMPORTANT: Bump this whenever you modify this model and/or add a migration.
    VERSION = 4

    # Cache entry versioning.
    version = IntegerField()

    # Course identification
    id = CourseKeyField(db_index=True, primary_key=True, max_length=255)
    _location = UsageKeyField(max_length=255)
    org = TextField(max_length=255, default='outdated_entry')
    display_name = TextField(null=True)
    display_number_with_default = TextField()
    display_org_with_default = TextField()

    # Start/end dates
    start = DateTimeField(null=True)
    end = DateTimeField(null=True)
    advertised_start = TextField(null=True)
    announcement = DateTimeField(null=True)

    # URLs
    course_image_url = TextField()
    social_sharing_url = TextField(null=True)
    end_of_course_survey_url = TextField(null=True)

    # Certification data
    certificates_display_behavior = TextField(null=True)
    certificates_show_before_end = BooleanField(default=False)
    cert_html_view_enabled = BooleanField(default=False)
    has_any_active_web_certificate = BooleanField(default=False)
    cert_name_short = TextField()
    cert_name_long = TextField()

    # Grading
    lowest_passing_grade = DecimalField(max_digits=5, decimal_places=2, null=True)

    # Access parameters
    days_early_for_beta = FloatField(null=True)
    mobile_available = BooleanField(default=False)
    visible_to_staff_only = BooleanField(default=False)
    _pre_requisite_courses_json = TextField()  # JSON representation of list of CourseKey strings

    # Enrollment details
    enrollment_start = DateTimeField(null=True)
    enrollment_end = DateTimeField(null=True)
    enrollment_domain = TextField(null=True)
    invitation_only = BooleanField(default=False)
    max_student_enrollments_allowed = IntegerField(null=True)

    # Catalog information
    catalog_visibility = TextField(null=True)
    short_description = TextField(null=True)
    course_video_url = TextField(null=True)
    effort = TextField(null=True)
    self_paced = BooleanField(default=False)
    marketing_url = TextField(null=True)
    eligible_for_financial_aid = BooleanField(default=True)

    @classmethod
    def _create_or_update(cls, course):
        """
        Creates or updates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: created or updated overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from openedx.core.lib.courses import course_image_url

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        display_name = course.display_name
        start = course.start
        end = course.end
        max_student_enrollments_allowed = course.max_student_enrollments_allowed
        if isinstance(course.id, CCXLocator):
            from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator
            ccx = get_ccx_from_ccx_locator(course.id)
            display_name = ccx.display_name
            start = ccx.start
            end = ccx.due
            max_student_enrollments_allowed = ccx.max_student_enrollments_allowed

        course_overview = cls.objects.filter(id=course.id)
        if course_overview.exists():
            log.info('Updating course overview for %s.', unicode(course.id))
            course_overview = course_overview.first()
        else:
            log.info('Creating course overview for %s.', unicode(course.id))
            course_overview = cls()

        course_overview.version = cls.VERSION
        course_overview.id = course.id
        course_overview._location = course.location
        course_overview.org = course.location.org
        course_overview.display_name = display_name
        course_overview.display_number_with_default = course.display_number_with_default
        course_overview.display_org_with_default = course.display_org_with_default

        course_overview.start = start
        course_overview.end = end
        course_overview.advertised_start = course.advertised_start
        course_overview.announcement = course.announcement

        course_overview.course_image_url = course_image_url(course)
        course_overview.social_sharing_url = course.social_sharing_url

        course_overview.certificates_display_behavior = course.certificates_display_behavior
        course_overview.certificates_show_before_end = course.certificates_show_before_end
        course_overview.cert_html_view_enabled = course.cert_html_view_enabled
        course_overview.has_any_active_web_certificate = (get_active_web_certificate(course) is not None)
        course_overview.cert_name_short = course.cert_name_short
        course_overview.cert_name_long = course.cert_name_long
        course_overview.lowest_passing_grade = lowest_passing_grade
        course_overview.end_of_course_survey_url = course.end_of_course_survey_url

        course_overview.days_early_for_beta = course.days_early_for_beta
        course_overview.mobile_available = course.mobile_available
        course_overview.visible_to_staff_only = course.visible_to_staff_only
        course_overview._pre_requisite_courses_json = json.dumps(course.pre_requisite_courses)

        course_overview.enrollment_start = course.enrollment_start
        course_overview.enrollment_end = course.enrollment_end
        course_overview.enrollment_domain = course.enrollment_domain
        course_overview.invitation_only = course.invitation_only
        course_overview.max_student_enrollments_allowed = max_student_enrollments_allowed

        course_overview.catalog_visibility = course.catalog_visibility
        course_overview.short_description = CourseDetails.fetch_about_attribute(course.id, 'short_description')
        course_overview.effort = CourseDetails.fetch_about_attribute(course.id, 'effort')
        course_overview.course_video_url = CourseDetails.fetch_video_url(course.id)
        course_overview.self_paced = course.self_paced

        return course_overview

    @classmethod
    def load_from_module_store(cls, course_id):
        """
        Load a CourseDescriptor, create or update a CourseOverview from it, cache the
        overview, and return it.

        Arguments:
            course_id (CourseKey): the ID of the course overview to be loaded.

        Returns:
            CourseOverview: overview of the requested course.

        Raises:
            - CourseOverview.DoesNotExist if the course specified by course_id
                was not found.
            - IOError if some other error occurs while trying to load the
                course from the module store.
        """
        store = modulestore()
        with store.bulk_operations(course_id):
            course = store.get_course(course_id)
            if isinstance(course, CourseDescriptor):
                course_overview = cls._create_or_update(course)
                try:
                    with transaction.atomic():
                        course_overview.save()
                        # Remove and recreate all the course tabs
                        CourseOverviewTab.objects.filter(course_overview=course_overview).delete()
                        CourseOverviewTab.objects.bulk_create([
                            CourseOverviewTab(tab_id=tab.tab_id, course_overview=course_overview)
                            for tab in course.tabs
                        ])
                        CourseOverviewImageSet.create_or_update(course_overview, course)

                except IntegrityError:
                    # There is a rare race condition that will occur if
                    # CourseOverview.get_from_id is called while a
                    # another identical overview is already in the process
                    # of being created.
                    # One of the overviews will be saved normally, while the
                    # other one will cause an IntegrityError because it tries
                    # to save a duplicate.
                    # (see: https://openedx.atlassian.net/browse/TNL-2854).
                    pass
                return course_overview
            elif course is not None:
                raise IOError(
                    "Error while loading course {} from the module store: {}",
                    unicode(course_id),
                    course.error_msg if isinstance(course, ErrorDescriptor) else unicode(course)
                )
            else:
                raise cls.DoesNotExist()

    @classmethod
    def get_from_id(cls, course_id):
        """
        Load a CourseOverview object for a given course ID.

        First, we try to load the CourseOverview from the database. If it
        doesn't exist, we load the entire course from the modulestore, create a
        CourseOverview object from it, and then cache it in the database for
        future use.

        Arguments:
            course_id (CourseKey): the ID of the course overview to be loaded.

        Returns:
            CourseOverview: overview of the requested course.

        Raises:
            - CourseOverview.DoesNotExist if the course specified by course_id
                was not found.
            - IOError if some other error occurs while trying to load the
                course from the module store.
        """
        try:
            course_overview = cls.objects.select_related('image_set').get(id=course_id)
            if course_overview.version < cls.VERSION:
                # Throw away old versions of CourseOverview, as they might contain stale data.
                course_overview.delete()
                course_overview = None
        except cls.DoesNotExist:
            course_overview = None

        # Regenerate the thumbnail images if they're missing (either because
        # they were never generated, or because they were flushed out after
        # a change to CourseOverviewImageConfig.
        if course_overview and not hasattr(course_overview, 'image_set'):
            CourseOverviewImageSet.create_or_update(course_overview)

        return course_overview or cls.load_from_module_store(course_id)

    @classmethod
    def get_from_ids_if_exists(cls, course_ids):
        """
        Return a dict mapping course_ids to CourseOverviews, if they exist.

        This method will *not* generate new CourseOverviews or delete outdated
        ones. It exists only as a small optimization used when CourseOverviews
        are known to exist, for common situations like the student dashboard.

        Callers should assume that this list is incomplete and fall back to
        get_from_id if they need to guarantee CourseOverview generation.
        """
        return {
            overview.id: overview
            for overview
            in cls.objects.select_related('image_set').filter(
                id__in=course_ids,
                version__gte=cls.VERSION
            )
        }

    def clean_id(self, padding_char='='):
        """
        Returns a unique deterministic base32-encoded ID for the course.

        Arguments:
            padding_char (str): Character used for padding at end of base-32
                                -encoded string, defaulting to '='
        """
        return course_metadata_utils.clean_course_key(self.location.course_key, padding_char)

    @property
    def location(self):
        """
        Returns the UsageKey of this course.

        UsageKeyField has a strange behavior where it fails to parse the "run"
        of a course out of the serialized form of a Mongo Draft UsageKey. This
        method is a wrapper around _location attribute that fixes the problem
        by calling map_into_course, which restores the run attribute.
        """
        if self._location.run is None:
            self._location = self._location.map_into_course(self.id)
        return self._location

    @property
    def number(self):
        """
        Returns this course's number.

        This is a "number" in the sense of the "course numbers" that you see at
        lots of universities. For example, given a course
        "Intro to Computer Science" with the course key "edX/CS-101/2014", the
        course number would be "CS-101"
        """
        return course_metadata_utils.number_for_course_location(self.location)

    @property
    def url_name(self):
        """
        Returns this course's URL name.
        """
        return block_metadata_utils.url_name_for_block(self)

    @property
    def display_name_with_default(self):
        """
        Return reasonable display name for the course.
        """
        return block_metadata_utils.display_name_with_default(self)

    @property
    def display_name_with_default_escaped(self):
        """
        DEPRECATED: use display_name_with_default

        Return html escaped reasonable display name for the course.

        Note: This newly introduced method should not be used.  It was only
        introduced to enable a quick search/replace and the ability to slowly
        migrate and test switching to display_name_with_default, which is no
        longer escaped.
        """
        return block_metadata_utils.display_name_with_default_escaped(self)

    @property
    def dashboard_start_display(self):
        """
         Return start date to diplay on learner's dashboard, preferably `Course Advertised Start`
        """
        return self.advertised_start or self.start

    def has_started(self):
        """
        Returns whether the the course has started.
        """
        return course_metadata_utils.has_course_started(self.start)

    def has_ended(self):
        """
        Returns whether the course has ended.
        """
        return course_metadata_utils.has_course_ended(self.end)

    def has_marketing_url(self):
        """
        Returns whether the course has marketing url.
        """
        return settings.FEATURES.get('ENABLE_MKTG_SITE') and bool(self.marketing_url)

    def has_social_sharing_url(self):
        """
        Returns whether the course has social sharing url.
        """
        is_social_sharing_enabled = getattr(settings, 'SOCIAL_SHARING_SETTINGS', {}).get('CUSTOM_COURSE_URLS')
        return is_social_sharing_enabled and bool(self.social_sharing_url)

    def starts_within(self, days):
        """
        Returns True if the course starts with-in given number of days otherwise returns False.
        """
        return course_metadata_utils.course_starts_within(self.start, days)

    @property
    def start_date_is_still_default(self):
        """
        Checks if the start date set for the course is still default, i.e.
        .start has not been modified, and .advertised_start has not been set.
        """
        return course_metadata_utils.course_start_date_is_default(
            self.start,
            self.advertised_start,
        )

    @property
    def sorting_score(self):
        """
        Returns a tuple that can be used to sort the courses according
        the how "new" they are. The "newness" score is computed using a
        heuristic that takes into account the announcement and
        (advertised) start dates of the course if available.

        The lower the number the "newer" the course.
        """
        return course_metadata_utils.sorting_score(self.start, self.advertised_start, self.announcement)

    @property
    def start_type(self):
        """
        Returns the type of the course's 'start' field.
        """
        if self.advertised_start:
            return u'string'
        elif self.start != DEFAULT_START_DATE:
            return u'timestamp'
        else:
            return u'empty'

    @property
    def start_display(self):
        """
        Returns the display value for the course's start date.
        """
        if self.advertised_start:
            return self.advertised_start
        elif self.start != DEFAULT_START_DATE:
            return defaultfilters.date(self.start, "DATE_FORMAT")
        else:
            return None

    def may_certify(self):
        """
        Returns whether it is acceptable to show the student a certificate
        download link.
        """
        return course_metadata_utils.may_certify_for_course(
            self.certificates_display_behavior,
            self.certificates_show_before_end,
            self.has_ended()
        )

    @property
    def pre_requisite_courses(self):
        """
        Returns a list of ID strings for this course's prerequisite courses.
        """
        return json.loads(self._pre_requisite_courses_json)

    @classmethod
    def get_select_courses(cls, course_keys):
        """
        Returns CourseOverview objects for the given course_keys.
        """
        course_overviews = []

        log.info('Generating course overview for %d courses.', len(course_keys))
        log.debug('Generating course overview(s) for the following courses: %s', course_keys)

        for course_key in course_keys:
            try:
                course_overviews.append(CourseOverview.get_from_id(course_key))
            except Exception as ex:  # pylint: disable=broad-except
                log.exception(
                    'An error occurred while generating course overview for %s: %s',
                    unicode(course_key),
                    ex.message,
                )

        log.info('Finished generating course overviews.')

        return course_overviews

    @classmethod
    def get_all_courses(cls, orgs=None, filter_=None):
        """
        Returns all CourseOverview objects in the database.

        Arguments:
            orgs (list[string]): Optional parameter that allows case-insensitive
                filtering by organization.
            filter_ (dict): Optional parameter that allows custom filtering.
        """
        # Note: If a newly created course is not returned in this QueryList,
        # make sure the "publish" signal was emitted when the course was
        # created. For tests using CourseFactory, use emit_signals=True.
        course_overviews = CourseOverview.objects.all()

        if orgs:
            # In rare cases, courses belonging to the same org may be accidentally assigned
            # an org code with a different casing (e.g., Harvardx as opposed to HarvardX).
            # Case-insensitive matching allows us to deal with this kind of dirty data.
            course_overviews = course_overviews.filter(org__iregex=r'(' + '|'.join(orgs) + ')')

        if filter_:
            course_overviews = course_overviews.filter(**filter_)

        return course_overviews

    @classmethod
    def get_all_course_keys(cls):
        """
        Returns all course keys from course overviews.
        """
        return [
            CourseKey.from_string(course_overview['id'])
            for course_overview in CourseOverview.objects.values('id')
        ]

    def is_discussion_tab_enabled(self):
        """
        Returns True if course has discussion tab and is enabled
        """
        tabs = self.tabs.all()
        # creates circular import; hence explicitly referenced is_discussion_enabled
        for tab in tabs:
            if tab.tab_id == "discussion" and django_comment_client.utils.is_discussion_enabled(self.id):
                return True
        return False

    @property
    def image_urls(self):
        """
        Return a dict with all known URLs for this course image.

        Current resolutions are:
          raw = original upload from the user
          small = thumbnail with dimensions CourseOverviewImageConfig.current().small
          large = thumbnail with dimensions CourseOverviewImageConfig.current().large

        If no thumbnails exist, the raw (originally uploaded) image will be
        returned for all resolutions.
        """
        # This is either the raw image that the course team uploaded, or the
        # settings.DEFAULT_COURSE_ABOUT_IMAGE_URL if they didn't specify one.
        raw_image_url = self.course_image_url

        # Default all sizes to return the raw image if there is no
        # CourseOverviewImageSet associated with this CourseOverview. This can
        # happen because we're disabled via CourseOverviewImageConfig.
        urls = {
            'raw': raw_image_url,
            'small': raw_image_url,
            'large': raw_image_url,
        }

        # If we do have a CourseOverviewImageSet, we still default to the raw
        # images if our thumbnails are blank (might indicate that there was a
        # processing error of some sort while trying to generate thumbnails).
        if hasattr(self, 'image_set') and CourseOverviewImageConfig.current().enabled:
            urls['small'] = self.image_set.small_url or raw_image_url
            urls['large'] = self.image_set.large_url or raw_image_url

        return self.apply_cdn_to_urls(urls)

    @property
    def pacing(self):
        """ Returns the pacing for the course.

        Potential values:
            self: Self-paced courses
            instructor: Instructor-led courses
        """
        return 'self' if self.self_paced else 'instructor'

    def apply_cdn_to_urls(self, image_urls):
        """
        Given a dict of resolutions -> urls, return a copy with CDN applied.

        If CDN does not exist or is disabled, just returns the original. The
        URLs that we store in CourseOverviewImageSet are all already top level
        paths, so we don't need to go through the /static remapping magic that
        happens with other course assets. We just need to add the CDN server if
        appropriate.
        """
        cdn_config = AssetBaseUrlConfig.current()
        if not cdn_config.enabled:
            return image_urls

        base_url = cdn_config.base_url

        return {
            resolution: self._apply_cdn_to_url(url, base_url)
            for resolution, url in image_urls.items()
        }

    def _apply_cdn_to_url(self, url, base_url):
        """
        Applies a new CDN/base URL to the given URL.

        If a URL is absolute, we skip switching the host since it could
        be a hostname that isn't behind our CDN, and we could unintentionally
        break the URL overall.
        """

        # The URL can't be empty.
        if not url:
            return url

        _, netloc, path, params, query, fragment = urlparse(url)

        # If this is an absolute URL, just return it as is.  It could be a domain
        # that isn't ours, and thus CDNing it would actually break it.
        if netloc:
            return url

        return urlunparse((None, base_url, path, params, query, fragment))

    def __unicode__(self):
        """Represent ourselves with the course key."""
        return unicode(self.id)
Exemple #26
0
class AssignmentGradeHistory(models.Model):
    user = ForeignKey(User, related_name="u_grade_h")
    grader = ForeignKey(User, related_name="g_grade_h")
    ctime = DateTimeField(default=datetime.now())
    grade = IntegerField()
    source = ForeignKey(Source)
 def __init__(self, field):
     super().__init__(
         Case(When(**{field: False}, then=1),
              default=0,
              output_field=IntegerField()))
Exemple #28
0
class Shoppinglist(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    list_source = CharField(max_length=50, blank=False, default=None)
    source_id = IntegerField(blank=True, null=True)
    completed = models.BooleanField(default=False)
    name = CharField(max_length=200, blank=True, null=True, default=None)
     there was a real change.
   - Varchar sizes are reported back without unicode support so there size
     may change in comparison to the real length of the varchar.   
   - Some of the 'fixes' to counter these problems might create false 
     positives or false negatives.
"""

from django.core.management.base import BaseCommand
from django.core.management import sql as _sql
from django.core.management import CommandError
from django.core.management.color import no_style
from django.db import transaction, connection
from django.db.models.fields import IntegerField
from optparse import make_option

ORDERING_FIELD = IntegerField('_order', null=True)


def flatten(l, ltypes=(list, tuple)):
    ltype = type(l)
    l = list(l)
    i = 0
    while i < len(l):
        while isinstance(l[i], ltypes):
            if not l[i]:
                l.pop(i)
                i -= 1
                break
            else:
                l[i:i + 1] = l[i]
        i += 1
Exemple #30
0
class PageSeen(models.Model):
    source              = ForeignKey(Source)
    page                = IntegerField()
    session             = ForeignKey(Session, null=True)
    user                = ForeignKey(User, null=True) #duplicate (cf session) but inlined for performance
    ctime               = DateTimeField(default=datetime.now)