Added Pokédexes to replace dex numbers' generations.
[zzz-pokedex.git] / pokedex / lookup.py
index b5b0c6d..69eedc4 100644 (file)
@@ -94,6 +94,7 @@ def open_index(directory=None, session=None, recreate=False):
         table=whoosh.fields.ID(stored=True),
         row_id=whoosh.fields.ID(stored=True),
         language=whoosh.fields.STORED,
         table=whoosh.fields.ID(stored=True),
         row_id=whoosh.fields.ID(stored=True),
         language=whoosh.fields.STORED,
+        iso3166=whoosh.fields.STORED,
         display_name=whoosh.fields.STORED,  # non-lowercased name
         forme_name=whoosh.fields.ID,
     )
         display_name=whoosh.fields.STORED,  # non-lowercased name
         forme_name=whoosh.fields.ID,
     )
@@ -117,9 +118,10 @@ def open_index(directory=None, session=None, recreate=False):
                            row_id=unicode(row.id),
                            forme_name=u'XXX')
 
                            row_id=unicode(row.id),
                            forme_name=u'XXX')
 
-            def add(name, language, score):
+            def add(name, language, iso3166, score):
                 writer.add_document(name=name.lower(), display_name=name,
                                     language=language,
                 writer.add_document(name=name.lower(), display_name=name,
                                     language=language,
+                                    iso3166=iso3166,
                                     **row_key)
                 speller_entries.append((name.lower(), score))
 
                                     **row_key)
                 speller_entries.append((name.lower(), score))
 
@@ -128,7 +130,7 @@ def open_index(directory=None, session=None, recreate=False):
                 row_key['forme_name'] = row.forme_name
 
             name = row.name
                 row_key['forme_name'] = row.forme_name
 
             name = row.name
-            add(name, None, 1)
+            add(name, None, u'us', 1)
 
             # Pokemon also get other languages
             for foreign_name in getattr(row, 'foreign_names', []):
 
             # Pokemon also get other languages
             for foreign_name in getattr(row, 'foreign_names', []):
@@ -138,12 +140,14 @@ def open_index(directory=None, session=None, recreate=False):
                     # no point and it makes spell results confusing
                     continue
 
                     # no point and it makes spell results confusing
                     continue
 
-                add(moonspeak, foreign_name.language.name, 3)
+                add(moonspeak, foreign_name.language.name,
+                               foreign_name.language.iso3166,
+                               3)
 
                 # Add Roomaji too
                 if foreign_name.language.name == 'Japanese':
                     roomaji = romanize(foreign_name.name)
 
                 # Add Roomaji too
                 if foreign_name.language.name == 'Japanese':
                     roomaji = romanize(foreign_name.name)
-                    add(roomaji, u'Roomaji', 8)
+                    add(roomaji, u'Roomaji', u'jp', 8)
 
     writer.commit()
 
 
     writer.commit()
 
@@ -176,7 +180,7 @@ class LanguageWeighting(whoosh.scoring.Weighting):
 rx_is_number = re.compile('^\d+$')
 
 LookupResult = namedtuple('LookupResult',
 rx_is_number = re.compile('^\d+$')
 
 LookupResult = namedtuple('LookupResult',
-                          ['object', 'name', 'language', 'exact'])
+                          ['object', 'name', 'language', 'iso3166', 'exact'])
 
 def _parse_table_name(name):
     """Takes a singular table name, table name, or table object and returns the
 
 def _parse_table_name(name):
     """Takes a singular table name, table name, or table object and returns the
@@ -194,19 +198,49 @@ def _parse_table_name(name):
         # Bogus.  Be nice and return dummy
         return None
 
         # Bogus.  Be nice and return dummy
         return None
 
+def _whoosh_records_to_results(records, session, exact=True):
+    """Converts a list of whoosh's indexed records to LookupResult tuples
+    containing database objects.
+    """
+    # XXX this 'exact' thing is getting kinda leaky.  would like a better way
+    # to handle it, since only lookup() cares about fuzzy results
+    seen = {}
+    results = []
+    for record in records:
+        # Skip dupes
+        seen_key = record['table'], record['row_id']
+        if seen_key in seen:
+            continue
+        seen[seen_key] = True
+
+        cls = indexed_tables[record['table']]
+        obj = session.query(cls).get(record['row_id'])
+
+        results.append(LookupResult(object=obj,
+                                    name=record['display_name'],
+                                    language=record['language'],
+                                    iso3166=record['iso3166'],
+                                    exact=exact))
+
+    return results
+
 
 def lookup(input, valid_types=[], session=None, indices=None, exact_only=False):
     """Attempts to find some sort of object, given a database session and name.
 
 
 def lookup(input, valid_types=[], session=None, indices=None, exact_only=False):
     """Attempts to find some sort of object, given a database session and name.
 
-    Returns a list of named (object, name, language, exact) tuples.  `object`
-    is a database object, `name` is the name under which the object was found,
-    `language` is the name of the language in which the name was found, and
-    `exact` is True iff this was an exact match.
+    Returns a list of named (object, name, language, iso3166, exact) tuples.
+    `object` is a database object, `name` is the name under which the object
+    was found, `language` and `iso3166` are the name and country code of the
+    language in which the name was found, and `exact` is True iff this was an
+    exact match.
 
     This function currently ONLY does fuzzy matching if there are no exact
     matches.
 
 
     This function currently ONLY does fuzzy matching if there are no exact
     matches.
 
-    Formes are not returned; "Shaymin" will return only grass Shaymin.
+    Formes are not returned unless requested; "Shaymin" will return only grass
+    Shaymin.
+
+    Extraneous whitespace is removed with extreme prejudice.
 
     Recognizes:
     - Names: "Eevee", "Surf", "Run Away", "Payapa Berry", etc.
 
     Recognizes:
     - Names: "Eevee", "Surf", "Run Away", "Payapa Berry", etc.
@@ -251,18 +285,20 @@ def lookup(input, valid_types=[], session=None, indices=None, exact_only=False):
     else:
         index, speller = open_index()
 
     else:
         index, speller = open_index()
 
-    name = unicode(input).lower()
+    name = unicode(input).strip().lower()
     exact = True
     form = None
 
     # Remove any type prefix (pokemon:133) before constructing a query
     if ':' in name:
     exact = True
     form = None
 
     # Remove any type prefix (pokemon:133) before constructing a query
     if ':' in name:
-        prefix_chunk, name = name.split(':', 2)
-        prefixes = prefix_chunk.split(',')
+        prefix_chunk, name = name.split(':', 1)
+        name = name.strip()
+
         if not valid_types:
             # Only use types from the query string if none were explicitly
             # provided
         if not valid_types:
             # Only use types from the query string if none were explicitly
             # provided
-            valid_types = prefixes
+            prefixes = prefix_chunk.split(',')
+            valid_types = [_.strip() for _ in prefixes]
 
     # Random lookup
     if name == 'random':
 
     # Random lookup
     if name == 'random':
@@ -287,7 +323,7 @@ def lookup(input, valid_types=[], session=None, indices=None, exact_only=False):
 
         # If there's a space in the input, this might be a form
         if ' ' in name:
 
         # If there's a space in the input, this might be a form
         if ' ' in name:
-            form, formless_name = name.split(' ', 2)
+            form, formless_name = name.split(' ', 1)
             form_query = whoosh.query.Term(u'name', formless_name) \
                        & whoosh.query.Term(u'forme_name', form)
             query = query | form_query
             form_query = whoosh.query.Term(u'name', formless_name) \
                        & whoosh.query.Term(u'forme_name', form)
             query = query | form_query
@@ -319,22 +355,7 @@ def lookup(input, valid_types=[], session=None, indices=None, exact_only=False):
             results.extend(searcher.search(query))
 
     ### Convert results to db objects
             results.extend(searcher.search(query))
 
     ### Convert results to db objects
-    objects = []
-    seen = {}
-    for result in results:
-        # Skip dupe results
-        seen_key = result['table'], result['row_id']
-        if seen_key in seen:
-            continue
-        seen[seen_key] = True
-
-        cls = indexed_tables[result['table']]
-        obj = session.query(cls).get(result['row_id'])
-
-        objects.append(LookupResult(object=obj,
-                                    name=result['display_name'],
-                                    language=result['language'],
-                                    exact=exact))
+    objects = _whoosh_records_to_results(results, session, exact=exact)
 
     # Only return up to 10 matches; beyond that, something is wrong.
     # We strip out duplicate entries above, so it's remotely possible that we
 
     # Only return up to 10 matches; beyond that, something is wrong.
     # We strip out duplicate entries above, so it's remotely possible that we
@@ -375,3 +396,28 @@ def random_lookup(valid_types=[], session=None, indices=None):
 
     return lookup(unicode(n), valid_types=[ partitions[0][0] ],
                   indices=indices, session=session)
 
     return lookup(unicode(n), valid_types=[ partitions[0][0] ],
                   indices=indices, session=session)
+
+def prefix_lookup(prefix, session=None, indices=None):
+    """Returns terms starting with the given exact prefix.
+
+    No special magic is currently done with the name; type prefixes are not
+    recognized.
+
+    `session` and `indices` are treated as with `lookup()`.
+    """
+
+    if not session:
+        session = connect()
+
+    if indices:
+        index, speller = indices
+    else:
+        index, speller = open_index()
+
+    query = whoosh.query.Prefix(u'name', prefix.lower())
+
+    searcher = index.searcher()
+    searcher.weighting = LanguageWeighting()
+    results = searcher.search(query)  # XXX , limit=MAX_LOOKUP_RESULTS)
+
+    return _whoosh_records_to_results(results, session)