def test_announcement_news(self):
        actions.login('*****@*****.**')
        actions.register(self, 'John Doe')
        time.sleep(1)
        locale = 'de'
        announcement = self._add_announcement_and_translation(
            locale, is_draft=True)
        sent_data = {
            'key': str(announcement.key()),
            'title': 'Test Announcement',
            'date': utc.to_text(seconds=utc.now_as_timestamp()),
            'is_draft': False,
        }
        actions.login(self.ADMIN_EMAIL)
        response = self._put_announcement(sent_data)
        actions.login('*****@*****.**')

        # Verify announcement news item using news API directly
        news_items = news.CourseNewsDao.get_news_items()
        self.assertEquals(1, len(news_items))
        item = news_items[0]
        now_timestamp = utc.now_as_timestamp()
        self.assertEquals(
            announcements.AnnouncementsStudentHandler.URL.lstrip('/'), item.url)
        self.assertEquals(
            str(announcements.TranslatableResourceAnnouncement.key_for_entity(
                announcement)),
            item.resource_key)
        self.assertAlmostEqual(
            now_timestamp, utc.datetime_to_timestamp(item.when), delta=10)

        # Verify announcement news item looking at HTTP response to /course
        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals(
            [news_tests_lib.NewsItem(
                'Test Announcement',
                announcements.AnnouncementsStudentHandler.URL.lstrip('/'),
                True)],
            news_tests_lib.extract_news_items_from_soup(soup))

        # Verify announcement news item translated title.
        self._set_prefs_locale(locale)
        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals(
            [news_tests_lib.NewsItem(
                'TEST ANNOUNCEMENT',
                announcements.AnnouncementsStudentHandler.URL.lstrip('/'),
                True)],
            news_tests_lib.extract_news_items_from_soup(soup))

        # Delete the announcement; news item should also go away.
        actions.login(self.ADMIN_EMAIL)
        self._delete_announcement(str(announcement.key()))
        actions.login('*****@*****.**')
        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals([], news_tests_lib.extract_news_items_from_soup(soup))
Пример #2
0
    def seconds_since_last_modified(self, now=None):
        lm = self.last_modified  # Copy to lessen races with other modifiers.
        if not lm:
            return 0  # Last modified not recorded, so no elapsed time since.

        now = now if now is not None else utc.now_as_timestamp()
        return now - lm
Пример #3
0
    def setUp(self):
        self.now_seconds = utc.now_as_timestamp()
        self.now_st = time.gmtime(self.now_seconds)
        self.now_text = time.strftime(self.ISO_8601_STRUCT_TIME_FORMAT, self.now_st)
        self.now_dt = datetime.datetime.utcfromtimestamp(self.now_seconds)

        self.day_dt = datetime.datetime(self.now_dt.year, self.now_dt.month, self.now_dt.day)
        self.day_st = (
            self.now_st.tm_year,
            self.now_st.tm_mon,
            self.now_st.tm_mday,
            0,
            0,
            0,  # Force to 00:00:00, leaving all else unchanged.
            self.now_st.tm_wday,
            self.now_st.tm_yday,
            self.now_st.tm_isdst,
        )

        self.month_dt = datetime.datetime(self.now_dt.year, self.now_dt.month, 1)
        self.month_st = self.month_dt.utctimetuple()
        self.month_text = time.strftime(self.ISO_8601_STRUCT_TIME_FORMAT, self.month_st)

        self.year_dt = datetime.datetime(self.now_dt.year, 1, 1)
        self.year_st = self.year_dt.utctimetuple()
        self.year_text = time.strftime(self.ISO_8601_STRUCT_TIME_FORMAT, self.year_st)
Пример #4
0
    def seconds_since_last_modified(self, now=None):
        lm = self.last_modified  # Copy to lessen races with other modifiers.
        if not lm:
            return 0  # Last modified not recorded, so no elapsed time since.

        now = now if now is not None else utc.now_as_timestamp()
        return now - lm
    def test_get_news_some_old_some_new(self):
        user = actions.login(self.STUDENT_EMAIL)
        actions.register(self, 'John Smith')

        # Newsworthy thing happened beyond newsworthy time limit,
        then_ts = utc.now_as_timestamp() - news.NEWSWORTHINESS_SECONDS - 1
        then = utc.timestamp_to_datetime(then_ts)
        self._set_student_enroll_date(user, then)
        news_item = news.NewsItem('test:one', 'url_one', then)
        news.CourseNewsDao.add_news_item(news_item)
        news_item = news.NewsItem('test:two', 'url_two', then)
        news.CourseNewsDao.add_news_item(news_item)
        # But student has seen the thing, so it's marked as non-new.
        news.StudentNewsDao.mark_item_seen('test:one')

        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals(['has_new_news'],
                          self._get_news_title_styles(soup))
        self.assertEquals(
            [
                news_tests_lib.NewsItem('Test Item two', 'url_two', True),
                news_tests_lib.NewsItem('Test Item one', 'url_one', False),
            ],
            news_tests_lib.extract_news_items_from_soup(soup))
Пример #6
0
    def test_news_notification(self):
        assessment = self.course.add_assessment()
        assessment.title = 'Assessment'
        assessment.html_content = 'assessment content'
        assessment.availability = courses.AVAILABILITY_AVAILABLE
        self.course.save()
        self.certificate_criteria.append(
            {'assessment_id': assessment.unit_id, 'pass_percent': 70.0})
        actions.submit_assessment(
            self, assessment.unit_id,
            {'answers': '', 'score': 70, 'assessment_type': assessment.unit_id},
            presubmit_checks=False)

        news_items = news.StudentNewsDao.get_news_items()
        self.assertEquals(1, len(news_items))
        item = news_items[0]
        now_ts = utc.now_as_timestamp()
        self.assertEquals(certificate.CERTIFICATE_HANDLER_PATH, item.url)
        self.assertEquals(certificate.RESOURCE_KEY, item.resource_key)
        self.assertAlmostEqual(
            now_ts, utc.datetime_to_timestamp(item.when), delta=10)

        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals(
            [news_tests_lib.NewsItem(
                'Course completion certificate earned!',
                certificate.CERTIFICATE_HANDLER_PATH, True)],
            news_tests_lib.extract_news_items_from_soup(soup))
    def setUp(self):
        self.now_seconds = utc.now_as_timestamp()
        self.now_st = time.gmtime(self.now_seconds)
        self.now_text = time.strftime(self.ISO_8601_STRUCT_TIME_FORMAT,
                                      self.now_st)
        self.now_dt = datetime.datetime.utcfromtimestamp(self.now_seconds)

        self.day_dt = datetime.datetime(self.now_dt.year, self.now_dt.month,
                                        self.now_dt.day)
        self.day_st = (
            self.now_st.tm_year,
            self.now_st.tm_mon,
            self.now_st.tm_mday,
            0,
            0,
            0,  # Force to 00:00:00, leaving all else unchanged.
            self.now_st.tm_wday,
            self.now_st.tm_yday,
            self.now_st.tm_isdst)

        self.month_dt = datetime.datetime(self.now_dt.year, self.now_dt.month,
                                          1)
        self.month_st = self.month_dt.utctimetuple()
        self.month_text = time.strftime(self.ISO_8601_STRUCT_TIME_FORMAT,
                                        self.month_st)

        self.year_dt = datetime.datetime(self.now_dt.year, 1, 1)
        self.year_st = self.year_dt.utctimetuple()
        self.year_text = time.strftime(self.ISO_8601_STRUCT_TIME_FORMAT,
                                       self.year_st)
 def should_start_jobs():
     now_timestamp = utc.hour_start(utc.now_as_timestamp())
     status = StartAvailabilityJobsStatus.get_singleton()
     last_run = utc.hour_start(
         utc.datetime_to_timestamp(status.last_run))
     if now_timestamp > last_run:
         status.last_run = utc.timestamp_to_datetime(now_timestamp)
         StartAvailabilityJobsStatus.update_singleton(status)
         return True
     return False
Пример #9
0
 def should_start_jobs():
     now_timestamp = utc.hour_start(utc.now_as_timestamp())
     status = StartAvailabilityJobsStatus.get_singleton()
     last_run = utc.hour_start(
         utc.datetime_to_timestamp(status.last_run))
     if now_timestamp > last_run:
         status.last_run = utc.timestamp_to_datetime(now_timestamp)
         StartAvailabilityJobsStatus.update_singleton(status)
         return True
     return False
    def test_news_label_filtering(self):
        actions.login(self.STUDENT_EMAIL)
        actions.register(self, 'John Smith')

        label_foo = models.LabelDAO.save(models.LabelDTO(
            None, {'title': 'Foo',
                   'descripton': 'foo',
                   'type': models.LabelDTO.LABEL_TYPE_COURSE_TRACK}))
        label_bar = models.LabelDAO.save(models.LabelDTO(
            None, {'title': 'Bar',
                   'descripton': 'bar',
                   'type': models.LabelDTO.LABEL_TYPE_COURSE_TRACK}))

        now_ts = utc.now_as_timestamp() + 3  # Avoid filtering in-past items
        news.CourseNewsDao.add_news_item(news.NewsItem(
            'test:no_labels', 'url_no_labels',
            when=utc.timestamp_to_datetime(now_ts)))
        news.CourseNewsDao.add_news_item(news.NewsItem(
            'test:with_labels', 'url_with_labels',
            labels=common_utils.list_to_text([label_foo]),
            when=utc.timestamp_to_datetime(now_ts - 1)))

        # Student starts life with no labels, so should match both items.
        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals(
            [news_tests_lib.NewsItem(
                'Test Item no_labels', 'url_no_labels', True),
             news_tests_lib.NewsItem(
                 'Test Item with_labels', 'url_with_labels', True)],
            news_tests_lib.extract_news_items_from_soup(soup))

        # Apply non-matching label to Student; should not see labeled news.
        models.Student.set_labels_for_current(
            common_utils.list_to_text([label_bar]))
        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals(
            [news_tests_lib.NewsItem(
                'Test Item no_labels', 'url_no_labels', True)],
            news_tests_lib.extract_news_items_from_soup(soup))

        # Apply matching label to Student; should again see labeled news.
        models.Student.set_labels_for_current(
            common_utils.list_to_text([label_foo, label_bar]))
        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals(
            [news_tests_lib.NewsItem(
                'Test Item no_labels', 'url_no_labels', True),
             news_tests_lib.NewsItem(
                 'Test Item with_labels', 'url_with_labels', True)],
            news_tests_lib.extract_news_items_from_soup(soup))
    def test_create_announcement_defaults(self):
        key = self._add_announcement()
        data = self._get_announcement(key)

        expected_date = utc.to_text(
            seconds=utc.day_start(utc.now_as_timestamp()))
        self.assertEquals(data['date'], expected_date)
        expected_key = str(db.Key.from_path(
            announcements.AnnouncementEntity.kind(), 1))
        self.assertEquals(data['key'], expected_key)
        self.assertEquals(data['html'], '')
        self.assertEquals(data['is_draft'], True)
        self.assertEquals(
            data['title'],
            announcements.AnnouncementsDashboardHandler.DEFAULT_TITLE_TEXT)
    def test_create_announcement_defaults(self):
        key = self._add_announcement()
        data = self._get_announcement(key)

        expected_date = utc.to_text(
            seconds=utc.day_start(utc.now_as_timestamp()))
        self.assertEquals(data['date'], expected_date)
        expected_key = str(
            db.Key.from_path(announcements.AnnouncementEntity.kind(), 1))
        self.assertEquals(data['key'], expected_key)
        self.assertEquals(data['html'], '')
        self.assertEquals(data['is_draft'], True)
        self.assertEquals(
            data['title'],
            announcements.AnnouncementsDashboardHandler.DEFAULT_TITLE_TEXT)
Пример #13
0
    def reduce(cls, key, values):
        total = sum(int(value) for value in values)
        ns_name = namespace_manager.get_namespace()

        if key == TotalEnrollmentEntity.COUNTING:
            TotalEnrollmentDAO.set(ns_name, total)
            yield key, total
        else:
            # key is actually a daily 'adds' counter bin seconds since epoch.
            bin_seconds_since_epoch = long(key)
            today = utc.day_start(utc.now_as_timestamp())
            # Avoid race conditions by not updating today's daily bin (which
            # is being updated by student lifecycle events).
            if bin_seconds_since_epoch != today:
                date_time = utc.timestamp_to_datetime(bin_seconds_since_epoch)
                EnrollmentsAddedDAO.set(ns_name, date_time, total)
Пример #14
0
    def reduce(cls, key, values):
        total = sum(int(value) for value in values)
        ns_name = namespace_manager.get_namespace()

        if key == TotalEnrollmentEntity.COUNTING:
            TotalEnrollmentDAO.set(ns_name, total)
            yield key, total
        else:
            # key is actually a daily 'adds' counter bin seconds since epoch.
            bin_seconds_since_epoch = long(key)
            today = utc.day_start(utc.now_as_timestamp())
            # Avoid race conditions by not updating today's daily bin (which
            # is being updated by student lifecycle events).
            if bin_seconds_since_epoch != today:
                date_time = utc.timestamp_to_datetime(bin_seconds_since_epoch)
                EnrollmentsAddedDAO.set(ns_name, date_time, total)
    def test_get_news_unseen(self):
        user = actions.login(self.STUDENT_EMAIL)
        actions.register(self, 'John Smith')

        # Added item is older than newsworthiness cutoff, but student has
        # not been marked as having seen it, so it's new news.
        then_ts = utc.now_as_timestamp() - news.NEWSWORTHINESS_SECONDS - 1
        then = utc.timestamp_to_datetime(then_ts)
        self._set_student_enroll_date(user, then)
        news_item = news.NewsItem('test:key', 'test:url', then)
        news.CourseNewsDao.add_news_item(news_item)

        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals(['has_new_news'],
                          self._get_news_title_styles(soup))
        self.assertEquals(
            [news_tests_lib.NewsItem('Test Item key', 'test:url', True)],
            news_tests_lib.extract_news_items_from_soup(soup))
    def test_student_and_course_news(self):
        user = actions.login(self.STUDENT_EMAIL)
        actions.register(self, 'John Smith')

        then_ts = utc.now_as_timestamp() - news.NEWSWORTHINESS_SECONDS - 1
        then = utc.timestamp_to_datetime(then_ts)
        self._set_student_enroll_date(user, then)
        news_item = news.NewsItem('test:one', 'url_one', then)
        news.CourseNewsDao.add_news_item(news_item)
        news_item = news.NewsItem('test:two', 'url_two', then)
        news.StudentNewsDao.add_news_item(news_item)

        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals(['has_new_news'],
                          self._get_news_title_styles(soup))
        self.assertEquals(
            [
                news_tests_lib.NewsItem('Test Item two', 'url_two', True),
                news_tests_lib.NewsItem('Test Item one', 'url_one', True),
            ],
            news_tests_lib.extract_news_items_from_soup(soup))
    def _test_add_newer_and_older_news_item(self, dao_class):
        now_ts = utc.now_as_timestamp()

        older = news.NewsItem('test:key', 'test:url',
                              when=utc.timestamp_to_datetime(now_ts))
        newer = news.NewsItem('test:key', 'test:url',
                              when=utc.timestamp_to_datetime(now_ts + 1))

        dao_class.add_news_item(older)
        self.assertEquals([older], dao_class.get_news_items())

        # Newer items added with no-overwrite flag do not displace older item.
        dao_class.add_news_item(newer, overwrite_existing=False)
        self.assertEquals([older], dao_class.get_news_items())

        # Newer items displace older items with the same key.
        dao_class.add_news_item(newer)
        self.assertEquals([newer], dao_class.get_news_items())

        # But older items w/ same key do not displace newer ones.
        dao_class.add_news_item(older)
        self.assertEquals([newer], dao_class.get_news_items())
Пример #18
0
 def get(self):
     eta_timestamp = utc.hour_end(utc.now_as_timestamp()) + 1
     eta = utc.timestamp_to_datetime(eta_timestamp)
     deferred.defer(self.maybe_start_jobs, _eta=eta)
     logging.info(
         'StartAvailabilityJobs - scheduling deferred task at %s', eta)
Пример #19
0
 def set_last_modified_to_now(self):
     """Sets the last_modified property to the current UTC time."""
     self._force_last_modified(utc.now_as_timestamp())
    def test_announcement_news(self):
        actions.login('*****@*****.**')
        actions.register(self, 'John Doe')
        time.sleep(1)
        locale = 'de'
        announcement = self._add_announcement_and_translation(locale,
                                                              is_draft=True)
        sent_data = {
            'key': str(announcement.key()),
            'title': 'Test Announcement',
            'date': utc.to_text(seconds=utc.now_as_timestamp()),
            'is_draft': False,
        }
        actions.login(self.ADMIN_EMAIL)
        response = self._put_announcement(sent_data)
        actions.login('*****@*****.**')

        # Verify announcement news item using news API directly
        news_items = news.CourseNewsDao.get_news_items()
        self.assertEquals(1, len(news_items))
        item = news_items[0]
        now_timestamp = utc.now_as_timestamp()
        self.assertEquals(
            announcements.AnnouncementsStudentHandler.URL.lstrip('/'),
            item.url)
        self.assertEquals(
            str(
                announcements.TranslatableResourceAnnouncement.key_for_entity(
                    announcement)), item.resource_key)
        self.assertAlmostEqual(now_timestamp,
                               utc.datetime_to_timestamp(item.when),
                               delta=10)

        # Verify announcement news item looking at HTTP response to /course
        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals([
            news_tests_lib.NewsItem(
                'Test Announcement',
                announcements.AnnouncementsStudentHandler.URL.lstrip('/'),
                True)
        ], news_tests_lib.extract_news_items_from_soup(soup))

        # Verify announcement news item translated title.
        self._set_prefs_locale(locale)
        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals([
            news_tests_lib.NewsItem(
                'TEST ANNOUNCEMENT',
                announcements.AnnouncementsStudentHandler.URL.lstrip('/'),
                True)
        ], news_tests_lib.extract_news_items_from_soup(soup))

        # Delete the announcement; news item should also go away.
        actions.login(self.ADMIN_EMAIL)
        self._delete_announcement(str(announcement.key()))
        actions.login('*****@*****.**')
        response = self.get('course')
        soup = self.parse_html_string_to_soup(response.body)
        self.assertEquals([],
                          news_tests_lib.extract_news_items_from_soup(soup))
    def test_old_news_excluded_by_new_news(self):
        NUM_OLD_ITEMS = news.MIN_NEWS_ITEMS_TO_DISPLAY * 2
        NUM_NEW_ITEMS = news.MIN_NEWS_ITEMS_TO_DISPLAY * 2

        user = actions.login(self.STUDENT_EMAIL)
        actions.register(self, 'John Smith')
        then_ts = (
            utc.now_as_timestamp() - news.NEWSWORTHINESS_SECONDS -
            NUM_OLD_ITEMS - 1)
        self._set_student_enroll_date(user, utc.timestamp_to_datetime(then_ts))

        # Add many old items - twice as many as we're willing to show.
        expected_old_items = []
        for i in xrange(NUM_OLD_ITEMS):
            expected_old_items.append(news_tests_lib.NewsItem(
                'Test Item %i' % i, 'u%i' % i, False))

            then = utc.timestamp_to_datetime(then_ts + i)
            item = news.NewsItem('test:%d' % i, 'u%d' % i, then)
            news.CourseNewsDao.add_news_item(item)
            news.StudentNewsDao.mark_item_seen('test:%d' % i)

        # Force everything we just did to be old news.
        try:
            save_newsworthiness_seconds = news.NEWSWORTHINESS_SECONDS
            news.NEWSWORTHINESS_SECONDS = 1
            time.sleep(2)

            # Expect that we see old items in newest-first order.
            expected_old_items.reverse()

            # Verify that we are only shown half of the old-news items before
            # we add any new ones.
            response = self.get('course')
            soup = self.parse_html_string_to_soup(response.body)
            self.assertEquals(
                expected_old_items[0:NUM_OLD_ITEMS / 2],
                news_tests_lib.extract_news_items_from_soup(soup))

            # Start adding new news items, one at a time.
            expected_new_items = []
            for i in xrange(NUM_NEW_ITEMS):
                j = NUM_OLD_ITEMS + i
                expected_new_items.append(news_tests_lib.NewsItem(
                    'Test Item %i' % j, 'u%i' % j, True))
                then = utc.timestamp_to_datetime(then_ts + j)
                item = news.NewsItem('test:%d' % j, 'u%d' % j, then)
                news.CourseNewsDao.add_news_item(item)

                # Expect to see all new items, and maybe some old items,
                # as long as the new ones are not crowding them out.
                # New items should appear strictly first.
                expected_items = list(reversed(expected_new_items))
                if i < news.MIN_NEWS_ITEMS_TO_DISPLAY:
                    expected_items += expected_old_items[
                        :news.MIN_NEWS_ITEMS_TO_DISPLAY - i - 1]

                response = self.get('course')
                soup = self.parse_html_string_to_soup(response.body)
                actual_items = news_tests_lib.extract_news_items_from_soup(soup)
                self.assertEquals(expected_items, actual_items)

        finally:
            news.NEWSWORTHINESS_SECONDS = save_newsworthiness_seconds
Пример #22
0
 def set_last_modified_to_now(self):
     """Sets the last_modified property to the current UTC time."""
     self._force_last_modified(utc.now_as_timestamp())
Пример #23
0
 def get(self):
     eta_timestamp = utc.hour_end(utc.now_as_timestamp()) + 1
     eta = utc.timestamp_to_datetime(eta_timestamp)
     deferred.defer(self.maybe_start_jobs, _eta=eta)
     logging.info('StartAvailabilityJobs - scheduling deferred task at %s',
                  eta)