def search(cls, phrase, limit=10, keys_only=False): """Queries search indices for phrases using a merge-join. Use of this class method lets you easily restrict searches to a kind and retrieve entities or keys. Args: phrase: Search phrase (string) limit: Number of entities or keys to return. keys_only: If True, return only keys with title of parent entity. Returns: A list. If keys_only is True, the list holds (key, title) tuples. If keys_only is False, the list holds Model instances. """ key_list = Searchable.full_text_search( phrase, limit=limit, kind=cls.kind(), stemming=cls.INDEX_STEMMING, multi_word_literal=cls.INDEX_MULTI_WORD ) if keys_only: logging.debug("key_list: %s", key_list) return key_list else: # Andrew changed this to do a mass get and some cleanup. ret = IP4DB.get([resultKey for stemKey, resultKey, resultTitle in key_list]) badStems = [stemKey for result, (stemKey, resultKey, resultTitle) in zip(ret, key_list) if result is None] if badStems: from google.appengine.ext import deferred deferred.defer(db.delete, badStems[:500]) # Google only allows 500 in a delete. ret = [r for r in ret if r is not None] return ret
def post(self): key_str = self.request.get("key") only_index_str = self.request.get("only_index") if key_str: key = db.Key(key_str) entity = IP4DB.get(key) if not entity: self.response.set_status(200) # Clear task because it's a bad key else: only_index = only_index_str.split(",") if only_index_str else None entity.index()
def post(self): key_str = self.request.get('key') only_index_str = self.request.get('only_index') if key_str: key = db.Key(key_str) entity = IP4DB.get(key) if not entity: self.response.set_status( 200) # Clear task because it's a bad key else: only_index = only_index_str.split( ',') if only_index_str else None entity.index()
def indexed_title_changed(self): """Renames index entities for this model to match new title.""" klass = StemmedIndex if self.INDEX_STEMMING else LiteralIndex query = klass.all(keys_only=True).ancestor(self.key()) old_index_keys = query.fetch(1000) if not hasattr(self, "INDEX_TITLE_FROM_PROP"): raise IndexTitleError("Must declare a property name via INDEX_TITLE_FROM_PROP") new_keys = [] for old_key in old_index_keys: old_index = IP4DB.get(old_key) index_num = SearchIndex.get_index_num(old_key.name()) index_key = klass.put_index(parent=self, index_num=index_num, phrases=old_index.phrases) new_keys.append(index_key) delete_keys = filter(lambda key: key not in new_keys, old_index_keys) db.delete(delete_keys)
def indexed_title_changed(self): """Renames index entities for this model to match new title.""" klass = StemmedIndex if self.INDEX_STEMMING else LiteralIndex query = klass.all(keys_only=True).ancestor(self.key()) old_index_keys = query.fetch(1000) if not hasattr(self, 'INDEX_TITLE_FROM_PROP'): raise IndexTitleError( 'Must declare a property name via INDEX_TITLE_FROM_PROP') new_keys = [] for old_key in old_index_keys: old_index = IP4DB.get(old_key) index_num = SearchIndex.get_index_num(old_key.name()) index_key = klass.put_index(parent=self, index_num=index_num, phrases=old_index.phrases) new_keys.append(index_key) delete_keys = filter(lambda key: key not in new_keys, old_index_keys) db.delete(delete_keys)
def get(self): user = users.get_current_user() fixkey = self.request.get('fixkey', None) if fixkey is not None: fixModel = IP4DB.get(fixkey) if fixModel is not None: fixModel.deleteSearchResult() fixModel.putSearchResult() return self.redirect(self.request.referrer) # Show me everything I own. myResults = user and [ sr for sr in SearchModels.SearchResult.SearchRequest().setUser( user).get() ] or [] myTitles = [mr.title for mr in myResults] myModels = [mr.model for mr in myResults] myKeys = [str(mm.key()) for mm in myModels] myViewablesOrNones = [((user in mm.viewers) and mm or None) for mm in myModels] modelHtml = [] for thisResult, thisTitle, thisModel, thisKey, thisViewableOrNone \ in zip(myResults, myTitles, myModels, myKeys, myViewablesOrNones): notViewableLink = '' if thisViewableOrNone is None: notViewableLink = '(Should not be viewable. <a href="fixme?fixkey=%(thisKey)s">Fix this</a>)' % locals( ) modelHtml.append(\ """ %(thisTitle)s %(notViewableLink)s """ % locals()) modelHtml = '<br/>'.join(modelHtml) self.response.out.write(\ """ <html> <body> %(modelHtml)s </body> </html> """ % locals())
def search(cls, phrase, limit=10, keys_only=False): """Queries search indices for phrases using a merge-join. Use of this class method lets you easily restrict searches to a kind and retrieve entities or keys. Args: phrase: Search phrase (string) limit: Number of entities or keys to return. keys_only: If True, return only keys with title of parent entity. Returns: A list. If keys_only is True, the list holds (key, title) tuples. If keys_only is False, the list holds Model instances. """ key_list = Searchable.full_text_search( phrase, limit=limit, kind=cls.kind(), stemming=cls.INDEX_STEMMING, multi_word_literal=cls.INDEX_MULTI_WORD) if keys_only: logging.debug("key_list: %s", key_list) return key_list else: # Andrew changed this to do a mass get and some cleanup. ret = IP4DB.get( [resultKey for stemKey, resultKey, resultTitle in key_list]) badStems = [ stemKey for result, (stemKey, resultKey, resultTitle) in zip(ret, key_list) if result is None ] if badStems: from google.appengine.ext import deferred deferred.defer( db.delete, badStems[:500]) # Google only allows 500 in a delete. ret = [r for r in ret if r is not None] return ret
def get(self): user = users.get_current_user() fixkey = self.request.get('fixkey', None) if fixkey is not None: fixModel = IP4DB.get(fixkey) if fixModel is not None: fixModel.deleteSearchResult() fixModel.putSearchResult() return self.redirect(self.request.referrer) # Show me everything I own. myResults = user and [ sr for sr in SearchModels.SearchResult.SearchRequest().setUser(user).get() ] or [] myTitles = [ mr.title for mr in myResults ] myModels = [ mr.model for mr in myResults ] myKeys = [ str(mm.key()) for mm in myModels ] myViewablesOrNones = [ ((user in mm.viewers) and mm or None) for mm in myModels ] modelHtml = [] for thisResult, thisTitle, thisModel, thisKey, thisViewableOrNone \ in zip(myResults, myTitles, myModels, myKeys, myViewablesOrNones): notViewableLink = '' if thisViewableOrNone is None: notViewableLink = '(Should not be viewable. <a href="fixme?fixkey=%(thisKey)s">Fix this</a>)' % locals() modelHtml.append(\ """ %(thisTitle)s %(notViewableLink)s """ % locals()) modelHtml = '<br/>'.join(modelHtml) self.response.out.write(\ """ <html> <body> %(modelHtml)s </body> </html> """ % locals())
def get(self): user = users.get_current_user() if not (user and (users.is_current_user_admin() or LINK_SQUAD.find(user.email()) != -1 or LINK_SQUAD.find(user.user_id()) != -1)): return self.kickOutUser(user) userEmail, userId = user.email(), user.user_id() fixkey = self.request.get('fixkey', None) if fixkey is not None: fixModel = IP4DB.get(fixkey) if fixModel is not None: fixModel.fixedurl = self.request.get('fixurl'); fixModel.put() allModels = models.ReportedUrl.all().filter('fixedurl', '') allModels.filter('url !=', '/characters/missingCompendium') # Put back when above line removed! XXX allModels.order('-modified') reportedUrlModels = [ rum for rum in allModels ] reportedCharKeys = [ ru.character for ru in reportedUrlModels ] reportedKeys = [ ru.key() for ru in reportedUrlModels ] reportedNames = [ ru.name for ru in reportedUrlModels ] reportedNameNoPluses = [ ru.namenoplus for ru in reportedUrlModels ] reportedUrls = [ ru.url for ru in reportedUrlModels ] reportedUrlGuesses = [ ru.urlguess or '' for ru in reportedUrlModels ] reportedFixedUrls = [ ru.fixedurl for ru in reportedUrlModels ] modelHtml, scriptHtml = [], [] for modelIndex, model, charKey, key, name, reportedNameNoPlus, url, urlGuess, fixedUrl \ in zip(range(len(reportedUrls)), reportedUrlModels, reportedCharKeys, reportedKeys, reportedNames, reportedNameNoPluses, reportedUrls, reportedUrlGuesses, reportedFixedUrls): defaultedFixedUrl = fixedUrl or DEFAULT_URL scriptHtml.append("""\ $('fixedlink' + %(modelIndex)d).href = $('fixedUrl' + %(modelIndex)d).value; """ % locals()) modelHtml.append("""\ <tr> <td style="padding-right:24px;"> <a target="character" id="charlink%(modelIndex)d" href="/view?key=%(charKey)s">Open</a> </td> <td style="padding-right:24px;"> <a name="%(key)s" target="linksquad" id="link%(modelIndex)d" href="%(url)s">%(name)s</a> </td> <td style="padding-right:24px;"> <input target="linksquad" value="%(reportedNameNoPlus)s" onclick="this.focus();this.select();" /> </td> <td style="padding-right:24px;"> <input style="width:400px;" id="fixedUrl%(modelIndex)d" value="%(urlGuess)s" onkeyup=" $('fixedlink' + %(modelIndex)d).href = $('fixedUrl' + %(modelIndex)d).value; " /> </td> <td style="padding-right:24px;"> <a target="linksquad" id="fixedlink%(modelIndex)d" href="%(defaultedFixedUrl)s" onclick=" $('savebutton' + %(modelIndex)d).style.display = 'inline'; " >%(name)s</a> <input type="button" id="savebutton%(modelIndex)s" value="Save URL" style="display:none;" onclick=" $('fixkey').value = '%(key)s'; $('fixurl').value = $('fixedUrl%(modelIndex)d').value; $(this).up('form').submit(); " </td> </tr> """ % locals()) modelHtml = ''.join(modelHtml) scriptHtml = ''.join(scriptHtml) self.response.out.write(\ """ <html> <head> <title> iPlay4e Link Squad, Attack! </title> <script type="text/javascript" language="javascript" src="/TIME_TOKEN/js/combo.js"></script> </head> <body> <h2> Greetings, iPlay4e Link Squad! </h2> <h2> Instructions </h2> <p> The items below have been reported but not yet fixed. To fix an item, please: <ol> <li> <a href="http://www.wizards.com/dndinsider/compendium/" target="compendium">Open the Compendium</a> </li> <li> Search the Compendium for the item in question, or a related entry that includes it. </li> <li> While viewing the entry, right-click the power/item card and click "Open frame in new tab" (or your browser's equivalent) </li> <li> Copy the address from the compendium entry's browser tab. </li> <li> Paste the address into the Fixed URL box for the item below. </li> <li> Click the Fixed Link next to the Fixed URL box to make sure it works. </li> <li> Click the "Save URL" button next to the Fixed Link. </li> </ol> </p> <form action="/linksquad" method="POST"> <input type="hidden" id="fixkey" name="fixkey" value=""/> <input type="hidden" id="fixurl" name="fixurl" value=""/> <table> <tr> <th style="text-align:left;">Character</th> <th style="text-align:left;">Original Link</th> <th style="text-align:left;">Base Name</th> <th style="text-align:left;">Fixed URL</th> <th style="text-align:left;">Fixed Link</th> </tr> %(modelHtml)s </table> </form> <script> %(scriptHtml)s </script> </body> </html> """ % locals())
def get(self): ret = None # We have a string or an owner or both. isViable() said so, right? if self.__string: # If they didn't enter something like "1-10", we just search for what they typed. levelRangeMatch = self.LEVEL_RANGE_REGEX.match(self.__string) if not levelRangeMatch: searchStrings = [self.__string] else: lowLevel, highLevel = [ int(g) for g in levelRangeMatch.groups() ] baseSearch = self.__string.replace( '%d-%d' % (lowLevel, highLevel), '') # But for level-ranged searches, we actually perform multiple searches. lowAndHighLevel = [min(lowLevel, 30), min(highLevel, 30)] lowAndHighLevel.sort( ) # in case they specified 20-11 (high first) lowLevel, highLevel = lowAndHighLevel searchStrings = [ '%s %d' % (baseSearch, l) for l in range(lowLevel, highLevel + 1) ] #logging.debug('searchStrings: %(searchStrings)r' % locals()) resultsPerLevel = 200 / len(searchStrings) ret = [] [ ret.extend([r for r in SearchResult.search(ss, limit=resultsPerLevel) if r.isPublic]) \ for ss in searchStrings \ ] if self.__user: userId = self.__user.user_id() # Limit by owner at either the query or result level if ret is not None: ret = [ r for r in ret if userId in (r.viewerIds or ()) or self.__user in r.viewers or self.__user == r.owner ] else: # XXX Hopefully, this is temporary code. It handles the fact that # earlier versions of v31 didn't have the viewers field. badResults = \ [ sr for sr in SearchResult.gql('WHERE owner = :1', self.__user) \ if self.__user not in sr.viewers \ ] db.delete(badResults) [br.model.putSearchResult() for br in badResults] ret = {} for propName, matchValue in \ ( ('viewers', self.__user), ('viewerIds', userId), ): [ ret.update({str(sr.key()): sr}) \ for sr in SearchResult.gql('WHERE %s = :1' % propName, matchValue) \ ] ret = ret.values() # We had a bug for a while that caused 2 SearchResult models for each character. # Clean up from that. newRet, dupResults = {}, [] for thisResult in ret: firstRec = newRet.get(thisResult.modelKey, None) if firstRec is None: newRet[thisResult.modelKey] = thisResult else: dupResults.append(thisResult) dupResults and db.delete(dupResults) ret = newRet.values() ret.sort(lambda x, y: cmp(x.title, y.title)) if self.__campaignKey: from IP4ELibs import models self.__theCampaign = models.Campaign.get(self.__campaignKey) characterKeys = [ str(c.key()) for c in self.__theCampaign.characters ] ret = characterKeys and SearchResult.gql( 'WHERE modelKey IN :1 ORDER BY title', characterKeys) or [] # Either way, we can limit by type ret = ret or [] if self.__typeList is not None: ret = [r for r in ret if r.modelType in self.__typeList] # Finally, make sure the search results refer to models that still exist. # We'd like to remove this code eventually; it exists because we weren't # always deleting search results when models were deleted. Now we are. from IP4ELibs import models rawModelKeysAndTypes = [(r.modelKey, r.modelType) for r in ret] modelKeysAndTypes = [ (db.Key(IP4DB.hrdKey(r.modelKey)), r.modelType) for r in ret ] #raise RuntimeError, 'RAW: %(rawModelKeysAndTypes)r, KEYED: %(modelKeysAndTypes)r' % locals() modelExistenceBooleans = \ [ getattr(models, mType).all(keys_only=True).filter('__key__ =', mKey).count() \ for mKey, mType in modelKeysAndTypes \ ] db.delete([ r for (r, mBool) in zip(ret, modelExistenceBooleans) if not mBool ]) ret = [ r for (r, mBool) in zip(ret, modelExistenceBooleans) if mBool ] return ret
def model(self): return IP4DB.get(self.modelKey)
def get(self): user = users.get_current_user() if not (user and (users.is_current_user_admin() or LINK_SQUAD.find( user.email()) != -1 or LINK_SQUAD.find(user.user_id()) != -1)): return self.kickOutUser(user) userEmail, userId = user.email(), user.user_id() fixkey = self.request.get('fixkey', None) if fixkey is not None: fixModel = IP4DB.get(fixkey) if fixModel is not None: fixModel.fixedurl = self.request.get('fixurl') fixModel.put() allModels = models.ReportedUrl.all().filter('fixedurl', '') allModels.filter('url !=', '/characters/missingCompendium') # Put back when above line removed! XXX allModels.order('-modified') reportedUrlModels = [rum for rum in allModels] reportedCharKeys = [ru.character for ru in reportedUrlModels] reportedKeys = [ru.key() for ru in reportedUrlModels] reportedNames = [ru.name for ru in reportedUrlModels] reportedNameNoPluses = [ru.namenoplus for ru in reportedUrlModels] reportedUrls = [ru.url for ru in reportedUrlModels] reportedUrlGuesses = [ru.urlguess or '' for ru in reportedUrlModels] reportedFixedUrls = [ru.fixedurl for ru in reportedUrlModels] modelHtml, scriptHtml = [], [] for modelIndex, model, charKey, key, name, reportedNameNoPlus, url, urlGuess, fixedUrl \ in zip(range(len(reportedUrls)), reportedUrlModels, reportedCharKeys, reportedKeys, reportedNames, reportedNameNoPluses, reportedUrls, reportedUrlGuesses, reportedFixedUrls): defaultedFixedUrl = fixedUrl or DEFAULT_URL scriptHtml.append("""\ $('fixedlink' + %(modelIndex)d).href = $('fixedUrl' + %(modelIndex)d).value; """ % locals()) modelHtml.append("""\ <tr> <td style="padding-right:24px;"> <a target="character" id="charlink%(modelIndex)d" href="/view?key=%(charKey)s">Open</a> </td> <td style="padding-right:24px;"> <a name="%(key)s" target="linksquad" id="link%(modelIndex)d" href="%(url)s">%(name)s</a> </td> <td style="padding-right:24px;"> <input target="linksquad" value="%(reportedNameNoPlus)s" onclick="this.focus();this.select();" /> </td> <td style="padding-right:24px;"> <input style="width:400px;" id="fixedUrl%(modelIndex)d" value="%(urlGuess)s" onkeyup=" $('fixedlink' + %(modelIndex)d).href = $('fixedUrl' + %(modelIndex)d).value; " /> </td> <td style="padding-right:24px;"> <a target="linksquad" id="fixedlink%(modelIndex)d" href="%(defaultedFixedUrl)s" onclick=" $('savebutton' + %(modelIndex)d).style.display = 'inline'; " >%(name)s</a> <input type="button" id="savebutton%(modelIndex)s" value="Save URL" style="display:none;" onclick=" $('fixkey').value = '%(key)s'; $('fixurl').value = $('fixedUrl%(modelIndex)d').value; $(this).up('form').submit(); " </td> </tr> """ % locals()) modelHtml = ''.join(modelHtml) scriptHtml = ''.join(scriptHtml) self.response.out.write(\ """ <html> <head> <title> iPlay4e Link Squad, Attack! </title> <script type="text/javascript" language="javascript" src="/TIME_TOKEN/js/combo.js"></script> </head> <body> <h2> Greetings, iPlay4e Link Squad! </h2> <h2> Instructions </h2> <p> The items below have been reported but not yet fixed. To fix an item, please: <ol> <li> <a href="http://www.wizards.com/dndinsider/compendium/" target="compendium">Open the Compendium</a> </li> <li> Search the Compendium for the item in question, or a related entry that includes it. </li> <li> While viewing the entry, right-click the power/item card and click "Open frame in new tab" (or your browser's equivalent) </li> <li> Copy the address from the compendium entry's browser tab. </li> <li> Paste the address into the Fixed URL box for the item below. </li> <li> Click the Fixed Link next to the Fixed URL box to make sure it works. </li> <li> Click the "Save URL" button next to the Fixed Link. </li> </ol> </p> <form action="/linksquad" method="POST"> <input type="hidden" id="fixkey" name="fixkey" value=""/> <input type="hidden" id="fixurl" name="fixurl" value=""/> <table> <tr> <th style="text-align:left;">Character</th> <th style="text-align:left;">Original Link</th> <th style="text-align:left;">Base Name</th> <th style="text-align:left;">Fixed URL</th> <th style="text-align:left;">Fixed Link</th> </tr> %(modelHtml)s </table> </form> <script> %(scriptHtml)s </script> </body> </html> """ % locals())
def get(self): ret = None # We have a string or an owner or both. isViable() said so, right? if self.__string: # If they didn't enter something like "1-10", we just search for what they typed. levelRangeMatch = self.LEVEL_RANGE_REGEX.match(self.__string) if not levelRangeMatch: searchStrings = [self.__string] else: lowLevel, highLevel = [ int(g) for g in levelRangeMatch.groups() ] baseSearch = self.__string.replace('%d-%d' % (lowLevel, highLevel), '') # But for level-ranged searches, we actually perform multiple searches. lowAndHighLevel = [min(lowLevel, 30), min(highLevel, 30)] lowAndHighLevel.sort() # in case they specified 20-11 (high first) lowLevel, highLevel = lowAndHighLevel searchStrings = [ '%s %d' % (baseSearch, l) for l in range(lowLevel, highLevel+1) ] #logging.debug('searchStrings: %(searchStrings)r' % locals()) resultsPerLevel=200/len(searchStrings) ret = [] [ ret.extend([r for r in SearchResult.search(ss, limit=resultsPerLevel) if r.isPublic]) \ for ss in searchStrings \ ] if self.__user: userId = self.__user.user_id() # Limit by owner at either the query or result level if ret is not None: ret = [ r for r in ret if userId in (r.viewerIds or ()) or self.__user in r.viewers or self.__user == r.owner ] else: # XXX Hopefully, this is temporary code. It handles the fact that # earlier versions of v31 didn't have the viewers field. badResults = \ [ sr for sr in SearchResult.gql('WHERE owner = :1', self.__user) \ if self.__user not in sr.viewers \ ] db.delete(badResults) [ br.model.putSearchResult() for br in badResults ] ret = {} for propName, matchValue in \ ( ('viewers', self.__user), ('viewerIds', userId), ): [ ret.update({str(sr.key()): sr}) \ for sr in SearchResult.gql('WHERE %s = :1' % propName, matchValue) \ ] ret = ret.values() # We had a bug for a while that caused 2 SearchResult models for each character. # Clean up from that. newRet, dupResults = {}, [] for thisResult in ret: firstRec = newRet.get(thisResult.modelKey, None) if firstRec is None: newRet[thisResult.modelKey] = thisResult else: dupResults.append(thisResult) dupResults and db.delete(dupResults) ret = newRet.values() ret.sort(lambda x,y:cmp(x.title, y.title)) if self.__campaignKey: from IP4ELibs import models self.__theCampaign = models.Campaign.get(self.__campaignKey) characterKeys = [ str(c.key()) for c in self.__theCampaign.characters ] ret = characterKeys and SearchResult.gql('WHERE modelKey IN :1 ORDER BY title', characterKeys) or [] # Either way, we can limit by type ret = ret or [] if self.__typeList is not None: ret = [ r for r in ret if r.modelType in self.__typeList ] # Finally, make sure the search results refer to models that still exist. # We'd like to remove this code eventually; it exists because we weren't # always deleting search results when models were deleted. Now we are. from IP4ELibs import models rawModelKeysAndTypes = [ (r.modelKey, r.modelType) for r in ret ] modelKeysAndTypes = [ (db.Key(IP4DB.hrdKey(r.modelKey)), r.modelType) for r in ret ] #raise RuntimeError, 'RAW: %(rawModelKeysAndTypes)r, KEYED: %(modelKeysAndTypes)r' % locals() modelExistenceBooleans = \ [ getattr(models, mType).all(keys_only=True).filter('__key__ =', mKey).count() \ for mKey, mType in modelKeysAndTypes \ ] db.delete([ r for (r, mBool) in zip(ret, modelExistenceBooleans ) if not mBool ]) ret = [ r for (r, mBool) in zip(ret, modelExistenceBooleans ) if mBool ] return ret