def build_reporting_unit(self, pc_id, checkpoints, tasks): url_base = '{protocol}://{domain}'.format( protocol='http' if util.is_localhost() else 'https', domain=('localhost:8888' if util.is_localhost() else os.environ['HOSTING_DOMAIN']), ) # Find the right task for this project cohort c = next(c for c in checkpoints if c.project_cohort_id == pc_id) t = next(t for t in tasks if t.checkpoint_id == c.uid) # Assemble the URLs that RServe will need to post back to. dataset_url = '{base}/api/datasets?parent_id={parent_id}'.format( base=url_base, parent_id=pc_id, ) task_attachment_url = '{base}/api/tasks/{task_id}/attachment'.format( base=url_base, task_id=t.uid, ) return { 'project_cohort_id': pc_id, 'post_url': dataset_url, 'post_task_attachment_url': task_attachment_url, }
def get_url(script): return '{protocol}://{domain}/api/scripts/{script}'.format( protocol='http' if util.is_localhost() else 'https', domain=('localhost:9080' if util.is_localhost() else os.environ['RSERVE_DOMAIN']), script=script, )
def setUp(self): """Sets self.testbed and activates it, among other global settings. This function (noticeably not named with PERTS-standard underscore case) is automatically called when starting a test by the unittest module. We use it for basic configuration and delegate further set up to the more canonically named set_up() of inheriting classes. """ if not util.is_localhost(): # Logging test activity in production causes errors. This # suppresses all logs of level critical and lower, which means all # of them. See # https://docs.python.org/2/library/logging.html#logging.disable logging.disable(logging.CRITICAL) # Start a clean testing environment for one test. self.testbed = testbed.Testbed() self.testbed.activate() # We have to use a memcache stub so that appstats doesn't complain in # production. self.testbed.init_memcache_stub() # NDB has lots of fancy caching features, whic are normally great, but # get in the way of testing consistency. # https://cloud.google.com/appengine/docs/python/ndb/cache ndb_context = ndb.get_context() ndb_context.set_cache_policy(lambda x: False) ndb_context.set_memcache_policy(lambda x: False) # Let inheriting classes to their own set up. if hasattr(self, 'set_up'): self.set_up()
def write(self, template_filename, template_path=None, **kwargs): if template_path is None: template_path = 'templates' jinja_environment = jinja2.Environment( autoescape=True, extensions=['jinja2.ext.autoescape'], loader=jinja2.FileSystemLoader(template_path), ) # Jinja environment filters: @jinja2.evalcontextfilter def jinja_json_filter(eval_context, value): """Seralize value as JSON and mark as safe for jinja.""" return jinja2.Markup(json.dumps(value)) jinja_environment.filters['to_json'] = jinja_json_filter # default parameters that all views get kwargs['google_logout_url'] = app_engine_users.create_login_url() kwargs['hosting_domain'] = os.environ['HOSTING_DOMAIN'] kwargs['is_localhost'] = util.is_localhost() # Try to load the requested template. If it doesn't exist, replace # it with a 404. try: template = jinja_environment.get_template(template_filename) except jinja2.exceptions.TemplateNotFound: return self.http_not_found() # Render the template with data and write it to the HTTP response. self.response.write(template.render(kwargs))
def connect_to_db(self): """Establish connection to MySQL db instance. Either Google Cloud SQL or local MySQL server. Detects environment with functions from util module. """ if util.is_localhost() or util.is_codeship(): credentials = { 'host': self.local_ip, 'port': self.local_port, 'user': self.local_user, 'passwd': self.local_password } else: # Note: for second generation cloud sql instances, the instance # name must include the region, e.g. 'us-central1:production-01'. credentials = { 'unix_socket': '/cloudsql/{app_id}:{instance_name}'.format( app_id=app_identity.get_application_id(), instance_name=self.cloud_sql_instance), 'user': self.cloud_sql_user, } if self.db_name: credentials['db'] = self.db_name # Although the docs say you can specify a `cursorclass` keyword # here as an easy way to get dictionaries out instead of lists, that # only works in version 1.2.5, and App Engine only has 1.2.4b4 # installed as of 2015-03-30. Don't use it unless you know the # production library has been updated. # tl;dr: the following not allowed! # self.connection = MySQLdb.connect( # charset='utf8', cursorclass=MySQLdb.cursors.DictCursor, **creds) self.connection = MySQLdb.connect(charset='utf8', **credentials) self.cursor = self.connection.cursor()
def get(self, template_name): """Overrides BaseHandler.get and session management for max speed.""" params = {} # Needed for shortcut to register with neptune. if util.is_localhost(): params['neptune_domain'] = 'localhost:8080' params['neptune_protocol'] = 'http' params['triton_domain'] = 'localhost:10080' params['triton_protocol'] = 'http' else: params['neptune_domain'] = os.environ['NEPTUNE_DOMAIN'] params['neptune_protocol'] = 'https' params['triton_domain'] = os.environ['TRITON_DOMAIN'] params['triton_protocol'] = 'https' # Provide the current template name to jinja so it can highlight the # active navbar link. params['template_name'] = template_name template = template_name + '.html' if not os.path.isfile('templates/' + template): template, params = self.http_not_found() jinja_environment = jinja2.Environment( autoescape=True, loader=jinja2.FileSystemLoader('templates')) html_str = jinja_environment.get_template(template).render(params) self.response.write(html_str)
def setUp(self): """Sets self.testbed and activates it, among other global settings. This function (noticeably not named with PERTS-standard undrescore case) is automatically called when starting a test by the unittest module. We use it for basic configuration and delegate further set up to the more canonically named set_up() of inheriting classes. """ if not util.is_localhost(): # Logging test activity in production causes errors. This # suppresses all logs of level critical and lower, which means all # of them. See # https://docs.python.org/2/library/logging.html#logging.disable logging.disable(logging.CRITICAL) # Start a clean testing environment for one test. self.testbed = testbed.Testbed() self.testbed.activate() # We have to use a memcache stub so that appstats doesn't complain in # production. self.testbed.init_memcache_stub() # Let inheriting classes to their own set up. if hasattr(self, 'set_up'): self.set_up()
def render(self): """Interpolate data into an html string. Ignores `{items}` b/c that's for digest(). """ # Add environment and user details to params. protocol = 'http' if util.is_localhost() else 'https' domain = ('localhost:3000' if util.is_localhost() else os.environ['HOSTING_DOMAIN']) params = dict( self.template_params, triton_domain='{}://{}'.format(protocol, domain), user_id=self.user_id, short_user_id=Notification.convert_uid(self.user_id), ) return self.get_template().render(**params)
def get_credentials(self): """Establish connection to MySQL db instance. Either Google Cloud SQL or local MySQL server. Detects environment with functions from util module. """ if util.is_localhost() or util.is_codeship(): credentials = { 'host': self.local_ip, 'port': self.local_port, 'user': self.local_user, 'passwd': self.local_password } else: # Note: for second generation cloud sql instances, the instance # name must include the region, e.g. 'us-central1:production-01'. credentials = { 'unix_socket': '/cloudsql/{app_id}:{instance_name}'.format( app_id=app_identity.get_application_id(), instance_name=self.cloud_sql_instance), 'user': self.cloud_sql_user, } if self.db_name: credentials['db'] = self.db_name return credentials
def send(template_data={}, **kwargs): # Determine if message should send if util.is_localhost() and not config.should_deliver_smtp_dev: logging.info('Email not sent, check config!') return None subject = render(kwargs['subject'], **template_data) # Determine if using html string or a template body = '' if 'body' in kwargs: body = render(kwargs['body'], **template_data) elif 'template' in kwargs: body = render_template(kwargs['template'], **template_data) # JSON for Mandrill HTTP POST request json_mandrill = { "key": config.mandrill_api_key, "message": { "html": body, "subject": subject, "from_email": config.from_server_email_address, "from_name": config.from_server_name, "inline_css": True, "to": format_to_address(kwargs['to_address']) } } # URL for Mandrill HTTP POST request url = "https://mandrillapp.com/api/1.0/messages/send.json" rpc = urlfetch.create_rpc() urlfetch.make_fetch_call( rpc, url=url, payload=json.dumps(json_mandrill), method=urlfetch.POST, headers={'Content-Type': 'application/x-www-form-urlencoded'}) try: result = rpc.get_result() except urlfetch.DownloadError: # Request timed out or failed. On localhost this can be from network # issues. logging.error("Mandrill RPC failed.") return None result_info = json.loads(result.content)[0] logging.info("Mandrill response: {} {}" .format(result.status_code, result_info)) if result.status_code == 200: if result_info['status'] != 'sent': logging.error("Email failed to send.") else: logging.error("Mandrill response wasn't a 200.") return result
def do_wrapper(self, *args, **kwargs): try: output = self.do(*args, **kwargs) except Exception as error: logging.error("{}".format(error)) if debug: import traceback self.response.write('<pre>{}</pre>'.format( traceback.format_exc())) else: self.response.write("We are having technical difficulties.") return if output is not None: # do methods might not put out rendering info # todo: they really should always do that. make sure they do then # remove this check template = output[0] params = output[1] if len(output) >= 3: template_directory = output[2] else: template_directory = 'templates' jinja_environment = jinja2.Environment( autoescape=True, loader=jinja2.FileSystemLoader(template_directory), # # These change jinja's template syntax # variable_start_string='[[', # variable_end_string=']]', # block_start_string='[%', # block_end_string='%]' ) jinja_environment.globals['gae_mini_profiler_includes'] = ( gae_mini_profiler.templatetags.profiler_includes) # Jinja environment filters: jinja_environment.filters['tojson'] = json.dumps # default parameters that all views get user = self.get_current_user() normal_user = self.get_current_user(method='normal') params['user'] = user params['normal_user'] = normal_user params['config'] = config params['currently_impersonating'] = user != normal_user params['connected_to_facebook'] = bool(self.facebook_cookie()) if params['connected_to_facebook']: params['facebook_user'] = self.get_third_party_auth('facebook') else: params['facebook_user'] = None params['connected_to_google'] = app_engine_users.get_current_user() params['google_user'] = app_engine_users.get_current_user() params['google_admin'] = app_engine_users.is_current_user_admin() params[ 'current_url'] = self.request.path + '?' + self.request.query_string params['is_localhost'] = util.is_localhost() if debug: params['session'] = json.dumps(self.session) # render html_str = jinja_environment.get_template(template).render(params) self.response.write(html_str)
def notify_for_single_report(self, program, team, classroom=None): """Notifications for class contact or all members re: one report.""" if not team.report_reminders: logging.info( "{} has report reminders disabled; not notifying".format( team.uid)) return if classroom: recipients = [User.get_by_id(classroom.contact_id)] else: recipients = User.query_by_team(team.uid) protocol = 'http' if util.is_localhost() else 'https' # Note that reports are hosted by the local _backend_ gae sdk, not the # react-app dev server on :3000. But for now we're just pointing to the # list of a team's reports, which _is_ on :3000. domain = ('localhost:3000' if util.is_localhost() else os.environ['HOSTING_DOMAIN']) reports_page = '{}://{}/teams/{}/reports'.format( protocol, domain, team.short_uid) # If we do want to link directly to the report, we'll have to wrestle # with authentication and what that looks like. We can either generate # a token right here (but that would be the link was shareable, and it # would expire, both of which seem wrong) or build some additional # client code that would know how to redirect an unauthed user to # login and back. notes = [] for user in recipients: notes.append( Notification.create( user_id=user.uid, type='report', template_params={ 'context_name': classroom.name if classroom else team.name, 'first_name': user.first_name, 'program_name': program.name, 'url': reports_page, })) return notes
def report_link(self, report): parent_kind = SqlModel.get_url_kind(report.parent_id) short_id = SqlModel.convert_uid(report.parent_id) if report.gcs_path: platform = 'triton' prefix = '' view_path = '/api/{parent_kind}/{id}/reports/{filename}'.format( parent_kind=parent_kind, id=short_id, filename=report.filename, ) elif report.dataset_id: platform = 'neptune' prefix = '{protocol}://{domain}'.format( protocol='http' if util.is_localhost() else 'https', domain=('localhost:8888' if util.is_localhost() else os.environ['NEPTUNE_DOMAIN']), ) view_path = '/datasets/{ds_id}/{template}/{filename}'.format( ds_id=SqlModel.convert_uid(report.dataset_id), # short form template=report.template, filename=report.filename, ) # Permit report clients to query some data about participation. parent_path = '/api/{parent_kind}/{id}'.format(parent_kind=parent_kind, id=short_id) data_path = '/api/{parent_kind}/{id}/report_data'.format( parent_kind=parent_kind, id=short_id) link_jwt = jwt_helper.encode( { 'allowed_endpoints': [ self.get_endpoint_str(platform=platform, path=view_path), self.get_endpoint_str(platform='triton', path=parent_path), self.get_endpoint_str(platform='triton', path=data_path), ] }, expiration_minutes=(30 * 24 * 60), # thirty days ) return util.set_query_parameters(prefix + view_path, token=link_jwt)
def write(self, template_filename, template_path='templates', **kwargs): jinja_environment = self.get_jinja_environment(template_path) # Jinja environment filters: @jinja2.evalcontextfilter def jinja_json_filter(eval_context, value): """Seralize value as JSON and mark as safe for jinja.""" return jinja2.Markup(json.dumps(value)) jinja_environment.filters['to_json'] = jinja_json_filter def format_datetime(value): # Formats datetime as Ex: "January 9, 2015" return '{dt:%B} {dt.day}, {dt.year}'.format(dt=value) jinja_environment.filters['datetime'] = format_datetime user = self.get_current_user() # default parameters that all views get kwargs['user'] = user.to_client_dict() # Python keeps time to the microsecond, but we don't need it, and # it's easier to render as ISO 8601 without it. kwargs['server_time'] = datetime.datetime.today().replace( microsecond=0) kwargs['is_localhost'] = util.is_localhost() kwargs['hosting_domain'] = os.environ['HOSTING_DOMAIN'] kwargs['yellowstone_domain'] = os.environ['YELLOWSTONE_DOMAIN'] kwargs['browser_api_key'] = (os.environ['LOCALHOST_BROWSER_API_KEY'] if util.is_localhost() else os.environ['DEPLOYED_BROWSER_API_KEY']) # Try to load the requested template. If it doesn't exist, replace # it with a 404. try: template = jinja_environment.get_template(template_filename) except jinja2.exceptions.TemplateNotFound: logging.error("TemplateNotFound: {}".format(template_filename)) return self.http_not_found() # Render the template with data and write it to the HTTP response. self.response.write(template.render(kwargs))
def get(self, *args, **kwargs): self.write('dist/neptune.html', is_localhost=util.is_localhost(), sentry_url=os.environ.get('SENTRY_URL', None), triton_domain=os.environ.get('TRITON_DOMAIN', None), include_profiler=os.environ.get('INCLUDE_PROFILER', None) == 'true', profiler_includes=gae_mini_profiler.templatetags. profiler_includes())
def http_not_found(self, **kwargs): """Respond with a 404. Example use: class Foo(ViewHandler): def get(self): return self.http_not_found() """ # default parameters that all views get user = self.get_current_user() # Sets up the google sign in link, used in modal on all pages, which # must include a special flag to alert this handler that google # credentials are present in the cookie. It should also incorporate any # redirect already set in the URL. redirect = str(self.request.get('redirect')) or self.request.url google_redirect = util.set_query_parameters(redirect, google_login='******') google_login_url = app_engine_users.create_login_url(google_redirect) kwargs['user'] = user kwargs['google_login_url'] = google_login_url kwargs['hosting_domain'] = os.environ['HOSTING_DOMAIN'] kwargs['share_url'] = self.request.url kwargs['google_client_id'] = config.google_client_id # Determine which Facebook app depending on environment kwargs['localhost'] = False if util.is_localhost(): kwargs['localhost'] = True # Fetch all themes and topics for navigation courses = self.api.get('Theme') if courses: # fetch topics for each theme course_topic_ids = [ id for course in courses for id in course.topics ] course_topics = self.api.get_by_id(course_topic_ids) # associate topics with appropriate courses for course in courses: course.associate_topics(course_topics) # Special case for "Teachers" kit if course.name == 'Growth Mindset for Teachers': kwargs['teacher_topics'] = course.topics_list kwargs['courses'] = courses self.error(404) jinja_environment = self.get_jinja_environment() template = jinja_environment.get_template('404.html') self.response.write(template.render(kwargs))
def build_reporting_unit(entity): """Compose dict describing one unit for which to generate a report.""" neptune_url_base = '{protocol}://{domain}'.format( protocol='http' if util.is_localhost() else 'https', domain=('localhost:8888' if util.is_localhost() else os.environ['NEPTUNE_DOMAIN']), ) triton_url_base = '{protocol}://{domain}'.format( protocol='http' if util.is_localhost() else 'https', domain=('localhost:10080' if util.is_localhost() else os.environ['HOSTING_DOMAIN']), ) # Assemble the URLs that RServe will need to post back to. dataset_url = '{base}/api/datasets?parent_id={parent_id}'.format( base=neptune_url_base, parent_id=entity.uid, ) report_url = '{base}/api/reports'.format(base=triton_url_base) reporting_unit = { 'id': entity.uid, 'team_id': None, 'classroom_id': None, 'post_url': dataset_url, 'post_report_url': report_url, } if DatastoreModel.get_kind(entity) == 'Organization': reporting_unit['organization_id'] = entity.uid elif DatastoreModel.get_kind(entity) == 'Team': reporting_unit['team_id'] = entity.uid elif DatastoreModel.get_kind(entity) == 'Classroom': # This kind of report needs two ids because it reports on two levels # at once. Classroom data is present in the context of team data. reporting_unit['team_id'] = entity.team_id reporting_unit['classroom_id'] = entity.uid return reporting_unit
def setUp(self): """Sets self.testbed and activates it, among other global settings. This function (noticeably not named with PERTS-standard underscore case) is automatically called when starting a test by the unittest module. We use it for basic configuration and delegate further set up to the more canonically named set_up() of inheriting classes. """ if not util.is_localhost(): # Logging test activity in production causes errors. This # suppresses all logs of level critical and lower, which means all # of them. See # https://docs.python.org/2/library/logging.html#logging.disable logging.disable(logging.CRITICAL) # Use the same namespace our test apps will use # i.e. webapp2.WSGIApplication if os.environ['NAMESPACE']: namespace_manager.set_namespace(os.environ['NAMESPACE']) # Start a clean testing environment for one test. self.testbed = testbed.Testbed() self.testbed.activate() # Stubs for services we use throughout. self.testbed.init_memcache_stub() self.testbed.init_taskqueue_stub() self.testbed.init_app_identity_stub() self.testbed.init_urlfetch_stub() self.testbed.init_blobstore_stub() # NDB has lots of fancy caching features, whic are normally great, but # get in the way of testing consistency. # https://cloud.google.com/appengine/docs/python/ndb/cache ndb_context = ndb.get_context() ndb_context.set_cache_policy(lambda x: False) ndb_context.set_memcache_policy(lambda x: False) # for simulating google users self.testbed.init_user_stub() # Allow code to detect whether or not it's running in a unit test. self.testbed.setup_env(currently_testing='true', overwrite=True) # Let inheriting classes to their own set up. if hasattr(self, 'set_up'): self.set_up()
def get(self): self.log_out() redirect = self.request.get('redirect') or '/' if util.is_localhost(): # In the SDK, it makes sense to log the current user out of Google # entirely (otherwise admins have to click logout twice, b/c # existing code will attempt to sign them right in again). self.redirect(app_engine_users.create_logout_url(redirect)) else: # In production, we don't want to sign users out of their Google # account entirely, because that would break their gmail, youtube, # etc. etc. Instead, just clear the cookies on *this* domain that # Google creates. That's what self.log_out() does above. So we're # done, except for a simple redirect. self.redirect(redirect)
def mandrill_send(template_data={}, **kwargs): # Determine if message should send if util.is_development() and not config.should_deliver_smtp_dev: logging.info('Email not sent, check config!') return None subject = render(kwargs['subject'], **template_data) # Add in default template data template_data['to_address'] = kwargs.get('to_address', None) template_data['domain'] = util.get_domain() # Python keeps time to the microsecond, but we don't need it, and # it's easier to render as ISO 8601 without it. template_data['server_time'] = datetime.datetime.today().replace(microsecond=0) template_data['contact_email_address'] = config.from_server_email_address # Determine if using html string or a template html_body = None if 'html' in kwargs: html_body = kwargs['html'] elif 'body' in kwargs: html_body = render(kwargs['body'], **template_data) elif 'template' in kwargs: html_body = render_template(kwargs['template'], **template_data) text_body = kwargs.get('text', None) if util.is_localhost() or util.is_testing(): sender = _send_localhost_and_testing elif util.is_development(): sender = _send_development else: sender = _send_production optional_mandrill_keys = ('from_address', 'reply_to', 'from_name') optional_mandrill_kwargs = {k: kwargs[k] for k in optional_mandrill_keys if k in kwargs} return sender(kwargs['to_address'], subject, html_body, text_body, kwargs.get('mandrill_template', None), kwargs.get('mandrill_template_content', None), kwargs.get('cc_address', None), kwargs.get('bcc_address', None), **optional_mandrill_kwargs)
def participation_query_url(cycle, classrooms): if util.is_localhost(): protocol = 'http' neptune_domain = 'localhost:8080' else: protocol = 'https' neptune_domain = os.environ['NEPTUNE_DOMAIN'] url = '{protocol}://{domain}/api/project_cohorts/participation'.format( protocol=protocol, domain=neptune_domain, ) return util.set_query_parameters( url, uid=[c.url_code for c in classrooms], start=cycle.start_date.strftime(config.iso_datetime_format), end=cycle.end_date.strftime(config.iso_datetime_format), )
def get(self, *args): if util.is_localhost(): neptune_protocol = 'http' neptune_domain = 'localhost:8080' triton_protocol = 'http' triton_domain = 'localhost:10080' else: neptune_protocol = 'https' neptune_domain = os.environ['NEPTUNE_DOMAIN'] triton_protocol = 'https' triton_domain = os.environ['HOSTING_DOMAIN'] self.write( 'index.html', neptune_protocol=neptune_protocol, neptune_domain=neptune_domain, triton_protocol=triton_protocol, triton_domain=triton_domain, sentry_config_url=os.environ['SENTRY_CONFIG_URL'], )
def get_participation_for_cycle(self, classrooms, cycle): if not cycle.start_date or not cycle.end_date: return {} handler = TeamsClassrooms() user = User.create(id='triton', email='') jwt = handler.classroom_participation_jwt(user, classrooms) if util.is_localhost(): protocol = 'http' neptune_domain = 'localhost:8080' else: protocol = 'https' neptune_domain = os.environ['NEPTUNE_DOMAIN'] start_datetime = datetime.datetime.combine( cycle.start_date, datetime.datetime.min.time()) end_datetime = datetime.datetime.combine(cycle.end_date, datetime.datetime.max.time()) url = ( '{protocol}://{domain}/api/project_cohorts/participation?{ids}&start={start_date}&end={end_date}' .format( protocol=protocol, domain=neptune_domain, ids='&'.join(['uid={}'.format(c.url_code) for c in classrooms]), start_date=util.datelike_to_iso_string(start_datetime), end_date=util.datelike_to_iso_string(end_datetime), )) result = urlfetch.fetch( url=url, method=urlfetch.GET, headers={'Authorization': 'Bearer {}'.format(jwt)}) if not result or result.status_code != 200: raise Exception("Failed to get participation {}".format(result)) return json.loads(result.content)
def connect_to_db(self): """Establish connection to MySQL db instance. Either Google Cloud SQL or local MySQL server. Detects environment with functions from util module. """ if util.is_localhost(): env_type = 'localhost' elif util.is_development(): env_type = 'development' else: env_type = 'production' creds = self.credentials[env_type] # Although the docs say you can specify a `cursorclass` keyword # here as an easy way to get dictionaries out instead of lists, that # only works in version 1.2.5, and App Engine only has 1.2.4b4 # installed as of 2015-03-30. Don't use it unless you know the # production library has been updated. # tl;dr: the following not allowed! # self.connection = MySQLdb.connect( # charset='utf8', cursorclass=MySQLdb.cursors.DictCursor, **creds) self.connection = MySQLdb.connect(charset='utf8', **creds)
def get_params(): """Get MySQL db connection for any environment.""" if util.is_localhost(): if util.is_testing(): # testing on localhost params = { 'db_name': os.environ['LOCAL_SQL_TEST_DB_NAME'], 'local_user': os.environ['LOCAL_SQL_USER'], 'local_password': os.environ['LOCAL_SQL_PASSWORD'], } else: # normal app running on localhost params = { 'db_name': os.environ['LOCAL_SQL_DB_NAME'], 'local_user': os.environ['LOCAL_SQL_USER'], 'local_password': os.environ['LOCAL_SQL_PASSWORD'], } elif util.is_codeship(): # testing on codeship params = { # These are set up for us by codeship. # https://documentation.codeship.com/databases/mysql/ 'db_name': 'test', 'local_user': os.environ['MYSQL_USER'], 'local_password': os.environ['MYSQL_PASSWORD'], } else: # Deployed, either on the dev app or the production app params = { 'db_name': os.environ['CLOUD_SQL_DB_NAME'], 'cloud_sql_instance': os.environ['CLOUD_SQL_INSTANCE_ID'], 'cloud_sql_user': '******', } return params
def setUp(self): """Sets self.testbed and activates it, among other global settings. This function (noticeably not named with PERTS-standard undrescore case) is automatically called when starting a test by the unittest module. We use it for basic configuration and delegate further set up to the more canonically named set_up() of inheriting classes. """ if not util.is_localhost(): # Logging test activity in production causes errors. This # suppresses all logs of level critical and lower, which means all # of them. See # https://docs.python.org/2/library/logging.html#logging.disable logging.disable(logging.CRITICAL) # Start a clean testing environment for one test. self.testbed = testbed.Testbed() self.testbed.activate() # user services self.testbed.init_user_stub() # Writing students involves tasks and memcache, so we need stubs for # those. self.testbed.init_memcache_stub() self.testbed.init_taskqueue_stub(root_path='.') self.taskqueue_stub = self.testbed.get_stub( testbed.TASKQUEUE_SERVICE_NAME) # Basic apis not related to specific users. self.public_api = Api(User(user_type='public')) self.internal_api = Api(User(user_type='god')) # Let inheriting classes to their own set up. if hasattr(self, 'set_up'): self.set_up()
def delete_everything(self): if not (self.user and self.user.is_admin and util.is_localhost()): raise PermissionDenied("Only admins working on a localhost " "server can delete everything.") util.delete_everything() util.delete_all_in_index(config.content_index)
def dispatch(self): """Wraps the other request handlers. * Manages sessions * Manages request profiling """ util.profiler.add_event("BaseHandler.dispatch()") # ** Code to run before all handlers goes here. ** # # The App Engine runtime does weird caching of classes and class # properties such that you can't expect them to be cleanly segregated # or reset between requests. But we want to use this property to avoid # multiple lookups of the same user within a request. So make sure it # has a clean start. # https://cloud.google.com/appengine/docs/standard/python/how-requests-are-handled#app-caching self._user = None if util.is_localhost(): # ports are arbitrary, but convenient os.environ['YELLOWSTONE_DOMAIN'] = 'localhost:9080' os.environ['YELLOWSTONE_PROTOCOL'] = 'http' os.environ['NEPTUNE_DOMAIN'] = 'localhost:8080' os.environ['NEPTUNE_PROTOCOL'] = 'http' os.environ['TRITON_DOMAIN'] = 'localhost:10080' os.environ['TRITON_PROTOCOL'] = 'http' else: # Various DOMAINs remain set as in app.yaml os.environ['YELLOWSTONE_PROTOCOL'] = 'https' os.environ['NEPTUNE_PROTOCOL'] = 'https' os.environ['TRITON_PROTOCOL'] = 'https' # Set the namespace, which varies by branch. namespace = os.environ['NAMESPACE'] if namespace: logging.info("Setting namespace: {}".format(namespace)) namespace_manager.set_namespace(namespace) # Newly deployed dev branches might not have a database in their # namespace yet. self.init_database() if self.using_sessions(): # Get a session store for this request. self.session_store = sessions.get_store(request=self.request) # Allow load testing services to log in quickly. if util.is_development() and self.request.get('demo_login', None) == 'wamxdkrwnkgey': user = User.get_by_id('User_demo') self.log_in(user) self.redirect(self.request.path) # Handler classes may set a class property `requires_auth` which triggers a check # for an authenticated user. If there isn't one, the request is immediately # rejeted with a 401. This does not apply to preflight OPTIONS calls which never # include Authorization headers (they're about figuring out the server's CORS # rules, not taking any actions). authed = getattr(self, 'requires_auth', False) options = self.request.method == 'OPTIONS' # This may be used by downstream handlers to override permissions if # necessary. self.allowed_by_jwt = self.jwt_allows_endpoint(self.get_endpoint_str()) if self.allowed_by_jwt: logging.info("BaseHandler: this request is ALLOWED by the jwt.") if authed and not options: user = self.get_current_user() if user.user_type == 'public' and not self.allowed_by_jwt: return self.http_unauthorized() try: # Call the overridden dispatch(), which has the effect of running # the get() or post() etc. of the inheriting class. webapp2.RequestHandler.dispatch(self) finally: # ** Code to run after all handlers goes here. ** # if self.using_sessions(): # Save all sessions. self.session_store.save_sessions(self.response) util.profiler.add_event("END") # Turn on for debugging/profiling. # logging.info(util.profiler) util.profiler.clear()
from model import SecretValue from task_handlers import task_routes from view_handlers import view_routes import config import logging import util # Attempt to look up the encryption key used to protect our session cookies. # In normal operation there should be one stored in a SecretValue entity. If # we can't find it, fill in a default value, but log an error to bother the # devs. sv_entity = SecretValue.get_by_id('session_cookie_secret_key') if sv_entity is None: secret_key = config.default_session_cookie_secret_key if not util.is_localhost(): logging.error("No SecretValue set for 'session_cookie_secret_key'.") else: # Why the string coercion? If the value here is a unicode, app # engine complains, "TypeError: character mapping must return # integer, None or unicode" secret_key = str(sv_entity.value) webapp2_config = { 'webapp2_extras.sessions': { # Related to cookie security, see: # http://webapp-improved.appspot.com/api/webapp2_extras/sessions.html 'secret_key': secret_key }, }
def write(self, template_filename, template_path='templates', **kwargs): util.profiler.add_event("Begin ViewHandler:write") jinja_environment = self.get_jinja_environment(template_path) # Jinja environment filters: @jinja2.evalcontextfilter def jinja_json_filter(eval_context, value): """Seralize value as JSON and mark as safe for jinja.""" return jinja2.Markup(json.dumps(value)) jinja_environment.filters['to_json'] = jinja_json_filter def nl2br(value): """Replace new lines with <br> for html view""" return value.replace('\n', '<br>\n') jinja_environment.filters['nl2br'] = nl2br def format_datetime(value): # Formats datetime as Ex: "January 9, 2015" return '{dt:%B} {dt.day}, {dt.year}'.format(dt=value) jinja_environment.filters['datetime'] = format_datetime def format_ampescape(value): return value.replace('&', '%26') jinja_environment.filters['ampescape'] = format_ampescape def format_filetype(value): if value.split('/')[0] in ['application']: if value.split('/')[1] in ['pdf']: formatted_type = 'pdf file' elif value.split('/')[1].find('wordprocessing') > -1: formatted_type = 'word document' elif value.split('/')[1].find('presentation') > -1: formatted_type = 'presentation' else: formatted_type = 'document' elif value.split('/')[0] in ['image']: formatted_type = 'image file' else: formatted_type = value.split('/')[0] return formatted_type jinja_environment.filters['filetype'] = format_filetype util.profiler.add_event("Begin ViewHandler:add_jinja_filters") user = self.get_current_user() util.profiler.add_event("Begin ViewHandler:get_current_user()") # Only get sign in links if no user is present if user is None: # Sets up the google sign in link, used in modal on all pages, # which must include a special flag to alert this handler that # google credentials are present in the cookie. It should also # incorporate any redirect already set in the URL. redirect = str(self.request.get('redirect')) or self.request.url google_redirect = util.set_query_parameters(redirect, google_login='******') google_login_url = app_engine_users.create_login_url( google_redirect) else: google_login_url = '' util.profiler.add_event("Begin ViewHandler:get_login_redirects") # default parameters that all views get kwargs['user'] = user kwargs['google_login_url'] = google_login_url kwargs['hosting_domain'] = os.environ['HOSTING_DOMAIN'] kwargs['share_url'] = self.request.url kwargs['google_client_id'] = config.google_client_id util.profiler.add_event("Begin ViewHandler:set_user_params") # Determine which Facebook app depending on environment kwargs['localhost'] = False if util.is_localhost(): kwargs['localhost'] = True util.profiler.add_event("Begin ViewHandler:start_fetching_themes") # Fetch all themes and topics for navigation courses = self.api.get('Theme') if courses: # Fetch all topics for courses course_topic_ids = [ id for course in courses for id in course.topics ] course_topics = self.api.get_by_id(course_topic_ids) # Associate topics with appropriate courses for course in courses: course.associate_topics(course_topics) # Special case for "Teachers" kit if course.name == 'Growth Mindset for Teachers': # IDK WHAT THIS IS kwargs['teacher_topics'] = course.topics_list kwargs['courses'] = courses util.profiler.add_event("Begin ViewHandler:finish_fetching_themes") logging.info(util.profiler) # Try to load the requested template. If it doesn't exist, replace # it with a 404. try: template = jinja_environment.get_template(template_filename) except jinja2.exceptions.TemplateNotFound: logging.error("TemplateNotFound: {}".format(template_filename)) return self.http_not_found() # logging.info('kwargs={}', kwargs['book']) # Render the template with data and write it to the HTTP response. self.response.write(template.render(kwargs))