def get_owner_property(self, id_or_entity): owner_props = { 'Team': 'owned_teams', 'Organization': 'owned_organizations', } kind = SqlModel.get_kind(id_or_entity) return getattr(self, owner_props[kind])if kind in owner_props else None
def resolve_id_mismatch(klass, user, new_id): """Change all references to user's id to a new id. N.B. this is obviously brittle; when the relationship schema changes, this will also have to change. """ # The auth server has a different id for this user; defer to it. teams = Team.get(captain_id=user.uid) for t in teams: t.captain_id = new_id Team.put_multi(teams) classrooms = Classroom.get(contact_id=user.uid) for c in classrooms: c.contact_id = new_id Classroom.put_multi(classrooms) params = {'uid': new_id, 'short_uid': SqlModel.convert_uid(new_id)} with mysql_connection.connect() as sql: sql.update_row(klass.table, 'uid', user.uid, **params) for k, v in params.items(): setattr(user, k, v) return user
def __call__(self): # Calling code may have pulled the entity for us already, no sense # in doing it again; on the other hand, merely an id may be # sufficient, depending on the kind, so only run this code if its # called. if self._entity is None: self._entity = SqlModel.kind_to_class(kind).get_by_id(self.uid) return self._entity
def __init__(self, id_or_entity): if isinstance(id_or_entity, basestring): self.uid = str(id_or_entity) self._entity = None else: self.uid = id_or_entity.uid self._entity = id_or_entity self.kind = SqlModel.get_kind(self.uid)
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 post(self): # Anyone is allowed to post responses. params = self.get_params(Response.property_types()) user = self.get_current_user() if 'user_id' not in params: params['user_id'] = user.uid required_params = ('team_id', 'parent_id', 'module_label') for k in required_params: if not params.get(k, None): return self.http_bad_request("{} required.".format(k)) # Validate the parent, if it's a cycle. parent_id = params.get('parent_id', None) parent_kind = SqlModel.get_kind(parent_id) if parent_kind == 'Cycle': cycle = SqlModel.kind_to_class(parent_kind).get_by_id(parent_id) if not cycle or not owns(user, parent_id): return self.http_forbidden("Must own the parent to respond.") # ...else this is a step label, so allow any truthy string. # Permission to create varies by type/level. params['type'] = params.get('type', Response.USER_LEVEL_SYMBOL) if params['type'] == Response.TEAM_LEVEL_SYMBOL: if not owns(user, params['team_id']): return self.http_forbidden("Must own the team to respond.") elif not owns(user, params['user_id']): return self.http_forbidden("May not create responses for others.") try: new_entity = Response.insert_or_conflict(params) except ResponseIndexConflict: return self.http_conflict( "Response for this type-user-team-parent-module combination " "exists. Send a request like `PUT /api/responses/:id`.") except JsonTextValueLengthError: return self.http_payload_too_large("Value too long.") except JsonTextDictLengthError: return self.http_payload_too_large("Body has too many keys.") self.write(new_entity)
def test_completion_ids_exclude_testing(self): """Don't count testing pd as that participant being done.""" pid = self.test_create_portal_pd(testing=True) participant = Participant.create( id=SqlModel.convert_uid(pid), name='Pascal', organization_id='Organization_PERTS', ) participant.put() results = ParticipantData.completion_ids( project_cohort_id='ProjectCohort_12345678') self.assertEqual(results, [])
def associated_organization_ids(self, depth=0, pending_network=None): """Traverse all network-to-network relationships to associated orgs. Returns a flat and unique list of org ids. """ # While we support network-to-network, this recursive function could # generate many inefficient db calls if we get carried away. if depth >= 4: raise InvalidNetworkAssociation( "Too much depth in network associations: {}" .format(self.uid) ) org_ids = set() for assc_id in self.association_ids: kind = SqlModel.get_kind(assc_id) if kind == 'Network': # Note! This function is often run as a before_put check that # the associations are valid. This means we have to consider # the as-of-yet-unsaved "root" network (the `pending_network`) # and not any version of it we might fetch from the db in order # to catch the introduction of circular references. if pending_network and assc_id == pending_network.uid: child_network = pending_network else: child_network = Network.get_by_id(assc_id) if child_network: child_org_ids = child_network.associated_organization_ids( depth=depth + 1, pending_network=pending_network, ) org_ids.update(child_org_ids) else: # No exception here because we don't want Networks to # become unusable if an associated thing gets deleted. # @todo: consider having this actually remove the # association ids from the list. logging.warning( "Bad reference in {}: association {} doesn't exist." .format(self.uid, assc_id) ) elif kind == 'Organization': org_ids.add(assc_id) else: raise InvalidNetworkAssociation( "Invalid association kind: {}".format(kind)) return org_ids
def test_completion_ids(self): pid = self.test_create_portal_pd() participant = Participant.create( id=SqlModel.convert_uid(pid), name='Pascal', organization_id='Organization_PERTS', ) participant.put() results = ParticipantData.completion_ids( project_cohort_id='ProjectCohort_12345678') expected = [ { 'module': 1, 'percent_progress': '100', 'token': 'Pascal' }, { 'module': 2, 'percent_progress': '33', 'token': 'Pascal' }, ] self.assertEqual(results, expected)