def test_admin_scoped_token_can_create_and_send_email(self, mock_auth, mock_mail): token = ApiOAuth2PersonalToken( owner=self.user, name='Admin Token', scopes='osf.admin' ) mock_cas_resp = CasResponse( authenticated=True, user=self.user._id, attributes={ 'accessToken': token.token_id, 'accessTokenScope': [s for s in token.scopes.split(' ')] } ) mock_auth.return_value = self.user, mock_cas_resp assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0) res = self.app.post_json_api( '{}?send_email=true'.format(self.base_url), self.data, headers={'Authorization': 'Bearer {}'.format(token.token_id)} ) assert_equal(res.status_code, 201) assert_equal(res.json['data']['attributes']['username'], self.unconfirmed_email) assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 1) assert_equal(mock_mail.call_count, 1)
def test_improperly_scoped_token_can_not_create_or_email(self, mock_auth, mock_mail): token = ApiOAuth2PersonalToken( owner=self.user, name='Unauthorized Token', scopes='osf.full_write' ) mock_cas_resp = CasResponse( authenticated=True, user=self.user._id, attributes={ 'accessToken': token.token_id, 'accessTokenScope': [s for s in token.scopes.split(' ')] } ) mock_auth.return_value = self.user, mock_cas_resp assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0) res = self.app.post_json_api( '{}?send_email=true'.format(self.base_url), self.data, headers={'Authorization': 'Bearer {}'.format(token.token_id)}, expect_errors=True ) assert_equal(res.status_code, 403) assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0) assert_equal(mock_mail.call_count, 0)
def test_properly_scoped_token_can_create_without_username_but_not_send_email(self, mock_auth, mock_mail): token = ApiOAuth2PersonalToken( owner=self.user, name='Authorized Token', scopes='osf.users.create' ) mock_cas_resp = CasResponse( authenticated=True, user=self.user._id, attributes={ 'accessToken': token.token_id, 'accessTokenScope': [s for s in token.scopes.split(' ')] } ) mock_auth.return_value = self.user, mock_cas_resp self.data['data']['attributes'] = {'full_name': 'No Email'} assert_equal(User.find(Q('fullname', 'eq', 'No Email')).count(), 0) res = self.app.post_json_api( '{}?send_email=true'.format(self.base_url), self.data, headers={'Authorization': 'Bearer {}'.format(token.token_id)} ) assert_equal(res.status_code, 201) username = res.json['data']['attributes']['username'] try: no_failure = UUID(username) except ValueError: raise AssertionError('Username is not a valid UUID') assert_equal(User.find(Q('fullname', 'eq', 'No Email')).count(), 1) assert_equal(mock_mail.call_count, 0)
def get_events(self, date): super(UserSummary, self).get_events(date) # Convert to a datetime at midnight for queries and the timestamp timestamp_datetime = datetime(date.year, date.month, date.day).replace(tzinfo=pytz.UTC) query_datetime = timestamp_datetime + timedelta(1) active_user_query = (Q('is_registered', 'eq', True) & Q('password', 'ne', None) & Q('merged_by', 'eq', None) & Q('date_disabled', 'eq', None) & Q('date_confirmed', 'ne', None) & Q('date_confirmed', 'lt', query_datetime)) active_users = 0 depth_users = 0 profile_edited = 0 user_pages = paginated(OSFUser, query=active_user_query) for user in user_pages: active_users += 1 log_count = count_user_logs(user) if log_count >= LOG_THRESHOLD: depth_users += 1 if user.social or user.schools or user.jobs: profile_edited += 1 counts = { 'keen': { 'timestamp': timestamp_datetime.isoformat() }, 'status': { 'active': active_users, 'depth': depth_users, 'unconfirmed': OSFUser.find( Q('date_registered', 'lt', query_datetime) & Q('date_confirmed', 'eq', None)).count(), 'deactivated': OSFUser.find( Q('date_disabled', 'ne', None) & Q('date_disabled', 'lt', query_datetime)).count(), 'merged': OSFUser.find( Q('date_registered', 'lt', query_datetime) & Q('merged_by', 'ne', None)).count(), 'profile_edited': profile_edited } } logger.info( 'Users counted. Active: {}, Depth: {}, Unconfirmed: {}, Deactivated: {}, Merged: {}, Profile Edited: {}' .format(counts['status']['active'], counts['status']['depth'], counts['status']['unconfirmed'], counts['status']['deactivated'], counts['status']['merged'], counts['status']['profile_edited'])) return [counts]
def test_logged_out_user_cannot_create_other_user_or_send_mail(self, mock_mail): assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0) res = self.app.post_json_api( '{}?send_email=true'.format(self.base_url), self.data, expect_errors=True ) assert_equal(res.status_code, 401) assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0) assert_equal(mock_mail.call_count, 0)
def test_cookied_requests_can_create_and_email(self, mock_mail): session = Session(data={'auth_user_id': self.user._id}) session.save() cookie = itsdangerous.Signer(settings.SECRET_KEY).sign(session._id) self.app.set_cookie(settings.COOKIE_NAME, str(cookie)) assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 0) res = self.app.post_json_api( '{}?send_email=true'.format(self.base_url), self.data ) assert_equal(res.status_code, 201) assert_equal(User.find(Q('username', 'eq', self.unconfirmed_email)).count(), 1) assert_equal(mock_mail.call_count, 1)
def revoke_oauth_access(self, external_account, auth, save=True): """Revoke all access to an ``ExternalAccount``. TODO: This should accept node and metadata params in the future, to allow fine-grained revocation of grants. That's not yet been needed, so it's not yet been implemented. """ for node in self.get_nodes_with_oauth_grants(external_account): try: node.get_addon(external_account.provider, deleted=True).deauthorize(auth=auth) except AttributeError: # No associated addon settings despite oauth grant # Remove grant in `for` loop below pass if OSFUser.find(Q('external_accounts', 'eq', external_account._id)).count() == 1: # Only this user is using the account, so revoke remote access as well. self.revoke_remote_oauth_access(external_account) for key in self.oauth_grants: self.oauth_grants[key].pop(external_account._id, None) if save: self.save()
def redirect_to_twitter(twitter_handle): """Redirect GET requests for /@TwitterHandle/ to respective the OSF user account if it associated with an active account :param uid: uid for requested User :return: Redirect to User's Twitter account page """ try: user = User.find_one(Q('social.twitter', 'iexact', twitter_handle)) except NoResultsFound: raise HTTPError(http.NOT_FOUND, data={ 'message_short': 'User Not Found', 'message_long': 'There is no active user associated with the Twitter handle: {0}.'.format(twitter_handle) }) except MultipleResultsFound: users = User.find(Q('social.twitter', 'iexact', twitter_handle)) message_long = 'There are multiple OSF accounts associated with the ' \ 'Twitter handle: <strong>{0}</strong>. <br /> Please ' \ 'select from the accounts below. <br /><ul>'.format(markupsafe.escape(twitter_handle)) for user in users: message_long += '<li><a href="{0}">{1}</a></li>'.format(user.url, markupsafe.escape(user.fullname)) message_long += '</ul>' raise HTTPError(http.MULTIPLE_CHOICES, data={ 'message_short': 'Multiple Users Found', 'message_long': message_long }) return redirect(user.url)
def redirect_to_twitter(twitter_handle): """Redirect GET requests for /@TwitterHandle/ to respective the OSF user account if it associated with an active account :param uid: uid for requested User :return: Redirect to User's Twitter account page """ try: user = OSFUser.find_one(Q('social.twitter', 'iexact', twitter_handle)) except NoResultsFound: raise HTTPError( http.NOT_FOUND, data={ 'message_short': 'User Not Found', 'message_long': 'There is no active user associated with the Twitter handle: {0}.' .format(twitter_handle) }) except MultipleResultsFound: users = OSFUser.find(Q('social.twitter', 'iexact', twitter_handle)) message_long = 'There are multiple OSF accounts associated with the ' \ 'Twitter handle: <strong>{0}</strong>. <br /> Please ' \ 'select from the accounts below. <br /><ul>'.format(markupsafe.escape(twitter_handle)) for user in users: message_long += '<li><a href="{0}">{1}</a></li>'.format( user.url, markupsafe.escape(user.fullname)) message_long += '</ul>' raise HTTPError(http.MULTIPLE_CHOICES, data={ 'message_short': 'Multiple Users Found', 'message_long': message_long }) return redirect(user.url)
def test_integration(self, mock_upload, mock_send_mail): fullname = 'John Deacon' username = '******' title = 'good songs' conference = ConferenceFactory() body = 'dragon on my back' content = 'dragon attack' recipient = '{0}{1}[email protected]'.format( 'test-' if settings.DEV_MODE else '', conference.endpoint, ) self.app.post( api_url_for('meeting_hook'), { 'X-Mailgun-Sscore': 0, 'timestamp': '123', 'token': 'secret', 'signature': hmac.new( key=settings.MAILGUN_API_KEY, msg='{}{}'.format('123', 'secret'), digestmod=hashlib.sha256, ).hexdigest(), 'attachment-count': '1', 'X-Mailgun-Sscore': 0, 'from': '{0} <{1}>'.format(fullname, username), 'recipient': recipient, 'subject': title, 'stripped-text': body, }, upload_files=[ ('attachment-1', 'attachment-1', content), ], ) assert_true(mock_upload.called) users = OSFUser.find(Q('username', 'eq', username)) assert_equal(users.count(), 1) nodes = AbstractNode.find(Q('title', 'eq', title)) assert_equal(nodes.count(), 1) node = nodes[0] assert_equal(node.get_wiki_page('home').content, body) assert_true(mock_send_mail.called) call_args, call_kwargs = mock_send_mail.call_args assert_absolute(call_kwargs['conf_view_url']) assert_absolute(call_kwargs['set_password_url']) assert_absolute(call_kwargs['profile_url']) assert_absolute(call_kwargs['file_url']) assert_absolute(call_kwargs['node_url'])
def get_targets(): logger.info('Acquiring targets...') targets = [ u for u in OSFUser.find(Q('merged_by', 'eq', None)) if Collection.find( Q('is_bookmark_collection', 'eq', True) & Q('is_deleted', 'eq', False) & Q('creator', 'eq', u)).count() == 0 ] logger.info('Found {} target users.'.format(len(targets))) return targets
def find_inactive_users_with_no_inactivity_email_sent_or_queued(): inactive_users = User.find( (Q('date_last_login', 'lt', timezone.now() - settings.NO_LOGIN_WAIT_TIME) & Q('tags__name', 'ne', 'osf4m')) | (Q('date_last_login', 'lt', timezone.now() - settings.NO_LOGIN_OSF4M_WAIT_TIME) & Q('tags__name', 'eq', 'osf4m')) ) inactive_emails = QueuedMail.find(Q('email_type', 'eq', NO_LOGIN_TYPE)) #This is done to prevent User query returns comparison to User, as equality fails #on datetime fields due to pymongo rounding. Instead here _id is compared. users_sent_id = [email.user._id for email in inactive_emails] inactive_ids = [user._id for user in inactive_users if user.is_active] users_to_send = [User.load(id) for id in (set(inactive_ids) - set(users_sent_id))] return users_to_send
def get_enabled_authorized_linked(user_settings_list, has_external_account, short_name): """ Gather the number of users who have at least one node in each of the stages for an addon :param user_settings_list: list of user_settings for a particualr addon :param has_external_account: where addon is derrived from, determines method to load node settings :param short_name: short name of addon to get correct node_settings :return: dict with number of users that have at least one project at each stage """ from addons.forward.models import NodeSettings as ForwardNodeSettings num_enabled = 0 # of users w/ 1+ addon account connected num_authorized = 0 # of users w/ 1+ addon account connected to 1+ node num_linked = 0 # of users w/ 1+ addon account connected to 1+ node and configured # osfstorage and wiki don't have user_settings, so always assume they're enabled, authorized, linked if short_name == 'osfstorage' or short_name == 'wiki': num_enabled = num_authorized = num_linked = User.find( Q('is_registered', 'eq', True) & Q('password', 'ne', None) & Q('merged_by', 'eq', None) & Q('date_disabled', 'eq', None) & Q('date_confirmed', 'ne', None) ).count() elif short_name == 'forward': num_enabled = num_authorized = ForwardNodeSettings.find().count() num_linked = ForwardNodeSettings.find(Q('url', 'ne', None)).count() else: for user_settings in paginated(user_settings_list): node_settings_list = [] if has_external_account: if user_settings.has_auth: num_enabled += 1 node_settings_list = [Node.load(guid).get_addon(short_name) for guid in user_settings.oauth_grants.keys()] else: num_enabled += 1 node_settings_list = [Node.load(guid).get_addon(short_name) for guid in user_settings.nodes_authorized] if any([ns.has_auth for ns in node_settings_list if ns]): num_authorized += 1 if any([(ns.complete and ns.configured) for ns in node_settings_list if ns]): num_linked += 1 return { 'enabled': num_enabled, 'authorized': num_authorized, 'linked': num_linked }
def test_integration(self, mock_upload, mock_send_mail): fullname = 'John Deacon' username = '******' title = 'good songs' conference = ConferenceFactory() body = 'dragon on my back' content = 'dragon attack' recipient = '{0}{1}[email protected]'.format( 'test-' if settings.DEV_MODE else '', conference.endpoint, ) self.app.post( api_url_for('meeting_hook'), { 'X-Mailgun-Sscore': 0, 'timestamp': '123', 'token': 'secret', 'signature': hmac.new( key=settings.MAILGUN_API_KEY, msg='{}{}'.format('123', 'secret'), digestmod=hashlib.sha256, ).hexdigest(), 'attachment-count': '1', 'X-Mailgun-Sscore': 0, 'from': '{0} <{1}>'.format(fullname, username), 'recipient': recipient, 'subject': title, 'stripped-text': body, }, upload_files=[ ('attachment-1', 'attachment-1', content), ], ) assert_true(mock_upload.called) users = User.find(Q('username', 'eq', username)) assert_equal(users.count(), 1) nodes = Node.find(Q('title', 'eq', title)) assert_equal(nodes.count(), 1) node = nodes[0] assert_equal(node.get_wiki_page('home').content, body) assert_true(mock_send_mail.called) call_args, call_kwargs = mock_send_mail.call_args assert_absolute(call_kwargs['conf_view_url']) assert_absolute(call_kwargs['set_password_url']) assert_absolute(call_kwargs['profile_url']) assert_absolute(call_kwargs['file_url']) assert_absolute(call_kwargs['node_url'])
def get_enabled_authorized_linked(user_settings_list, has_external_account, short_name): """ Gather the number of users who have at least one node in each of the stages for an addon :param user_settings_list: list of user_settings for a particualr addon :param has_external_account: where addon is derrived from, determines method to load node settings :param short_name: short name of addon to get correct node_settings :return: dict with number of users that have at least one project at each stage """ from addons.forward.models import NodeSettings as ForwardNodeSettings num_enabled = 0 # of users w/ 1+ addon account connected num_authorized = 0 # of users w/ 1+ addon account connected to 1+ node num_linked = 0 # of users w/ 1+ addon account connected to 1+ node and configured # osfstorage and wiki don't have user_settings, so always assume they're enabled, authorized, linked if short_name == 'osfstorage' or short_name == 'wiki': num_enabled = num_authorized = num_linked = OSFUser.find( Q('is_registered', 'eq', True) & Q('password', 'ne', None) & Q('merged_by', 'eq', None) & Q('date_disabled', 'eq', None) & Q('date_confirmed', 'ne', None) ).count() elif short_name == 'forward': num_enabled = num_authorized = ForwardNodeSettings.find().count() num_linked = ForwardNodeSettings.find(Q('url', 'ne', None)).count() else: for user_settings in paginated(user_settings_list): node_settings_list = [] if has_external_account: if user_settings.has_auth: num_enabled += 1 node_settings_list = [AbstractNode.load(guid).get_addon(short_name) for guid in user_settings.oauth_grants.keys()] else: num_enabled += 1 node_settings_list = [AbstractNode.load(guid).get_addon(short_name) for guid in user_settings.nodes_authorized] if any([ns.has_auth for ns in node_settings_list if ns]): num_authorized += 1 if any([(ns.complete and ns.configured) for ns in node_settings_list if ns]): num_linked += 1 return { 'enabled': num_enabled, 'authorized': num_authorized, 'linked': num_linked }
def ensure_external_identity_uniqueness(provider, identity, user=None): from osf.models import OSFUser users_with_identity = OSFUser.find(Q('external_identity.{}.{}'.format(provider, identity), 'ne', None)) for existing_user in users_with_identity: if user and user._id == existing_user._id: continue if existing_user.external_identity[provider][identity] == 'VERIFIED': if user and user.external_identity.get(provider, {}).get(identity, {}): user.external_identity[provider].pop(identity) if user.external_identity[provider] == {}: user.external_identity.pop(provider) user.save() # Note: This won't work in v2 because it rolls back transactions when status >= 400 raise ValidationError('Another user has already claimed this external identity') existing_user.external_identity[provider].pop(identity) if existing_user.external_identity[provider] == {}: existing_user.external_identity.pop(provider) existing_user.save() return
def ensure_external_identity_uniqueness(provider, identity, user=None): from osf.models import OSFUser users_with_identity = OSFUser.find(Q('external_identity.{}.{}'.format(provider, identity), 'ne', None)) for existing_user in users_with_identity: if user and user._id == existing_user._id: continue if existing_user.external_identity[provider][identity] == 'VERIFIED': if user and user.external_identity.get(provider, {}).get(identity, {}): user.external_identity[provider].pop(identity) if user.external_identity[provider] == {}: user.external_identity.pop(provider) user.save() # Note: This won't work in v2 because it rolls back transactions when status >= 400 raise ValidationError('Another user has already claimed this external identity') existing_user.external_identity[provider].pop(identity) if existing_user.external_identity[provider] == {}: existing_user.external_identity.pop(provider) existing_user.save() return
def find_by_name(name): try: parts = re.split(r'\s+', name.strip()) except: return None if len(parts) < 2: return None users = OSFUser.find( reduce( lambda acc, value: acc & value, [ Q('fullname', 'icontains', part.decode('utf-8', 'ignore')) for part in parts ] ) ).sort('-date_created') if not users: return None if len(users) > 1: logger.warn('Multiple users found for name {}'.format(name)) return users[0]
def find_by_name(name): try: parts = re.split(r'\s+', name.strip()) except: return None if len(parts) < 2: return None users = User.find( reduce( lambda acc, value: acc & value, [ Q('fullname', 'icontains', part.decode('utf-8', 'ignore')) for part in parts ] ) ).sort('-date_created') if not users: return None if len(users) > 1: logger.warn('Multiple users found for name {}'.format(name)) return users[0]
def revoke_oauth_access(self, external_account, auth, save=True): """Revoke all access to an ``ExternalAccount``. TODO: This should accept node and metadata params in the future, to allow fine-grained revocation of grants. That's not yet been needed, so it's not yet been implemented. """ for node in self.get_nodes_with_oauth_grants(external_account): try: node.get_addon(external_account.provider, deleted=True).deauthorize(auth=auth) except AttributeError: # No associated addon settings despite oauth grant # Remove grant in `for` loop below pass if User.find(Q('external_accounts', 'eq', external_account._id)).count() == 1: # Only this user is using the account, so revoke remote access as well. self.revoke_remote_oauth_access(external_account) for key in self.oauth_grants: self.oauth_grants[key].pop(external_account._id, None) if save: self.save()
def get_events(self, date): super(InstitutionSummary, self).get_events(date) institutions = self.get_institutions() counts = [] # Convert to a datetime at midnight for queries and the timestamp timestamp_datetime = datetime(date.year, date.month, date.day).replace(tzinfo=pytz.UTC) query_datetime = timestamp_datetime + timedelta(1) for institution in institutions: user_query = Q('affiliated_institutions', 'eq', institution) node_query = ( Q('is_deleted', 'ne', True) & Q('date_created', 'lt', query_datetime) ) project_query = node_query & Q('parent_nodes', 'eq', None) public_query = Q('is_public', 'eq', True) private_query = Q('is_public', 'eq', False) node_public_query = node_query & public_query node_private_query = node_query & private_query project_public_query = project_query & public_query project_private_query = project_query & private_query count = { 'institution': { 'id': institution._id, 'name': institution.name, }, 'users': { 'total': OSFUser.find(user_query).count(), }, 'nodes': { 'total': AbstractNode.find_by_institutions(institution, node_query).count(), 'public': AbstractNode.find_by_institutions(institution, node_public_query).count(), 'private': AbstractNode.find_by_institutions(institution, node_private_query).count(), }, 'projects': { 'total': AbstractNode.find_by_institutions(institution, project_query).count(), 'public': AbstractNode.find_by_institutions(institution, project_public_query).count(), 'private': AbstractNode.find_by_institutions(institution, project_private_query).count(), }, 'registered_nodes': { 'total': Registration.find_by_institutions(institution, node_query).count(), 'public': Registration.find_by_institutions(institution, node_public_query).count(), 'embargoed': Registration.find_by_institutions(institution, node_private_query).count(), }, 'registered_projects': { 'total': Registration.find_by_institutions(institution, project_query).count(), 'public': Registration.find_by_institutions(institution, project_public_query).count(), 'embargoed': Registration.find_by_institutions(institution, project_private_query).count(), }, 'keen': { 'timestamp': timestamp_datetime.isoformat() } } logger.info( '{} Nodes counted. Nodes: {}, Projects: {}, Registered Nodes: {}, Registered Projects: {}'.format( count['institution']['name'], count['nodes']['total'], count['projects']['total'], count['registered_nodes']['total'], count['registered_projects']['total'] ) ) counts.append(count) return counts
def get_queryset(self): query = self.get_query_from_request() return OSFUser.find(query)
def get_queryset(self): # TODO: sort query = self.get_query_from_request() return User.find(query)
def get_events(self, date): super(UserSummary, self).get_events(date) # Convert to a datetime at midnight for queries and the timestamp timestamp_datetime = datetime(date.year, date.month, date.day).replace(tzinfo=pytz.UTC) query_datetime = timestamp_datetime + timedelta(1) active_user_query = ( Q('is_registered', 'eq', True) & Q('password', 'ne', None) & Q('merged_by', 'eq', None) & Q('date_disabled', 'eq', None) & Q('date_confirmed', 'ne', None) & Q('date_confirmed', 'lt', query_datetime) ) active_users = 0 depth_users = 0 profile_edited = 0 user_pages = paginated(User, query=active_user_query) for user in user_pages: active_users += 1 log_count = count_user_logs(user) if log_count >= LOG_THRESHOLD: depth_users += 1 if user.social or user.schools or user.jobs: profile_edited += 1 counts = { 'keen': { 'timestamp': timestamp_datetime.isoformat() }, 'status': { 'active': active_users, 'depth': depth_users, 'unconfirmed': User.find( Q('date_registered', 'lt', query_datetime) & Q('date_confirmed', 'eq', None) ).count(), 'deactivated': User.find( Q('date_disabled', 'ne', None) & Q('date_disabled', 'lt', query_datetime) ).count(), 'merged': User.find( Q('date_registered', 'lt', query_datetime) & Q('merged_by', 'ne', None) ).count(), 'profile_edited': profile_edited } } logger.info( 'Users counted. Active: {}, Depth: {}, Unconfirmed: {}, Deactivated: {}, Merged: {}, Profile Edited: {}'.format( counts['status']['active'], counts['status']['depth'], counts['status']['unconfirmed'], counts['status']['deactivated'], counts['status']['merged'], counts['status']['profile_edited'] ) ) return [counts]
def get_queryset(self): return OSFUser.find(Q('_id', 'eq', self.context['request'].user._id))
def get_events(self, date): super(InstitutionSummary, self).get_events(date) from osf.models import AbstractNode, Registration institutions = self.get_institutions() counts = [] # Convert to a datetime at midnight for queries and the timestamp timestamp_datetime = datetime(date.year, date.month, date.day).replace(tzinfo=pytz.UTC) query_datetime = timestamp_datetime + timedelta(1) for institution in institutions: user_query = Q('affiliated_institutions', 'eq', institution) node_query = ( Q('is_deleted', 'ne', True) & Q('date_created', 'lt', query_datetime) ) project_query = node_query & Q('parent_nodes', 'eq', None) public_query = Q('is_public', 'eq', True) private_query = Q('is_public', 'eq', False) node_public_query = node_query & public_query node_private_query = node_query & private_query project_public_query = project_query & public_query project_private_query = project_query & private_query count = { 'institution': { 'id': institution._id, 'name': institution.name, }, 'users': { 'total': User.find(user_query).count(), }, 'nodes': { 'total': AbstractNode.find_by_institutions(institution, node_query).count(), 'public': AbstractNode.find_by_institutions(institution, node_public_query).count(), 'private': AbstractNode.find_by_institutions(institution, node_private_query).count(), }, 'projects': { 'total': Node.find_by_institutions(institution, project_query).count(), 'public': Node.find_by_institutions(institution, project_public_query).count(), 'private': Node.find_by_institutions(institution, project_private_query).count(), }, 'registered_nodes': { 'total': Registration.find_by_institutions(institution, node_query).count(), 'public': Registration.find_by_institutions(institution, node_public_query).count(), 'embargoed': Registration.find_by_institutions(institution, node_private_query).count(), }, 'registered_projects': { 'total': Registration.find_by_institutions(institution, project_query).count(), 'public': Registration.find_by_institutions(institution, project_public_query).count(), 'embargoed': Registration.find_by_institutions(institution, project_private_query).count(), }, 'keen': { 'timestamp': timestamp_datetime.isoformat() } } logger.info( '{} Nodes counted. Nodes: {}, Projects: {}, Registered Nodes: {}, Registered Projects: {}'.format( count['institution']['name'], count['nodes']['total'], count['projects']['total'], count['registered_nodes']['total'], count['registered_projects']['total'] ) ) counts.append(count) return counts