def testCalculateThroughPostWithCorrectXsrfToken(self): """Tests that duplicate proposals can be calculated with correct XSRF token. Through HTTP POST with correct XSRF token, the duplicate proposals in a given program for a student can be calculated on a per organization basis and a new taskqueue is spawned if there are more than one organization after the org_cursor. """ pds = pd_logic.getForFields({'program': self.program}) self.assertEqual(len(pds), 0) fields = {'scope': self.program, 'slots >': 0, 'status': 'active'} # Get an organization and save the cursor q = gsoc_organization_logic.getQueryForFields(fields) orgs = q.fetch(1) org_cursor = q.cursor() url = '/tasks/gsoc/proposal_duplicates/calculate' postdata = {'program_key': self.program.key().name(), 'org_cursor': unicode(org_cursor)} xsrf_token = self.getXsrfToken(url, data=postdata) postdata.update(xsrf_token=xsrf_token) response = self.client.post(url, postdata) self.assertEqual(response.status_code, httplib.OK) pds = pd_logic.getForFields({'program': self.program}) self.assertEqual(len(pds), 1) self.assertTrue(pds[0].is_duplicate) task_url = '/tasks/gsoc/proposal_duplicates/calculate' self.assertTasksInQueue(n=1, url=task_url)
def testCalculateFinishedThroughPostWithCorrectXsrfToken(self): """Tests the result after calculating duplicate proposals is finished. Through HTTP POST with correct XSRF token, when finished (there is no org to calculate), all pb records with no duplicate proposals are deleted and there is no taskqueue spawned. """ pd_fields = { 'program': self.program, 'student': self.student, 'orgs':[self.organization.key()], 'duplicates': [self.student_proposal.key()], 'is_duplicate': False } pd_logic.updateOrCreateFromFields(pd_fields) pds = pd_logic.getForFields({'program': self.program}) self.assertEqual(len(pds), 1) fields = {'scope': self.program, 'slots >': 0, 'status': 'active'} # Get all organizations and save the cursor q = gsoc_organization_logic.getQueryForFields(fields) q.fetch(3) org_cursor = q.cursor() url = '/tasks/gsoc/proposal_duplicates/calculate' postdata = {'program_key': self.program.key().name(), 'org_cursor': unicode(org_cursor)} xsrf_token = self.getXsrfToken(url, data=postdata) postdata.update(xsrf_token=xsrf_token) response = self.client.post(url, postdata) self.assertEqual(response.status_code, httplib.OK) pds = pd_logic.getForFields({'program': self.program}) self.assertEqual(len(pds), 0) task_url = '/tasks/gsoc/proposal_duplicates/calculate' self.assertTasksInQueue(n=0, url=task_url)
def testCalculateFinishedThroughPostWithCorrectXsrfToken(self): """Tests the result after calculating duplicate proposals is finished. Through HTTP POST with correct XSRF token, when finished (there is no org to calculate), all pb records with no duplicate proposals are deleted and there is no taskqueue spawned. """ pd_fields = { 'program': self.program, 'student': self.student, 'orgs': [self.organization.key()], 'duplicates': [self.student_proposal.key()], 'is_duplicate': False } pd_logic.updateOrCreateFromFields(pd_fields) pds = pd_logic.getForFields({'program': self.program}) self.assertEqual(len(pds), 1) fields = {'scope': self.program, 'slots >': 0, 'status': 'active'} # Get all organizations and save the cursor q = gsoc_organization_logic.getQueryForFields(fields) q.fetch(3) org_cursor = q.cursor() url = '/tasks/gsoc/proposal_duplicates/calculate' postdata = { 'program_key': self.program.key().name(), 'org_cursor': unicode(org_cursor) } xsrf_token = self.getXsrfToken(url, data=postdata) postdata.update(xsrf_token=xsrf_token) response = self.client.post(url, postdata) self.assertEqual(response.status_code, httplib.OK) pds = pd_logic.getForFields({'program': self.program}) self.assertEqual(len(pds), 0) task_url = '/tasks/gsoc/proposal_duplicates/calculate' self.assertTasksInQueue(n=0, url=task_url)
def testCalculateThroughPostWithCorrectXsrfToken(self): """Tests that duplicate proposals can be calculated with correct XSRF token. Through HTTP POST with correct XSRF token, the duplicate proposals in a given program for a student can be calculated on a per organization basis and a new taskqueue is spawned if there are more than one organization after the org_cursor. """ pds = pd_logic.getForFields({'program': self.program}) self.assertEqual(len(pds), 0) fields = {'scope': self.program, 'slots >': 0, 'status': 'active'} # Get an organization and save the cursor q = gsoc_organization_logic.getQueryForFields(fields) orgs = q.fetch(1) org_cursor = q.cursor() url = '/tasks/gsoc/proposal_duplicates/calculate' postdata = { 'program_key': self.program.key().name(), 'org_cursor': unicode(org_cursor) } xsrf_token = self.getXsrfToken(url, data=postdata) postdata.update(xsrf_token=xsrf_token) response = self.client.post(url, postdata) self.assertEqual(response.status_code, httplib.OK) pds = pd_logic.getForFields({'program': self.program}) self.assertEqual(len(pds), 1) self.assertTrue(pds[0].is_duplicate) task_url = '/tasks/gsoc/proposal_duplicates/calculate' self.assertTasksInQueue(n=1, url=task_url)
def showDuplicates(self, request, access_type, page_name=None, params=None, **kwargs): """View in which a host can see which students have been assigned multiple slots. For params see base.view.Public(). """ from google.appengine.api import taskqueue from soc.modules.gsoc.logic.models.proposal_duplicates import logic \ as duplicates_logic from soc.modules.gsoc.logic.models.proposal_duplicates_status import \ logic as ds_logic program_entity = program_logic.getFromKeyFieldsOr404(kwargs) context = helper.responses.getUniversalContext(request) helper.responses.useJavaScript(context, params['js_uses_all']) context['page_name'] = page_name fields = {'program': program_entity, 'is_duplicate': True} template = 'modules/gsoc/program/show_duplicates.html' context['duplicates'] = duplicates_logic.getForFields(fields) duplicates_status = ds_logic.getOrCreateForProgram(program_entity) context['duplicates_status'] = duplicates_status if request.POST: post_data = request.POST # pass along these params as POST to the new task task_params = {'program_key': program_entity.key().id_or_name()} task_url = '/tasks/gsoc/proposal_duplicates/start' # checks if the task newly added is the first task # and must be performed repeatedly every hour or # just be performed once right away if 'calculate' in post_data: task_params['repeat'] = 'yes' elif 'recalculate' in post_data: task_params['repeat'] = 'no' # adds a new task new_task = taskqueue.Task(params=task_params, url=task_url) new_task.add() return helper.responses.respond(request, template=template, context=context)
def _getDefaultReviewContext(self, entity, org_admin, mentor): """Returns the default context for the review page. Args: entity: Student Proposal entity org_admin: org admin entity for the current user/proposal (iff available) mentor: mentor entity for the current user/proposal (iff available) """ from soc.modules.gsoc.logic.models.proposal_duplicates import logic \ as duplicates_logic from soc.modules.gsoc.logic.models.review_follower import logic as \ review_follower_logic context = {} context['student'] = entity.scope context['student_name'] = entity.scope.name() if entity.mentor: context['mentor_name'] = entity.mentor.name() else: context['mentor_name'] = "No mentor assigned" # set the possible mentors in the context possible_mentors = entity.possible_mentors if not possible_mentors: context['possible_mentors'] = "None" else: mentor_names = [] for mentor_key in possible_mentors: possible_mentor = mentor_logic.logic.getFromKeyName( mentor_key.id_or_name()) mentor_names.append(possible_mentor.name()) context['possible_mentors'] = ', '.join(mentor_names) # update the reviews context self._updateReviewsContext(context, entity) # update the scores context self._updateScoresContext(context, entity) if mentor: context['is_mentor'] = True if not entity.mentor or entity.mentor.key() != mentor.key(): # which button to (un)propose yourself as mentor should we show if mentor.key() in possible_mentors: # show "No longer willing to mentor" context['remove_me_as_mentor'] = True else: # show "I am willing to mentor" context['add_me_as_mentor'] = True if org_admin: context['is_org_admin'] = True # when the duplicates can be visible obtain the # duplicates for this proposal if entity.program.duplicates_visible: fields = {'student': entity.scope, 'is_duplicate': True} duplicate_entity = duplicates_logic.getForFields(fields, unique=True) if duplicate_entity: # this list also contains the current proposal # entity, so remove it duplicate_keys = duplicate_entity.duplicates duplicate_keys.remove(entity.key()) context['sp_duplicates'] = db.get(duplicate_keys) user_entity = user_logic.logic.getCurrentUser() # check if the current user is subscribed to public or private reviews fields = {'scope': entity, 'user': user_entity,} follower_entity = review_follower_logic.getForFields(fields, unique=True) if follower_entity: # pylint: disable=E1103 context['is_subscribed'] = follower_entity.subscribed_public return context
def calculate(request, *args, **kwargs): """Calculates the duplicate proposals in a given program for a student on a per Organization basis. Expects the following to be present in the POST dict: program_key: Specifies the program key name for which to find the duplicate proposals org_cursor: Specifies the organization datastore cursor from which to start the processing of finding the duplicate proposals Args: request: Django Request object """ from soc.modules.gsoc.logic.models.student_proposal import logic \ as sp_logic post_dict = request.POST program_key = post_dict.get('program_key') if not program_key: # invalid task data, log and return OK return error_handler.logErrorAndReturnOK('Invalid program key: %s' % post_dict) program_entity = program_logic.getFromKeyName(program_key) if not program_entity: # invalid program specified, log and return OK return error_handler.logErrorAndReturnOK( 'Invalid program specified: %s' % program_key) fields = {'scope': program_entity, 'slots >': 0, 'status': 'active'} # get the organization and update the cursor if possible q = org_logic.getQueryForFields(fields) # retrieve the org_cursor from POST data org_cursor = post_dict.get('org_cursor') if org_cursor: org_cursor = str(org_cursor) q.with_cursor(org_cursor) result = q.fetch(1) # update the cursor org_cursor = q.cursor() if result: org_entity = result[0] # get all the proposals likely to be accepted in the program accepted_proposals = sp_logic.getProposalsToBeAcceptedForOrg( org_entity) for ap in accepted_proposals: student_entity = ap.scope proposal_duplicate = pd_logic.getForFields( {'student': student_entity}, unique=True) if proposal_duplicate and ap.key( ) not in proposal_duplicate.duplicates: # non-counted (to-be) accepted proposal found pd_fields = { 'duplicates': proposal_duplicate.duplicates + [ap.key()], } pd_fields['is_duplicate'] = len(pd_fields['duplicates']) >= 2 if org_entity.key() not in proposal_duplicate.orgs: pd_fields['orgs'] = proposal_duplicate.orgs + [ org_entity.key() ] proposal_duplicate = pd_logic.updateEntityProperties( proposal_duplicate, pd_fields) else: pd_fields = { 'program': program_entity, 'student': student_entity, 'orgs': [org_entity.key()], 'duplicates': [ap.key()], 'is_duplicate': False } proposal_duplicate = pd_logic.updateOrCreateFromFields( pd_fields) # Adds a new task that performs duplicate calculation for # the next organization. task_params = { 'program_key': program_key, 'org_cursor': unicode(org_cursor) } task_url = '/tasks/gsoc/proposal_duplicates/calculate' new_task = taskqueue.Task(params=task_params, url=task_url) new_task.add() else: # There aren't any more organizations to process. So delete # all the proposals for which there are not more than one # proposal for duplicates property. pd_logic.deleteAllForProgram(program_entity, non_dupes_only=True) # update the proposal duplicate status and its timestamp pds_entity = pds_logic.getOrCreateForProgram(program_entity) new_fields = { 'status': 'idle', 'calculated_on': datetime.datetime.now() } pds_logic.updateEntityProperties(pds_entity, new_fields) # return OK return http.HttpResponse()
def getListProposalsData(self, request, params_collection, org_entity): """Returns the list data for listProposals. Args: request: HTTPRequest object params_collection: List of list Params indexed with the idx of the list org_entity: GSoCOrganization entity for which the lists are generated """ from soc.modules.gsoc.logic.models.proposal_duplicates import logic \ as pd_logic from soc.modules.gsoc.logic.models.ranker_root import logic \ as ranker_root_logic idx = lists.getListIndex(request) # default list settings args = [] visibility = None if idx == 0: filter = {'org': org_entity, 'status': 'new'} elif idx == 1: # retrieve the ranker fields = { 'link_id': student_proposal.DEF_RANKER_NAME, 'scope': org_entity } ranker_root = ranker_root_logic.getForFields(fields, unique=True) ranker = ranker_root_logic.getRootFromEntity(ranker_root) status = {} program_entity = org_entity.scope # only when the program allows allocations # we show that proposals are likely to be # accepted or rejected if program_entity.allocations_visible: proposals = sp_logic.getProposalsToBeAcceptedForOrg(org_entity) duplicate_proposals = [] # get all the duplicate entities if duplicates can be shown # to the organizations and make a list of all such proposals. if program_entity.duplicates_visible: duplicate_properties = { 'orgs': org_entity, 'is_duplicate': True } duplicates = pd_logic.getForFields(duplicate_properties) for duplicate in duplicates: duplicate_proposals.extend(duplicate.duplicates) for proposal in proposals: proposal_key = proposal.key() if proposal.status == 'pending' and proposal_key in duplicate_proposals: status[proposal_key] = """<strong><font color="red"> Duplicate</font></strong>""" else: status[proposal_key] = """<strong><font color="green"> Pending acceptance</font><strong>""" filter = { 'org': org_entity, 'status': ['accepted', 'pending', 'rejected'] } # some extras for the list args = [ranker, status] visibility = 'review' elif idx == 2: # check if the current user is a mentor user_entity = user_logic.getCurrentUser() fields = { 'user': user_entity, 'scope': org_entity, 'status': ['active', 'inactive'] } mentor_entity = mentor_logic.getForFields(fields, unique=True) filter = { 'org': org_entity, 'mentor': mentor_entity, 'status': ['pending', 'accepted', 'rejected'] } elif idx == 3: filter = {'org': org_entity, 'status': 'invalid'} else: return lists.getErrorResponse(request, "idx not valid") params = params_collection[idx] contents = helper.lists.getListData(request, params, filter, visibility=visibility, args=args) return lists.getResponse(request, contents)
def getListProposalsData(self, request, params_collection, org_entity): """Returns the list data for listProposals. Args: request: HTTPRequest object params_collection: List of list Params indexed with the idx of the list org_entity: GSoCOrganization entity for which the lists are generated """ from soc.modules.gsoc.logic.models.proposal_duplicates import logic \ as pd_logic from soc.modules.gsoc.logic.models.ranker_root import logic \ as ranker_root_logic idx = lists.getListIndex(request) # default list settings args = [] visibility = None if idx == 0: filter = {'org': org_entity, 'status': 'new'} elif idx == 1: # retrieve the ranker fields = {'link_id': student_proposal.DEF_RANKER_NAME, 'scope': org_entity} ranker_root = ranker_root_logic.getForFields(fields, unique=True) ranker = ranker_root_logic.getRootFromEntity(ranker_root) status = {} program_entity = org_entity.scope # only when the program allows allocations # we show that proposals are likely to be # accepted or rejected if program_entity.allocations_visible: proposals = sp_logic.getProposalsToBeAcceptedForOrg(org_entity) duplicate_proposals = [] # get all the duplicate entities if duplicates can be shown # to the organizations and make a list of all such proposals. if program_entity.duplicates_visible: duplicate_properties = { 'orgs': org_entity, 'is_duplicate': True } duplicates = pd_logic.getForFields(duplicate_properties) for duplicate in duplicates: duplicate_proposals.extend(duplicate.duplicates) for proposal in proposals: proposal_key = proposal.key() if proposal.status == 'pending' and proposal_key in duplicate_proposals: status[proposal_key] = """<strong><font color="red"> Duplicate</font></strong>""" else: status[proposal_key] = """<strong><font color="green"> Pending acceptance</font><strong>""" filter = {'org': org_entity, 'status': ['accepted','pending','rejected']} # some extras for the list args = [ranker, status] visibility = 'review' elif idx == 2: # check if the current user is a mentor user_entity = user_logic.getCurrentUser() fields = {'user': user_entity, 'scope': org_entity, 'status': ['active', 'inactive']} mentor_entity = mentor_logic.getForFields(fields, unique=True) filter = {'org': org_entity, 'mentor': mentor_entity, 'status': ['pending', 'accepted', 'rejected']} elif idx == 3: filter = {'org': org_entity, 'status': 'invalid'} else: return lists.getErrorResponse(request, "idx not valid") params = params_collection[idx] contents = helper.lists.getListData(request, params, filter, visibility=visibility, args=args) return lists.getResponse(request, contents)
def showDuplicates(self, request, access_type, page_name=None, params=None, **kwargs): """View in which a host can see which students have been assigned multiple slots. For params see base.view.Public(). """ from django.utils import simplejson from soc.modules.gsoc.logic.models.proposal_duplicates import logic as duplicates_logic program_entity = program_logic.getFromKeyFieldsOr404(kwargs) if request.POST and request.POST.get('result'): # store result in the datastore fields = { 'link_id': program_entity.link_id, 'scope': program_entity, 'scope_path': program_entity.key().id_or_name(), 'json_representation': request.POST['result'] } key_name = duplicates_logic.getKeyNameFromFields(fields) duplicates_logic.updateOrCreateFromKeyName(fields, key_name) response = simplejson.dumps({'status': 'done'}) return http.HttpResponse(response) context = helper.responses.getUniversalContext(request) helper.responses.useJavaScript(context, params['js_uses_all']) context['uses_duplicates'] = True context['uses_json'] = True context['page_name'] = page_name # get all orgs for this program who are active and have slots assigned fields = {'scope': program_entity, 'slots >': 0, 'status': 'active'} query = org_logic.logic.getQueryForFields(fields) to_json = { 'nr_of_orgs': query.count(), 'program_key': program_entity.key().id_or_name() } json = simplejson.dumps(to_json) context['info'] = json context['offset_length'] = 10 fields = {'link_id': program_entity.link_id, 'scope': program_entity} duplicates = duplicates_logic.getForFields(fields, unique=True) if duplicates: # we have stored information # pylint: disable-msg=E1103 context['duplicate_cache_content'] = duplicates.json_representation context['date_of_calculation'] = duplicates.calculated_on else: # no information stored context['duplicate_cache_content'] = simplejson.dumps({}) template = 'soc/program/show_duplicates.html' return helper.responses.respond(request, template=template, context=context)
def calculate(request, *args, **kwargs): """Calculates the duplicate proposals in a given program for a student on a per Organization basis. Expects the following to be present in the POST dict: program_key: Specifies the program key name for which to find the duplicate proposals org_cursor: Specifies the organization datastore cursor from which to start the processing of finding the duplicate proposals Args: request: Django Request object """ from soc.modules.gsoc.logic.models.student_proposal import logic \ as sp_logic post_dict = request.POST program_key = post_dict.get('program_key') if not program_key: # invalid task data, log and return OK return error_handler.logErrorAndReturnOK( 'Invalid program key: %s' % post_dict) program_entity = program_logic.getFromKeyName(program_key) if not program_entity: # invalid program specified, log and return OK return error_handler.logErrorAndReturnOK( 'Invalid program specified: %s' % program_key) fields = {'scope': program_entity, 'slots >': 0, 'status': 'active'} # get the organization and update the cursor if possible q = org_logic.getQueryForFields(fields) # retrieve the org_cursor from POST data org_cursor = post_dict.get('org_cursor') if org_cursor: org_cursor = str(org_cursor) q.with_cursor(org_cursor) result = q.fetch(1) # update the cursor org_cursor = q.cursor() if result: org_entity = result[0] # get all the proposals likely to be accepted in the program accepted_proposals = sp_logic.getProposalsToBeAcceptedForOrg(org_entity) for ap in accepted_proposals: student_entity = ap.scope proposal_duplicate = pd_logic.getForFields({'student': student_entity}, unique=True) if proposal_duplicate and ap.key() not in proposal_duplicate.duplicates: # non-counted (to-be) accepted proposal found pd_fields = { 'duplicates': proposal_duplicate.duplicates + [ap.key()], } pd_fields['is_duplicate'] = len(pd_fields['duplicates']) >= 2 if org_entity.key() not in proposal_duplicate.orgs: pd_fields['orgs'] = proposal_duplicate.orgs + [org_entity.key()] proposal_duplicate = pd_logic.updateEntityProperties( proposal_duplicate, pd_fields) else: pd_fields = { 'program': program_entity, 'student': student_entity, 'orgs':[org_entity.key()], 'duplicates': [ap.key()], 'is_duplicate': False } proposal_duplicate = pd_logic.updateOrCreateFromFields(pd_fields) # Adds a new task that performs duplicate calculation for # the next organization. task_params = {'program_key': program_key, 'org_cursor': unicode(org_cursor)} task_url = '/tasks/gsoc/proposal_duplicates/calculate' new_task = taskqueue.Task(params=task_params, url=task_url) new_task.add() else: # There aren't any more organizations to process. So delete # all the proposals for which there are not more than one # proposal for duplicates property. pd_logic.deleteAllForProgram(program_entity, non_dupes_only=True) # update the proposal duplicate status and its timestamp pds_entity = pds_logic.getOrCreateForProgram(program_entity) new_fields = {'status': 'idle', 'calculated_on': datetime.datetime.now()} pds_logic.updateEntityProperties(pds_entity, new_fields) # return OK return http.HttpResponse()