def serialize_user(user, node=None, full=False): """Return a dictionary representation of a registered user. :param User user: A User object :param bool full: Include complete user properties """ fullname = user.display_full_name(node=node) rv = { 'id': str(user._primary_key), 'registered': user.is_registered, 'surname': user.family_name, 'fullname': fullname, 'shortname': fullname if len(fullname) < 50 else fullname[:23] + "..." + fullname[-23:], 'gravatar_url': gravatar( user, use_ssl=True, size=settings.GRAVATAR_SIZE_ADD_CONTRIBUTOR ), 'active': user.is_active(), } if node is not None: rv.update({ 'visible': user._id in node.visible_contributor_ids, 'permission': reduce_permissions(node.get_permissions(user)), }) if user.is_registered: rv.update({ 'url': user.url, 'absolute_url': user.absolute_url, 'display_absolute_url': user.display_absolute_url, 'date_registered': user.date_registered.strftime("%Y-%m-%d"), }) if full: if user.is_merged: merger = user.merged_by merged_by = { 'id': str(merger._primary_key), 'url': merger.url, 'absolute_url': merger.absolute_url } else: merged_by = None rv.update({ 'number_projects': len(get_projects(user)), 'number_public_projects': len(get_public_projects(user)), 'activity_points': user.get_activity_points(), 'gravatar_url': gravatar( user, use_ssl=True, size=settings.GRAVATAR_SIZE_PROFILE ), 'is_merged': user.is_merged, 'merged_by': merged_by, }) return rv
def test_serialize_user_full(self): user = UserFactory() ProjectFactory(creator=user, is_public=False) NodeFactory(creator=user) ProjectFactory(creator=user, is_public=True) CollectionFactory(creator=user) d = utils.serialize_user(user, full=True, include_node_counts=True) gravatar = filters.gravatar( user, use_ssl=True, size=settings.PROFILE_IMAGE_LARGE ) assert_equal(d['id'], user._primary_key) assert_equal(d['url'], user.url) assert_equal(d.get('username'), None) assert_equal(d['fullname'], user.fullname) assert_equal(d['registered'], user.is_registered) assert_equal(d['gravatar_url'], gravatar) assert_equal(d['absolute_url'], user.absolute_url) assert_equal(d['date_registered'], user.date_registered.strftime('%Y-%m-%d')) projects = [ node for node in user.contributed if node.category == 'project' and not node.is_registration and not node.is_deleted ] public_projects = [p for p in projects if p.is_public] assert_equal(d['number_projects'], len(projects)) assert_equal(d['number_public_projects'], len(public_projects))
def add_contributor_json(user, current_user=None): # get shared projects if current_user: n_projects_in_common = current_user.n_projects_in_common(user) else: n_projects_in_common = 0 current_employment = None education = None if user.jobs: current_employment = user.jobs[0]['institution'] if user.schools: education = user.schools[0]['institution'] return { 'fullname': user.fullname, 'email': user.username, 'id': user._primary_key, 'employment': current_employment, 'education': education, 'n_projects_in_common': n_projects_in_common, 'registered': user.is_registered, 'active': user.is_active, 'gravatar_url': gravatar( user, use_ssl=True, size=settings.GRAVATAR_SIZE_ADD_CONTRIBUTOR ), 'profile_url': user.profile_url }
def get_gravatar(user, size=None): if size is None: size = settings.GRAVATAR_SIZE_PROFILE return gravatar( user, use_ssl=True, size=size )
def comment_discussion(auth, node, **kwargs): users = collect_discussion(node) anonymous = has_anonymous_link(node, auth) # Sort users by comment frequency # TODO: Allow sorting by recency, combination of frequency and recency sorted_users = sorted( users.keys(), key=lambda item: len(users[item]), reverse=True, ) return { 'discussion': [ { 'id': privacy_info_handle(user._id, anonymous), 'url': privacy_info_handle(user.url, anonymous), 'fullname': privacy_info_handle(user.fullname, anonymous, name=True), 'isContributor': node.is_contributor(user), 'gravatarUrl': privacy_info_handle( gravatar( user, use_ssl=True, size=settings.GRAVATAR_SIZE_DISCUSSION, ), anonymous ), } for user in sorted_users ] }
def get_gravatar(user, size=None): if size is None: size = settings.PROFILE_IMAGE_LARGE return gravatar( user, use_ssl=True, size=size )
def serialize_comment(comment, auth, anonymous=False): return { 'id': comment._id, 'author': { 'id': privacy_info_handle(comment.user._id, anonymous), 'url': privacy_info_handle(comment.user.url, anonymous), 'name': privacy_info_handle( comment.user.fullname, anonymous, name=True ), 'gravatarUrl': privacy_info_handle( gravatar( comment.user, use_ssl=True, size=settings.GRAVATAR_SIZE_DISCUSSION ), anonymous ), }, 'dateCreated': comment.date_created.isoformat(), 'dateModified': comment.date_modified.isoformat(), 'content': comment.content, 'hasChildren': bool(getattr(comment, 'commented', [])), 'canEdit': comment.user == auth.user, 'modified': comment.modified, 'isDeleted': comment.is_deleted, 'isAbuse': auth.user and auth.user._id in comment.reports, }
def serialize_unregistered(fullname, email): """Serializes an unregistered user.""" user = auth.get_user(email=email) if user is None: serialized = { 'fullname': fullname, 'id': None, 'registered': False, 'active': False, 'gravatar': gravatar(email, use_ssl=True, size=settings.PROFILE_IMAGE_MEDIUM), 'email': email, } else: serialized = add_contributor_json(user) serialized['fullname'] = fullname serialized['email'] = email return serialized
def serialize_unregistered(fullname, email): """Serializes an unregistered user. """ user = framework.auth.get_user(email=email) if user is None: serialized = { 'fullname': fullname, 'id': None, 'registered': False, 'active': False, 'gravatar': gravatar(email, use_ssl=True, size=settings.GRAVATAR_SIZE_ADD_CONTRIBUTOR), 'email': email, } else: serialized = add_contributor_json(user) serialized['fullname'] = fullname serialized['email'] = email return serialized
def add_contributor_json(user, current_user=None): """ Generate a dictionary representation of a user, optionally including # projects shared with `current_user` :param User user: The user object to serialize :param User current_user : The user object for a different user, to calculate number of projects in common :return dict: A dict representing the serialized user data """ # get shared projects if current_user: n_projects_in_common = current_user.n_projects_in_common(user) else: n_projects_in_common = 0 current_employment = None education = None if user.jobs: current_employment = user.jobs[0]['institution'] if user.schools: education = user.schools[0]['institution'] return { 'fullname': user.fullname, 'email': user.username, 'id': user._primary_key, 'employment': current_employment, 'education': education, 'n_projects_in_common': n_projects_in_common, 'registered': user.is_registered, 'active': user.is_active, 'gravatar_url': gravatar( user, use_ssl=True, size=settings.PROFILE_IMAGE_MEDIUM ), 'profile_url': user.profile_url }
def _gravatar_url(self, size): return filters.gravatar(self, use_ssl=True, size=size)
def serialize_user(user, node=None, admin=False, full=False, is_profile=False, include_node_counts=False): """ Return a dictionary representation of a registered user. :param User user: A User object :param bool full: Include complete user properties """ from website.project.utils import PROJECT_QUERY contrib = None if isinstance(user, Contributor): contrib = user user = contrib.user fullname = user.display_full_name(node=node) ret = { 'id': str(user._id), 'registered': user.is_registered, 'surname': user.family_name, 'fullname': fullname, 'shortname': fullname if len(fullname) < 50 else fullname[:23] + '...' + fullname[-23:], 'gravatar_url': gravatar(user, use_ssl=True, size=settings.PROFILE_IMAGE_MEDIUM), 'active': user.is_active, } if node is not None: if admin: flags = { 'visible': False, 'permission': 'read', } else: is_contributor_obj = isinstance(contrib, Contributor) flags = { 'visible': contrib.visible if is_contributor_obj else node.contributor_set.filter(user=user, visible=True).exists(), 'permission': get_contributor_permissions(contrib, as_list=False) if is_contributor_obj else reduce_permissions( node.get_permissions(user)), } ret.update(flags) if user.is_registered: ret.update({ 'url': user.url, 'absolute_url': user.absolute_url, 'display_absolute_url': user.display_absolute_url, 'date_registered': user.date_registered.strftime('%Y-%m-%d'), }) if full: # Add emails if is_profile: ret['emails'] = [ { 'address': each, 'primary': each.strip().lower() == user.username.strip().lower(), 'confirmed': True, } for each in user.emails.values_list('address', flat=True) ] + [{ 'address': each, 'primary': each.strip().lower() == user.username.strip().lower(), 'confirmed': False } for each in user.get_unconfirmed_emails_exclude_external_identity()] if user.is_merged: merger = user.merged_by merged_by = { 'id': str(merger._primary_key), 'url': merger.url, 'absolute_url': merger.absolute_url } else: merged_by = None ret.update({ 'activity_points': user.get_activity_points(), 'gravatar_url': gravatar(user, use_ssl=True, size=settings.PROFILE_IMAGE_LARGE), 'is_merged': user.is_merged, 'merged_by': merged_by, }) if include_node_counts: projects = user.nodes.filter(PROJECT_QUERY).get_roots() ret.update({ 'number_projects': projects.count(), 'number_public_projects': projects.filter(is_public=True).count(), }) return ret
def gravatar_url(self): return filters.gravatar( self, use_ssl=True, size=settings.GRAVATAR_SIZE_ADD_CONTRIBUTOR )
def serialize_user(user, node=None, admin=False, full=False, is_profile=False): """ Return a dictionary representation of a registered user. :param User user: A User object :param bool full: Include complete user properties """ fullname = user.display_full_name(node=node) ret = { 'id': str(user._primary_key), 'registered': user.is_registered, 'surname': user.family_name, 'fullname': fullname, 'shortname': fullname if len(fullname) < 50 else fullname[:23] + "..." + fullname[-23:], 'gravatar_url': gravatar(user, use_ssl=True, size=settings.PROFILE_IMAGE_MEDIUM), 'active': user.is_active, } if node is not None: if admin: flags = { 'visible': False, 'permission': 'read', } else: flags = { 'visible': user._id in node.visible_contributor_ids, 'permission': reduce_permissions(node.get_permissions(user)), } ret.update(flags) if user.is_registered: ret.update({ 'url': user.url, 'absolute_url': user.absolute_url, 'display_absolute_url': user.display_absolute_url, 'date_registered': user.date_registered.strftime("%Y-%m-%d"), }) if full: # Add emails if is_profile: ret['emails'] = [{ 'address': each, 'primary': each.strip().lower() == user.username.strip().lower(), 'confirmed': True, } for each in user.emails] + [{ 'address': each, 'primary': each.strip().lower() == user.username.strip().lower(), 'confirmed': False } for each in user.unconfirmed_emails] if user.is_merged: merger = user.merged_by merged_by = { 'id': str(merger._primary_key), 'url': merger.url, 'absolute_url': merger.absolute_url } else: merged_by = None ret.update({ 'number_projects': get_projects(user).count(), 'number_public_projects': get_public_projects(user).count(), 'activity_points': user.get_activity_points(), 'gravatar_url': gravatar(user, use_ssl=True, size=settings.PROFILE_IMAGE_LARGE), 'is_merged': user.is_merged, 'merged_by': merged_by, }) return ret
def get_gravatar(user, size=None): if size is None: size = settings.PROFILE_IMAGE_LARGE return gravatar(user, use_ssl=True, size=size)
def search_contributor(query, page=0, size=10, exclude=None, current_user=None): """Search for contributors to add to a project using elastic search. Request must include JSON data with a "query" field. :param query: The substring of the username to search for :param page: For pagination, the page number to use for results :param size: For pagination, the number of results per page :param exclude: A list of User objects to exclude from the search :param current_user: A User object of the current user :return: List of dictionaries, each containing the ID, full name, most recent employment and education, gravatar URL of an OSF user """ start = page * size items = re.split(r"[\s-]+", query) exclude = exclude or [] normalized_items = [] for item in items: try: normalized_item = six.u(item) except TypeError: normalized_item = item normalized_item = unicodedata.normalize("NFKD", normalized_item).encode("ascii", "ignore") normalized_items.append(normalized_item) items = normalized_items query = " AND ".join("{}*~".format(re.escape(item)) for item in items) + "".join( ' NOT id:"{}"'.format(excluded._id) for excluded in exclude ) results = search(build_query(query, start=start, size=size), index=INDEX, doc_type="user") docs = results["results"] pages = math.ceil(results["counts"].get("user", 0) / size) validate_page_num(page, pages) users = [] for doc in docs: # TODO: use utils.serialize_user user = User.load(doc["id"]) if current_user and current_user._id == user._id: n_projects_in_common = -1 elif current_user: n_projects_in_common = current_user.n_projects_in_common(user) else: n_projects_in_common = 0 if user is None: logger.error("Could not load user {0}".format(doc["id"])) continue if user.is_active: # exclude merged, unregistered, etc. current_employment = None education = None if user.jobs: current_employment = user.jobs[0]["institution"] if user.schools: education = user.schools[0]["institution"] users.append( { "fullname": doc["user"], "id": doc["id"], "employment": current_employment, "education": education, "n_projects_in_common": n_projects_in_common, "gravatar_url": gravatar(user, use_ssl=True, size=settings.PROFILE_IMAGE_MEDIUM), "profile_url": user.profile_url, "registered": user.is_registered, "active": user.is_active, } ) return {"users": users, "total": results["counts"]["total"], "pages": pages, "page": page}
def gravatar_url(self): return filters.gravatar(self, use_ssl=True, size=settings.GRAVATAR_SIZE_ADD_CONTRIBUTOR)
def _gravatar_url(self, size): return filters.gravatar( self, use_ssl=True, size=size )
def search_contributor(query, page=0, size=10, exclude=[], current_user=None): """Search for contributors to add to a project using elastic search. Request must include JSON data with a "query" field. :param query: The substring of the username to search for :param page: For pagination, the page number to use for results :param size: For pagination, the number of results per page :param exclude: A list of User objects to exclude from the search :param current_user: A User object of the current user :return: List of dictionaries, each containing the ID, full name, most recent employment and education, gravatar URL of an OSF user """ start = (page * size) items = re.split(r'[\s-]+', query) query = '' query = " AND ".join('{}*~'.format(item) for item in items) + \ "".join(' NOT "{}"'.format(excluded) for excluded in exclude) results = search(build_query(query, start=start, size=size), index='website', search_type='user') docs = results['results'] pages = math.ceil(results['counts'].get('user', 0) / size) users = [] for doc in docs: # TODO: use utils.serialize_user user = User.load(doc['id']) if current_user: n_projects_in_common = current_user.n_projects_in_common(user) else: n_projects_in_common = 0 if user is None: logger.error('Could not load user {0}'.format(doc['id'])) continue if user.is_active(): # exclude merged, unregistered, etc. current_employment = None education = None if user.jobs: current_employment = user.jobs[0]['institution'] if user.schools: education = user.schools[0]['institution'] users.append({ 'fullname': doc['user'], 'id': doc['id'], 'employment': current_employment, 'education': education, 'n_projects_in_common': n_projects_in_common, 'gravatar_url': gravatar( user, use_ssl=True, size=settings.GRAVATAR_SIZE_ADD_CONTRIBUTOR, ), 'profile_url': user.profile_url, 'registered': user.is_registered, 'active': user.is_active() }) return \ { 'users': users, 'total': results['counts']['total'], 'pages': pages, 'page': page, }
def search_contributor(query, page=0, size=10, exclude=None, current_user=None): """Search for contributors to add to a project using elastic search. Request must include JSON data with a "query" field. :param query: The substring of the username to search for :param page: For pagination, the page number to use for results :param size: For pagination, the number of results per page :param exclude: A list of User objects to exclude from the search :param current_user: A User object of the current user :return: List of dictionaries, each containing the ID, full name, most recent employment and education, gravatar URL of an OSF user """ start = (page * size) items = re.split(r'[\s-]+', query) exclude = exclude or [] normalized_items = [] for item in items: try: normalized_item = six.u(item) except TypeError: normalized_item = item normalized_item = unicodedata.normalize('NFKD', normalized_item).encode('ascii', 'ignore') normalized_items.append(normalized_item) items = normalized_items query = " AND ".join('{}*~'.format(re.escape(item)) for item in items) + \ "".join(' NOT id:"{}"'.format(excluded._id) for excluded in exclude) results = search(build_query(query, start=start, size=size), index=INDEX, doc_type='user') docs = results['results'] pages = math.ceil(results['counts'].get('user', 0) / size) validate_page_num(page, pages) users = [] for doc in docs: # TODO: use utils.serialize_user user = User.load(doc['id']) if current_user: n_projects_in_common = current_user.n_projects_in_common(user) else: n_projects_in_common = 0 if user is None: logger.error('Could not load user {0}'.format(doc['id'])) continue if user.is_active: # exclude merged, unregistered, etc. current_employment = None education = None if user.jobs: current_employment = user.jobs[0]['institution'] if user.schools: education = user.schools[0]['institution'] users.append({ 'fullname': doc['user'], 'id': doc['id'], 'employment': current_employment, 'education': education, 'n_projects_in_common': n_projects_in_common, 'gravatar_url': gravatar( user, use_ssl=True, size=settings.GRAVATAR_SIZE_ADD_CONTRIBUTOR, ), 'profile_url': user.profile_url, 'registered': user.is_registered, 'active': user.is_active }) return { 'users': users, 'total': results['counts']['total'], 'pages': pages, 'page': page, }
def get_gravatar(user, size=None): if size is None: size = settings.GRAVATAR_SIZE_PROFILE return gravatar(user, use_ssl=True, size=size)
def serialize_user(user, node=None, admin=False, full=False): """Return a dictionary representation of a registered user. :param User user: A User object :param bool full: Include complete user properties """ fullname = user.display_full_name(node=node) ret = { 'id': str(user._primary_key), 'registered': user.is_registered, 'surname': user.family_name, 'fullname': fullname, 'shortname': fullname if len(fullname) < 50 else fullname[:23] + "..." + fullname[-23:], 'gravatar_url': gravatar(user, use_ssl=True, size=settings.GRAVATAR_SIZE_ADD_CONTRIBUTOR), 'active': user.is_active, } if node is not None: if admin: flags = { 'visible': False, 'permission': 'read', } else: flags = { 'visible': user._id in node.visible_contributor_ids, 'permission': reduce_permissions(node.get_permissions(user)), } ret.update(flags) if user.is_registered: ret.update({ 'url': user.url, 'absolute_url': user.absolute_url, 'display_absolute_url': user.display_absolute_url, 'date_registered': user.date_registered.strftime("%Y-%m-%d"), }) if full: if user.is_merged: merger = user.merged_by merged_by = { 'id': str(merger._primary_key), 'url': merger.url, 'absolute_url': merger.absolute_url } else: merged_by = None ret.update({ 'number_projects': len(get_projects(user)), 'number_public_projects': len(get_public_projects(user)), 'activity_points': user.get_activity_points(), 'gravatar_url': gravatar(user, use_ssl=True, size=settings.GRAVATAR_SIZE_PROFILE), 'is_merged': user.is_merged, 'merged_by': merged_by, }) return ret
def search_contributor(query, page=0, size=10, exclude=None, current_user=None): """Search for contributors to add to a project using elastic search. Request must include JSON data with a "query" field. :param query: The substring of the username to search for :param page: For pagination, the page number to use for results :param size: For pagination, the number of results per page :param exclude: A list of User objects to exclude from the search :param current_user: A User object of the current user :return: List of dictionaries, each containing the ID, full name, most recent employment and education, gravatar URL of an OSF user """ start = (page * size) items = re.split(r'[\s-]+', query) exclude = exclude or [] normalized_items = [] for item in items: try: normalized_item = six.u(item) except TypeError: normalized_item = item normalized_item = unicodedata.normalize('NFKD', normalized_item).encode( 'ascii', 'ignore') normalized_items.append(normalized_item) items = normalized_items query = " AND ".join('{}*~'.format(re.escape(item)) for item in items) + \ "".join(' NOT id:"{}"'.format(excluded._id) for excluded in exclude) results = search(build_query(query, start=start, size=size), index=INDEX, doc_type='user') docs = results['results'] pages = math.ceil(results['counts'].get('user', 0) / size) validate_page_num(page, pages) users = [] for doc in docs: # TODO: use utils.serialize_user user = User.load(doc['id']) if current_user and current_user._id == user._id: n_projects_in_common = -1 elif current_user: n_projects_in_common = current_user.n_projects_in_common(user) else: n_projects_in_common = 0 if user is None: logger.error('Could not load user {0}'.format(doc['id'])) continue if user.is_active: # exclude merged, unregistered, etc. current_employment = None education = None if user.jobs: current_employment = user.jobs[0]['institution'] if user.schools: education = user.schools[0]['institution'] users.append({ 'fullname': doc['user'], 'id': doc['id'], 'employment': current_employment, 'education': education, 'n_projects_in_common': n_projects_in_common, 'gravatar_url': gravatar(user, use_ssl=True, size=settings.PROFILE_IMAGE_MEDIUM), 'profile_url': user.profile_url, 'registered': user.is_registered, 'active': user.is_active }) return { 'users': users, 'total': results['counts']['total'], 'pages': pages, 'page': page, }
def serialize_user(user, node=None, admin=False, full=False, is_profile=False): """ Return a dictionary representation of a registered user. :param User user: A User object :param bool full: Include complete user properties """ fullname = user.display_full_name(node=node) ret = { 'id': str(user._primary_key), 'registered': user.is_registered, 'surname': user.family_name, 'fullname': fullname, 'shortname': fullname if len(fullname) < 50 else fullname[:23] + "..." + fullname[-23:], 'gravatar_url': gravatar( user, use_ssl=True, size=settings.PROFILE_IMAGE_MEDIUM ), 'active': user.is_active, } if node is not None: if admin: flags = { 'visible': False, 'permission': 'read', } else: flags = { 'visible': user._id in node.visible_contributor_ids, 'permission': reduce_permissions(node.get_permissions(user)), } ret.update(flags) if user.is_registered: ret.update({ 'url': user.url, 'absolute_url': user.absolute_url, 'display_absolute_url': user.display_absolute_url, 'date_registered': user.date_registered.strftime("%Y-%m-%d"), }) if full: # Add emails if is_profile: ret['emails'] = [ { 'address': each, 'primary': each.strip().lower() == user.username.strip().lower(), 'confirmed': True, } for each in user.emails ] + [ { 'address': each, 'primary': each.strip().lower() == user.username.strip().lower(), 'confirmed': False } for each in user.unconfirmed_emails ] if user.is_merged: merger = user.merged_by merged_by = { 'id': str(merger._primary_key), 'url': merger.url, 'absolute_url': merger.absolute_url } else: merged_by = None ret.update({ 'number_projects': get_projects(user).count(), 'number_public_projects': get_public_projects(user).count(), 'activity_points': user.get_activity_points(), 'gravatar_url': gravatar( user, use_ssl=True, size=settings.PROFILE_IMAGE_LARGE ), 'is_merged': user.is_merged, 'merged_by': merged_by, }) return ret
def serialize_user(user, node=None, admin=False, full=False, is_profile=False, include_node_counts=False): """ Return a dictionary representation of a registered user. :param User user: A User object :param bool full: Include complete user properties """ from website.project.utils import PROJECT_QUERY contrib = None if isinstance(user, Contributor): contrib = user user = contrib.user fullname = user.display_full_name(node=node) ret = { 'id': str(user._primary_key), 'registered': user.is_registered, 'surname': user.family_name, 'fullname': fullname, 'shortname': fullname if len(fullname) < 50 else fullname[:23] + '...' + fullname[-23:], 'gravatar_url': gravatar( user, use_ssl=True, size=settings.PROFILE_IMAGE_MEDIUM ), 'active': user.is_active, } if node is not None: if admin: flags = { 'visible': False, 'permission': 'read', } else: flags = { 'visible': contrib.visible if isinstance(contrib, Contributor) else node.contributor_set.filter(user=user, visible=True).exists(), 'permission': reduce_permissions(node.get_permissions(user)), } ret.update(flags) if user.is_registered: ret.update({ 'url': user.url, 'absolute_url': user.absolute_url, 'display_absolute_url': user.display_absolute_url, 'date_registered': user.date_registered.strftime('%Y-%m-%d'), }) if full: # Add emails if is_profile: ret['emails'] = [ { 'address': each, 'primary': each.strip().lower() == user.username.strip().lower(), 'confirmed': True, } for each in user.emails.values_list('address', flat=True) ] + [ { 'address': each, 'primary': each.strip().lower() == user.username.strip().lower(), 'confirmed': False } for each in user.get_unconfirmed_emails_exclude_external_identity() ] if user.is_merged: merger = user.merged_by merged_by = { 'id': str(merger._primary_key), 'url': merger.url, 'absolute_url': merger.absolute_url } else: merged_by = None projects = Node.find_for_user(user, PROJECT_QUERY).get_roots() ret.update({ 'activity_points': user.get_activity_points(), 'gravatar_url': gravatar( user, use_ssl=True, size=settings.PROFILE_IMAGE_LARGE ), 'is_merged': user.is_merged, 'merged_by': merged_by, }) if include_node_counts: ret.update({ 'number_projects': projects.count(), 'number_public_projects': projects.filter(is_public=True).count(), }) return ret