Ejemplo n.º 1
0
    def test_remove_user_override(self, initial_date, override_date,
                                  expected_date):
        items = make_items()
        first = items[0]
        block_id = first[0]
        items[0][1]['due'] = initial_date

        api.set_dates_for_course(six.text_type(block_id.course_key), items)

        api.set_date_for_block(block_id.course_key,
                               block_id,
                               'due',
                               override_date,
                               user=self.user)
        DEFAULT_REQUEST_CACHE.clear()
        retrieved = api.get_dates_for_course(block_id.course_key,
                                             user=self.user.id)
        assert len(retrieved) == NUM_OVERRIDES
        assert retrieved[block_id, 'due'] == expected_date

        api.set_date_for_block(block_id.course_key,
                               block_id,
                               'due',
                               None,
                               user=self.user)
        DEFAULT_REQUEST_CACHE.clear()
        retrieved = api.get_dates_for_course(block_id.course_key,
                                             user=self.user.id)
        assert len(retrieved) == NUM_OVERRIDES
        if isinstance(initial_date, timedelta):
            user_initial_date = self.schedule.start_date + initial_date
        else:
            user_initial_date = initial_date
        assert retrieved[block_id, 'due'] == user_initial_date
Ejemplo n.º 2
0
    def test_get_dates_for_course(self):
        items = make_items()
        api.set_dates_for_course(items[0][0].course_key, items)
        retrieved = api.get_dates_for_course(items[0][0].course_key)
        assert len(retrieved) == NUM_OVERRIDES
        first = items[0]
        assert retrieved[(first[0], 'due')] == first[1]['due']

        # second time is cached
        retrieved = api.get_dates_for_course(items[0][0].course_key)
        assert len(retrieved) == NUM_OVERRIDES

        # third time with new course_id

        course2 = DummyCourse(id='course-v1:testX+tt202+2019')
        course2.save()
        new_items = make_items(course2.id)

        enrollment2 = DummyEnrollment(user=self.user, course=course2)
        enrollment2.save()

        schedule2 = DummySchedule(enrollment=enrollment2,
                                  start_date=datetime(2019, 4, 1))
        schedule2.save()

        api.set_dates_for_course(new_items[0][0].course_key, new_items)
        new_retrieved = api.get_dates_for_course(new_items[0][0].course_key)
        assert len(new_retrieved) == NUM_OVERRIDES
        first_id = list(new_retrieved.keys())[0][0]
        last_id = list(retrieved.keys())[0][0]
        assert first_id.course_key != last_id.course_key
        return items
Ejemplo n.º 3
0
    def test_get_dates_for_course_outline(self):
        items = make_items()
        course_key = items[0][0].course_key
        items.append((make_block_id(course_key, block_type='video'), {'start': datetime(2019, 3, 21), 'test': '1'}))
        api.set_dates_for_course(course_key, items)
        # Ensure the video block *was* returned normally.
        retrieved = api.get_dates_for_course(
            course_key, subsection_and_higher_only=False, published_version=self.course_version
        )
        assert len(retrieved) == NUM_OVERRIDES + 1
        # Ensure the video block *was not* returned with subsection and higher blocks.
        retrieved = api.get_dates_for_course(
            course_key, subsection_and_higher_only=True, published_version=self.course_version
        )
        assert len(retrieved) == NUM_OVERRIDES

        # Set all the ContentDates for this course's structural blocks to have
        # None for their block_type to test compatibilty with a half-migrated
        # state. They should still be returned by get_dates_for_course with
        # subsection_and_higher_only=True.
        structural_dates = models.ContentDate.objects.filter(
            course_id=course_key,
            block_type__in=['course', 'chapter', 'sequential']
        )
        structural_dates.update(block_type=None)
        retrieved = api.get_dates_for_course(
            course_key, subsection_and_higher_only=True, published_version=self.course_version, use_cached=False
        )
        assert len(retrieved) == NUM_OVERRIDES
Ejemplo n.º 4
0
    def test_hidden_content_with_transformer_override(self):
        """
        Tests content is hidden if the date changes after collection and
        during the transform phase (for example, by the DateOverrideTransformer).
        """
        with mock_registered_transformers(
            [DateOverrideTransformer, self.TRANSFORMER_CLASS_TO_TEST]):
            transformers = BlockStructureTransformers([
                DateOverrideTransformer(self.student),
                self.TRANSFORMER_CLASS_TO_TEST()
            ])

        block = self.get_block(1)
        block.hide_after_due = True
        update_block(block)
        set_date_for_block(self.course.id, block.location, 'due',
                           self.DueDateType.PAST_DATE)

        # Due date is in the past so some blocks are hidden
        self.assert_transform_results(
            self.student,
            self.ALL_BLOCKS - {1, 3, 4},
            blocks_with_differing_access=None,
            transformers=transformers,
        )

        # Set an override for the due date to be in the future
        set_date_for_block(self.course.id,
                           block.location,
                           'due',
                           self.DueDateType.FUTURE_DATE,
                           user=self.student)
        # this line is just to bust the cache for the user so it returns the updated date.
        get_dates_for_course(self.course.id,
                             user=self.student,
                             use_cached=False)

        # Now all blocks are returned for the student
        self.assert_transform_results(
            self.student,
            self.ALL_BLOCKS,
            blocks_with_differing_access=None,
            transformers=transformers,
        )

        # But not for a different user
        different_user = UserFactory()
        with mock_registered_transformers(
            [DateOverrideTransformer, self.TRANSFORMER_CLASS_TO_TEST]):
            transformers = BlockStructureTransformers([
                DateOverrideTransformer(different_user),
                self.TRANSFORMER_CLASS_TO_TEST()
            ])
        self.assert_transform_results(
            different_user,
            self.ALL_BLOCKS - {1, 3, 4},
            blocks_with_differing_access=None,
            transformers=transformers,
        )
Ejemplo n.º 5
0
 def test_get_dates_no_schedule(self):
     items = make_items(with_relative=True)
     course_key = items[0][0].course_key
     api.set_dates_for_course(course_key, items)
     retrieved = api.get_dates_for_course(course_key, user=self.user)
     assert len(retrieved) == 6
     self.schedule.delete()
     retrieved = api.get_dates_for_course(course_key, user=self.user, use_cached=False)
     assert len(retrieved) == 3
Ejemplo n.º 6
0
    def test_relative_date_past_cutoff_date(self):
        course_key = CourseLocator('testX', 'tt101', '2019')
        start_block = make_block_id(course_key, block_type='course')
        start_date = datetime(2019, 3, 15)
        first_block = make_block_id(course_key)
        first_delta = timedelta(days=1)
        second_block = make_block_id(course_key)
        second_delta = timedelta(days=10)
        end_block = make_block_id(course_key, block_type='course')
        end_date = datetime(2019, 4, 20)
        items = [
            (start_block, {
                'start': start_date
            }),  # start dates are always absolute
            (first_block, {
                'due': first_delta
            }),  # relative
            (second_block, {
                'due': second_delta
            }),  # relative
            (end_block, {
                'end': end_date
            }),  # end dates are always absolute
        ]
        api.set_dates_for_course(course_key, items)

        # Try one with just enough as a sanity check
        self.schedule.created = end_date - second_delta
        self.schedule.save()
        dates = [
            ((start_block, 'start'), start_date),
            ((first_block, 'due'), self.schedule.start_date + first_delta),
            ((second_block, 'due'), self.schedule.start_date + second_delta),
            ((end_block, 'end'), end_date),
        ]
        assert api.get_dates_for_course(course_key,
                                        schedule=self.schedule) == dict(dates)

        cache.clear()
        DEFAULT_REQUEST_CACHE.clear()

        # Now set schedule start date too close to the end date and verify that we no longer get due dates
        self.schedule.created = datetime(2019, 4, 15)
        self.schedule.save()
        dates = [
            ((start_block, 'start'), start_date),
            ((first_block, 'due'), None),
            ((second_block, 'due'), None),
            ((end_block, 'end'), end_date),
        ]
        assert api.get_dates_for_course(course_key,
                                        schedule=self.schedule) == dict(dates)
Ejemplo n.º 7
0
    def test_clear_dates_for_course(self):
        items = self.test_get_dates_for_course()
        keep_date = models.ContentDate.objects.get(location=items[1][0])

        with self.assertNumQueries(1):
            api._clear_dates_for_course(items[0][0].course_key, keep=[keep_date.id])  # pylint: disable=protected-access

        retrieved = api.get_dates_for_course(items[0][0].course_key, use_cached=False)
        self.assertEqual(len(retrieved), 1)
        self.assertEqual(list(retrieved.keys())[0][0], items[1][0])

        with self.assertNumQueries(1):
            api._clear_dates_for_course(items[0][0].course_key)  # pylint: disable=protected-access
        self.assertEqual(api.get_dates_for_course(items[0][0].course_key, use_cached=False), {})
Ejemplo n.º 8
0
    def test_get_user_date_no_schedule(self):
        items = make_items()
        course_key = items[0][0].course_key
        api.set_dates_for_course(course_key, items)
        before_override = api.get_dates_for_course(course_key, user=self.user)
        assert len(before_override) == 3

        # Override a date for the user with a relative date, but remove the schedule
        # so that the override can't be applied
        api.set_date_for_block(course_key, items[0][0], 'due', timedelta(days=2), user=self.user)
        self.schedule.delete()

        after_override = api.get_dates_for_course(course_key, user=self.user, use_cached=False)
        assert before_override == after_override
Ejemplo n.º 9
0
def get_units_with_due_date(course):
    """
    Returns all top level units which have due dates.  Does not return
    descendents of those nodes.
    """
    units = []

    # Pass in a schedule here so that we get back any relative dates in the course, but actual value
    # doesn't matter, since we don't care about the dates themselves, just whether they exist.
    # Thus we don't save or care about this temporary schedule object.
    schedule = Schedule(start_date=course.start)
    course_dates = api.get_dates_for_course(course.id, schedule=schedule)

    def visit(node):
        """
        Visit a node.  Checks to see if node has a due date and appends to
        `units` if it does.  Otherwise recurses into children to search for
        nodes with due dates.
        """
        if (node.location, 'due') in course_dates:
            units.append(node)
        else:
            for child in node.get_children():
                visit(child)

    visit(course)
    #units.sort(key=_title_or_url)
    return units
Ejemplo n.º 10
0
    def test_set_user_override(self, initial_date, override_date,
                               expected_date):
        items = make_items()
        first = items[0]
        block_id = first[0]
        items[0][1]['due'] = initial_date

        api.set_dates_for_course(str(block_id.course_key), items)

        api.set_date_for_block(block_id.course_key,
                               block_id,
                               'due',
                               override_date,
                               user=self.user)
        DEFAULT_REQUEST_CACHE.clear()
        cache.clear()
        retrieved = api.get_dates_for_course(block_id.course_key,
                                             user=self.user.id)
        assert len(retrieved) == NUM_OVERRIDES
        assert retrieved[block_id, 'due'] == expected_date

        overrides = api.get_overrides_for_block(block_id.course_key, block_id)
        assert len(overrides) == 1
        assert overrides[0][2] == expected_date

        overrides = list(
            api.get_overrides_for_user(block_id.course_key, self.user))
        assert len(overrides) == 1
        assert overrides[0] == {
            'location': block_id,
            'actual_date': expected_date
        }
Ejemplo n.º 11
0
def get_course_assignment_due_dates(course, user, request, num_return=None, include_past_dates=False):
    """
    Returns a list of assignment (at the subsection/sequential level) due date
    blocks for the given course. Will return num_return results or all results
    if num_return is None in date increasing order.
    """
    store = modulestore()
    all_course_dates = get_dates_for_course(course.id, user)
    date_blocks = []
    for (block_key, date_type), date in all_course_dates.items():
        if date_type == 'due' and block_key.block_type == 'sequential':
            item = store.get_item(block_key)
            if item.graded:
                date_block = CourseAssignmentDate(course, user)
                date_block.date = date

                block_url = None
                now = datetime.now().replace(tzinfo=pytz.UTC)
                assignment_released = item.start < now if item.start else None
                if assignment_released:
                    block_url = reverse('jump_to', args=[course.id, block_key])
                    block_url = request.build_absolute_uri(block_url) if request else None
                assignment_title = item.display_name if item.display_name else _('Assignment')
                date_block.set_title(assignment_title, link=block_url)

                date_blocks.append(date_block)
    date_blocks = sorted((b for b in date_blocks if b.is_enabled or include_past_dates), key=date_block_key_fn)
    if num_return:
        return date_blocks[:num_return]
    return date_blocks
Ejemplo n.º 12
0
    def test_relative_date_past_end_date(self):
        course_key = CourseLocator('testX', 'tt101', '2019')
        start_block = make_block_id(course_key, block_type='course')
        start_date = datetime(2019, 3, 15)
        before_end_date_block = make_block_id(course_key)
        before_end_date_delta = timedelta(days=1)
        before_end_date = self.schedule.start_date + before_end_date_delta
        after_end_date_block = make_block_id(course_key)
        after_end_date_delta = timedelta(days=10)
        end_block = make_block_id(course_key, block_type='course')
        end_date = datetime(2019, 4, 4)
        self.schedule.created = datetime(2019, 3, 20)  # set a while back, before the 4/1 start_date
        self.schedule.save()
        items = [
            (start_block, {'start': start_date}),  # start dates are always absolute
            (before_end_date_block, {'due': before_end_date_delta}),  # relative
            (after_end_date_block, {'due': after_end_date_delta}),  # relative
            (end_block, {'end': end_date}),  # end dates are always absolute
        ]
        api.set_dates_for_course(course_key, items)

        dates = [
            ((start_block, 'start'), start_date),
            ((before_end_date_block, 'due'), before_end_date),
            # Because the end date for this block would have been after the course end date,
            # the block will have an end date of the course end date
            ((after_end_date_block, 'due'), end_date),
            ((end_block, 'end'), end_date),
        ]
        assert api.get_dates_for_course(course_key, schedule=self.schedule) == dict(dates)
Ejemplo n.º 13
0
    def test_get_dates_for_course_query_counts(self, has_schedule,
                                               pass_user_object, pass_schedule,
                                               item_count):
        if not has_schedule:
            self.schedule.delete()
        items = [(make_block_id(self.course.id), {
            'due': datetime(2020, 1, 1) + timedelta(days=i)
        }) for i in range(item_count)]
        api.set_dates_for_course(self.course.id, items)

        user = self.user if pass_user_object else self.user.id
        schedule = self.schedule if pass_schedule and has_schedule else None

        if has_schedule and pass_schedule:
            query_count = 2
        else:
            query_count = 3
        with self.assertNumQueries(query_count):
            dates = api.get_dates_for_course(course_id=self.course.id,
                                             user=user,
                                             schedule=schedule)

        # Second time, the request cache eliminates all querying...
        with self.assertNumQueries(0):
            cached_dates = api.get_dates_for_course(course_id=self.course.id,
                                                    user=user,
                                                    schedule=schedule)
            assert dates == cached_dates

        # Now wipe the request cache...
        DEFAULT_REQUEST_CACHE.clear()

        # This time, test the external cache (which eliminates the one large
        # query to ContentDates).
        with self.assertNumQueries(query_count - 1):
            externally_cached_dates = api.get_dates_for_course(
                course_id=self.course.id, user=user, schedule=schedule)
            assert dates == externally_cached_dates

        # Finally, force uncached behavior with used_cache=False
        with self.assertNumQueries(query_count):
            uncached_dates = api.get_dates_for_course(course_id=self.course.id,
                                                      user=user,
                                                      schedule=schedule,
                                                      use_cached=False)
            assert dates == uncached_dates
Ejemplo n.º 14
0
    def test_get_dates_for_course_query_counts(self, has_schedule, pass_user_object, pass_schedule, item_count):
        if not has_schedule:
            self.schedule.delete()
        items = [
            (make_block_id(self.course.id), {'due': datetime(2020, 1, 1) + timedelta(days=i)})
            for i in range(item_count)
        ]
        api.set_dates_for_course(self.course.id, items)

        user = self.user if pass_user_object else self.user.id
        schedule = self.schedule if pass_schedule and has_schedule else None

        if has_schedule and pass_schedule:
            query_count = 2
        else:
            query_count = 3
        with self.assertNumQueries(query_count):
            dates = api.get_dates_for_course(
                course_id=self.course.id, user=user, schedule=schedule
            )

        # Second time, the request cache eliminates all querying (sometimes)...
        # If a schedule is not provided, we will get the schedule to avoid caching outdated dates
        with self.assertNumQueries(0 if schedule else 1):
            cached_dates = api.get_dates_for_course(
                course_id=self.course.id, user=user, schedule=schedule
            )
            assert dates == cached_dates

        # Now wipe all cache tiers...
        TieredCache.dangerous_clear_all_tiers()

        # No cached values - so will do *all* queries again.
        with self.assertNumQueries(query_count):
            externally_cached_dates = api.get_dates_for_course(
                course_id=self.course.id, user=user, schedule=schedule
            )
            assert dates == externally_cached_dates

        # Finally, force uncached behavior with used_cache=False
        with self.assertNumQueries(query_count):
            uncached_dates = api.get_dates_for_course(
                course_id=self.course.id, user=user, schedule=schedule, use_cached=False
            )
            assert dates == uncached_dates
Ejemplo n.º 15
0
    def test_set_date_for_block(self, initial_date, override_date, expected_date):
        items = make_items()
        first = items[0]
        block_id = first[0]
        items[0][1]['due'] = initial_date

        api.set_dates_for_course(str(block_id.course_key), items)
        api.set_date_for_block(block_id.course_key, block_id, 'due', override_date)
        TieredCache.dangerous_clear_all_tiers()
        retrieved = api.get_dates_for_course(block_id.course_key, user=self.user.id)
        assert len(retrieved) == NUM_OVERRIDES
        assert retrieved[block_id, 'due'] == expected_date
    def load_data(self):
        """Pull dates information from edx-when."""
        # (usage_key, 'due'): datetime.datetime(2019, 12, 11, 15, 0, tzinfo=<UTC>)
        # TODO: Merge https://github.com/edx/edx-when/pull/48 and add `outline_only=True`
        self.dates = get_dates_for_course(self.course_key, self.user)

        for (usage_key, field_name), date in self.dates.items():
            self.keys_to_schedule_fields[usage_key][field_name] = date

        course_usage_key = self.course_key.make_usage_key('course', 'course')
        self._course_start = self.keys_to_schedule_fields[course_usage_key].get('start')
        self._course_end = self.keys_to_schedule_fields[course_usage_key].get('end')
        self._is_beta_tester = user_has_role(self.user, CourseBetaTesterRole(self.course_key))
def get_due_dates(request, course_key, user):
    """
    Get due date information for a user for blocks in a course.

    Arguments:
        request: the request object
        course_key (CourseKey): the CourseKey for the course
        user: the user object for which we want due date information

    Returns:
        due_dates (list): a list of dictionaries containing due date information
            keys:
                name: the display name of the block
                url: the deep link to the block
                date: the due date for the block
    """
    try:
        outline = get_course_outline(course_key)
    except (ValueError, CourseOutlineData.DoesNotExist):
        # Either this course is Old Mongo-backed or doesn't have a generated course outline.
        course_version = None
    else:
        course_version = outline.published_version

    dates = get_dates_for_course(course_key,
                                 user,
                                 published_version=course_version)

    store = modulestore()

    due_dates = []
    for (block_key, date_type), date in dates.items():
        if date_type == 'due':
            try:
                block_display_name = store.get_item(block_key).display_name
            except ItemNotFoundError:
                logger.exception(
                    f'Failed to get block for due date item with key: {block_key}'
                )
                block_display_name = UNKNOWN_BLOCK_DISPLAY_NAME

            # get url to the block in the course
            block_url = reverse('jump_to', args=[course_key, block_key])
            block_url = request.build_absolute_uri(block_url)

            due_dates.append({
                'name': block_display_name,
                'url': block_url,
                'date': date,
            })
    return due_dates
Ejemplo n.º 18
0
    def test_remove_user_override(self, initial_date, override_date, expected_date):
        items = make_items()
        first = items[0]
        block_id = first[0]
        items[0][1]['due'] = initial_date

        api.set_dates_for_course(str(block_id.course_key), items)

        api.set_date_for_block(block_id.course_key, block_id, 'due', override_date, user=self.user)
        TieredCache.dangerous_clear_all_tiers()
        retrieved = api.get_dates_for_course(block_id.course_key, user=self.user.id)
        assert len(retrieved) == NUM_OVERRIDES
        assert retrieved[block_id, 'due'] == expected_date

        api.set_date_for_block(block_id.course_key, block_id, 'due', None, user=self.user)
        TieredCache.dangerous_clear_all_tiers()
        retrieved = api.get_dates_for_course(block_id.course_key, user=self.user.id)
        assert len(retrieved) == NUM_OVERRIDES
        if isinstance(initial_date, timedelta):
            user_initial_date = self.schedule.start_date + initial_date
        else:
            user_initial_date = initial_date
        assert retrieved[block_id, 'due'] == user_initial_date
Ejemplo n.º 19
0
    def test_set_date_for_block(self, initial_date, override_date,
                                expected_date):
        items = make_items()
        first = items[0]
        block_id = first[0]
        items[0][1]['due'] = initial_date

        api.set_dates_for_course(str(block_id.course_key), items)

        api.set_date_for_block(block_id.course_key, block_id, 'due',
                               override_date)
        DEFAULT_REQUEST_CACHE.clear()
        cache.clear()
        retrieved = api.get_dates_for_course(block_id.course_key,
                                             user=self.user.id)
        assert len(retrieved) == NUM_OVERRIDES
        assert retrieved[block_id, 'due'] == expected_date
Ejemplo n.º 20
0
def get_due_dates(request, course_key, user):
    """
    Get due date information for a user for blocks in a course.

    Arguments:
        request: the request object
        course_key (CourseKey): the CourseKey for the course
        user: the user object for which we want due date information

    Returns:
        due_dates (list): a list of dictionaries containing due date information
            keys:
                name: the display name of the block
                url: the deep link to the block
                date: the due date for the block
    """
    dates = get_dates_for_course(
        course_key,
        user,
    )

    store = modulestore()

    due_dates = []
    for (block_key, date_type), date in six.iteritems(dates):
        if date_type == 'due':
            try:
                block_display_name = store.get_item(block_key).display_name
            except ItemNotFoundError:
                logger.exception(
                    'Failed to get block for due date item with key: {}'.
                    format(block_key))
                block_display_name = UNKNOWN_BLOCK_DISPLAY_NAME

            # get url to the block in the course
            block_url = reverse('jump_to', args=[course_key, block_key])
            block_url = request.build_absolute_uri(block_url)

            due_dates.append({
                'name': block_display_name,
                'url': block_url,
                'date': date,
            })
    return due_dates
Ejemplo n.º 21
0
    def get_due_dates(request, course_key, user):
        """
        Get due date information for a user for blocks in a course.

        Arguments:
            request: the request object
            course_key (CourseKey): the CourseKey for the course
            user: the user object for which we want due date information

        Returns:
            due_dates (list): a list of dictionaries containing due date information
                keys:
                    name: the display name of the block
                    url: the deep link to the block
                    date: the due date for the block
        """
        dates = get_dates_for_course(
            course_key,
            user,
        )

        store = modulestore()

        due_dates = []
        for (block_key, date_type), date in iteritems(dates):
            if date_type == 'due':
                block = store.get_item(block_key)

                # get url to the block in the course
                block_url = reverse('jump_to', args=[course_key, block_key])
                block_url = request.build_absolute_uri(block_url)

                due_dates.append({
                    'name': block.display_name,
                    'url': block_url,
                    'date': date,
                })
        return due_dates
Ejemplo n.º 22
0
    def get_due_dates(request, course_key, user):
        """
        Get due date information for a user for blocks in a course.

        Arguments:
            request: the request object
            course_key (CourseKey): the CourseKey for the course
            user: the user object for which we want due date information

        Returns:
            due_dates (list): a list of dictionaries containing due date information
                keys:
                    name: the display name of the block
                    url: the deep link to the block
                    date: the due date for the block
        """
        dates = get_dates_for_course(
            course_key,
            user,
        )

        store = modulestore()

        due_dates = []
        for (block_key, date_type), date in iteritems(dates):
            if date_type == 'due':
                block = store.get_item(block_key)

                # get url to the block in the course
                block_url = reverse('jump_to', args=[course_key, block_key])
                block_url = request.build_absolute_uri(block_url)

                due_dates.append({
                    'name': block.display_name,
                    'url': block_url,
                    'date': date,
                })
        return due_dates
Ejemplo n.º 23
0
def get_course_assignments(course_key, user, request, include_access=False):
    """
    Returns a list of assignment (at the subsection/sequential level) due dates for the given course.

    Each returned object is a namedtuple with fields: block_key, title, url, date, requires_full_access
    """
    store = modulestore()
    all_course_dates = get_dates_for_course(course_key, user)
    block_data = course_blocks_api.get_course_blocks(
        user, store.make_course_usage_key(course_key))
    assignments = []
    for (block_key, date_type), date in all_course_dates.items():
        if date_type != 'due' or block_key.block_type != 'sequential':
            continue

        if block_key not in block_data:
            continue

        block = block_data[block_key]
        if not block.graded:
            continue

        requires_full_access = include_access and _requires_full_access(
            block_data, block, user)
        title = block.display_name or _('Assignment')

        url = None
        assignment_released = not block.start or block.start < datetime.now(
            pytz.UTC)
        if assignment_released:
            url = reverse('jump_to', args=[course_key, block_key])
            url = request and request.build_absolute_uri(url)

        assignments.append(
            _Assignment(block_key, title, url, date, requires_full_access))

    return assignments
Ejemplo n.º 24
0
def get_course_assignments(course_key, user, request, include_access=False):
    """
    Returns a list of assignment (at the subsection/sequential level) due dates for the given course.

    Each returned object is a namedtuple with fields: block_key, title, url, date, requires_full_access
    """
    store = modulestore()
    all_course_dates = get_dates_for_course(course_key, user)
    assignments = []
    for (block_key, date_type), date in all_course_dates.items():
        if date_type != 'due' or block_key.block_type != 'sequential':
            continue

        try:
            item = store.get_item(block_key)
        except ItemNotFoundError:
            continue

        if not item.graded:
            continue

        requires_full_access = include_access and _requires_full_access(
            store, user, block_key)
        title = item.display_name or _('Assignment')

        url = None
        assignment_released = not item.start or item.start < datetime.now(
            pytz.UTC)
        if assignment_released:
            url = reverse('jump_to', args=[course_key, block_key])
            url = request and request.build_absolute_uri(url)

        assignments.append(
            _Assignment(block_key, title, url, date, requires_full_access))

    return assignments
Ejemplo n.º 25
0
    def test_allow_relative_dates(self):
        course_key = CourseLocator('testX', 'tt101', '2019')
        block1 = make_block_id(course_key)
        date1 = datetime(2019, 3, 22)
        block2 = make_block_id(course_key)
        date2 = datetime(2019, 3, 23)
        date2_override_delta = timedelta(days=10)
        date2_override = date2 + date2_override_delta
        block3 = make_block_id(course_key)
        date3_delta = timedelta(days=1)
        date3 = self.schedule.start_date + date3_delta
        block4 = make_block_id(course_key)
        date4_delta = timedelta(days=2)
        date4 = self.schedule.start_date + date4_delta
        date4_override = datetime(2019, 4, 24)
        items = [
            (block1, {
                'due': date1
            }),  # absolute
            (block2, {
                'due': date2
            }),  # absolute, to be overwritten by relative date
            (block3, {
                'due': date3_delta
            }),  # relative
            (block4, {
                'due': date4_delta
            }),  # relative, to be overwritten by absolute date
        ]
        api.set_dates_for_course(course_key, items)
        api.set_date_for_block(course_key,
                               block2,
                               'due',
                               date2_override_delta,
                               user=self.user)
        api.set_date_for_block(course_key,
                               block4,
                               'due',
                               date4_override,
                               user=self.user)

        # get_dates_for_course
        dates = [
            ((block1, 'due'), date1),
            ((block2, 'due'), date2),
            ((block3, 'due'), date3),
            ((block4, 'due'), date4),
        ]
        user_dates = [
            ((block1, 'due'), date1),
            ((block2, 'due'), date2_override),
        ]
        assert api.get_dates_for_course(course_key,
                                        schedule=self.schedule) == dict(dates)
        with patch('edx_when.api._are_relative_dates_enabled',
                   return_value=False):
            assert api.get_dates_for_course(
                course_key, schedule=self.schedule) == dict(dates[0:2])
            assert api.get_dates_for_course(course_key,
                                            schedule=self.schedule,
                                            user=self.user) == dict(user_dates)

        # get_date_for_block
        assert api.get_date_for_block(course_key, block2) == date2
        assert api.get_date_for_block(course_key, block4,
                                      user=self.user) == date4_override
        with patch('edx_when.api._are_relative_dates_enabled',
                   return_value=False):
            assert api.get_date_for_block(course_key, block2) == date2
            assert api.get_date_for_block(course_key, block1,
                                          user=self.user) == date1
            assert api.get_date_for_block(course_key, block2,
                                          user=self.user) == date2_override
            assert api.get_date_for_block(course_key, block4,
                                          user=self.user) is None

        # get_overrides_for_block
        block2_overrides = [(self.user.username, 'unknown', date2_override)]
        assert api.get_overrides_for_block(course_key,
                                           block2) == block2_overrides
        with patch('edx_when.api._are_relative_dates_enabled',
                   return_value=False):
            assert api.get_overrides_for_block(course_key, block2) == [
                (self.user.username, 'unknown', date2_override)
            ]

        # get_overrides_for_user
        user_overrides = [
            {
                'location': block4,
                'actual_date': date4_override
            },
            {
                'location': block2,
                'actual_date': date2_override
            },
        ]
        assert list(api.get_overrides_for_user(course_key,
                                               self.user)) == user_overrides
        with patch('edx_when.api._are_relative_dates_enabled',
                   return_value=False):
            assert list(api.get_overrides_for_user(
                course_key, self.user)) == user_overrides
Ejemplo n.º 26
0
 def test_clear_dates_for_course(self):
     items = self.test_get_dates_for_course()
     api.clear_dates_for_course(items[0][0].course_key)
     retrieved = api.get_dates_for_course(items[0][0].course_key,
                                          use_cached=False)
     assert not retrieved
Ejemplo n.º 27
0
def set_due_date_extension(course,
                           unit,
                           student,
                           due_date,
                           actor=None,
                           reason=''):
    """
    Sets a due date extension.

    Raises:
        DashboardError if the unit or extended, due date is invalid or user is
        not enrolled in the course.
    """
    mode, __ = CourseEnrollment.enrollment_mode_for_user(
        user=student, course_id=six.text_type(course.id))
    if not mode:
        raise DashboardError(
            _("Could not find student enrollment in the course."))

    # We normally set dates at the subsection level. But technically dates can be anywhere down the tree (and
    # usually are in self paced courses, where the subsection date gets propagated down).
    # So find all children that we need to set the date on, then set those dates.
    course_dates = api.get_dates_for_course(course.id, user=student)
    blocks_to_set = {
        unit
    }  # always include the requested unit, even if it doesn't appear to have a due date now

    def visit(node):
        """
        Visit a node.  Checks to see if node has a due date and appends to
        `blocks_to_set` if it does.  And recurses into children to search for
        nodes with due dates.
        """
        if (node.location, 'due') in course_dates:
            blocks_to_set.add(node)
        for child in node.get_children():
            visit(child)

    visit(unit)

    for block in blocks_to_set:
        if due_date:
            try:
                api.set_date_for_block(course.id,
                                       block.location,
                                       'due',
                                       due_date,
                                       user=student,
                                       reason=reason,
                                       actor=actor)
            except api.MissingDateError:
                raise DashboardError(
                    _(u"Unit {0} has no due date to extend.").format(
                        unit.location))
            except api.InvalidDateError:
                raise DashboardError(
                    _("An extended due date must be later than the original due date."
                      ))
        else:
            api.set_date_for_block(course.id,
                                   block.location,
                                   'due',
                                   None,
                                   user=student,
                                   reason=reason,
                                   actor=actor)