Ejemplo n.º 1
0
    def do(self, kind):
        params = util.get_request_dictionary(self.request)
        entity = self.api.create(kind, params)
        data = entity.to_dict()

        # Special case for activity management: when teachers create a
        # classroom for the first time, activity entities need to be created
        # for them.
        if kind == 'classroom':
            teacher_id = params['user']
            is_test = params['is_test'] if 'is_test' in params else False
            activities = self.api.init_activities('student',
                                                  teacher_id,
                                                  params['program'],
                                                  cohort_id=params['cohort'],
                                                  classroom_id=entity.id,
                                                  is_test=is_test)

            # If these activities are being created FOR the teacher by an admin
            # or researcher, we need to do extra work to make sure those
            # activities are owned by the teacher.
            if self.get_current_user().id != teacher_id:
                teacher = self.internal_api.get_from_path('user', teacher_id)
                for a in activities:
                    self.api.associate('set_owner', teacher, a)

            # Include the created activities with the new classroom so the
            # client gets them immediately. We've had problems with eventual
            # consistency here.
            data['_student_activity_list'] = [a.to_dict() for a in activities]

        return {'success': True, 'data': data}
Ejemplo n.º 2
0
    def do(self):
        # Make sure only public users make this call.
        user = self.get_current_user()
        if user.user_type != 'public':
            # Warn the javascript on the page that
            # there's a problem so it can redirect.
            return {'success': True, 'data': 'logout_required'}

        params = util.get_request_dictionary(self.request)
        # if there's a javascript stand-in for the calendar, there will be an
        # extraneous parameter that's just for display; remove it
        if 'display_birth_date' in params:
            del params['display_birth_date']
        # The client supplies the classroom and cohort ids, but we want the
        # entities.
        params['classroom'] = Classroom.get_by_id(params['classroom'])
        params['cohort'] = Cohort.get_by_id(params['cohort'])
        # user may not be there, so check separately
        user_id = self.request.get('user')
        if user_id:
            # the user has selected this among partial matches as themselves
            # check the id; if it's valid, log them in
            User.get_by_id(user_id)  # an invalid id will raise errors
            self.session['user'] = user_id
            data = {
                'exact_match': user_id,
                'partial_matches': [],
                'new_user': False
            }
        else:
            # the user has provided identifying info, attempt to look them up
            # see url_handlers.BaseHandler.identify() for structure of the
            # data returned
            data = self.identify(**params)
        return {'success': True, 'data': data}
Ejemplo n.º 3
0
    def do(self):
        """Put many users of different names but similar relationships.

        Args:
            user_names: List of dictionaries, each with a 'first_name' and
                'last_name' property.
            ...: All other properties expected of a user (e.g. user_type), which
                will get applied to all the elements of the user_names list.
        Example:
            {
              "user_names": [
                {
                  "first_name": "Deanna",
                  "last_name": "Troi"
                },
                {
                  "first_name": "Beverly",
                  "last_name": "Crusher"
                }
              ],
              "user_type": "student",
              "classroom": "Classroom_XYZ"
            }
        """
        params = util.get_request_dictionary(self.request)
        users = self.api.batch_put_user(params)
        return {'success': True, 'data': [user.to_dict() for user in users]}
Ejemplo n.º 4
0
    def do_wrapper(self, *args, **kwargs):
        params = {}
        try:
            if 'connection_attempts' in kwargs:
                del kwargs['connection_attempts']
            params = util.get_request_dictionary(self.request)
            # When using jQuery (as we do), params also includes the key '_',
            # which is a timestamp jQuery uses to prevent the browser from
            # caching this resource.
            condition = self.api.stratify(params['program'], params['name'],
                                          params['proportions'],
                                          params['attributes'])

            user = User.get_by_id(params['user'])
            pd = Api(user).create(
                'pd', {
                    'program': params['program'],
                    'variable': 'condition',
                    'value': condition,
                    'scope_kind': 'user',
                    'scope': params['user'],
                })
        except Exception as error:
            trace = traceback.format_exc()
            logging.error("Error while stratifying via JSONP: {}\n{}".format(
                error, trace))
            condition = '__ERROR__'

        r = self.response
        r.headers['Content-Type'] = 'application/javascript; charset=utf-8'
        jsonp = '{}("{}");'.format(params.get('callback', ''), condition)
        logging.info("Responding with: {}".format(jsonp))
        r.write(jsonp)
Ejemplo n.º 5
0
    def do_wrapper(self, api_path):
        # Try to handle the request data, or log an error. Normally, inheriting
        # from ApiHandler would take care of this, but this doesn't return JSON
        # so we have to duplicate some code.
        try:
            params = util.get_request_dictionary(self.request)
            logging.info(params)

            if api_path == 'put/pd':
                self.put_pd(params)
            else:
                raise Exception(
                    "This cross-site api has not been implemented: {}.".format(
                        '/api/' + api_path))

        except Exception as error:
            trace = traceback.format_exc()
            logging.error("{}\n{}".format(error, trace))

        # No matter what happens, return the gif.
        self.response.headers['Content-Type'] = 'image/gif'
        # A 1x1 transparent pixel in a base64 encoded string. See
        # http://stackoverflow.com/questions/2933251/code-golf-1x1-black-pixel
        gif_data = 'R0lGODlhAQABAAAAACwAAAAAAQABAAACAkwBADs='.decode('base64')
        self.response.out.write(gif_data)
Ejemplo n.º 6
0
 def do(self):
     params = util.get_request_dictionary(self.request)
     if len(params['failed_tests']) is 0:
         logging.info(params)
     else:
         # There were failed tests, log an error (and generate an email).
         logging.error(params)
     # Don't freak out the user, either way.
     return {'success': True}
Ejemplo n.º 7
0
    def do(self):
        params = util.get_request_dictionary(self.request)

        # The list values must be given in the GET or POST as
        # 'list_values_json' so they are interpreted as a list by
        # util.get_request_dictionary. Check that the list came through.
        if type(params['list_values']) is not list:
            raise Exception("Parameter 'list_values_json' missing or invalid.")

        # Params not in this list will be used to filter previews.
        expected_keys = ['n', 'preview', 'list_name', 'list_values', 'salt']

        # Don't run the map reduce job, just show a sample of what it
        # would do.
        if 'preview' in params and params['preview'] is True:
            n = int(params['n']) if 'n' in params else 100

            # Set up a fake job context for the mapper
            conf = map_module.deidentify(
                params['list_name'], params['list_values'], params['salt'],
                submit_job=False)
            context = map_module.get_fake_context(conf)

            # This function will modify the user if they should be deidentified
            # (if the user has the specified relationship).
            mapper = map_module.DeidentifyMapper()

            # Get some entities to preview.
            query = id_model.User.all()
            for k, v in params.items():
                if k not in expected_keys:
                    query.filter(k + ' =', v)
            sample = query.fetch(n)
            before = [e.to_dict() for e in sample]

            results = [mapper.do(context, e) for e in sample]
            after = [e.to_dict() for e in results]

            return {
                'success': True,
                'preview': True,
                'n': n,
                'data': {
                    'before': before,
                    'after': after,
                },
                'message': (
                    "Warning: the results returned here are the result of a "
                    "simple query-and-modify, not a true map reduce job. "
                    "Also, no changes have been saved."),
            }

        # Run it for real
        else:
            conf = map_module.deidentify(
                params['list_name'], params['list_values'], params['salt'])
            return {'success': True, 'data': conf.job_id}
Ejemplo n.º 8
0
 def do(self):
     params = util.get_request_dictionary(self.request)
     cross_site_result = self.cross_site_test(**params['cross_site_test'])
     return {
         'success': True,
         'data': {
             'cross_site_test': cross_site_result,
         }
     }
Ejemplo n.º 9
0
 def POST(self, id):
     if (not self.api.user.user_type == 'god'):
         raise Exception("Permission denied.")
     value = util.get_request_dictionary(self.request).get('value', None)
     if value is None:
         raise Exception("Must POST with a value.")
     sv = SecretValue.get_or_insert(id)
     sv.value = value
     sv.put()
     return {'success': True, 'data': id}
Ejemplo n.º 10
0
    def do(self):
        # The URL should specify assc_cohort_list.
        params = util.get_request_dictionary(self.request)

        results, from_memcache = self.api.get_schedule(
            params['assc_cohort_list'])
        return {
            'success': True,
            'from_memcache': from_memcache,
            'data': results
        }
Ejemplo n.º 11
0
 def do(self, kind):
     params = util.get_request_dictionary(self.request)
     ancestor = None
     # If an ancestor is specified, look it up by id and pass it in.
     if 'ancestor' in params:
         ancestor_kind = get_kind(params['ancestor'])
         ancestor_klass = kind_to_class(ancestor_kind)
         ancestor = ancestor_klass.get_by_id(params['ancestor'])
         del params['ancestor']
     results = self.api.get(kind, params, ancestor=ancestor)
     return {'success': True, 'data': [e.to_dict() for e in results]}
Ejemplo n.º 12
0
    def do(self):
        # The URL should specify either assc_classroom_list or assc_cohort_list
        params = util.get_request_dictionary(self.request)

        results, from_memcache = self.api.get_roster(
            params.get('assc_cohort_list', None)
            or params.get('assc_classroom_list', None))
        return {
            'success': True,
            'from_memcache': from_memcache,
            'data': results
        }
Ejemplo n.º 13
0
    def do(self):
        params = util.get_request_dictionary(self.request)
        if isinstance(params[u'name'], unicode):
            test_names = [params[u'name']]
        elif isinstance(params[u'name'], list):
            test_names = params[u'name']
        else:
            raise Exception("Invalid name: {}.".format(params['name']))

        test_loader = unittest.loader.TestLoader()
        test_suite = test_loader.loadTestsFromNames(test_names, unit_testing)
        test_result, print_statements = self.run_test_suite(test_suite)
        result_dict = self.test_result_to_dict(test_result, print_statements)

        return {'success': True, 'data': result_dict}
Ejemplo n.º 14
0
    def do(self):
        params = util.get_request_dictionary(self.request)

        result = self.internal_api.get('cohort', {'code': params['code']})

        data = []
        for cohort in result:
            safe_cohort_data = {
                'id': cohort.id,
                'name': cohort.name,
                'code': cohort.code,
                'identification_type': cohort.identification_type,
            }
            data.append(safe_cohort_data)

        return {'success': True, 'data': data}
Ejemplo n.º 15
0
    def do(self):
        params = util.get_request_dictionary(self.request)

        # Don't run the map reduce job, just show a sample of what it
        # would do.
        if 'preview' in params and params['preview'] is True:
            n = int(params['n']) if 'n' in params else 100

            # Set up a fake job context for the mapper
            conf = map_module.lower_case_login(submit_job=False)
            context = map_module.get_fake_context(conf)

            # This function will modify the entities.
            mapper = map_module.LowerCaseLoginMapper()

            # Get some entities to preview.
            query = core.User.all()
            for k, v in params.items():
                if k not in ['n', 'preview']:
                    query.filter(k + ' =', v)
            sample = query.fetch(n)
            before = [e.to_dict() for e in sample]

            results = [mapper.do(context, e) for e in sample]
            after = [e.to_dict() for e in sample]

            return {
                'success':
                True,
                'preview':
                True,
                'n':
                n,
                'data': {
                    'before': before,
                    'after': after,
                },
                'message':
                ("Warning: the results returned here are the result of a "
                 "simple query-and-modify, not a true map reduce job. "
                 "Also, no changes have been saved."),
            }

        # Run it for real
        else:
            job_config = map_module.lower_case_login()
            return {'success': True, 'data': job_config.job_id}
Ejemplo n.º 16
0
    def do(self, kind):
        # kind must be one of these
        if kind not in ['cohort', 'classroom']:
            raise Exception("Invalid kind: {}".format(kind))

        params = util.get_request_dictionary(self.request)

        # Don't run the map reduce job, just show a sample of what it
        # would do.
        if 'preview' in params and params['preview'] is True:
            n = int(params['n']) if 'n' in params else 1

            # Set up a fake job context for the mapper
            conf = map_module.cache_contents(kind, submit_job=False)
            context = map_module.get_fake_context(conf)

            # This function will cache rosters and schedules via
            # api.get_roster()
            mapper = map_module.CacheContentsMapper()

            # Get some entities to preview.
            klass = core.kind_to_class(kind)
            query = klass.all()
            sample = query.fetch(n)
            [mapper(context, e) for e in sample]

            return {
                'success':
                True,
                'preview':
                True,
                'n':
                n,
                'data': {
                    'entities_processed': [e.id for e in sample]
                },
                'message':
                ("Warning: this is the result of a limited preview. No "
                 "system-wide mapreduce job has been run."),
            }

        # Run it for real
        else:
            # Actually have to run THREE mapreduce jobs, one for each kind.
            conf = map_module.cache_contents(kind)
            return {'success': True, 'data': conf.job_id}
Ejemplo n.º 17
0
    def do(self, mapper_name):
        params = util.get_request_dictionary(self.request)

        kind, mapper, params = getattr(self, mapper_name)(params)
        klass = core.kind_to_class(kind)

        # Don't run the map reduce job, just show a sample of what it
        # would do.
        if 'preview' in params and params['preview'] is True:
            # Get some entities to preview.
            n = int(params['n']) if 'n' in params else 10
            query = id_model.User.all()
            for k, v in params.items():
                if k not in ['n', 'preview']:
                    query.filter(k + ' =', v)
            sample = query.fetch(n)
            before = [e.to_dict() for e in sample]

            # Set up a fake job context for the mapper and run it on each
            # entity.
            context = self.get_fake_context(kind, mapper)
            results = [mapper().do(context, e) for e in sample]
            after = [e.to_dict() for e in sample]

            return {
                'success': True,
                'preview': True,
                'n': n,
                'data': {
                    'before': before,
                    'after': after,
                },
                'message': (
                    "Warning: the results returned here are the result of a "
                    "simple query-and-modify, not a true map reduce job. "
                    "Also, no changes have been saved."),
            }

        # Run it for real
        else:
            job_config = map_module.modify_kind(kind, mapper)
            return {'success': True, 'data': job_config.job_id}
Ejemplo n.º 18
0
    def do(self):
        # No call to self.access_restricted; publicly available page.

        params = util.get_request_dictionary(self.request)

        # Ignore certain calls that typically come from Qualtrics when people
        # are testing.
        is_preview = (params['program'] and params['user'] == ''
                      and params['activity_ordinal'] is None)
        if is_preview:
            logging.info("Interpreted as Qualtrics testing. Ignoring.")
            return 'qualtrics_session_complete.html', {'show_footer': False}

        if 'program' in params:
            params['program'] = params['program'].upper()

        try:
            program = self.check_params(params)
        except IdError:
            logging.error("User does not exist: {}.".format(params['user']))
            return self.http_not_found()
        except Exception as e:
            logging.error(e)
            return self.http_not_found()

        # Save a progress pd, value 100. Aggregation will make sure they get a
        # COM code.
        o = int(params['activity_ordinal'])
        self.internal_api.create(
            'pd', {
                'program': program.id,
                'activity_ordinal': o,
                'scope': params['user'],
                'scope_kind': 'user',
                'variable': 's{}__progress'.format(o),
                'value': 100,
            })

        return 'qualtrics_session_complete.html', {'show_footer': False}
Ejemplo n.º 19
0
    def do(self):
        """Accepts several pd at once as JSON via POST.

        Args:
            pd_batch: List of dictionaries, each with a 'variable' and 'value'
                property.
            ...: All other properties expected of a pd (e.g. user, cohort,
                activity), which will get applied to all the elements of the
                pd_batch list to create a set of pd entities.
        Example:
            {
              "pd_batch": [
                {
                  "variable": "s2__toi_1",
                  "value": 1
                },
                {
                  "variable": "s2__toi_2",
                  "value": 1
                }
              ],
              "activity": "Activity_QD6FnPKYkRjrGfSr0wT7",
              "activity_ordinal": 2,
              "program": "Program_M4OQDVDcS0WjvAn8ujR5",
              "scope": "User_NISmPygw44gxopWrivjg",
              "is_test": false,
            }
        """
        params = util.get_request_dictionary(self.request)
        # We're good at re-trying pds when there's a transaction collision,
        # so demote this to a warning rather than an error, so we don't get
        # so many useless emails.
        try:
            pds = self.api.batch_put_pd(params)
        except db.TransactionFailedError:
            logging.warning("TransactionFailedError: {}".format(params))
            return {'success': False, 'message': "TransactionFailedError"}
        else:
            return {'success': True, 'data': [pd.to_dict() for pd in pds]}
Ejemplo n.º 20
0
    def do(self, kind, id=None):
        params = util.get_request_dictionary(self.request)
        if 'recurse_children' in params and params['recurse_children'] is True:
            # This flag just serves to forks the update request to
            # recursive_update(); toss it b/c it's not data, really.
            del params['recurse_children']

            # This flag causes recursive_update() to do nothing except return
            # the entity ids it WOULD change.
            if 'preview' in params:
                preview = params['preview'] is True
                del params['preview']
            else:
                preview = False

            entities_changed = self.api.recursive_update(kind,
                                                         id,
                                                         params,
                                                         preview=preview)
            data = [e.to_dict() for e in entities_changed]
        else:
            entity = self.api.update(kind, id, params)
            data = entity.to_dict()
        return {'success': True, 'data': data}
Ejemplo n.º 21
0
    def do(self, update_name):
        """The update name defines what kind of update to run. This will be
        used to:
        - Create a timestamp to track progress.
        - Query for entities based on configuration in
          config.systematic_update_settings, which must be defined.
        - Execute method of this class by the same name on each entity returned
          from the query. It must be defined. The method should take an entity
          and return and return either None (if the entity should not be
          updated) or a modified entity.

        Accepts parameters in request string:
        fetch_size (int) - how many entities to process at once
        start_time (str) - what "created" time to start searching for entities;
                           this overrides the normal "systematic" behavior
        preview (bool) - report on results without actually updating
        """
        from google.appengine.ext import db
        from datetime import datetime

        params = util.get_request_dictionary(self.request)

        # Check request string and apply defaults where necessary
        if 'fetch_size' in params:
            fetch_size = params['fetch_size']
        else:
            fetch_size = 100
        if 'start_time' in params:
            start_time = params['start_time']
        else:
            # Look up / create a timestamp to track progress
            ts = Timestamp.get_or_insert(update_name)
            start_time = ts.timestamp
        if 'preview' in params:
            preview = params['preview']
        else:
            preivew = False

        conf = config.systematic_update_settings[update_name]

        # Query for entities
        klass = kind_to_class(conf['kind'])
        query = klass.all()
        query.filter('created >', start_time)
        query.order('created')
        entity_list = query.fetch(fetch_size)

        before_snapshot = [e.to_dict() for e in entity_list]

        # Look up related method
        method = getattr(self, update_name)
        if not util.is_function(method):
            raise Exception("Invalid update name: method isn't callable.")

        # Execute the method on each entity
        modified_entities = []
        for entity in entity_list:
            # Check if this systematic update has been performed before
            if update_name in entity.systematic_updates:
                raise Exception(
                    "{} has already been performed on entity {}.".format(
                        update_name, entity.id))
            else:
                entity.systematic_updates.append(update_name)
            updated_entity = method(entity)
            if updated_entity is not None:
                modified_entities.append(updated_entity)

        # The entity variable is still set to the last one of the list;
        # use it to save our spot for next time.
        end_time = entity.created

        after_snapshot = [e.to_dict() for e in modified_entities]

        if not preview:
            db.put(modified_entities)

        if 'start_time' not in params:
            # Save progress
            ts.timestamp = end_time
            ts.put()

        return {
            'success': True,
            'data': {
                'entities_queried': len(entity_list),
                'entities_modified': len(modified_entities),
                'start_time': start_time,
                'end_time': end_time,
                'entities before update': before_snapshot,
                'entities after update': after_snapshot,
            }
        }
Ejemplo n.º 22
0
 def do(self):
     p = util.get_request_dictionary(self.request)
     condition = self.api.stratify(p['program'], p['name'],
                                   p['proportions'], p['attributes'])
     return {'success': True, 'data': condition}
Ejemplo n.º 23
0
    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_name = 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 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_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['request'] = util.get_request_dictionary(self.request)
            if debug:
                params['session'] = json.dumps(self.session)

            # Try to load the requested template. If it doesn't exist, replace
            # it with a 404.
            try:
                template = jinja_environment.get_template(template_name)
            except:
                self.error(404)
                jinja_environment = jinja2.Environment(
                    loader=jinja2.FileSystemLoader('templates'))
                template = jinja_environment.get_template('404.html')

            # Render the template with data and write it to the HTTP response.
            self.response.write(template.render(params))
Ejemplo n.º 24
0
    def do(self):
        return {'success': False,
                'message': "This tool needs further testing. See docstring of "
                           "ModifyPdHandler and pull request #268."}

        params = util.get_request_dictionary(self.request)
        to_match = params['to_match']
        to_change = params['to_change']

        # Must be at least a school admin to run this.
        user = self.get_current_user()
        if user.user_type not in ['god', 'researcher', 'school_admin']:
            raise core.PermissionDenied()

        # Although this mapper is written very generally and is capable of
        # changing any property of pd entities, we want to artificially limit
        # it to changing cohort and classroom, b/c that's all that our use
        # cases require.
        allowed_keys = set(['classroom', 'cohort'])
        illegal_keys = set(to_change.keys()).difference(allowed_keys)
        if len(to_change.keys()) is 0 or len(illegal_keys) > 0:
            raise Exception("Not allowed to change {}".format(illegal_keys))

        # You must, at minimum, specify a single cohort and single program
        # (not a list) in to_match, otherwise the scope of changes would be
        # out of control.
        if 'program' not in to_match or type(to_match['program']) is not unicode:
            raise Exception("Must specify a single program in to_match.")
        if 'cohort' not in to_match or type(to_match['cohort']) is not unicode:
            raise Exception("Must specify a single cohort in to_match.")

        # Check permissions. To run this job, the user must have permission on
        # any cohorts in either to_match or to_change.
        # These functions will raise their own exceptions if necessary.
        user.can_put_pd(to_match['program'], to_match['cohort'])
        if 'cohort' in to_change:
            user.can_put_pd(to_match['program'], to_change['cohort'])

        # Preview: don't run the map reduce job, just show a sample of what it
        # would do.
        if 'preview' in params and params['preview'] is True:
            n = int(params['n']) if 'n' in params else 100

            # Set up a fake job context for the mapper
            conf = map_module.modify_pd(to_match, to_change, submit_job=False)
            context = map_module.get_fake_context(conf)

            # This function will modify the entities.
            mapper = map_module.ModifyPdMapper()

            # Get some entities to preview.
            query = id_model.Pd.all()
            for k, v in to_match.items():
                if isinstance(v, list):
                    # Limit the length of the list b/c app engine has issues.
                    v = v[:30]
                    query.filter(k + ' IN', v)
                else:
                    query.filter(k + ' =', v)
            sample = query.fetch(n)
            before = [e.to_dict() for e in sample]

            results = [mapper.do(context, e) for e in sample]
            after = [e.to_dict() for e in sample]

            return {
                'success': True,
                'preview': True,
                'n': n,
                'data': {
                    'before': before,
                    'after': after,
                },
                'message': (
                    "Warning: the results returned here are the result of a "
                    "simple query-and-modify, not a true map reduce job. "
                    "Also, no changes have been saved."),
            }

        # Run it for real
        else:
            job_config = map_module.modify_pd(to_match, to_change)
            return {'success': True, 'data': job_config.job_id}
Ejemplo n.º 25
0
 def do(self):
     ids = util.get_request_dictionary(self.request)['ids']
     results = self.api.get_by_ids(ids)
     return {'success': True, 'data': [e.to_dict() for e in results]}
Ejemplo n.º 26
0
 def do(self):
     ids = util.get_request_dictionary(self.request)['ids']
     results = self.api.see_by_ids(ids)
     return {'success': True, 'data': results}
Ejemplo n.º 27
0
    def do(self, kind):
        # kind must be one of these
        if kind not in ['user', 'activity', 'cohort']:
            raise Exception("Invalid kind: {}".format(kind))

        params = util.get_request_dictionary(self.request)

        # Params not in this list will be used to filter previews.
        expected_keys = ['n', 'preview']

        # Don't run the map reduce job, just show a sample of what it
        # would do.
        if 'preview' in params and params['preview'] is True:
            n = int(params['n']) if 'n' in params else 100

            # Set up a fake job context for the mapper
            conf = map_module.fix_aggregation_json(kind, submit_job=False)
            context = map_module.get_fake_context(conf)

            # This function will modify the entity by copying aggregation data
            # to a new string property.
            mapper = map_module.AggregationJsonMapper()

            def summarize_entity(entity):
                return {
                    'id': entity.id,
                    'aggregation_data': entity.aggregation_data,
                    'aggregation_json': entity.aggregation_json
                }

            # Get some entities to preview.
            klass = core.kind_to_class(kind)
            query = klass.all()
            for k, v in params.items():
                if k not in expected_keys:
                    query.filter(k + ' =', v)
            sample = query.fetch(n)
            before = [summarize_entity(e) for e in sample]

            results = [mapper.do(context, e) for e in sample]

            after = [summarize_entity(e) for e in results]

            return {
                'success':
                True,
                'preview':
                True,
                'n':
                n,
                'data': {
                    'before': before,
                    'after': after,
                },
                'message':
                ("Warning: the results returned here are the result of a "
                 "simple query-and-modify, not a true map reduce job. "
                 "Also, no changes have been saved."),
            }

        # Run it for real
        else:
            # Actually have to run THREE mapreduce jobs, one for each kind.
            conf = map_module.fix_aggregation_json(kind)
            return {'success': True, 'data': conf.job_id}
Ejemplo n.º 28
0
 def do(self, kind):
     data = self.api.see(kind, util.get_request_dictionary(self.request))
     return {'success': True, 'data': data}
Ejemplo n.º 29
0
 def do(self, id):
     kind = get_kind(id)
     params = util.get_request_dictionary(self.request)
     entity = self.api.update(kind, id, params)
     return {'success': True, 'data': entity.to_dict()}
Ejemplo n.º 30
0
    def do(self):
        params = util.get_request_dictionary(self.request)

        # Check that the requested program allows public registration.
        program = self.internal_api.get_from_path('program', params['program'])
        program_config = Program.get_app_configuration(program.abbreviation)
        if not getattr(program_config, 'public_registration', False):
            user = self.get_current_user()
            logging.error("User {} attempted public registration on program "
                          "{}, but it isn't allowed.".format(
                              user, program.abbreviation))

        # Create a school admin based on the user's information.
        # They get the special auth_type 'public', preventing them from
        # appearing in sign-in queries or reset-password queries.
        # However, the user entity will still hold data and be associated with
        # the created schools.
        params['user']['user_type'] = 'school_admin'
        params['user']['auth_id'] = 'public_' + params['user']['login_email']
        school_admin = self.internal_api.create('user', params['user'])

        # If the school already exists, use the id to find the right cohort.
        if 'existing_school_id' in params:
            s_id = params['existing_school_id']
            cohort_list = self.internal_api.get('cohort',
                                                {'assc_school_list': s_id})
            if len(cohort_list) is not 1:
                raise Exception("Problem with public registration: found {} "
                                "cohorts for school {}".format(
                                    len(cohort_list), s_id))
            cohort = cohort_list[0]
            school = None
            classroom = None
            activities = None

        # Otherwise, create a school, cohort, and classroom based on the
        # provided data.
        else:
            school = self.internal_api.create('school', params['new_school'])
            cohort = self.internal_api.create(
                'cohort', {
                    'name': params['new_school']['name'],
                    'school': school.id,
                    'program': program.id,
                    'promised_students': params['promised_students'],
                })
            classroom = self.internal_api.create(
                'classroom', {
                    'name': 'All Students',
                    'program': program.id,
                    'cohort': cohort.id,
                    'user': school_admin.id,
                })
            activities = self.internal_api.init_activities(
                'student',
                school_admin.id,
                program.id,
                cohort_id=cohort.id,
                classroom_id=classroom.id)

        # Whether the cohort is new or exisiting, make the new user owner of it
        self.internal_api.associate('set_owner', school_admin, cohort)

        # Send an email to the user with all the information they need to
        # participate.
        mandrill.send(to_address=school_admin.login_email,
                      subject=program_config.registration_email_subject,
                      body=mandrill.render_markdown(
                          program_config.registration_email_body),
                      template_data={
                          'email': school_admin.login_email,
                          'cohort_id': cohort.id
                      })

        logging.info('api_handlers.CreatePublicSchoolHandler')
        logging.info('sending an email to: {}'.format(
            school_admin.login_email))

        return {
            'success': True,
            'data': {
                'user':
                school_admin.to_dict(),
                'program':
                program.to_dict(),
                'school':
                school.to_dict() if school else None,
                'cohort':
                cohort.to_dict(),
                'classroom':
                classroom.to_dict() if classroom else None,
                'activities':
                ([a.to_dict() for a in activities] if activities else None),
            }
        }