def get(self, linkKeyStr): logging.debug('linkKeyStr=' + linkKeyStr) # Collect inputs cursor = self.request.get('cursor', None) cursor = Cursor(urlsafe=cursor) if cursor else None getReasons = (self.request.get('getReasons', 'true') == 'true') logging.debug('getReasons=' + str(getReasons)) httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} cookieData = httpServer.validate(self.request, self.request.GET, responseData, self.response, idRequired=False) userId = cookieData.id() # Retrieve requestId from linkKey. destinationType must be RequestForProposals. linkKeyRecord = linkKey.LinkKey.get_by_id(linkKeyStr) logging.debug('GetRequestData.get() linkKeyRecord=' + str(linkKeyRecord)) if (linkKeyRecord == None) or (linkKeyRecord.destinationType != conf.REQUEST_CLASS_NAME): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) # Retrieve RequestForProposal by id, filter/transform fields for display. requestId = linkKeyRecord.destinationId requestRecordFuture = requestForProposals.RequestForProposals.get_by_id_async( int(requestId)) requestRecord = requestRecordFuture.get_result( ) if requestRecordFuture else None logging.debug('GetRequestData.get() userId=' + str(userId) + ' requestRecord.creator=' + str(requestRecord.creator)) # If userId exists... async-retrieve user's ReasonVotes by KeyProperty requestId x userId. voteRecordsFuture = None if getReasons and userId: voteRecordsFuture = reasonVote.ReasonVote.query( reasonVote.ReasonVote.requestId == requestId, reasonVote.ReasonVote.userId == userId).fetch_async() # Retrieve Proposals by KeyProperty requestId # Get all data up to current page maximum length. + Refreshes earlier proposal data. maxProposals = const.INITIAL_MAX_PROPOSALS proposalRecords, cursor, hasMore = proposal.retrieveTopProposals( requestId, maxProposals, cursor=cursor) cursor = cursor.urlsafe() if cursor else None # Async-retrieve top N reasons per proposal, equal number of pro/con reasons reasonRecordsFutures = [] if getReasons: for proposalRec in proposalRecords: maxReasonsPerType = conf.MAX_TOP_REASONS / 2 proRecordsFuture, conRecordsFuture = reason.retrieveTopReasonsAsync( proposalRec.key.id(), maxReasonsPerType) reasonRecordsFutures.append(proRecordsFuture) reasonRecordsFutures.append(conRecordsFuture) # Wait for parallel retrievals logging.debug('GetRequestData.get() requestRecord=' + str(requestRecord)) reasonRecords = [] for reasonRecordsFuture in reasonRecordsFutures: reasonRecordsForProp, cursor, hasMore = reasonRecordsFuture.get_result( ) logging.debug('GetRequestData.get() reasonRecordsForProp=' + str(reasonRecordsForProp)) reasonRecords.extend(reasonRecordsForProp) reasonRecords = sorted(reasonRecords, key=lambda r: -r.score) logging.debug('GetRequestData.get() reasonRecords=' + str(reasonRecords)) voteRecords = voteRecordsFuture.get_result( ) if voteRecordsFuture else [] logging.debug('GetRequestData.get() voteRecords=' + str(voteRecords)) # Transform records for display. linkKeyDisp = httpServer.linkKeyToDisplay(linkKeyRecord) logging.debug('GetRequestData.get() linkKeyDisp=' + str(linkKeyDisp)) requestDisp = httpServer.requestToDisplay(requestRecord, userId) logging.debug('GetRequestData.get() requestDisp=' + str(requestDisp)) proposalDisps = [ httpServer.proposalToDisplay(p, userId, requestRecord=requestRecord) for p in proposalRecords ] logging.debug('GetRequestData.get() proposalDisps=' + str(proposalDisps)) reasonDisps = [ httpServer.reasonToDisplay(r, userId) for r in reasonRecords ] logging.debug('GetRequestData.get() reasonDisps=' + str(reasonDisps)) # For each reason... collect user vote in reason.myVote reasonToVoteRec = {v.reasonId: v for v in voteRecords} if voteRecords else {} logging.debug('GetRequestData.get() reasonToVoteRec=' + str(reasonToVoteRec)) for r in reasonDisps: voteRec = reasonToVoteRec.get(r['id']) r['myVote'] = voteRec.voteUp if voteRec else False # Store request to user's recent requests (cookie). user.storeRecentLinkKey(linkKeyStr, cookieData) # Display request data. responseData = { 'success': True, 'linkKey': linkKeyDisp, 'request': requestDisp, 'proposals': proposalDisps, 'reasons': reasonDisps, 'maxProposals': maxProposals, 'cursor': cursor, } logging.debug( 'GetRequestData.get() responseData=' + json.dumps(responseData, indent=4, separators=(', ', ':'))) httpServer.outputJson(cookieData, responseData, self.response)
def get(self, linkKeyStr, proposalId): logging.debug('getProposalData.GetProposalData() linkKeyStr=' + linkKeyStr + ' proposalId=' + str(proposalId)) # Collect inputs. onlyTopReasons = (self.request.get('onlyTop', None) == 'true') logging.debug('onlyTopReasons=' + str(onlyTopReasons)) httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} cookieData = httpServer.validate(self.request, self.request.GET, responseData, self.response, idRequired=False) userId = cookieData.id() # Retrieve and check linkKey. linkKeyRecord = linkKey.LinkKey.get_by_id(linkKeyStr) requestId = None if linkKeyRecord is None: logging.debug('linkKeyRecord is None') return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) elif linkKeyRecord.destinationType == conf.PROPOSAL_CLASS_NAME: proposalId = linkKeyRecord.destinationId elif linkKeyRecord.destinationType == conf.REQUEST_CLASS_NAME: requestId = linkKeyRecord.destinationId else: logging.debug('linkKeyRecord has unhandled destinationType=' + str(linkKeyRecord.destinationType)) return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) # Retrieve Proposal by id, filter/transform fields for display. proposalRecord = proposal.Proposal.get_by_id(int(proposalId)) logging.debug('GetProposalData() proposalRecord=' + str(proposalRecord)) if proposalRecord.requestId != requestId: logging.debug('proposalRecord.requestId=' + str(proposalRecord.requestId) + ' != requestId=' + str(requestId)) return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) proposalDisp = httpServer.proposalToDisplay(proposalRecord, userId) logging.debug('GetProposalData() proposalDisp=' + str(proposalDisp)) # Prepare parallel retrieve reasons, with votes. if onlyTopReasons: reasonRecordsFutures = reason.retrieveTopReasonsAsync( proposalId, conf.MAX_TOP_REASONS) else: reasonRecordsFutures = [ reason.Reason.query( reason.Reason.proposalId == proposalId).fetch_async() ] # If userId exists... retrieve user's ReasonVotes by KeyProperty proposalId x userId. voteRecordsFuture = reasonVote.ReasonVote.query( reasonVote.ReasonVote.proposalId == proposalId, reasonVote.ReasonVote.userId == userId).fetch_async() if userId else None # Run parallel retrieval. Filter/transform fields for display. linkKeyDisplay = httpServer.linkKeyToDisplay(linkKeyRecord) logging.debug('GetProposalData() linkKeyDisplay=' + str(linkKeyDisplay)) reasonRecords = reason.fetchReasonRecordsFutures(reasonRecordsFutures) logging.debug('GetProposalData() reasonRecords=' + str(reasonRecords)) reasonDisps = [ httpServer.reasonToDisplay(r, userId) for r in reasonRecords ] logging.debug('GetProposalData() reasonDisps=' + str(reasonDisps)) voteRecords = voteRecordsFuture.get_result( ) if voteRecordsFuture else None logging.debug('GetProposalData() voteRecords=' + str(voteRecords)) # For each reason... lookup user vote ... set reason.myVote reasonToVoteRec = {v.reasonId: v for v in voteRecords} if voteRecords else {} logging.debug('GetProposalData() reasonToVoteRec=' + str(reasonToVoteRec)) for r in reasonDisps: voteRec = reasonToVoteRec.get(r['id']) r['myVote'] = voteRec.voteUp if voteRec else False # Store proposal to user's recent (cookie). user.storeRecentLinkKey(linkKeyStr, cookieData) # Display proposal data. responseData = { 'success': True, 'linkKey': linkKeyDisplay, 'proposal': proposalDisp, 'reasons': reasonDisps, } logging.debug( 'GetProposalData() responseData=' + json.dumps(responseData, indent=4, separators=(', ', ':'))) httpServer.outputJson(cookieData, responseData, self.response)
def post(self, linkKeyStr): logging.debug('linkKeyStr=' + linkKeyStr) # Collect inputs inputData = json.loads(self.request.body) if conf.isDev: logging.debug('SuggestReasons.post() inputData=' + str(inputData)) content = text.formTextToStored(inputData.get('content', '')) httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} cookieData = httpServer.validate(self.request, self.request.GET, responseData, self.response, idRequired=False) userId = cookieData.id() if not content: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='Empty input') # Retrieve link-record linkKeyRecord = linkKey.LinkKey.get_by_id(linkKeyStr) if (linkKeyRecord == None) or (linkKeyRecord.destinationType != conf.REQUEST_CLASS_NAME): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) requestId = linkKeyRecord.destinationId # Retrieve RequestForProposal requestRecord = requestForProposals.RequestForProposals.get_by_id( int(requestId)) if requestRecord and (requestRecord.freezeUserInput): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.FROZEN) # Retrieve Proposals proposalRecords = proposal.retrieveTopProposalsForStart( requestRecord.key.id(), content) linkKeyDisp = httpServer.linkKeyToDisplay(linkKeyRecord) requestDisp = httpServer.requestToDisplay(requestRecord, userId) proposalDisps = [ httpServer.proposalToDisplay(p, userId, requestRecord=requestRecord) for p in proposalRecords ] # Display responseData = { 'success': True, 'linkKey': linkKeyDisp, 'request': requestDisp, 'proposals': proposalDisps } httpServer.outputJson(cookieData, responseData, self.response)
def get(self, linkKeyStr, proposalId): if conf.isDev: logging.debug('TopReasons.get() linkKeyStr=' + str(linkKeyStr) + ' proposalId=' + str(proposalId)) # Collect inputs preview = self.request.get('preview', None) is not None cursorPro = self.request.get('cursorPro', None) cursorPro = Cursor(urlsafe=cursorPro) if cursorPro else None cursorCon = self.request.get('cursorCon', None) cursorCon = Cursor(urlsafe=cursorCon) if cursorCon else None httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} cookieData = httpServer.validate(self.request, self.request.GET, responseData, self.response, idRequired=False) userId = cookieData.id() # Retrieve top-level records (linkKeyRecord, proposalRecord, requestRecord) = retrieveRequestAndProposal(linkKeyStr, proposalId) if not linkKeyRecord: return # Bad link proposalId = str(proposalRecord.key.id()) # Retrieve reasons and vote, in parallel voteRecordsFuture = reasonVote.ReasonVote.query( reasonVote.ReasonVote.proposalId == proposalId, reasonVote.ReasonVote.userId == userId).fetch_async() if userId else None maxReasonsPerType = (conf.MAX_TOP_REASONS / 2) if preview else 10 proRecordsFuture, conRecordsFuture = reason.retrieveTopReasonsAsync( proposalId, maxReasonsPerType, cursorPro=cursorPro, cursorCon=cursorCon) proRecords, cursorPro, morePros = proRecordsFuture.get_result() conRecords, cursorCon, moreCons = conRecordsFuture.get_result() cursorPro = cursorPro.urlsafe() if cursorPro else None cursorCon = cursorCon.urlsafe() if cursorCon else None voteRecords = voteRecordsFuture.get_result( ) if voteRecordsFuture else None # Filter/transform fields for display linkKeyDisplay = httpServer.linkKeyToDisplay(linkKeyRecord) proposalDisp = httpServer.proposalToDisplay( proposalRecord, userId, requestRecord=requestRecord) reasonDisps = [ httpServer.reasonToDisplay(r, userId) for r in (proRecords + conRecords) ] mergeReasonVotes(voteRecords, reasonDisps) # Store request/proposal to user's recent (cookie) user.storeRecentLinkKey(linkKeyStr, cookieData) # Display proposal data responseData = { 'success': True, 'linkKey': linkKeyDisplay, 'proposal': proposalDisp, 'reasons': reasonDisps, 'cursorPro': cursorPro, 'cursorCon': cursorCon } httpServer.outputJson(cookieData, responseData, self.response)
def post(self): logging.debug('SubmitNewProposalForRequest.post() request.body=' + self.request.body) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'requestLogId': requestLogId} inputData = json.loads(self.request.body) logging.debug('SubmitNewProposalForRequest.post() inputData=' + str(inputData)) requestLinkKeyStr = inputData['requestId'] title = text.formTextToStored(inputData['title']) detail = text.formTextToStored(inputData['detail']) initialReason1 = text.formTextToStored( inputData.get('initialReason1', None)) initialReason2 = text.formTextToStored( inputData.get('initialReason2', None)) initialReason3 = text.formTextToStored( inputData.get('initialReason3', None)) browserCrumb = inputData['crumb'] loginCrumb = inputData.get('crumbForLogin', '') logging.debug('SubmitNewProposalForRequest.post() requestLinkKeyStr=' + str(requestLinkKeyStr) + ' title=' + str(title) + ' detail=' + str(detail) + ' browserCrumb=' + str(browserCrumb) + ' loginCrumb=' + str(loginCrumb)) cookieData = httpServer.validate(self.request, inputData, responseData, self.response) if not cookieData.valid(): return userId = cookieData.id() # Check proposal length if not httpServer.isLengthOk(title, detail, conf.minLengthProposal): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT) initialReasons = [ r for r in [initialReason1, initialReason2, initialReason3] if r is not None ] for initialReason in initialReasons: if initialReason is not None and not httpServer.isLengthOk( initialReason, None, conf.minLengthReason): httpServer.outputJsonError(conf.REASON_TOO_SHORT, responseData, self.response) return # Require link-key, and convert it to requestId. if requestLinkKeyStr is None: httpServer.outputJsonError('requestLinkKeyStr is null', responseData, self.response) return requestLinkKeyRec = linkKey.LinkKey.get_by_id(requestLinkKeyStr) logging.debug('SubmitNewProposalForRequest.post() requestLinkKeyRec=' + str(requestLinkKeyRec)) if requestLinkKeyRec is None: httpServer.outputJsonError('requestLinkKey not found', responseData, self.response) return if requestLinkKeyRec.destinationType != conf.REQUEST_CLASS_NAME: httpServer.outputJsonError('requestLinkKey not a request', responseData, self.response) return requestId = requestLinkKeyRec.destinationId if requestLinkKeyRec.loginRequired and not cookieData.loginId: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.NO_LOGIN) # Get user id from cookie requestRec = requestForProposals.RequestForProposals.get_by_id( int(requestId)) if not requestRec: return # Construct new proposal record proposalRecord = proposal.Proposal( requestId=requestId, creator=userId, title=title, detail=detail, allowEdit=(len(initialReasons) == 0)) # Store proposal record proposalRecordKey = proposalRecord.put() proposalId = str(proposalRecordKey.id()) logging.debug('proposalRecordKey.id={}'.format(proposalRecordKey.id())) # For each initial reason... reasonDisplays = [] for initialReason in initialReasons: # Construct new reason record. reasonRecord = reason.Reason(requestId=requestId, proposalId=proposalId, creator=userId, proOrCon=conf.PRO, content=initialReason, allowEdit=True) # Store reason record. reasonRecordKey = reasonRecord.put() logging.debug('reasonRecordKey={}'.format(reasonRecordKey)) # Convert reason for display. reasonDisplays.append( httpServer.reasonToDisplay(reasonRecord, userId)) # Display proposal. proposalDisplay = httpServer.proposalToDisplay(proposalRecord, userId) responseData.update({ 'success': True, 'proposal': proposalDisplay, 'reasons': reasonDisplays }) httpServer.outputJson(cookieData, responseData, self.response) # Mark request-for-proposals as not editable. if (requestRec.allowEdit): requestForProposals.setEditable(requestId, False)
def post(self): logging.debug('SubmitEditProposal.post() request.body=' + self.request.body) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) inputData = json.loads(self.request.body) logging.debug('SubmitEditProposal.post() inputData=' + str(inputData)) title = text.formTextToStored(inputData['title']) detail = text.formTextToStored(inputData['detail']) linkKeyString = inputData['linkKey'] proposalId = str(int(inputData['proposalId'])) browserCrumb = inputData['crumb'] loginCrumb = inputData.get('crumbForLogin', '') logging.debug('SubmitEditProposal.post() title=' + str(title) + ' detail=' + str(detail) + ' browserCrumb=' + str(browserCrumb) + ' loginCrumb=' + str(loginCrumb) + ' linkKeyString=' + str(linkKeyString) + ' proposalId=' + str(proposalId)) # User id from cookie, crumb... responseData = {'success': False, 'requestLogId': requestLogId} cookieData = httpServer.validate(self.request, inputData, responseData, self.response) if not cookieData.valid(): return userId = cookieData.id() # Check proposal length if not httpServer.isLengthOk(title, detail, conf.minLengthProposal): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT) # Require link-key. linkKeyRec = linkKey.LinkKey.get_by_id(linkKeyString) logging.debug('SubmitEditProposal.post() linkKeyRec=' + str(linkKeyRec)) if linkKeyRec is None: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='linkKey not found') if linkKeyRec.loginRequired and not cookieData.loginId: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.NO_LOGIN) # Retrieve proposal record. proposalRec = proposal.Proposal.get_by_id(int(proposalId)) logging.debug('SubmitEditProposal.post() proposalRec=' + str(proposalRec)) if proposalRec is None: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='proposal not found') # Verify that proposal matches link-key. if linkKeyRec.destinationType == conf.REQUEST_CLASS_NAME: if proposalRec.requestId != linkKeyRec.destinationId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage= 'proposalRec.requestId != linkKeyRec.destinationId') elif linkKeyRec.destinationType == conf.PROPOSAL_CLASS_NAME: if proposalId != linkKeyRec.destinationId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='proposalId != linkKeyRec.destinationId') else: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='linkKey destinationType=' + str(linkKeyRec.destinationType)) # Verify that proposal is editable if userId != proposalRec.creator: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.NOT_OWNER) if not proposalRec.allowEdit: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.HAS_RESPONSES) # Update proposal record. proposalRec.title = title proposalRec.detail = detail proposalRec.put() # Display updated proposal. proposalDisplay = httpServer.proposalToDisplay(proposalRec, userId) responseData.update({'success': True, 'proposal': proposalDisplay}) httpServer.outputJson(cookieData, responseData, self.response)
def post(self): logging.debug('SubmitNewProposal.post() request.body=' + self.request.body) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'requestLogId': requestLogId} inputData = json.loads(self.request.body) logging.debug('SubmitNewProposal.post() inputData=' + str(inputData)) title = text.formTextToStored(inputData.get('title', '')) detail = text.formTextToStored(inputData.get('detail', '')) loginRequired = inputData.get('loginRequired', False) browserCrumb = inputData.get('crumb', '') loginCrumb = inputData.get('crumbForLogin', '') logging.debug('SubmitNewProposal.post() title=' + str(title) + ' detail=' + str(detail) + ' browserCrumb=' + str(browserCrumb) + ' loginCrumb=' + str(loginCrumb) + ' loginRequired=' + str(loginRequired)) # Voter login not required to create initial proposal, though login may be required to use proposal cookieData = httpServer.validate(self.request, inputData, responseData, self.response, loginRequired=loginRequired) if not cookieData.valid(): return userId = cookieData.id() # Check proposal length. if not httpServer.isLengthOk(title, detail, conf.minLengthProposal): return httpServer.outputJson(responseData, self.response, errorMessage=conf.TOO_SHORT) # Construct new proposal record. proposalRecord = proposal.Proposal( creator=userId, title=title, detail=detail, allowEdit=True, ) # Store proposal record. proposalRecordKey = proposalRecord.put() logging.debug('proposalRecordKey.id={}'.format(proposalRecordKey.id())) # Construct and store link key. proposalId = str(proposalRecordKey.id()) proposalLinkKeyRecord = httpServer.createAndStoreLinkKey( conf.PROPOSAL_CLASS_NAME, proposalId, loginRequired, cookieData) # Display proposal linkKeyDisplay = httpServer.linkKeyToDisplay(proposalLinkKeyRecord) proposalDisplay = httpServer.proposalToDisplay(proposalRecord, userId) responseData.update({ 'success': True, 'linkKey': linkKeyDisplay, 'proposal': proposalDisplay }) httpServer.outputJson(cookieData, responseData, self.response)