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 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 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 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))
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
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)
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())
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)
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
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)