def get(self, linkKeyStr): if conf.isDev: logging.debug('SlicesFromCreator.get() linkKeyStr=' + str(linkKeyStr)) # Collect inputs httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} cookieData = httpServer.validate(self.request, self.request.GET, responseData, self.response, crumbRequired=False, signatureRequired=False) if not cookieData.valid(): return userId = cookieData.id() # Retrieve and check linkKey linkKeyRecord = linkKey.LinkKey.get_by_id(linkKeyStr) if conf.isDev: logging.debug('SlicesFromCreator.get() linkKeyRecord=' + str(linkKeyRecord)) if (linkKeyRecord is None) or (linkKeyRecord.destinationType != conf.BUDGET_CLASS_NAME): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) budgetId = linkKeyRecord.destinationId # Check that user is budget-creator budgetRecord = budget.Budget.get_by_id(int(budgetId)) if conf.isDev: logging.debug('SlicesFromCreator.get() budgetRecord=' + str(budgetRecord)) if budgetRecord is None: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='budget is null') if (budgetRecord.creator != userId): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.NOT_OWNER) # Retrieve all slices for this budget and creator sliceRecords = slice.Slice.query( slice.Slice.budgetId == budgetId, slice.Slice.creator == userId, slice.Slice.fromEditPage == True).fetch() slicesByTitle = sorted(sliceRecords, key=lambda a: a.title) sliceDisplays = [ httpServerBudget.sliceToDisplay(a, userId) for a in slicesByTitle ] # Display slices data responseData = {'success': True, 'slices': sliceDisplays} httpServer.outputJson(cookieData, responseData, self.response)
def get( self, linkKeyStr ): logging.debug( 'getBudget.GetBudget() linkKeyStr=' + linkKeyStr ) # Collect inputs 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 ) if (linkKeyRecord is None) or (linkKeyRecord.destinationType != conf.BUDGET_CLASS_NAME): return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.BAD_LINK ) budgetId = linkKeyRecord.destinationId # Retrieve Budget by id, filter/transform fields for display. budgetRecord = budget.Budget.get_by_id( int(budgetId) ) logging.debug( 'GetBudgetData() budgetRecord=' + str(budgetRecord) ) budgetDisp = httpServerBudget.budgetToDisplay( budgetRecord, userId ) logging.debug( 'GetBudgetData() budgetDisp=' + str(budgetDisp) ) linkKeyDisplay = httpServer.linkKeyToDisplay( linkKeyRecord ) # Store budget to user's recent (cookie). user.storeRecentLinkKey( linkKeyStr, cookieData ) # Display budget data. responseData = { 'success':True , 'linkKey':linkKeyDisplay , 'budget':budgetDisp } httpServer.outputJson( cookieData, responseData, self.response )
def post(self): logging.debug('DeleteAnswer.post() request.body=' + self.request.body) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) inputData = json.loads(self.request.body) logging.debug('DeleteAnswer.post() inputData=' + str(inputData)) linkKeyString = inputData['linkKey'] answerId = inputData['answerId'] browserCrumb = inputData.get('crumb', None) logging.debug('DeleteAnswer.post() browserCrumb=' + str(browserCrumb) + ' linkKeyString=' + str(linkKeyString) + ' answerId=' + str(answerId)) responseData = {'success': False, 'requestLogId': requestLogId} cookieData = httpServer.validate(self.request, inputData, responseData, self.response) if not cookieData.valid(): return userId = cookieData.id() surveyId, loginRequired = retrieveSurveyIdFromLinkKey( cookieData, linkKeyString, responseData, self.response) if surveyId is None: return # Retrieve survey record to check survey creator surveyRec = survey.Survey.get_by_id(int(surveyId)) if surveyRec is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='survey record not found') if surveyRec.creator != userId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='surveyRec.creator != userId') # Delete old answer. if answerId: oldAnswerRec = answer.Answer.get_by_id(answerId) if oldAnswerRec is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='answer record not found') if oldAnswerRec.surveyId != surveyId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='oldAnswerRec.surveyId != surveyId') oldAnswerRec.key.delete() # Display result. responseData.update({'success': True}) httpServer.outputJson(cookieData, responseData, self.response)
def post(self): logging.debug( 'SignLoginRequest.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( 'SignLoginRequest.post() inputData=' + str(inputData) ) loginRequestId = user.randomStringWithLength( conf.VOTER_ID_LOGIN_REQUEST_ID_LENGTH ) signature = user.signLoginRequest( loginRequestId ) # Store login-request-id, to later check against spoofed login-return cookieData = httpServer.validate( self.request, inputData, responseData, self.response ) if not cookieData.browserId: return browserId = cookieData.browserId browserRecord = browser.BrowserRecord.get_by_id( browserId ) logging.debug( 'LoginReturn.post() browserRecord=' + str(browserRecord) ) if not browserRecord: browserRecord = browser.BrowserRecord( id=browserId ) now = int( time.time() ) browserRecord.loginRequestTime = now browserRecord.voterLoginRequestId = loginRequestId browserRecord.put() responseData.update( { 'success':True , 'loginRequestId':loginRequestId, 'signature':signature } ) httpServer.outputJson( cookieData, responseData, self.response )
def post(self): logging.debug('self.request=' + str(self.request)) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'requestLogId': requestLogId} inputData = json.loads(self.request.body) # Set cookie with signature based on browser-fingerprint from javascript cookieData = httpServer.validate(self.request, inputData, responseData, self.response, idRequired=False, makeValid=True) if not cookieData.valid(): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='cookieData invalid') responseData['success'] = True responseData['crumb'] = user.createCrumb( cookieData.browserId) if cookieData.valid() else '' httpServer.outputJson(cookieData, responseData, self.response)
def post(self, linkKeyStr, proposalId): if conf.isDev: logging.debug('SuggestReasons.post() linkKeyStr=' + str(linkKeyStr) + ' proposalId=' + str(proposalId)) # Collect inputs inputData = json.loads(self.request.body) if conf.isDev: logging.debug('SuggestReasons.post() inputData=' + str(inputData)) reasonStart = 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 reasonStart: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='Empty input') # Retrieve top-level records (linkKeyRecord, proposalRecord, requestRecord) = retrieveRequestAndProposal(linkKeyStr, proposalId) if not linkKeyRecord: return # Bad link proposalId = str(proposalRecord.key.id()) if proposalRecord.freezeUserInput or (requestRecord and requestRecord.freezeUserInput): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.FROZEN) # Retrieve reasons and vote, in parallel voteRecordsFuture = reasonVote.ReasonVote.query( reasonVote.ReasonVote.proposalId == proposalId, reasonVote.ReasonVote.userId == userId).fetch_async() if userId else None reasonRecords = reason.retrieveTopReasonsForStart( proposalId, reasonStart) # Retrieve reasons while vote-record is retrieving voteRecords = voteRecordsFuture.get_result( ) if voteRecordsFuture else None # Filter/transform fields for display reasonDisps = [ httpServer.reasonToDisplay(r, userId) for r in reasonRecords ] mergeReasonVotes(voteRecords, reasonDisps) # Display proposal data responseData = {'success': True, 'reasons': reasonDisps} httpServer.outputJson(cookieData, responseData, self.response)
def post(self): logging.debug('SubmitNewSurvey.post() request.body=' + self.request.body) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) inputData = json.loads(self.request.body) logging.debug('SubmitNewSurvey.post() inputData=' + str(inputData)) introduction = text.formTextToStored(inputData.get('introduction', '')) browserCrumb = inputData.get('crumb', '') loginCrumb = inputData.get('crumbForLogin', '') loginRequired = inputData.get('loginRequired', False) logging.debug('SubmitNewSurvey.post() introduction=' + str(introduction) + ' browserCrumb=' + str(browserCrumb) + ' loginCrumb=' + str(loginCrumb) + ' loginRequired=' + str(loginRequired)) responseData = {'success': False, 'requestLogId': requestLogId} cookieData = httpServer.validate(self.request, inputData, responseData, self.response, loginRequired=loginRequired) if not cookieData.valid(): return userId = cookieData.id() # Check survey introduction length. if not httpServer.isLengthOk(introduction, '', conf.minLengthSurveyIntro): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT) # Construct and store new survey record. surveyRecord = survey.Survey(creator=userId, introduction=introduction, allowEdit=True) surveyRecordKey = surveyRecord.put() logging.debug('surveyRecordKey.id={}'.format(surveyRecordKey.id())) # Construct and store link key. surveyId = str(surveyRecordKey.id()) linkKeyRecord = httpServer.createAndStoreLinkKey( conf.SURVEY_CLASS_NAME, surveyId, loginRequired, cookieData) # Display survey. surveyDisplay = httpServerAutocomplete.surveyToDisplay( surveyRecord, userId) linkKeyDisplay = httpServer.linkKeyToDisplay(linkKeyRecord) responseData.update({ 'success': True, 'linkKey': linkKeyDisplay, 'survey': surveyDisplay }) httpServer.outputJson(cookieData, responseData, self.response)
def post(self): logging.debug( 'SubmitEditSurvey.post() request.body=' + self.request.body ) # Collect inputs requestLogId = os.environ.get( conf.REQUEST_LOG_ID ) inputData = json.loads( self.request.body ) logging.debug( 'SubmitEditSurvey.post() inputData=' + str(inputData) ) title = text.formTextToStored( inputData['title'] ) introduction = text.formTextToStored( inputData['introduction'] ) linkKeyString = inputData['linkKey'] browserCrumb = inputData.get( 'crumb', '' ) loginCrumb = inputData.get( 'crumbForLogin', '' ) logging.debug( 'SubmitEditSurvey.post() introduction=' + str(introduction) + ' browserCrumb=' + str(browserCrumb) + ' loginCrumb=' + str(loginCrumb) + ' linkKeyString=' + str(linkKeyString) ) responseData = { 'success':False, 'requestLogId':requestLogId } cookieData = httpServer.validate( self.request, inputData, responseData, self.response ) if not cookieData.valid(): return userId = cookieData.id() # Retrieve link-key record if linkKeyString is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='linkKeyString is null' ) linkKeyRecord = linkKey.LinkKey.get_by_id( linkKeyString ) logging.debug( 'SubmitEditSurvey.post() linkKeyRecord=' + str(linkKeyRecord) ) if linkKeyRecord is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='linkKey not found' ) if linkKeyRecord.destinationType != conf.SURVEY_CLASS_NAME: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='linkKey destinationType=' + str(linkKeyRecord.destinationType) ) surveyId = linkKeyRecord.destinationId loginRequired = linkKeyRecord.loginRequired if linkKeyRecord.loginRequired and not cookieData.loginId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.NO_LOGIN ) # Check survey length if not httpServer.isLengthOk( title, introduction, conf.minLengthSurveyIntro ): return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT ) # Retrieve survey record. surveyRec = survey.Survey.get_by_id( int(surveyId) ) logging.debug( 'SubmitEditSurvey.post() surveyRec=' + str(surveyRec) ) if surveyRec is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='survey not found' ) # Verify that survey is editable if userId != surveyRec.creator: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.NOT_OWNER ) if not surveyRec.allowEdit: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.HAS_RESPONSES ) # Update survey record surveyRec.title = title surveyRec.introduction = introduction surveyRec.put() # Display updated survey. surveyDisplay = httpServerAutocomplete.surveyToDisplay( surveyRec, userId ) responseData.update( { 'success':True, 'survey':surveyDisplay } ) httpServer.outputJson( cookieData, responseData, self.response )
def post(self): logging.debug('SetAnswer.post() request.body=' + self.request.body) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) inputData = json.loads(self.request.body) logging.debug('SetAnswer.post() inputData=' + str(inputData)) content = answer.standardizeContent(inputData.get('content', None)) linkKeyString = inputData['linkKey'] questionId = str(int(inputData['questionId'])) browserCrumb = inputData.get('crumb', None) logging.debug('SetAnswer.post() content=' + str(content) + ' browserCrumb=' + str(browserCrumb) + ' linkKeyString=' + str(linkKeyString)) responseData = {'success': False, 'requestLogId': requestLogId} cookieData = httpServer.validate(self.request, inputData, responseData, self.response) if not cookieData.valid(): return userId = cookieData.id() surveyId, loginRequired = retrieveSurveyIdFromLinkKey( cookieData, linkKeyString, responseData, self.response) if surveyId is None: return # Check answer length if (content is not None) and not httpServer.isLengthOk( content, '', conf.minLengthAnswer): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT) # Retrieve question record. questionRec = question.Question.get_by_id(int(questionId)) logging.debug('SetAnswer.post() questionRec=' + str(questionRec)) if questionRec is None: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='question not found') if questionRec.surveyId != surveyId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='questionRec.surveyId != surveyId') # Update answer and vote. answerRec, voteRecord = answer.vote(questionId, surveyId, content, userId, questionRec.creator) # Display updated answer. responseData.update({'success': True, 'answerContent': content}) httpServer.outputJson(cookieData, responseData, self.response)
def get(self, linkKeyStr): logging.debug(LogMessage('UserAnswers', 'linkKeyStr=', linkKeyStr)) # Collect inputs. httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} cookieData = httpServer.validate(self.request, self.request.GET, responseData, self.response, crumbRequired=False, signatureRequired=False) if not cookieData.valid(): return userId = cookieData.id() # Retrieve and check linkKey. linkKeyRecord = linkKey.LinkKey.get_by_id(linkKeyStr) if (linkKeyRecord is None) or (linkKeyRecord.destinationType != conf.SURVEY_CLASS_NAME): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) surveyId = linkKeyRecord.destinationId # Retrieve all answers for this survey and voter answerVoteRecs = answerVote.AnswerVote.query( answerVote.AnswerVote.surveyId == surveyId, answerVote.AnswerVote.userId == userId).fetch() logging.debug( LogMessage('UserAnswers', 'answerVoteRecs=', answerVoteRecs)) answerRecordKeys = [ ndb.Key(answer.Answer, a.answerId) for a in answerVoteRecs if (a is not None) and (a.answerId is not None) ] answerRecords = ndb.get_multi(answerRecordKeys) logging.debug( LogMessage('UserAnswers', 'answerRecords=', answerRecords)) answerIdToContent = {a.key.id(): a.content for a in answerRecords if a} questionIdToAnswerContent = { v.questionId: answerIdToContent.get(v.answerId, None) for v in answerVoteRecs } # Display answers data. responseData.update({ 'success': True, 'questionIdToAnswerContent': questionIdToAnswerContent }) httpServer.outputJson(cookieData, responseData, self.response)
def get(self, linkKeyStr): if conf.isDev: logging.debug('SliceTitleResults.get() linkKeyStr=' + str(linkKeyStr)) # Collect inputs httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} # No user-id required, works for any user with the link-key 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) if (linkKeyRecord is None) or (linkKeyRecord.destinationType != conf.BUDGET_CLASS_NAME): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) budgetId = linkKeyRecord.destinationId # No need to enforce login-required in GET calls, only on write operations that create/use link-key # But enforcing here because the search is expensive if linkKeyRecord.loginRequired and not cookieData.loginId: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.NO_LOGIN) # Check that budget is valid budgetRecord = budget.Budget.get_by_id(int(budgetId)) if budgetRecord is None: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='budget is null') # Retrieve best suggested slices for this slice-start maxTitles = 20 # With minimum-size 5%, cannot have more than 20 top slices titlesOrdered = slice.retrieveTopSliceTitlesByVotes(budgetId) if conf.isDev: logging.debug('SliceTitleResults.get() titlesOrdered=' + str(titlesOrdered)) # Display slices data titleDisplays = [t.toDisplay(userId) for t in titlesOrdered] responseData.update({'success': True, 'titles': titleDisplays}) httpServer.outputJson(cookieData, responseData, self.response)
def post(self): logging.debug( 'LoginReturn.post() request.body=' + self.request.body ) # Collect inputs requestLogId = os.environ.get( conf.REQUEST_LOG_ID ) responseData = { 'success':False, 'requestLogId':requestLogId } inputData = urlparse.parse_qs( self.request.body ) logging.debug( 'LoginReturn.post() inputData=' + str(inputData) ) requestId = inputData['requestId'][0] responseSignature = inputData['responseSignature'][0] voterId = inputData['voterId'][0] city = inputData['city'][0] # Check that browser-id exists # Cannot check browser crumb/fingerprint, because they do not exist in the referring page # Send fingerprint via ajax before auto-closing tab cookieData = httpServer.validate( self.request, inputData, responseData, self.response, crumbRequired=False, signatureRequired=False ) if not cookieData.browserId: return browserId = cookieData.browserId # Check responseSignature expectedResponseSignature = user.signLoginResult( requestId, voterId, city ) logging.debug( 'LoginReturn.post() expectedResponseSignature=' + str(expectedResponseSignature) ) if (responseSignature != expectedResponseSignature): return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='responseSignature does not match expected' ) # Check stored browserId -> loginRequestId , check timeout, then delete record browserRecord = browser.BrowserRecord.get_by_id( browserId ) logging.debug( 'LoginReturn.post() browserRecord=' + str(browserRecord) ) now = int( time.time() ) if not browserRecord: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='login browserRecord=null' ) if browserRecord.voterLoginRequestId != requestId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='login requestId does not match expected' ) if browserRecord.loginRequestTime + conf.VOTER_ID_TIMEOUT_SEC < now: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='login past timeout' ) browserRecordKey = ndb.Key( browser.BrowserRecord, browserId ) browserRecordKey.delete() # Send login-id to browser now, with response-page cookie, instead of server storing a mapping # To set crumbForLogin into original page's javascript variable, have to use separate getLoginCrumb call # Add voter-id to persistent cookie appVoterId = user.voterIdToApp( voterId ) cookieData.dataNew[ conf.COOKIE_FIELD_VOTER_ID ] = appVoterId cookieData.dataNew[ conf.COOKIE_FIELD_VOTER_CITY ] = city # Send page that closes tab responseData.update( { 'SITE_NAME': conf.SITE_NAME , 'crumb': user.createCrumb( browserId ) , 'city': city } ) httpServer.outputTemplate( 'loginReturn.html', responseData, self.response, cookieData=cookieData )
def get(self, linkKeyStr): if conf.isDev: logging.debug('SlicesForUser.get() linkKeyStr=' + linkKeyStr) # Collect inputs httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} cookieData = httpServer.validate(self.request, self.request.GET, responseData, self.response, crumbRequired=False, signatureRequired=False) if not cookieData.valid(): return userId = cookieData.id() # Retrieve and check linkKey linkKeyRecord = linkKey.LinkKey.get_by_id(linkKeyStr) if (linkKeyRecord is None) or (linkKeyRecord.destinationType != conf.BUDGET_CLASS_NAME): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) budgetId = linkKeyRecord.destinationId # Retrieve all slices for this budget and voter sliceVoteRecord = slice.SliceVotes.get(budgetId, userId) sliceRecordKeys = [ ndb.Key(slice.Slice, sliceId) for sliceId, size in sliceVoteRecord.slices.iteritems() ] if sliceVoteRecord else [] sliceRecords = ndb.get_multi(sliceRecordKeys) if conf.isDev: logging.debug('SlicesForUser.get() sliceRecords=' + str(sliceRecords)) # TODO: If slice-record does not exist for slice in slice-votes... remove that vote sliceIdToDisplay = { s.key.id(): httpServerBudget.sliceToDisplay(s, userId) for s in sliceRecords if s } votesDisplay = httpServerBudget.sliceVotesToDisplay( sliceVoteRecord, userId) # Display slices data. responseData.update({ 'success': True, 'slices': sliceIdToDisplay, 'votes': votesDisplay }) httpServer.outputJson(cookieData, responseData, self.response)
def get(self, linkKeyStr): logging.debug('GetSurveyQuestions.get() linkKeyStr=' + linkKeyStr) # Collect inputs. 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) if (linkKeyRecord is None) or (linkKeyRecord.destinationType != conf.SURVEY_CLASS_NAME): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) surveyId = linkKeyRecord.destinationId # Retrieve survey surveyRecord = survey.Survey.get_by_id(int(surveyId)) if surveyRecord is None: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) # Retrieve all questions for this survey. questionRecords = question.Question.query( question.Question.surveyId == surveyId).fetch() # Order questions based on survey order questionIdToRec = {str(q.key.id()): q for q in questionRecords} questionsOrdered = [ questionIdToRec.get(q, None) for q in surveyRecord.questionIds ] questionsOrdered = [q for q in questionsOrdered if q is not None] questionDisplays = [ httpServerAutocomplete.questionToDisplay(q, userId) for q in questionsOrdered ] for q in range(len(questionDisplays)): questionDisplays[q]['positionInSurvey'] = q # Display questions data. responseData = {'success': True, 'questions': questionDisplays} httpServer.outputJson(cookieData, responseData, self.response)
def post(self): logging.debug(('SetAnswer', 'request.body=', self.request.body)) # Collect inputs requestLogId = os.environ.get( conf.REQUEST_LOG_ID ) inputData = json.loads( self.request.body ) logging.debug(('SetAnswer', 'inputData=', inputData)) content = answer.standardizeContent( inputData.get( 'content', None ) ) linkKeyString = inputData['linkKey'] questionId = str( int( inputData['questionId'] ) ) browserCrumb = inputData.get( 'crumb', None ) logging.debug(('SetAnswer', 'content=', content, 'browserCrumb=', browserCrumb, 'linkKeyString=', linkKeyString)) responseData = { 'success':False, 'requestLogId':requestLogId } cookieData = httpServer.validate( self.request, inputData, responseData, self.response ) if not cookieData.valid(): return userId = cookieData.id() # Retrieve link-key record surveyId, loginRequired = retrieveSurveyIdFromLinkKey( cookieData, linkKeyString, responseData, self.response ) if surveyId is None: return # Check answer length if ( content is not None ) and not httpServer.isLengthOk( content, '', conf.minLengthAnswer ): return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT ) # Retrieve survey record to check whether survey is frozen surveyRec = survey.Survey.get_by_id( int(surveyId) ) if surveyRec is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='survey record not found' ) if surveyRec.freezeUserInput: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.FROZEN ) # Require that answer and reason both exist, or both can be empty answerStr, reason = answer.parseAnswerAndReason( content ) if surveyRec.hideReasons and reason: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='reasons hidden' ) if (not answerStr) and reason: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT ) if answerStr and (not reason) and (not surveyRec.hideReasons): return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.REASON_TOO_SHORT ) # Retrieve question record. questionRec = question.Question.get_by_id( int(questionId) ) logging.debug(('SetAnswer', 'questionRec=', questionRec)) if questionRec is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='question not found' ) if questionRec.surveyId != surveyId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='questionRec.surveyId != surveyId' ) # Update answer and vote. answerRec, voteRecord = answer.vote( questionId, surveyId, content, userId, questionRec.creator ) # Display updated answer. responseData.update( { 'success':True, 'answerContent':content } ) httpServer.outputJson( cookieData, responseData, self.response )
def get(self, linkKeyStr, questionId): logging.debug(LogMessage('UserAnswer', 'linkKeyStr=', linkKeyStr)) # Collect inputs. httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} cookieData = httpServer.validate(self.request, self.request.GET, responseData, self.response, crumbRequired=False, signatureRequired=False) if not cookieData.valid(): return userId = cookieData.id() # Retrieve and check linkKey. linkKeyRecord = linkKey.LinkKey.get_by_id(linkKeyStr) if (linkKeyRecord is None) or (linkKeyRecord.destinationType != conf.SURVEY_CLASS_NAME): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) surveyId = linkKeyRecord.destinationId # Retrieve all answers for this question and voter answerVoteRec = answerVote.get(questionId, userId) logging.debug(LogMessage('UserAnswer', 'answerVoteRec=', answerVoteRec)) answerRecord = answer.Answer.get_by_id(answerVoteRec.answerId) logging.debug(LogMessage('UserAnswer', 'answerRecord=', answerRecord)) if answerRecord.surveyId != surveyId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='answerRecord.surveyId != surveyId') answerDisplay = httpServerAutocomplete.answerToDisplay( answerRecord, userId) if answerRecord else None # Display answers data. responseData.update({'success': True, 'answer': answerDisplay}) httpServer.outputJson(cookieData, responseData, self.response)
def get(self): # Collect inputs responseData = { } cookieData = httpServer.validate( self.request, self.request.GET, responseData, self.response, crumbRequired=False, signatureRequired=False ) if not cookieData.valid(): return # Retrieve link-key records from cookie. recentLinkKeyToTime = user.retrieveRecentLinkKeys( self.request ) if recentLinkKeyToTime: recentLinkKeyRecordKeys = [ ndb.Key(conf.LINK_KEY_CLASS_NAME, k) for k in recentLinkKeyToTime ] recentLinkKeyRecords = ndb.get_multi( recentLinkKeyRecordKeys ) destTypeXIdToLink = { '{}:{}'.format(k.destinationType, k.destinationId) : k.key.id() for k in recentLinkKeyRecords if k } logging.debug( 'destTypeXIdToLink=' + str(destTypeXIdToLink) ) # Retrieve link-key destination records. recentLinkKeyRecordsFiltered = [ r for r in recentLinkKeyRecords if (r and (r.destinationType == requestForProposals.RequestForProposals.__name__ or r.destinationType == proposal.Proposal.__name__)) ] recentDestinationKeys = [ ndb.Key(r.destinationType, int(r.destinationId)) for r in recentLinkKeyRecordsFiltered ] recentDestinationRecords = ndb.get_multi( recentDestinationKeys ) # Collect destination summaries. recentDestSummaries = [] for r in recentDestinationRecords: if r is None: continue destTypeAndId = '{}:{}'.format( r.key.kind(), r.key.id() ) logging.debug( 'destTypeAndId=' + str(destTypeAndId) ) linkKey = destTypeXIdToLink.get( destTypeAndId ) logging.debug( 'linkKey=' + str(linkKey) ) if not linkKey: continue recentDestSummary = {'title':r.title, 'detail':r.detail, 'type':r.key.kind()} recentDestSummary['linkKey'] = linkKey recentDestSummary['time'] = recentLinkKeyToTime[ linkKey ] recentDestSummaries.append( recentDestSummary ) logging.debug( 'getRecent.GetRecent() recentDestSummaries=' + str(recentDestSummaries) ) # Order summaries by time. recentDestSummaries = sorted( [r for r in recentDestSummaries if r] , key=lambda r:r['time'] , reverse=True ) else: recentDestSummaries = [] logging.debug( 'getRecent.GetRecent() recentDestSummaries=' + str(recentDestSummaries) ) responseData = { 'success':True, 'recents':recentDestSummaries } httpServer.outputJson( cookieData, responseData, self.response )
def get(self, linkKeyStr, questionId): logging.debug('GetQuestion.get() linkKeyStr=' + str(linkKeyStr) + ' questionId=' + str(questionId)) # Collect inputs. 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) if (linkKeyRecord is None) or (linkKeyRecord.destinationType != conf.SURVEY_CLASS_NAME): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) surveyId = linkKeyRecord.destinationId # Retrieve Question by id, filter/transform fields for display. questionRecord = question.Question.get_by_id(int(questionId)) logging.debug('GetQuestion.get() questionRecord=' + str(questionRecord)) if questionRecord.surveyId != surveyId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='questionRecord.surveyId != surveyId') questionDisp = httpServerAutocomplete.questionToDisplay( questionRecord, userId) logging.debug('GetQuestion.get() questionDisp=' + str(questionDisp)) # Store question to user's recent (cookie). user.storeRecentLinkKey(linkKeyStr, cookieData) # Display question data. responseData = {'success': True, 'question': questionDisp} httpServer.outputJson(cookieData, responseData, self.response)
def post(self): logging.debug( 'FreezeSurvey.post() request.body=' + self.request.body ) # Collect inputs requestLogId = os.environ.get( conf.REQUEST_LOG_ID ) inputData = json.loads( self.request.body ) logging.debug( 'FreezeSurvey.post() inputData=' + str(inputData) ) linkKeyString = inputData['linkKey'] freeze = bool( inputData['freeze'] ) logging.debug( 'FreezeSurvey.post() freeze=' + str(freeze) + ' linkKeyString=' + str(linkKeyString) ) responseData = { 'success':False, 'requestLogId':requestLogId } cookieData = httpServer.validate( self.request, inputData, responseData, self.response ) if not cookieData.valid(): return userId = cookieData.id() # Retrieve link-key record if linkKeyString is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='linkKeyString is null' ) linkKeyRecord = linkKey.LinkKey.get_by_id( linkKeyString ) logging.debug( 'FreezeSurvey.post() linkKeyRecord=' + str(linkKeyRecord) ) if linkKeyRecord is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='linkKey not found' ) if linkKeyRecord.destinationType != conf.SURVEY_CLASS_NAME: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='linkKey destinationType=' + str(linkKeyRecord.destinationType) ) surveyId = linkKeyRecord.destinationId loginRequired = linkKeyRecord.loginRequired if linkKeyRecord.loginRequired and not cookieData.loginId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.NO_LOGIN ) # Retrieve survey record surveyRec = survey.Survey.get_by_id( int(surveyId) ) logging.debug( 'FreezeSurvey.post() surveyRec=' + str(surveyRec) ) if surveyRec is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='survey not found' ) # Verify that survey is editable if userId != surveyRec.creator: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.NOT_OWNER ) if not surveyRec.allowEdit: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.HAS_RESPONSES ) # Update survey record surveyRec.freezeUserInput = freeze surveyRec.put() # Display updated survey surveyDisplay = httpServerAutocomplete.surveyToDisplay( surveyRec, userId ) responseData.update( { 'success':True, 'survey':surveyDisplay } ) httpServer.outputJson( cookieData, responseData, self.response )
def get(self, linkKeyStr, questionId): logging.debug( LogMessage('QuestionAnswersFromCreator', 'linkKeyStr=', linkKeyStr, 'questionId=', questionId)) # Collect inputs. httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} cookieData = httpServer.validate(self.request, self.request.GET, responseData, self.response, crumbRequired=False, signatureRequired=False) if not cookieData.valid(): return userId = cookieData.id() # Retrieve and check linkKey. linkKeyRecord = linkKey.LinkKey.get_by_id(linkKeyStr) if (linkKeyRecord is None) or (linkKeyRecord.destinationType != conf.SURVEY_CLASS_NAME): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.BAD_LINK) surveyId = linkKeyRecord.destinationId # Retrieve all answers for this question and creator. answerRecords = answer.Answer.query( answer.Answer.questionId == questionId, answer.Answer.creator == userId, answer.Answer.fromEditPage == True).fetch() answersByContent = sorted(answerRecords, key=lambda a: a.content) answerDisplays = [ httpServerAutocomplete.answerToDisplay(a, userId) for a in answersByContent ] # Display answers data. responseData = {'success': True, 'answers': answerDisplays} httpServer.outputJson(cookieData, responseData, self.response)
def post(self): logging.debug( 'SubmitLogout.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( 'SubmitLogout.post() inputData=' + str(inputData) ) cookieData = httpServer.validate( self.request, inputData, responseData, self.response, crumbRequired=False, signatureRequired=False ) if not cookieData.valid(): return # Cookie already reset, no need to clear cookie fields # Remove voter-id from persistent cookie, even if cookie is already invalid cookieData.dataNew[ conf.COOKIE_FIELD_VOTER_ID ] = None cookieData.dataNew[ conf.COOKIE_FIELD_VOTER_CITY ] = None responseData.update( { 'success':True } ) httpServer.outputJson( cookieData, responseData, self.response )
def get(self): logging.debug('GetRecent.get()') # Collect inputs httpRequestId = os.environ.get(conf.REQUEST_LOG_ID) responseData = {'success': False, 'httpRequestId': httpRequestId} # No crumb because this is a get-call and we don't want to send crumb in the clear cookieData = httpServer.validate(self.request, self.request.GET, responseData, self.response, crumbRequired=False, signatureRequired=False) if not cookieData.valid(): return recentDestSummaries = [] # Read recent link-keys from cookie recentLinkKeyToTime = user.retrieveRecentLinkKeys(self.request) if recentLinkKeyToTime: # Retrieve link-key records, using link-keys from cookie recentLinkKeyRecordKeys = [ ndb.Key(linkKey.LinkKey, k) for k in recentLinkKeyToTime ] recentLinkKeyRecords = ndb.get_multi(recentLinkKeyRecordKeys) destSurveyIdToLink = { k.destinationId: k.key.id() for k in recentLinkKeyRecords if k } logging.debug('destSurveyIdToLink=' + str(destSurveyIdToLink)) # Retrieve link-key destination records. recentDestinationKeys = [ ndb.Key(survey.Survey, int(k.destinationId)) for k in recentLinkKeyRecords if k ] recentDestinationRecords = ndb.get_multi(recentDestinationKeys) # Collect destination summaries. for r in recentDestinationRecords: if r is None: continue # Is there a better way to match link-key to dest type & id, without using string concat? # Use map[ linkKey -> dest type & id ] ? No, dont have linkKey. # Don't need dest type, because all should be survey type. # Use tuple[type, id] ? link = destSurveyIdToLink.get(str(r.key.id()), None) logging.debug('link=' + str(link)) if not link: continue recentDestSummary = {'introduction': r.introduction} recentDestSummary['linkKey'] = link recentDestSummary['time'] = recentLinkKeyToTime[link] recentDestSummaries.append(recentDestSummary) logging.debug('getRecent.GetRecent() recentDestSummaries=' + str(recentDestSummaries)) # Order summaries by time. recentDestSummaries = sorted([r for r in recentDestSummaries if r], key=lambda r: r['time'], reverse=True) logging.debug('getRecent.GetRecent() recentDestSummaries=' + str(recentDestSummaries)) responseData = {'success': True, 'recents': recentDestSummaries} 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(('EditSlice.post()', 'request.body=', self.request.body)) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) inputData = json.loads(self.request.body) logging.debug(('EditSlice.post()', 'inputData=', inputData)) responseData = {'success': False, 'requestLogId': requestLogId} cookieData = httpServer.validate(self.request, inputData, responseData, self.response) if not cookieData.valid(): return userId = cookieData.id() title = slice.standardizeContent( text.formTextToStored(inputData['title'])) reason = slice.standardizeContent( text.formTextToStored(inputData['reason'])) linkKeyString = inputData['linkKey'] sliceId = inputData.get('sliceId', None) # Null if slice is new logging.debug( ('EditSlice.post()', 'sliceId=', sliceId, 'title=', title, 'reason=', reason, 'linkKeyString=', linkKeyString)) budgetId, loginRequired = retrieveBudgetIdFromLinkKey( cookieData, linkKeyString, responseData, self.response) if budgetId is None: return # Retrieve budget record to check budget creator budgetRec = budget.Budget.get_by_id(int(budgetId)) if budgetRec is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='budget record not found') if budgetRec.creator != userId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='budgetRec.creator != userId') # Check slice length if (not title) or (len(title) < conf.minLengthSliceTitle): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT) if budgetRec.hideReasons: if reason: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='reasons hidden') else: if (not reason) or (len(reason) < conf.minLengthSliceTitle): return httpServer.outputJson( cookieData, responseData, self.response, errorMessage=conf.REASON_TOO_SHORT) # Delete old slice if it has no votes if sliceId: oldSliceRec = slice.Slice.get_by_id(sliceId) if oldSliceRec is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='slice record not found') if oldSliceRec.budgetId != budgetId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='oldSliceRec.budgetId != budgetId') if oldSliceRec.creator != userId: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=NOT_OWNER) if (oldSliceRec.voteCount <= 0): oldSliceRec.key.delete() # Create new slice only if there is existing record/vote sliceRec = slice.Slice.get(budgetId, title, reason) if sliceRec is None: sliceRec = slice.Slice.create(budgetId, title, reason, creator=userId) # Store slice record sliceRec.fromEditPage = True sliceRec.put() # Display updated slices sliceDisplay = httpServerBudget.sliceToDisplay(sliceRec, userId) responseData.update({'success': True, 'slice': sliceDisplay}) 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): 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 post(self): logging.debug('SliceVote.post() request.body=' + self.request.body) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) inputData = json.loads(self.request.body) logging.debug(('SliceVote.post()', 'inputData=', inputData)) title = slice.standardizeContent(inputData.get('title', None)) reason = slice.standardizeContent(inputData.get('reason', None)) size = int(inputData.get('size', -1)) linkKeyString = inputData['linkKey'] sliceId = inputData.get('sliceId', None) logging.debug(('SliceVote.post()', 'title=', title, 'reason=', reason, 'linkKeyString=', linkKeyString)) responseData = {'success': False, 'requestLogId': requestLogId} cookieData = httpServer.validate(self.request, inputData, responseData, self.response) if not cookieData.valid(): return userId = cookieData.id() # Enforce size limits if (size < conf.SLICE_SIZE_MIN) or (conf.SLICE_SIZE_MAX < size): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='Size out of bounds') # Retrieve link-key record budgetId, loginRequired = retrieveBudgetIdFromLinkKey( cookieData, linkKeyString, responseData, self.response) if budgetId is None: return # Retrieve budget record to check whether budget is frozen budgetRec = budget.Budget.get_by_id(int(budgetId)) if budgetRec is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='budget record not found') if budgetRec.freezeUserInput: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.FROZEN) # Enforce minimum title/reason lengths if (title is None) or (len(title) < conf.minLengthSliceTitle): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT) if budgetRec.hideReasons: if reason: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='reasons hidden') else: if (reason is None) or (len(reason) < conf.minLengthSliceReason): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT) # Do not need to prevent duplicate titles here, because title & detail are already deduplicated by storage key # Storage-side deduplication would drop some user input, so have client-side warn if title is duplicate # Un-vote for old title and reason, if different newSliceId = slice.Slice.toKeyId(budgetId, title, reason) logging.debug(('SliceVote.post()', 'sliceId=', sliceId)) logging.debug(('SliceVote.post()', 'newSliceId=', newSliceId)) if sliceId and (sliceId != newSliceId): oldSliceRecord = slice.Slice.get_by_id( sliceId) if sliceId else None logging.debug( ('SliceVote.post()', 'oldSliceRecord=', oldSliceRecord)) if oldSliceRecord: if (oldSliceRecord.budgetId != budgetId): return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='sliceId does not match budgetId') titleOld = oldSliceRecord.title reasonOld = oldSliceRecord.reason sliceRecord, voteRecord, success = slice.vote( budgetId, titleOld, reasonOld, 0, userId) if not success: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='un-vote failed') # Update slice and vote sliceRecord, voteRecord, success = slice.vote(budgetId, title, reason, size, userId) if not success: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.OVER_BUDGET) # Display updated slice sliceDisplay = httpServerBudget.sliceToDisplay(sliceRecord, userId) responseData.update({'success': success, 'slice': sliceDisplay}) httpServer.outputJson(cookieData, responseData, self.response)
def post(self): logging.debug( LogMessage('SubmitNewReason', 'request.body=', self.request.body)) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) inputData = json.loads(self.request.body) logging.debug(LogMessage('SubmitNewReason', 'inputData=', inputData)) linkKeyStr = inputData.get('linkKey', None) proposalId = str(int(inputData.get('proposalId', None))) proOrCon = inputData.get('proOrCon', None) reasonContent = text.formTextToStored( inputData.get('reasonContent', '')) browserCrumb = inputData.get('crumb', '') loginCrumb = inputData.get('crumbForLogin', '') logging.debug( LogMessage('SubmitNewReason', 'linkKeyStr=', linkKeyStr, 'proposalId=', proposalId, 'proOrCon=', proOrCon, 'reasonContent=', reasonContent, 'browserCrumb=', browserCrumb, 'loginCrumb=', loginCrumb)) # 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 reason length. if not httpServer.isLengthOk(reasonContent, '', conf.minLengthReason): return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.TOO_SHORT) # Retrieve link-key record linkKeyRec = linkKey.LinkKey.get_by_id(linkKeyStr) if linkKeyRec is None: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='linkKey not found') logging.debug(LogMessage('SubmitNewReason', 'linkKeyRec=', linkKeyRec)) 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)) if proposalRec is None: return httpServer.outputJson(cookieDataresponseData, self.response, errorMessage='proposal not found') logging.debug( LogMessage('SubmitNewReason', 'proposalRec=', proposalRec)) # Verify that reason belongs to linkKey's request/proposal, and check whether frozen requestId = None if linkKeyRec.destinationType == conf.PROPOSAL_CLASS_NAME: if proposalId != linkKeyRec.destinationId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='proposalId != linkKeyRec.destinationId') if proposalRec.hideReasons: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='reasons hidden') if proposalRec.freezeUserInput: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.FROZEN) elif linkKeyRec.destinationType == conf.REQUEST_CLASS_NAME: requestId = proposalRec.requestId if requestId != linkKeyRec.destinationId: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='requestId != linkKeyRec.destinationId') # Retrieve request-for-proposals, and check whether frozen requestRec = requestForProposals.RequestForProposals.get_by_id( int(requestId)) if not requestRec: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='requestRec is null') if requestRec.hideReasons: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='reasons hidden') if requestRec.freezeUserInput: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.FROZEN) else: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='linkKey destinationType=' + linkKeyRec.destinationType) # Retrieve any existing identical reason, to prevent duplicates existingReasons = reason.Reason.query( reason.Reason.requestId == requestId, reason.Reason.proposalId == proposalId, reason.Reason.proOrCon == proOrCon, reason.Reason.content == reasonContent).fetch(1) if existingReasons: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.DUPLICATE) # Construct new reason record reasonRecord = reason.Reason(requestId=requestId, proposalId=proposalId, creator=userId, proOrCon=proOrCon, allowEdit=True) reasonRecord.setContent(reasonContent) # Store reason record reasonRecordKey = reasonRecord.put() logging.debug( LogMessage('SubmitNewReason', 'reasonRecordKey=', reasonRecordKey)) # Display reason reasonDisplay = httpServer.reasonToDisplay(reasonRecord, userId) responseData.update({'success': True, 'reason': reasonDisplay}) httpServer.outputJson(cookieData, responseData, self.response) # Mark proposal as not editable. if proposalRec.allowEdit: proposal.setEditable(proposalId, False)
def post(self): logging.debug('SliceDelete.post() request.body=' + self.request.body) # Collect inputs requestLogId = os.environ.get(conf.REQUEST_LOG_ID) inputData = json.loads(self.request.body) logging.debug('SliceDelete.post() inputData=' + str(inputData)) sliceId = inputData.get('sliceId', None) linkKeyString = inputData['linkKey'] logging.debug('SliceDelete.post() + sliceId=' + str(sliceId) + ' linkKeyString=' + str(linkKeyString)) responseData = {'success': False, 'requestLogId': requestLogId} cookieData = httpServer.validate(self.request, inputData, responseData, self.response) if not cookieData.valid(): return userId = cookieData.id() if not sliceId: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='sliceId is null') # Retrieve link-key record budgetId, loginRequired = retrieveBudgetIdFromLinkKey( cookieData, linkKeyString, responseData, self.response) if budgetId is None: return # Retrieve slice-record to get title and reason sliceRecord = slice.Slice.get_by_id(sliceId) if conf.isDev: logging.debug('SliceSetSize.post() sliceRecord=' + str(sliceRecord)) if sliceRecord is None: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='slice record not found') if (sliceRecord.budgetId != budgetId): return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='sliceId does not match budgetId') title = sliceRecord.title reason = sliceRecord.reason # Retrieve budget record to check whether budget is frozen budgetRec = budget.Budget.get_by_id(int(budgetId)) if budgetRec is None: return httpServer.outputJson( cookieData, responseData, self.response, errorMessage='budget record not found') if budgetRec.freezeUserInput: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage=conf.FROZEN) # Update slice-vote, and vote-aggregates size = 0 sliceRecord, voteRecord, success = slice.vote(budgetId, title, reason, size, userId) if conf.isDev: logging.debug('SliceSetSize.post() success=' + str(success) + ' sliceRecord=' + str(sliceRecord) + ' voteRecord=' + str(voteRecord)) if not success: return httpServer.outputJson(cookieData, responseData, self.response, errorMessage='success is false') # Display result voteRecord = httpServerBudget.sliceVotesToDisplay(voteRecord, userId) responseData.update({'success': True, 'vote': voteRecord}) httpServer.outputJson(cookieData, responseData, self.response)