def post(self): self._require_admin() accept_keys = map(int, self.request.POST.getall("accept_keys[]")) reject_keys = map(int, self.request.POST.getall("reject_keys[]")) accepted_suggestion_futures = [Suggestion.get_by_id_async(key) for key in accept_keys] rejected_suggestion_futures = [Suggestion.get_by_id_async(key) for key in reject_keys] accepted_suggestions = map(lambda a: a.get_result(), accepted_suggestion_futures) rejected_suggestions = map(lambda a: a.get_result(), rejected_suggestion_futures) MatchSuggestionAccepter.accept_suggestions(accepted_suggestions) all_suggestions = accepted_suggestions all_suggestions.extend(rejected_suggestions) for suggestion in all_suggestions: if suggestion.key.id() in accept_keys: suggestion.review_state = Suggestion.REVIEW_ACCEPTED if suggestion.key.id() in reject_keys: suggestion.review_state = Suggestion.REVIEW_REJECTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewer_at = datetime.datetime.now() ndb.put_multi(all_suggestions) self.redirect("/admin/suggestions/match/video/review")
def createApiWriteSuggestion(cls, author_account_key, event_key, affiliation, auth_types): """ Create a suggestion for auth keys request. Returns status (success, no_affiliation, bad_event) """ if not affiliation: return 'no_affiliation' if event_key: event = Event.get_by_id(event_key) if event: suggestion = Suggestion( author=author_account_key, target_model="api_auth_access", target_key=event_key, ) auth_types = [int(type) for type in auth_types] clean_auth_types = filter(lambda a: a in AuthType.write_type_names.keys(), auth_types) # If we're requesting keys for an official event, filter out everything but videos # Admin can still override this at review time, but it's unlikely if event.event_type_enum in EventType.SEASON_EVENT_TYPES: clean_auth_types = filter(lambda a: a == AuthType.MATCH_VIDEO, clean_auth_types) suggestion.contents = { 'event_key': event_key, 'affiliation': affiliation, 'auth_types': clean_auth_types, } suggestion.put() return 'success' else: return 'bad_event' else: return 'bad_event'
def post(self): self._require_login() match_key = self.request.get("match_key") youtube_url = self.request.get("youtube_url") youtube_id = None regex1 = re.match(r".*youtu\.be\/(.*)", youtube_url) if regex1 is not None: youtube_id = regex1.group(1) else: regex2 = re.match(r".*v=([a-zA-Z0-9_-]*)", youtube_url) if regex2 is not None: youtube_id = regex2.group(1) if youtube_id is not None: suggestion = Suggestion( author=self.user_bundle.account.key, target_key=match_key, target_model="match", ) suggestion.contents = {"youtube_videos": [youtube_id]} suggestion.put() self.redirect('/suggest/match/video?match_key=%s&success=1' % match_key)
def createMatchVideoSuggestion(self): user_bundle = UserBundle() match = Match.query().fetch(1)[0] #probably a cleaner way to do this suggestion = Suggestion( author=user_bundle.account.key, target_key=match.key_name, target_model="match") suggestion.contents = {"youtube_videos": [self.YOUTUBE_ID]} suggestion.put()
def createEventWebcastSuggestion(self): user_bundle = UserBundle() event = Event.query().fetch(1)[0] suggestion = Suggestion( author=user_bundle.account.key, target_key=event.key_name, target_model="event", ) suggestion.contents = {"webcast_url": self.YOUTUBE_URL} suggestion.put()
class TestMatchSuggestionAccepter(unittest2.TestCase): def setUp(self): self.testbed = testbed.Testbed() self.testbed.activate() self.testbed.init_datastore_v3_stub() self.testbed.init_memcache_stub() ndb.get_context().clear_cache() # Prevent data from leaking between tests self.testbed.init_taskqueue_stub(root_path=".") self.account = Account( email="*****@*****.**", ) self.account.put() self.suggestion = Suggestion( author=self.account.key, contents_json="{\"youtube_videos\":[\"123456\"]}", target_key="2012ct_qm1", target_model="match" ) self.suggestion.put() self.event = Event( id="2012ct", event_short="ct", year=2012, event_type_enum=EventType.REGIONAL, ) self.event.put() self.match = Match( id="2012ct_qm1", alliances_json="""{"blue": {"score": -1, "teams": ["frc3464", "frc20", "frc1073"]}, "red": {"score": -1, "teams": ["frc69", "frc571", "frc176"]}}""", comp_level="qm", event=self.event.key, year=2012, set_number=1, match_number=1, team_key_names=[u'frc69', u'frc571', u'frc176', u'frc3464', u'frc20', u'frc1073'], youtube_videos=["abcdef"] ) self.match.put() def tearDown(self): self.testbed.deactivate() def test_accept_suggestions(self): MatchSuggestionAccepter.accept_suggestion(self.match, self.suggestion) match = Match.get_by_id("2012ct_qm1") self.assertTrue("abcdef" in match.youtube_videos) self.assertTrue("123456" in match.youtube_videos)
def createOffseasonEventSuggestion(cls, author_account_key, name, start_date, end_date, website, address): """ Create a suggestion for offseason event. Returns (status, failures): ('success', None) ('validation_failure', failures) """ failures = {} if not name: failures['name'] = "Missing event name" if not start_date: failures['start_date'] = "Missing start date" if not end_date: failures['end_date'] = "Missing end date" if not website: failures['website'] = "Missing website" if not address: failures['venue_address'] = "Missing address" start_datetime = None end_datetime = None if start_date: try: start_datetime = datetime.strptime(start_date, "%Y-%m-%d") except ValueError: failures['start_date'] = "Invalid start date format (year-month-date)" if end_date: try: end_datetime = datetime.strptime(end_date, "%Y-%m-%d") except ValueError: failures['end_date'] = "Invalid end date format (year-month-date)" if start_datetime and end_datetime and end_datetime < start_datetime: failures['end_date'] = "End date must not be before the start date" if failures: return 'validation_failure', failures # Note that we don't specify an explicit key for event suggestions # We don't trust users to input correct event keys (that's for the moderator to do) suggestion = Suggestion( author=author_account_key, target_model="offseason-event", ) suggestion.contents = { 'name': name, 'start_date': start_date, 'end_date': end_date, 'website': website, 'address': address} suggestion.put() return 'success', None
def post(self): self._require_admin() if self.request.get("verdict") == "accept": webcast = dict() webcast["type"] = self.request.get("webcast_type") webcast["channel"] = self.request.get("webcast_channel") if self.request.get("webcast_file"): webcast["file"] = self.request.get("webcast_file") event = Event.get_by_id(self.request.get("event_key")) suggestion = Suggestion.get_by_id(int(self.request.get("suggestion_key"))) EventWebcastAdder.add_webcast(event, webcast) MemcacheWebcastFlusher.flush() suggestion.review_state = Suggestion.REVIEW_ACCEPTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewer_at = datetime.datetime.now() suggestion.put() self.redirect("/admin/suggestions/event/webcast/review?success=accept&event_key=%s" % event.key.id()) return elif self.request.get("verdict") == "reject": suggestion = Suggestion.get_by_id(int(self.request.get("suggestion_key"))) suggestion.review_state = Suggestion.REVIEW_REJECTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewer_at = datetime.datetime.now() suggestion.put() self.redirect("/admin/suggestions/event/webcast/review?success=reject") return elif self.request.get("verdict") == "reject_all": suggestion_keys = self.request.get("suggestion_keys").split(",") suggestions = [Suggestion.get_by_id(int(suggestion_key)) for suggestion_key in suggestion_keys] for suggestion in suggestions: event_key = suggestion.target_key suggestion.review_state = Suggestion.REVIEW_REJECTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewer_at = datetime.datetime.now() suggestion.put() self.redirect("/admin/suggestions/event/webcast/review?success=reject_all&event_key=%s" % event_key) return self.redirect("/admin/suggestions/event/webcast/review")
def testCleanUrl(self): status = SuggestionCreator.createTeamMediaSuggestion( self.account.key, " http://imgur.com/ruRAxDm?foo=bar#meow ", "frc1124", "2016") self.assertEqual(status, 'success') # Ensure the Suggestion gets created suggestion_id = Suggestion.render_media_key_name('2016', 'team', 'frc1124', 'imgur', 'ruRAxDm') suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_PENDING) self.assertEqual(suggestion.author, self.account.key) self.assertEqual(suggestion.target_model, 'media')
def get(self): self._require_registration() push_sitevar = Sitevar.get_by_id('notifications.enable') if push_sitevar is None or not push_sitevar.values_json == "true": ping_enabled = "disabled" else: ping_enabled = "" # Compute myTBA statistics user = self.user_bundle.account.key num_favorites = Favorite.query(ancestor=user).count() num_subscriptions = Subscription.query(ancestor=user).count() # Compute suggestion statistics submissions_pending = Suggestion.query(Suggestion.review_state==Suggestion.REVIEW_PENDING, Suggestion.author==user).count() submissions_accepted = Suggestion.query(Suggestion.review_state==Suggestion.REVIEW_ACCEPTED, Suggestion.author==user).count() # Suggestion review statistics review_permissions = False num_reviewed = 0 total_pending = 0 if self.user_bundle.account.permissions: review_permissions = True num_reviewed = Suggestion.query(Suggestion.reviewer==user).count() total_pending = Suggestion.query(Suggestion.review_state==Suggestion.REVIEW_PENDING).count() # Fetch trusted API keys api_keys = ApiAuthAccess.query(ApiAuthAccess.owner == user).fetch() write_keys = filter(lambda key: key.is_write_key, api_keys) read_keys = filter(lambda key: key.is_read_key, api_keys) self.template_values['status'] = self.request.get('status') self.template_values['webhook_verification_success'] = self.request.get('webhook_verification_success') self.template_values['ping_sent'] = self.request.get('ping_sent') self.template_values['ping_enabled'] = ping_enabled self.template_values['num_favorites'] = num_favorites self.template_values['num_subscriptions'] = num_subscriptions self.template_values['submissions_pending'] = submissions_pending self.template_values['submissions_accepted'] = submissions_accepted self.template_values['review_permissions'] = review_permissions self.template_values['num_reviewed'] = num_reviewed self.template_values['total_pending'] = total_pending self.template_values['read_keys'] = read_keys self.template_values['write_keys'] = write_keys self.template_values['auth_write_type_names'] = AuthType.write_type_names self.response.out.write(jinja2_engine.render('account_overview.html', self.template_values))
def get(self): self._require_admin() self.template_values['memcache_stats'] = memcache.get_stats() self.template_values['databasequery_stats'] = { 'hits': sum(filter(None, [memcache.get(key) for key in DatabaseQuery.DATABASE_HITS_MEMCACHE_KEYS])), 'misses': sum(filter(None, [memcache.get(key) for key in DatabaseQuery.DATABASE_MISSES_MEMCACHE_KEYS])) } # Gets the 5 recently created users users = Account.query().order(-Account.created).fetch(5) self.template_values['users'] = users # Retrieves the number of pending suggestions video_suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "match").count() self.template_values['video_suggestions'] = video_suggestions webcast_suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "event").count() self.template_values['webcast_suggestions'] = webcast_suggestions media_suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "media").count() self.template_values['media_suggestions'] = media_suggestions # version info try: fname = os.path.join(os.path.dirname(__file__), '../../version_info.json') with open(fname, 'r') as f: data = json.loads(f.read().replace('\r\n', '\n')) self.template_values['git_branch_name'] = data['git_branch_name'] self.template_values['build_time'] = data['build_time'] commit_parts = re.split("[\n]+", data['git_last_commit']) self.template_values['commit_hash'] = commit_parts[0].split(" ") self.template_values['commit_author'] = commit_parts[1] self.template_values['commit_date'] = commit_parts[2] self.template_values['commit_msg'] = commit_parts[3] except Exception, e: logging.warning("version_info.json parsing failed: %s" % e) pass
def get(self): self._require_registration() push_sitevar = Sitevar.get_by_id("notifications.enable") if push_sitevar is None or not push_sitevar.values_json == "true": ping_enabled = "disabled" else: ping_enabled = "" # Compute myTBA statistics user = self.user_bundle.account.key num_favorites = Favorite.query(ancestor=user).count() num_subscriptions = Subscription.query(ancestor=user).count() # Compute suggestion statistics submissions_pending = Suggestion.query( Suggestion.review_state == Suggestion.REVIEW_PENDING, Suggestion.author == user ).count() submissions_accepted = Suggestion.query( Suggestion.review_state == Suggestion.REVIEW_ACCEPTED, Suggestion.author == user ).count() # Suggestion review statistics review_permissions = False num_reviewed = 0 total_pending = 0 if self.user_bundle.account.permissions: review_permissions = True num_reviewed = Suggestion.query(Suggestion.reviewer == user).count() total_pending = Suggestion.query(Suggestion.review_state == Suggestion.REVIEW_PENDING).count() # Fetch trusted API keys trusted_keys = ApiAuthAccess.query(ApiAuthAccess.owner == user).fetch() self.template_values["status"] = self.request.get("status") self.template_values["webhook_verification_success"] = self.request.get("webhook_verification_success") self.template_values["ping_enabled"] = ping_enabled self.template_values["num_favorites"] = num_favorites self.template_values["num_subscriptions"] = num_subscriptions self.template_values["submissions_pending"] = submissions_pending self.template_values["submissions_accepted"] = submissions_accepted self.template_values["review_permissions"] = review_permissions self.template_values["num_reviewed"] = num_reviewed self.template_values["total_pending"] = total_pending self.template_values["trusted_keys"] = trusted_keys self.template_values["auth_type_names"] = AuthType.type_names self.response.out.write(jinja2_engine.render("account_overview.html", self.template_values))
def createSuggestion(self): status = SuggestionCreator.createTeamMediaSuggestion(self.account.key, 'http://imgur.com/foobar', 'frc1124', 2016) self.assertEqual(status[0], 'success') return Suggestion.query().fetch(keys_only=True)[0].id()
def get(self): self._require_admin() self.template_values['memcache_stats'] = memcache.get_stats() # Gets the 5 recently created users users = Account.query().order(-Account.created).fetch(5) self.template_values['users'] = users # Retrieves the number of pending suggestions video_suggestions = Suggestion.query().filter(Suggestion.review_state == Suggestion.REVIEW_PENDING).count() self.template_values['video_suggestions'] = video_suggestions # version info try: fname = os.path.join(os.path.dirname(__file__), '../../version_info.json') with open(fname, 'r') as f: data = json.loads(f.read().replace('\r\n', '\n')) self.template_values['git_branch_name'] = data['git_branch_name'] self.template_values['build_time'] = data['build_time'] commit_parts = re.split("[\n]+", data['git_last_commit']) self.template_values['commit_hash'] = commit_parts[0].split(" ") self.template_values['commit_author'] = commit_parts[1] self.template_values['commit_date'] = commit_parts[2] self.template_values['commit_msg'] = commit_parts[3] except Exception, e: logging.warning("version_info.json parsing failed: %s" % e) pass
def _process_accepted(self, accept_key): """ Performs all actions for an accepted Suggestion in a Transaction. Suggestions are processed one at a time (instead of in batch) in a Transaction to prevent possible race conditions. Actions include: - Creating and saving a new Media for the Suggestion - Removing a reference from another Media's preferred_references - Marking the Suggestion as accepted and saving it """ # Async get suggestion_future = Suggestion.get_by_id_async(accept_key) # Resolve async Futures suggestion = suggestion_future.get_result() # Make sure Suggestion hasn't been processed (by another thread) if suggestion.review_state != Suggestion.REVIEW_PENDING: return team_reference = Media.create_reference( suggestion.contents['reference_type'], suggestion.contents['reference_key']) media = MediaCreator.create_media(suggestion, team_reference) # Mark Suggestion as accepted suggestion.review_state = Suggestion.REVIEW_ACCEPTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewed_at = datetime.datetime.now() # Do all DB writes MediaManipulator.createOrUpdate(media) suggestion.put()
def setUp(self): self.testbed = testbed.Testbed() self.testbed.activate() self.testbed.init_datastore_v3_stub() self.testbed.init_memcache_stub() self.testbed.init_taskqueue_stub(root_path=".") self.account = Account(email="*****@*****.**") self.account.put() self.suggestion = Suggestion( author=self.account.key, contents_json='{"youtube_videos":["123456"]}', target_key="2012ct_qm1", target_model="match", ) self.suggestion.put() self.event = Event(id="2012ct", event_short="ct", year=2012, event_type_enum=EventType.REGIONAL) self.event.put() self.match = Match( id="2012ct_qm1", alliances_json="""{"blue": {"score": -1, "teams": ["frc3464", "frc20", "frc1073"]}, "red": {"score": -1, "teams": ["frc69", "frc571", "frc176"]}}""", comp_level="qm", event=self.event.key, game="frc_2012_rebr", set_number=1, match_number=1, team_key_names=[u"frc69", u"frc571", u"frc176", u"frc3464", u"frc20", u"frc1073"], youtube_videos=["abcdef"], ) self.match.put()
def test_create_suggestion_banned(self): status, _ = SuggestionCreator.createOffseasonEventSuggestion( self.account_banned.key, "Test Event", "2016-5-1", "2016-5-2", "http://foo.bar.com", "The Venue", "123 Fake Street", "New York", "NY", "USA") self.assertEqual(status, 'success') # Ensure the Suggestion gets created suggestions = Suggestion.query().fetch() self.assertIsNotNone(suggestions) self.assertEqual(len(suggestions), 1) suggestion = suggestions[0] self.assertIsNotNone(suggestion) self.assertEqual(suggestion.contents['name'], "Test Event") self.assertEqual(suggestion.contents['start_date'], '2016-5-1') self.assertEqual(suggestion.contents['end_date'], '2016-5-2') self.assertEqual(suggestion.contents['website'], 'http://foo.bar.com') self.assertEqual(suggestion.contents['address'], '123 Fake Street') self.assertEqual(suggestion.contents['city'], 'New York') self.assertEqual(suggestion.contents['state'], 'NY') self.assertEqual(suggestion.contents['country'], 'USA') self.assertEqual(suggestion.contents['venue_name'], 'The Venue') self.assertEqual(suggestion.review_state, Suggestion.REVIEW_AUTOREJECTED)
def test_webcast_good_date(self): event = Event(id="2016test", name="Test Event", event_short="Test Event", year=2016, event_type_enum=EventType.OFFSEASON) event.put() status = SuggestionCreator.createEventWebcastSuggestion( self.account.key, "http://twitch.tv/frcgamesense", "2017-02-28", "2016test") self.assertEqual(status, 'success') suggestions = Suggestion.query().fetch() self.assertIsNotNone(suggestions) self.assertEqual(len(suggestions), 1) suggestion = suggestions[0] self.assertIsNotNone(suggestion) self.assertEqual(suggestion.target_key, "2016test") self.assertEqual(suggestion.author, self.account.key) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_PENDING) self.assertIsNotNone(suggestion.contents) self.assertEqual(suggestion.contents.get('webcast_url'), "http://twitch.tv/frcgamesense") self.assertIsNotNone(suggestion.contents.get('webcast_dict')) self.assertEqual(suggestion.contents.get('webcast_date'), "2017-02-28")
def post(self): self.verify_permissions() suggestion_id = int(self.request.get("suggestion_id")) verdict = self.request.get("verdict") message = self.request.get("user_message") admin_email_body = None email_body = None user = None event_key = None status = '' if verdict == "accept": status = 'accept' auth_id, user, event_key, email_body = self._process_accepted(suggestion_id, message) admin_email_body = """{} ({}) has accepted the request with the following message: {} View the key: https://www.thebluealliance.com/admin/api_auth/edit/{} """.format(self.user_bundle.account.display_name, self.user_bundle.account.email, message, auth_id) elif verdict == "reject": suggestion = Suggestion.get_by_id(suggestion_id) event_key = suggestion.contents['event_key'] user = suggestion.author.get() event = Event.get_by_id(event_key) suggestion.review_state = Suggestion.REVIEW_REJECTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewed_at = datetime.now() suggestion.put() status = 'reject' email_body = """Hi {}, We have reviewer your request for auth tokens for {} {} and have regretfully declined with the following message: {} If you have any questions, please don't hesitate to reach out to us at [email protected] Thanks, TBA Admins """.format(user.display_name, event.year, event.name, message) admin_email_body = """{} ({}) has rejected this request with the following reason: {} """.format(self.user_bundle.account.display_name, self.user_bundle.account.email, message) # Notify the user their keys are available if email_body: mail.send_mail(sender="The Blue Alliance Contact <*****@*****.**>", to=user.email, subject="The Blue Alliance Auth Tokens for {}".format(event_key), body=email_body) if admin_email_body: # Subject should match the one in suggest_apiwrite_controller subject = "Trusted API Key Request for {}".format(event_key) SuggestionNotifier.send_admin_alert_email(subject, admin_email_body) self.redirect("/suggest/apiwrite/review?success={}".format(status))
def get(self): suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "media") reference_keys = [] for suggestion in suggestions: reference_keys.append(Media.create_reference( suggestion.contents['reference_type'], suggestion.contents['reference_key'])) if 'details_json' in suggestion.contents: suggestion.details = json.loads(suggestion.contents['details_json']) if 'image_partial' in suggestion.details: suggestion.details['thumbnail'] = suggestion.details['image_partial'].replace('_l', '_m') reference_futures = ndb.get_multi_async(reference_keys) references = map(lambda r: r.get_result(), reference_futures) suggestions_and_references = zip(suggestions, references) self.template_values.update({ "suggestions_and_references": suggestions_and_references, }) path = os.path.join(os.path.dirname(__file__), '../../templates/suggest_team_media_review_list.html') self.response.out.write(template.render(path, self.template_values))
def post(self): preferred_keys = self.request.POST.getall("preferred_keys[]") accept_keys = [] reject_keys = [] for value in self.request.POST.values(): logging.debug(value) split_value = value.split('::') if len(split_value) == 2: key = split_value[1] else: continue if value.startswith('accept'): accept_keys.append(key) elif value.startswith('reject'): reject_keys.append(key) # Process accepts for accept_key in accept_keys: self._process_accepted(accept_key, preferred_keys) # Process rejects rejected_suggestion_futures = [Suggestion.get_by_id_async(key) for key in reject_keys] rejected_suggestions = map(lambda a: a.get_result(), rejected_suggestion_futures) for suggestion in rejected_suggestions: if suggestion.review_state == Suggestion.REVIEW_PENDING: suggestion.review_state = Suggestion.REVIEW_REJECTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewed_at = datetime.datetime.now() ndb.put_multi(rejected_suggestions) self.redirect("/suggest/team/media/review")
def get(self): suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "offseason-event") year = datetime.now().year year_events_future = EventListQuery(year).fetch_async() last_year_events_future = EventListQuery(year - 1).fetch_async() events_and_ids = [self._create_candidate_event(suggestion) for suggestion in suggestions] year_events = year_events_future.get_result() year_offseason_events = [e for e in year_events if e.event_type_enum == EventType.OFFSEASON] last_year_events = last_year_events_future.get_result() last_year_offseason_events = [e for e in last_year_events if e.event_type_enum == EventType.OFFSEASON] similar_events = [self._get_similar_events(event[1], year_offseason_events) for event in events_and_ids] similar_last_year = [self._get_similar_events(event[1], last_year_offseason_events) for event in events_and_ids] self.template_values.update({ 'success': self.request.get("success"), 'event_key': self.request.get("event_key"), 'events_and_ids': events_and_ids, 'similar_events': similar_events, 'similar_last_year': similar_last_year, }) self.response.out.write( jinja2_engine.render('suggestions/suggest_offseason_event_review_list.html', self.template_values))
def get(self): super(SuggestDesignsReviewController, self).get() if self.request.get('action') and self.request.get('id'): # Fast-path review self._fastpath_review() suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "robot").fetch(limit=50) reference_keys = [] for suggestion in suggestions: reference_key = suggestion.contents['reference_key'] reference = Media.create_reference( suggestion.contents['reference_type'], reference_key) reference_keys.append(reference) reference_futures = ndb.get_multi_async(reference_keys) references = map(lambda r: r.get_result(), reference_futures) suggestions_and_references = zip(suggestions, references) self.template_values.update({ "suggestions_and_references": suggestions_and_references, }) self.response.out.write(jinja2_engine.render('suggestions/suggest_designs_review.html', self.template_values))
def get(self): suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "event") suggestions_by_event_key = {} for suggestion in suggestions: if 'webcast_dict' in suggestion.contents: suggestion.webcast_template = 'webcast/{}.html'.format(suggestion.contents['webcast_dict']['type']) suggestions_by_event_key.setdefault(suggestion.target_key, []).append(suggestion) suggestion_sets = [] for event_key, suggestions in suggestions_by_event_key.items(): suggestion_sets.append({ "event": Event.get_by_id(event_key), "suggestions": suggestions }) self.template_values.update({ "event_key": self.request.get("event_key"), "success": self.request.get("success"), "suggestion_sets": suggestion_sets }) path = os.path.join(os.path.dirname(__file__), '../../templates/suggest_event_webcast_review_list.html') self.response.out.write(template.render(path, self.template_values))
def test_accept_with_different_details(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm('review_{}'.format(suggestion_id)) form['webcast_type'] = 'youtube' form['webcast_channel'] = 'foobar' form['webcast_file'] = 'meow' response = form.submit('verdict', value='accept').follow() self.assertEqual(response.status_int, 200) request = response.request self.assertEqual(request.GET.get('success'), 'accept') # Process task queue tasks = self.testbed.get_stub(testbed.TASKQUEUE_SERVICE_NAME).get_filtered_tasks() for task in tasks: deferred.run(task.payload) # Make sure we mark the Suggestion as REVIEWED suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED) # Make sure the Event has no webcasts event = Event.get_by_id('2016necmp') self.assertIsNotNone(event.webcast) self.assertEqual(len(event.webcast), 1) webcast = event.webcast[0] self.assertEqual(webcast['type'], 'youtube') self.assertEqual(webcast['channel'], 'foobar') self.assertEqual(webcast['file'], 'meow')
def testAcceptWithDifferentDetails(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm('review_{}'.format(suggestion_id)) form['webcast_type'] = 'youtube' form['webcast_channel'] = 'foobar' form['webcast_file'] = 'meow' response = form.submit('verdict', value='accept').follow() self.assertEqual(response.status_int, 200) request = response.request self.assertEqual(request.GET.get('success'), 'accept') # Make sure we mark the Suggestion as REVIEWED suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED) # Make sure the Event has no webcasts event = Event.get_by_id('2016necmp') self.assertIsNotNone(event.webcast) self.assertEqual(len(event.webcast), 1) webcast = event.webcast[0] self.assertEqual(webcast['type'], 'youtube') self.assertEqual(webcast['channel'], 'foobar') self.assertEqual(webcast['file'], 'meow')
def testAcceptNewKey(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm() form.set('accept_keys[]', suggestion_id) form.set('key-{}'.format(suggestion_id), '2016necmp_f1m2') response = form.submit().follow() self.assertEqual(response.status_int, 200) # Make sure we mark the Suggestion as REVIEWED suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED) # Make sure the video gets associated match = Match.get_by_id(self.match2.key_name) self.assertIsNotNone(match) self.assertIsNotNone(match.youtube_videos) self.assertTrue('H-54KMwMKY0' in match.youtube_videos) # Make sure we don't add it to the first match match = Match.get_by_id(self.match.key_name) self.assertIsNotNone(match) self.assertIsNotNone(match.youtube_videos) self.assertFalse('H-54KMwMKY0' in match.youtube_videos)
def test_create_suggestion_banned(self): status, _ = SuggestionCreator.createEventMediaSuggestion( self.account_banned.key, "https://www.youtube.com/watch?v=H-54KMwMKY0", "2016nyny") self.assertEqual(status, 'success') # Ensure the Suggestion gets created suggestion_id = Suggestion.render_media_key_name('2016', 'event', '2016nyny', 'youtube', 'H-54KMwMKY0') suggestion = Suggestion.get_by_id(suggestion_id) expected_dict = MediaParser.partial_media_dict_from_url("https://www.youtube.com/watch?v=H-54KMwMKY0") self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_AUTOREJECTED) self.assertEqual(suggestion.author, self.account_banned.key) self.assertEqual(suggestion.target_model, 'event_media') self.assertDictContainsSubset(expected_dict, suggestion.contents)
def _process_accepted(self, accept_key): """ Performs all actions for an accepted Suggestion in a Transaction. Suggestions are processed one at a time (instead of in batch) in a Transaction to prevent possible race conditions. """ # Async get suggestion_future = Suggestion.get_by_id_async(accept_key) # Resolve async Futures suggestion = suggestion_future.get_result() # Make sure Suggestion hasn't been processed (by another thread) if suggestion.review_state != Suggestion.REVIEW_PENDING: return # Mark Suggestion as accepted suggestion.review_state = Suggestion.REVIEW_ACCEPTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewed_at = datetime.datetime.now() # Do all DB writes ret = self.create_target_model(suggestion) suggestion.put() return ret
def get(self): suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "event_media").fetch(limit=50) # Quick and dirty way to group images together suggestions = sorted(suggestions, key=lambda x: 0 if x.contents['media_type_enum'] in MediaType.image_types else 1) reference_keys = [] for suggestion in suggestions: reference_key = suggestion.contents['reference_key'] reference = Media.create_reference( suggestion.contents['reference_type'], reference_key) reference_keys.append(reference) if 'details_json' in suggestion.contents: suggestion.details = json.loads(suggestion.contents['details_json']) if 'image_partial' in suggestion.details: suggestion.details['thumbnail'] = suggestion.details['image_partial'].replace('_l', '_m') reference_futures = ndb.get_multi_async(reference_keys) references = map(lambda r: r.get_result(), reference_futures) suggestions_and_references = zip(suggestions, references) self.template_values.update({ "suggestions_and_references": suggestions_and_references, }) self.response.out.write(jinja2_engine.render('suggestions/suggest_event_media_review_list.html', self.template_values))
def testUnknownUrlScheme(self): event = Event(id="2016test", name="Test Event", event_short="Test Event", year=2016, event_type_enum=EventType.OFFSEASON) event.put() status = SuggestionCreator.createEventWebcastSuggestion( self.account.key, "http://myweb.site/somewebcast", "2016test") self.assertEqual(status, 'success') suggestions = Suggestion.query().fetch() self.assertIsNotNone(suggestions) self.assertEqual(len(suggestions), 1) suggestion = suggestions[0] self.assertIsNotNone(suggestion) self.assertIsNotNone(suggestion.contents) self.assertIsNone(suggestion.contents.get('webcast_dict')) self.assertEqual(suggestion.contents.get('webcast_url'), "http://myweb.site/somewebcast")
def test_remove_media_preferred(self): self.loginUser() self.giveTeamAdminAccess() team_reference = Media.create_reference('team', 'frc1124') suggestion_id = self.createMediaSuggestion() suggestion = Suggestion.get_by_id(suggestion_id) media = MediaCreator.create_media_model(suggestion, team_reference) media.preferred_references.append(team_reference) media_id = media.put() self.assertTrue(ndb.Key(Team, 'frc1124') in media.references) form = self.getMediaAdminForm('remove_preferred', media_id.id()) response = form.submit().follow() self.assertEqual(response.status_int, 301) media = media_id.get() self.assertTrue(team_reference in media.references) self.assertFalse(team_reference in media.preferred_references)
def get(self): suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "offseason-event") year = datetime.now().year year_events_future = EventListQuery(year).fetch_async() last_year_events_future = EventListQuery(year - 1).fetch_async() events_and_ids = [ self._create_candidate_event(suggestion) for suggestion in suggestions ] year_events = year_events_future.get_result() year_offseason_events = [ e for e in year_events if e.event_type_enum == EventType.OFFSEASON ] last_year_events = last_year_events_future.get_result() last_year_offseason_events = [ e for e in last_year_events if e.event_type_enum == EventType.OFFSEASON ] similar_events = [ self._get_similar_events(event[1], year_offseason_events) for event in events_and_ids ] similar_last_year = [ self._get_similar_events(event[1], last_year_offseason_events) for event in events_and_ids ] self.template_values.update({ 'success': self.request.get("success"), 'event_key': self.request.get("event_key"), 'events_and_ids': events_and_ids, 'similar_events': similar_events, 'similar_last_year': similar_last_year, }) self.response.out.write( jinja2_engine.render( 'suggestions/suggest_offseason_event_review_list.html', self.template_values))
def testCreateSuggestion(self): status, _ = SuggestionCreator.createOffseasonEventSuggestion( self.account.key, "Test Event", "2016-5-1", "2016-5-2", "http://foo.bar.com", "123 Fake Street, New York, NY") self.assertEqual(status, 'success') # Ensure the Suggestion gets created suggestions = Suggestion.query().fetch() self.assertIsNotNone(suggestions) self.assertEqual(len(suggestions), 1) suggestion = suggestions[0] self.assertIsNotNone(suggestion) self.assertEqual(suggestion.contents['name'], "Test Event") self.assertEqual(suggestion.contents['start_date'], '2016-5-1') self.assertEqual(suggestion.contents['end_date'], '2016-5-2') self.assertEqual(suggestion.contents['website'], 'http://foo.bar.com') self.assertEqual(suggestion.contents['address'], '123 Fake Street, New York, NY')
def testRejectSingleWebcast(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm('review_{}'.format(suggestion_id)) response = form.submit('verdict', value='reject').follow() self.assertEqual(response.status_int, 200) request = response.request self.assertEqual(request.GET.get('success'), 'reject') # Make sure we mark the Suggestion as REVIEWED suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_REJECTED) # Make sure the Event has no webcasts event = Event.get_by_id('2016necmp') self.assertIsNone(event.webcast)
def test_reject_robot_design(self): self.loginUser() self.giveTeamAdminAccess() suggestion_id = self.createDesignSuggestion() form = self.getSuggestionForm('robot') form['accept_reject-{}'.format(suggestion_id)] = 'reject::{}'.format( suggestion_id) response = form.submit().follow() self.assertEqual(response.status_int, 200) # Make sure the Media object doesn't get created medias = Media.query().fetch(keys_only=True) self.assertEqual(len(medias), 0) # Make sure we mark the Suggestion as REVIEWED suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_REJECTED)
def testAcceptSuggestion(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm() form.set('accept_keys[]', suggestion_id) response = form.submit().follow() self.assertEqual(response.status_int, 200) # Make sure we mark the Suggestion as REVIEWED suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED) # Make sure the video gets associated match = Match.get_by_id(self.match.key_name) self.assertIsNotNone(match) self.assertIsNotNone(match.youtube_videos) self.assertTrue('H-54KMwMKY0' in match.youtube_videos)
def testCreateSuggestion(self): event = Event(id="2016test", name="Test Event", event_short="Test Event", year=2016, event_type_enum=EventType.OFFSEASON) event.put() status = SuggestionCreator.createEventWebcastSuggestion( self.account.key, "http://twitch.tv/frcgamesense", "2016test") self.assertEqual(status, 'success') # Ensure the Suggestion gets created expected_key = "webcast_2016test_twitch_frcgamesense_None" suggestion = Suggestion.get_by_id(expected_key) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.target_key, "2016test") self.assertEqual(suggestion.author, self.account.key) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_PENDING) self.assertIsNotNone(suggestion.contents) self.assertEqual(suggestion.contents.get('webcast_url'), "http://twitch.tv/frcgamesense") self.assertIsNotNone(suggestion.contents.get('webcast_dict'))
def createTeamMediaSuggestion(cls, author_account_key, media_url, team_key, year_str, private_details_json=None): """Create a Team Media Suggestion. Returns status (success, suggestion_exists, media_exists, bad_url)""" media_dict = MediaParser.partial_media_dict_from_url(media_url.strip()) if media_dict is not None: existing_media = Media.get_by_id( Media.render_key_name(media_dict['media_type_enum'], media_dict['foreign_key'])) if existing_media is None or team_key not in [ reference.id() for reference in existing_media.references ]: foreign_type = Media.SLUG_NAMES[media_dict['media_type_enum']] suggestion_id = Suggestion.render_media_key_name( year_str, 'team', team_key, foreign_type, media_dict['foreign_key']) suggestion = Suggestion.get_by_id(suggestion_id) if not suggestion or suggestion.review_state != Suggestion.REVIEW_PENDING: media_dict['year'] = int(year_str) media_dict['reference_type'] = 'team' media_dict['reference_key'] = team_key if private_details_json is not None: media_dict[ 'private_details_json'] = private_details_json suggestion = Suggestion( id=suggestion_id, author=author_account_key, target_model="media", ) suggestion.contents = media_dict suggestion.put() return 'success' else: return 'suggestion_exists' else: return 'media_exists' else: return 'bad_url'
def test_accept_bad_key(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm() form.set('accept_keys[]', suggestion_id) form.set('key-{}'.format(suggestion_id), '2016necmp_f1m3') # This match doesn't exist response = form.submit().follow() self.assertEqual(response.status_int, 200) # Make sure we don't mark the Suggestion as REVIEWED suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_PENDING) # Make sure the video doesn't get associated match = Match.get_by_id(self.match.key_name) self.assertIsNotNone(match) self.assertIsNotNone(match.youtube_videos) self.assertFalse('H-54KMwMKY0' in match.youtube_videos)
def testUndefinedAuthType(self): event = Event(id="2016test", name="Test Event", event_short="Test Event", year=2016, event_type_enum=EventType.OFFSEASON) event.put() status = SuggestionCreator.createApiWriteSuggestion( self.account.key, "2016test", "Event Organizer", [1, 2, -1, -2]) # -1 and -2 should be filtered out self.assertEqual(status, 'success') # Ensure the Suggestion gets created suggestions = Suggestion.query().fetch() self.assertIsNotNone(suggestions) self.assertEqual(len(suggestions), 1) suggestion = suggestions[0] self.assertIsNotNone(suggestion) self.assertEqual(suggestion.contents['event_key'], "2016test") self.assertEqual(suggestion.contents['affiliation'], "Event Organizer") self.assertListEqual(suggestion.contents['auth_types'], [1, 2])
def test_accept_suggestion(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm() form['accept_reject-{}'.format(suggestion_id)] = 'accept::{}'.format( suggestion_id) response = form.submit().follow() self.assertEqual(response.status_int, 200) # Make sure the Media object gets created media = Media.query().fetch()[0] self.assertIsNotNone(media) self.assertEqual(media.media_type_enum, MediaType.GRABCAD) self.assertEqual(media.year, 2016) self.assertListEqual(media.references, [self.team.key]) # Make sure we mark the Suggestion as REVIEWED suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED)
def testAcceptSuggestion(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm(suggestion_id) response = form.submit('verdict', value='accept').follow() self.assertEqual(response.status_int, 200) # Make sure the ApiWrite object gets created auth = ApiAuthAccess.query().fetch()[0] self.assertIsNotNone(auth) self.assertEqual(auth.owner, self.account.key) self.assertListEqual(auth.event_list, [self.event.key]) self.assertListEqual(auth.auth_types_enum, [AuthType.EVENT_MATCHES]) self.assertIsNotNone(auth.secret) self.assertIsNotNone(auth.expiration) # Make sure we mark the Suggestion as REVIEWED suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED)
def test_fast_path_accept(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() response = self.testapp.get( '/suggest/cad/review?action=accept&id={}'.format(suggestion_id)) response = response.follow() self.assertEqual(response.status_int, 200) self.assertEqual(response.request.GET.get('status'), 'accepted') # Make sure the Media object gets created media = Media.query().fetch()[0] self.assertIsNotNone(media) self.assertEqual(media.media_type_enum, MediaType.GRABCAD) self.assertEqual(media.year, 2016) self.assertListEqual(media.references, [self.team.key]) suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED)
def testOfficialEvent(self): event = Event(id="2016test", name="Test Event", event_short="Test Event", year=2016, event_type_enum=EventType.REGIONAL) event.put() status = SuggestionCreator.createApiWriteSuggestion( self.account.key, "2016test", "Event Organizer", [AuthType.MATCH_VIDEO, AuthType.EVENT_MATCHES, AuthType.EVENT_ALLIANCES]) self.assertEqual(status, 'success') # Ensure the Suggestion gets created with only MATCH_VIDEO permission suggestions = Suggestion.query().fetch() self.assertIsNotNone(suggestions) self.assertEqual(len(suggestions), 1) suggestion = suggestions[0] self.assertIsNotNone(suggestion) self.assertEqual(suggestion.contents['event_key'], "2016test") self.assertEqual(suggestion.contents['affiliation'], "Event Organizer") self.assertListEqual(suggestion.contents['auth_types'], [AuthType.MATCH_VIDEO])
def test_accept_suggestion(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm() form['accept_reject-{}'.format(suggestion_id)] = 'accept::{}'.format( suggestion_id) response = form.submit().follow() self.assertEqual(response.status_int, 200) suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED) medias = Media.query().fetch() self.assertEqual(len(medias), 1) media = medias[0] self.assertIsNotNone(media) self.assertEqual(media.foreign_key, 'foobar') self.assertEqual(media.media_type_enum, MediaType.YOUTUBE_VIDEO) self.assertTrue(ndb.Key(Event, '2016nyny') in media.references)
def _process_rejected(self, reject_keys): """ Do everything we need to reject a batch of suggestions We can batch these, because we're just rejecting everything """ if not isinstance(reject_keys, list): reject_keys = [reject_keys] rejected_suggestion_futures = [ Suggestion.get_by_id_async(key) for key in reject_keys ] rejected_suggestions = map(lambda a: a.get_result(), rejected_suggestion_futures) for suggestion in rejected_suggestions: if suggestion.review_state == Suggestion.REVIEW_PENDING: suggestion.review_state = Suggestion.REVIEW_REJECTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewed_at = datetime.datetime.now() ndb.put_multi(rejected_suggestions)
def _fastpath_review(self): self.verify_permissions() suggestion = Suggestion.get_by_id(self.request.get('id')) status = None if suggestion and suggestion.target_model == 'robot': if suggestion.review_state == Suggestion.REVIEW_PENDING: slack_message = None if self.request.get('action') == 'accept': self._process_accepted(suggestion.key.id()) status = 'accepted' slack_message = "{0} ({1}) accepted the <https://grabcad.com/library/{2}|suggestion> for team <https://thebluealliance.com/team/{3}/{4}|{3} in {4}>".format( self.user_bundle.account.display_name, self.user_bundle.account.email, suggestion.contents['foreign_key'], suggestion.contents['reference_key'][3:], suggestion.contents['year']) elif self.request.get('action') == 'reject': self._process_rejected(suggestion.key.id()) status = 'rejected' slack_message = "{0} ({1}) rejected the <https://grabcad.com/library/{2}|suggestion> for team <https://thebluealliance.com/team/{3}/{4}|{3} in {4}>".format( self.user_bundle.account.display_name, self.user_bundle.account.email, suggestion.contents['foreign_key'], suggestion.contents['reference_key'][3:], suggestion.contents['year']) if slack_message: slack_sitevar = Sitevar.get_or_insert('slack.hookurls') if slack_sitevar: slack_url = slack_sitevar.contents.get('tbablog', '') OutgoingNotificationHelper.send_slack_alert( slack_url, slack_message) else: status = 'already_reviewed' else: status = 'bad_suggestion' if status: self.redirect('/suggest/review?status={}'.format(status), abort=True)
def get(self): suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "social-media").fetch(limit=50) reference_keys = [] for suggestion in suggestions: reference_key = suggestion.contents['reference_key'] reference = Media.create_reference( suggestion.contents['reference_type'], reference_key) reference_keys.append(reference) reference_futures = ndb.get_multi_async(reference_keys) references = map(lambda r: r.get_result(), reference_futures) suggestions_and_references = zip(suggestions, references) self.template_values.update({ "suggestions_and_references": suggestions_and_references, }) self.response.out.write(jinja2_engine.render('suggestions/suggest_team_social_review.html', self.template_values))
def test_accept_social_media(self): self.loginUser() self.giveTeamAdminAccess() suggestion_id = self.createSocialMediaSuggestion() form = self.getSuggestionForm('social-media') form['accept_reject-{}'.format(suggestion_id)] = 'accept::{}'.format( suggestion_id) response = form.submit().follow() self.assertEqual(response.status_int, 200) suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED) medias = Media.query().fetch() self.assertEqual(len(medias), 1) media = medias[0] self.assertIsNotNone(media) self.assertEqual(media.foreign_key, 'frc1124') self.assertEqual(media.media_type_enum, MediaType.TWITTER_PROFILE) self.assertTrue(ndb.Key(Team, 'frc1124') in media.references)
def testSuggestApiWrite(self): self.loginUser() form = self.getSuggestionForm() form['event_key'] = '2016necmp' form['role'] = 'Test Code' form.get('auth_types', index=0).checked = True form.get('auth_types', index=1).checked = True response = form.submit().follow() self.assertEqual(response.status_int, 200) # Make sure the Suggestion gets created suggestion = Suggestion.query().fetch()[0] self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_PENDING) self.assertEqual(suggestion.contents['event_key'], '2016necmp') self.assertEqual(suggestion.contents['affiliation'], 'Test Code') self.assertListEqual(suggestion.contents['auth_types'], [AuthType.MATCH_VIDEO, AuthType.EVENT_TEAMS]) # Ensure we show a success message on the page request = response.request self.assertEqual(request.GET.get('status'), 'success')
def test_accept_suggestion_as_preferred(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm() form['accept_reject-{}'.format(suggestion_id)] = 'accept::{}'.format( suggestion_id) form['preferred_keys[]'] = ['preferred::{}'.format(suggestion_id)] response = form.submit().follow() self.assertEqual(response.status_int, 200) suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED) medias = Media.query().fetch() self.assertEqual(len(medias), 1) media = medias[0] self.assertIsNotNone(media) self.assertEqual(media.foreign_key, 'foobar') self.assertEqual(media.media_type_enum, MediaType.IMGUR) self.assertTrue(ndb.Key(Team, 'frc1124') in media.preferred_references)
def get(self): suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "event_media").fetch(limit=50) # Quick and dirty way to group images together suggestions = sorted( suggestions, key=lambda x: 0 if x.contents['media_type_enum'] in MediaType.image_types else 1) reference_keys = [] for suggestion in suggestions: reference_key = suggestion.contents['reference_key'] reference = Media.create_reference( suggestion.contents['reference_type'], reference_key) reference_keys.append(reference) if 'details_json' in suggestion.contents: suggestion.details = json.loads( suggestion.contents['details_json']) if 'image_partial' in suggestion.details: suggestion.details['thumbnail'] = suggestion.details[ 'image_partial'].replace('_l', '_m') reference_futures = ndb.get_multi_async(reference_keys) references = map(lambda r: r.get_result(), reference_futures) suggestions_and_references = zip(suggestions, references) self.template_values.update({ "suggestions_and_references": suggestions_and_references, }) self.response.out.write( jinja2_engine.render( 'suggestions/suggest_event_media_review_list.html', self.template_values))
def get(self): suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "match").fetch(limit=50) # Roughly sort by event and match for easier review suggestions = sorted(suggestions, key=lambda s: s.target_key) event_futures = [ Event.get_by_id_async(suggestion.target_key.split("_")[0]) for suggestion in suggestions ] events = [event_future.get_result() for event_future in event_futures] self.template_values.update({ "suggestions_and_events": zip(suggestions, events), }) self.response.out.write( jinja2_engine.render( 'suggestions/suggest_match_video_review_list.html', self.template_values))
def test_create_suggestion_banned(self): event = Event(id="2016test", name="Test Event", event_short="Test Event", year=2016, event_type_enum=EventType.OFFSEASON) event.put() status = SuggestionCreator.createApiWriteSuggestion( self.account_banned.key, "2016test", "Event Organizer", [1, 2, 3]) self.assertEqual(status, 'success') # Ensure the Suggestion gets created suggestions = Suggestion.query().fetch() self.assertIsNotNone(suggestions) self.assertEqual(len(suggestions), 1) suggestion = suggestions[0] self.assertIsNotNone(suggestion) self.assertEqual(suggestion.contents['event_key'], "2016test") self.assertEqual(suggestion.contents['affiliation'], "Event Organizer") self.assertListEqual(suggestion.contents['auth_types'], [1, 2, 3]) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_AUTOREJECTED)
def testAcceptWithDefaultDetails(self): self.loginUser() self.givePermission() suggestion_id = self.createSuggestion() form = self.getSuggestionForm('review_{}'.format(suggestion_id)) response = form.submit('verdict', value='accept').follow() self.assertEqual(response.status_int, 200) request = response.request self.assertEqual(request.GET.get('success'), 'accept') # Make sure we mark the Suggestion as REVIEWED suggestion = Suggestion.get_by_id(suggestion_id) self.assertIsNotNone(suggestion) self.assertEqual(suggestion.review_state, Suggestion.REVIEW_ACCEPTED) # Make sure the Event has no webcasts event = Event.get_by_id('2016necmp') self.assertIsNotNone(event.webcast) self.assertEqual(len(event.webcast), 1) webcast = event.webcast[0] self.assertEqual(webcast['type'], 'twitch') self.assertEqual(webcast['channel'], 'frcgamesense')
def createOffseasonEventSuggestion(cls, author_account_key, name, start_date, end_date, website, venue_name, address, city, state, country, first_code=None, suggestion_id=None): """ Create a suggestion for offseason event. Returns (status, failures): ('success', None) ('validation_failure', failures) """ failures = {} if not name: failures['name'] = "Missing event name" if not start_date: failures['start_date'] = "Missing start date" if not end_date: failures['end_date'] = "Missing end date" if not website: failures['website'] = "Missing website" if not address: failures['venue_address'] = "Missing address" if not venue_name: failures['venue_name'] = "Missing venue name" if not city: failures['venue_city'] = "Missing city" if not state: failures['venue_state'] = "Missing state" if not country: failures['venue_country'] = "Missing country" start_datetime = None end_datetime = None if start_date: try: start_datetime = datetime.strptime(start_date, "%Y-%m-%d") except ValueError: failures[ 'start_date'] = "Invalid start date format (year-month-date)" if end_date: try: end_datetime = datetime.strptime(end_date, "%Y-%m-%d") except ValueError: failures[ 'end_date'] = "Invalid end date format (year-month-date)" if start_datetime and end_datetime and end_datetime < start_datetime: failures['end_date'] = "End date must not be before the start date" if failures and not suggestion_id: # Be more lenient with auto-added suggestions return 'validation_failure', failures # Note that we don't typically specify an explicit key for event suggestions # We don't trust users to input correct event keys (that's for the moderator to do) suggestion = Suggestion( id=suggestion_id) if suggestion_id else Suggestion() suggestion.author = author_account_key suggestion.target_model = "offseason-event" suggestion.contents = { 'name': name, 'start_date': start_date, 'end_date': end_date, 'website': website, 'venue_name': venue_name, 'address': address, 'city': city, 'state': state, 'country': country, 'first_code': first_code, } suggestion.put() return 'success', None
def createTeamMediaSuggestion(cls, author_account_key, media_url, team_key, year_str, private_details_json=None, is_social=False, default_preferred=False): """Create a Team Media Suggestion. Returns status (success, suggestion_exists, media_exists, bad_url)""" media_dict = MediaParser.partial_media_dict_from_url(media_url) if media_dict is not None: if media_dict.get("is_social", False) != is_social: return 'bad_url', None existing_media = Media.get_by_id( Media.render_key_name(media_dict['media_type_enum'], media_dict['foreign_key'])) if existing_media is None or team_key not in [ reference.id() for reference in existing_media.references ]: foreign_type = Media.SLUG_NAMES[media_dict['media_type_enum']] suggestion_id = Suggestion.render_media_key_name( year_str, 'team', team_key, foreign_type, media_dict['foreign_key']) suggestion = Suggestion.get_by_id(suggestion_id) if not suggestion or suggestion.review_state != Suggestion.REVIEW_PENDING: media_dict['year'] = int(year_str) if year_str else None media_dict['reference_type'] = 'team' media_dict['reference_key'] = team_key media_dict['default_preferred'] = default_preferred if private_details_json is not None: media_dict[ 'private_details_json'] = private_details_json target_model = "media" if media_dict.get("is_social", False): target_model = "social-media" if media_dict.get('media_type', '') in MediaType.robot_types: target_model = "robot" if Event.validate_key_name(team_key): target_model = 'event_media' media_dict['reference_type'] = 'event' suggestion = Suggestion( id=suggestion_id, author=author_account_key, target_model=target_model, ) suggestion.contents = media_dict suggestion.put() return 'success', suggestion else: return 'suggestion_exists', None else: return 'media_exists', None else: return 'bad_url', None
class SuggestionCreator(object): @classmethod def createTeamMediaSuggestion(cls, author_account_key, media_url, team_key, year_str, private_details_json=None, is_social=False, default_preferred=False): """Create a Team Media Suggestion. Returns status (success, suggestion_exists, media_exists, bad_url)""" media_dict = MediaParser.partial_media_dict_from_url(media_url) if media_dict is not None: if media_dict.get("is_social", False) != is_social: return 'bad_url', None existing_media = Media.get_by_id( Media.render_key_name(media_dict['media_type_enum'], media_dict['foreign_key'])) if existing_media is None or team_key not in [ reference.id() for reference in existing_media.references ]: foreign_type = Media.SLUG_NAMES[media_dict['media_type_enum']] suggestion_id = Suggestion.render_media_key_name( year_str, 'team', team_key, foreign_type, media_dict['foreign_key']) suggestion = Suggestion.get_by_id(suggestion_id) if not suggestion or suggestion.review_state != Suggestion.REVIEW_PENDING: media_dict['year'] = int(year_str) if year_str else None media_dict['reference_type'] = 'team' media_dict['reference_key'] = team_key media_dict['default_preferred'] = default_preferred if private_details_json is not None: media_dict[ 'private_details_json'] = private_details_json target_model = "media" if media_dict.get("is_social", False): target_model = "social-media" if media_dict.get('media_type', '') in MediaType.robot_types: target_model = "robot" if Event.validate_key_name(team_key): target_model = 'event_media' media_dict['reference_type'] = 'event' suggestion = Suggestion( id=suggestion_id, author=author_account_key, target_model=target_model, ) suggestion.contents = media_dict suggestion.put() return 'success', suggestion else: return 'suggestion_exists', None else: return 'media_exists', None else: return 'bad_url', None @classmethod def createEventMediaSuggestion(cls, author_account_key, media_url, event_key, private_details_json=None): """Create an Event Media Suggestion. Returns status (success, suggestion_exists, media_exists, bad_url)""" media_dict = MediaParser.partial_media_dict_from_url(media_url) if media_dict is not None: if media_dict[ 'media_type_enum'] != MediaType.YOUTUBE_VIDEO and media_dict[ 'media_type_enum'] != MediaType.INTERNET_ARCHIVE_VIDEO: return 'bad_url', None existing_media = Media.get_by_id( Media.render_key_name(media_dict['media_type_enum'], media_dict['foreign_key'])) if existing_media is None or event_key not in [ reference.id() for reference in existing_media.references ]: foreign_type = Media.SLUG_NAMES[media_dict['media_type_enum']] suggestion_id = Suggestion.render_media_key_name( event_key[:4], 'event', event_key, foreign_type, media_dict['foreign_key']) suggestion = Suggestion.get_by_id(suggestion_id) if not suggestion or suggestion.review_state != Suggestion.REVIEW_PENDING: media_dict['year'] = event_key[:4] media_dict['reference_type'] = 'event' media_dict['reference_key'] = event_key target_model = 'event_media' if private_details_json is not None: media_dict[ 'private_details_json'] = private_details_json suggestion = Suggestion( id=suggestion_id, author=author_account_key, target_model=target_model, ) suggestion.contents = media_dict suggestion.put() return 'success', suggestion else: return 'suggestion_exists', None else: return 'media_exists', None else: return 'bad_url', None @classmethod def createEventWebcastSuggestion(cls, author_account_key, webcast_url, webcast_date, event_key): """Create a Event Webcast Suggestion. Returns status string""" webcast_url = WebsiteHelper.format_url(webcast_url) webcast_date = webcast_date.strip() if webcast_date: try: datetime.strptime(webcast_date, "%Y-%m-%d") except ValueError: return 'invalid_date' else: webcast_date = None try: webcast_dict = WebcastParser.webcast_dict_from_url(webcast_url) except Exception, e: logging.exception(e) webcast_dict = None if webcast_dict is not None: # Check if webcast already exists in event event = Event.get_by_id(event_key) if not event: return 'bad_event' if event.webcast and webcast_dict in event.webcast: return 'webcast_exists' else: suggestion_id = Suggestion.render_webcast_key_name( event_key, webcast_dict) suggestion = Suggestion.get_by_id(suggestion_id) # Check if suggestion exists if not suggestion or suggestion.review_state != Suggestion.REVIEW_PENDING: suggestion = Suggestion( id=suggestion_id, author=author_account_key, target_model="event", target_key=event_key, ) suggestion.contents = { "webcast_dict": webcast_dict, "webcast_url": webcast_url, "webcast_date": webcast_date } suggestion.put() return 'success' else: return 'suggestion_exists' else: # Can't parse URL -- could be an obscure webcast. Save URL and let a human deal with it. contents = { "webcast_url": webcast_url, "webcast_date": webcast_date } # Check if suggestion exists existing_suggestions = Suggestion.query( Suggestion.target_model == 'event', Suggestion.target_key == event_key).fetch() for existing_suggestion in existing_suggestions: if existing_suggestion.contents == contents: return 'suggestion_exists' suggestion = Suggestion( author=author_account_key, target_model="event", target_key=event_key, ) suggestion.contents = contents suggestion.put() return 'success'
class SuggestionCreator(object): @classmethod def createTeamMediaSuggestion(cls, author_account_key, media_url, team_key, year_str, private_details_json=None, is_social=False): """Create a Team Media Suggestion. Returns status (success, suggestion_exists, media_exists, bad_url)""" media_dict = MediaParser.partial_media_dict_from_url(media_url) if media_dict is not None: if media_dict.get("is_social", False) != is_social: return 'bad_url', None existing_media = Media.get_by_id( Media.render_key_name(media_dict['media_type_enum'], media_dict['foreign_key'])) if existing_media is None or team_key not in [ reference.id() for reference in existing_media.references ]: foreign_type = Media.SLUG_NAMES[media_dict['media_type_enum']] suggestion_id = Suggestion.render_media_key_name( year_str, 'team', team_key, foreign_type, media_dict['foreign_key']) suggestion = Suggestion.get_by_id(suggestion_id) if not suggestion or suggestion.review_state != Suggestion.REVIEW_PENDING: media_dict['year'] = int(year_str) if year_str else None media_dict['reference_type'] = 'team' media_dict['reference_key'] = team_key if private_details_json is not None: media_dict[ 'private_details_json'] = private_details_json target_model = "media" if media_dict.get("is_social", False): target_model = "social-media" if media_dict.get('media_type', '') in MediaType.robot_types: target_model = "robot" suggestion = Suggestion( id=suggestion_id, author=author_account_key, target_model=target_model, ) suggestion.contents = media_dict suggestion.put() return 'success', suggestion else: return 'suggestion_exists', None else: return 'media_exists', None else: return 'bad_url', None @classmethod def createEventWebcastSuggestion(cls, author_account_key, webcast_url, event_key): """Create a Event Webcast Suggestion. Returns status string""" webcast_url = webcast_url.strip() if not webcast_url.startswith( 'http://') and not webcast_url.startswith('https://'): webcast_url = 'http://' + webcast_url try: webcast_dict = WebcastParser.webcast_dict_from_url(webcast_url) except Exception, e: logging.exception(e) webcast_dict = None if webcast_dict is not None: # Check if webcast already exists in event event = Event.get_by_id(event_key) if not event: return 'bad_event' if event.webcast and webcast_dict in event.webcast: return 'webcast_exists' else: suggestion_id = Suggestion.render_webcast_key_name( event_key, webcast_dict) suggestion = Suggestion.get_by_id(suggestion_id) # Check if suggestion exists if not suggestion or suggestion.review_state != Suggestion.REVIEW_PENDING: suggestion = Suggestion( id=suggestion_id, author=author_account_key, target_model="event", target_key=event_key, ) suggestion.contents = { "webcast_dict": webcast_dict, "webcast_url": webcast_url } suggestion.put() return 'success' else: return 'suggestion_exists' else: # Can't parse URL -- could be an obscure webcast. Save URL and let a human deal with it. contents = {"webcast_url": webcast_url} # Check if suggestion exists existing_suggestions = Suggestion.query( Suggestion.target_model == 'event', Suggestion.target_key == event_key).fetch() for existing_suggestion in existing_suggestions: if existing_suggestion.contents == contents: return 'suggestion_exists' suggestion = Suggestion( author=author_account_key, target_model="event", target_key=event_key, ) suggestion.contents = contents suggestion.put() return 'success'