def main(db: Database, search: Search, html: HtmlOutput): # First do generic serach html.prepare() LOG.info("Search for items..") items = search.list_items() LOG.info("Update status..") # Then update status of all items db.items_classify(items) # Then query details on new items LOG.info("Search for details ..") search.details( [item for item in items if item.status == DatabaseStatus.NEW]) LOG.info("Saving ..") db.items_save(items) # All done. prepare output def make_table_of_status(title, status): html.make_table(title, list(filter(lambda x: x.status == status, items))) make_table_of_status("New", DatabaseStatus.NEW) make_table_of_status("Gone", DatabaseStatus.GONE) make_table_of_status("Existing", DatabaseStatus.EXISTING) html.finish()
def main(): a = AudioAcquire() s = Search() while True: input('Press Enter to acquire...') text = speech_to_text(a.read()) s.google(text)
def test_get_jirachi(self): # Arrange db = FakeDatabase() engine = Search(db) text = "[Jirachi]" # Act match = engine.find_cards(text)[0] # Assert self.assertEqual("Jirachi", match.name, "Card name should match")
def test_get_jirachi_by_series(self): # Arrange db = FakeDatabase() engine = Search(db) text = "[Jirachi SM7]" # Act match = engine.find_cards(text)[0] # Assert self.assertEqual( match.series.name, "Celestial Storm", "We requested Jirachi {*} from Celestial Storm")
def test_get_incomplete_name(self): # Arrange db = FakeDatabase() engine = Search(db) text = "[Jirach]" # Act match = engine.find_cards(text)[0] # Assert self.assertTrue("Jirachi" in match.name, "Incomplete text should be enough to retrieve a card")
def test_get_last_jirachi(self): # Arrange db = FakeDatabase() engine = Search(db) text = "[Jirachi]" # Act match = engine.find_cards(text)[0] # Assert self.assertEqual( match.series.name, "Team Up", "Oldest Jirachi is in Team Up")
def test_get_jirachi_by_series_and_number(self): # Arrange db = FakeDatabase() engine = Search(db) text = "[Jirachi SM9 99]" # Act match = engine.find_cards(text)[0] # Assert self.assertEqual("Jirachi", match.name, "Card should be found") self.assertEqual("Team Up", match.series.name, "Card should have the right series") self.assertEqual(99, match.number, "Card should have the right collection number")
def test_get_none(self): # Arrange db = FakeDatabase() engine = Search(db) text = "[Kuriboh]" # Act match = engine.find_cards(text)[0] # Assert self.assertIsInstance( match, Card, "We always return a card object") self.assertIsInstance( match, ErrorCard, "We return an Error object instead") self.assertFalse( match.is_valid(), "No Pokémon card should have that name")
def __init__(self, lst_news, window_size=5): """Inits Graph Args: lst_news: list of string. list of news articles. """ self.window_size = window_size self.news = list(lst_news) self.reader = Reader() self.search = Search() self.nodes = self.__create_nodes() self.edges = self.__create_edges() self.edge_weights = self.__create_weights()
def test_get_EX_card(self): # Arrange db = FakeDatabase() engine = Search(db) text1 = "[Pikachu EX]" text2 = "[Pikachu-EX]" # Act match1 = engine.find_cards(text1)[0] match2 = engine.find_cards(text2)[0] # Assert self.assertNotIsInstance( match1, ErrorCard, "Should find a card") self.assertEqual(match1, match2, "Should be the same card")
def test_get_Vmax_card(self): # Arrange db = FakeDatabase() engine = Search(db) text1 = "[Inteleon Vmax]" text2 = "[Inteleon-Vmax]" # Act match1 = engine.find_cards(text1)[0] match2 = engine.find_cards(text2)[0] # Assert self.assertNotIsInstance( match1, ErrorCard, "Should find a card") self.assertEqual(match1, match2, "Should be the same card") self.assertNotEqual(match1.name, "Inteleon V" , "Should not pick the V card")
def test_get_jirachi_by_alternative_series(self): # Arrange db = FakeDatabase() engine = Search(db) text1 = "[Jirachi SM9]" text2 = "[Jirachi TEU]" text3 = "[Jirachi SM09]" # Act match1 = engine.find_cards(text1)[0] match2 = engine.find_cards(text2)[0] match3 = engine.find_cards(text3)[0] # Assert self.assertEqual( match1, match2, "Pokemon.com abbreviation and tournament official abbreviation should match") self.assertEqual( match1, match3, "Pokemon.com abbreviation and Pokécardex abbreviation should match")
def test_get_GX_card(self): # Arrange db = FakeDatabase() engine = Search(db) text1 = "[Pikachu GX]" text2 = "[Pikachu-GX]" # Act match1 = engine.find_cards(text1)[0] match2 = engine.find_cards(text2)[0] # Assert self.assertNotIsInstance( match1, ErrorCard, "Should find a card") # Beware of not picking Pikachu&Zekrom GX self.assertEqual(match1.name, "Pikachu GX", "The right card should be found") self.assertEqual(match1, match2, "Should be the same card")
def main(): from PIL import Image import os from src.gans import Modifier from src.features import AkiwiFeatureGenerator, ResnetFeatureGenerator from src.search import Search, CombinedSearch import warnings warnings.filterwarnings('ignore') folder_gens = { 'akiwi_50': AkiwiFeatureGenerator(50), 'resnet': ResnetFeatureGenerator() } dress_imgs = '../data/images/fashion/dresses/' model_imgs = '../data/images/fashion_models/dresses_clustered/' dress_feats = '../data/features/fashion/dresses/' model_feats = '../data/features/fashion_models/dresses/' dress_search = {} for dir_name, gen in folder_gens.items(): dress_search[dir_name] = Search(dress_imgs, os.path.join(dress_feats, dir_name), gen) model_search = {} for dir_name, gen in folder_gens.items(): model_search[dir_name] = Search(model_imgs, os.path.join(model_feats, dir_name), gen) # combined search dress_resnet50 = CombinedSearch( [dress_search['akiwi_50'], dress_search['resnet']], factors=[2, 1]) model_resnet50 = CombinedSearch( [model_search['akiwi_50'], model_search['resnet']], factors=[2, 1]) modifier = Modifier('../data/models/') app = FashionGANApp(modifier, dress_resnet50, model_resnet50) test_img = Image.open('../data/images/fashion/dresses/1DR21C07A-Q11.jpg') app.start(test_img) print('done')
class TestSearch(unittest.TestCase): def setUp(self): self.search = Search() def test_search(self): mylist = [("dLondon", "Location"), ("Obama", "Person"), ("UNICEF", "Organization"), ("jhfvjhegrfvh", "Location"), ("Noor_Nakhaei", "Person"), ("Center_For_Smart_Health", "Organization")] new, existing = self.search.query(mylist) assert existing == [('Obama', 'Person'), ('UNICEF', 'Organization')] assert new == [("dLondon", "Location"), ("jhfvjhegrfvh", "Location"), ("Noor_Nakhaei", "Person"), ("Center_For_Smart_Health", "Organization")]
def advancedSearch(self, search, startdate, enddate, beds, bathrooms, parking, location, distance): ''' Perform an advanced search on the database Firstly, advancedSearch will use database joins to efficiently filter out all items outside the given date range and location. It will then use the Search class to perform a search on the remaining items. All arguments should be strings. Dates are in the form dd/mm/YYYY. See Search.advancedSearch for a description of the arguments. The only required arguments are the start and end date, which should be set to today if not specified. All other options may be set to the empty string '' or None if they are not provided. ''' # ----------------- # 1. Pre-processing # ----------------- refine = False if startdate and enddate: self.get_available(startdate, enddate, refine=refine) refine = True else: raise Exception('No dates were found when searching.') if location: self.get_near(location.split(', '), distance, refine=refine) refine = True # ------------ # 2. Searching # ------------ s = Search(self._accommodations) result = s.advancedSearch(search, startdate, enddate, beds, bathrooms, parking, location, distance) return result
def main(file_a, file_b, dir_a, dir_b, path='C:\\'): search_in_OS = Search() all_file_list = [] try: all_paths = ( search_in_OS.search_by_criteria(path, file_a=file_a, file_b=file_b, dir_a=dir_a, dir_b=dir_b)) for path in all_paths: creation, modified, accessed = search_in_OS.get_time_list(path) all_file_list.append([path, search_in_OS.get_file_name(path), creation, modified, accessed]) search_in_OS.export_to_csv(all_file_list) except Exception as e: print('Exception' + str(e)) print(all_file_list)
def main(): DATABASE_URL = os.environ['DATABASE_URL'] TOKEN = os.environ['TOKEN'] db = create_engine(DATABASE_URL) conn = db.connect() Base = automap_base() Base.prepare(db, reflect=True) User = Base.classes.users Voice = Base.classes.voices session = Session(db) upload = Upload(session_db=session, tables=[User, Voice]) select = Search(session_db=session, tables=[User, Voice]) # with open('token.txt','r') as token_file: # token = token_file.readline().strip('\n') token = TOKEN updater = Updater(token=token, use_context=True) dsp = updater.dispatcher start_handler = CommandHandler('help', start) upload_handler = ConversationHandler( entry_points=[CommandHandler('upload', upload.upload_info)], states={ UploadState.UPLOAD_INFO: [MessageHandler(Filters.text, upload.upload_info)], UploadState.FILE_UPLOAD: [ MessageHandler(Filters.video | Filters.audio | Filters.voice, upload.parse_media_type) ], UploadState.CLIP_INTERVAL: [MessageHandler(Filters.text, upload.get_clip_interval)] }, fallbacks=[CommandHandler('cancel', upload.cancel)], allow_reentry=True) search_handler = InlineQueryHandler(select.search) dsp.add_handler(start_handler) dsp.add_handler(upload_handler) dsp.add_handler(search_handler) updater.start_polling() updater.idle()
def create_app(config=None): app = Flask(__name__, template_folder='template') Searcher = Search('./src/inverted/inverted_index.txt') # See http://flask.pocoo.org/docs/latest/config/ app.config.update(dict(DEBUG=True)) app.config['JSON_SORT_KEYS'] = False app.config.update(config or {}) # Setup cors headers to allow all domains # https://flask-cors.readthedocs.io/en/latest/ # CORS(app) # Definition of the routes. Put them into their own file. See also # Flask Blueprints: http://flask.pocoo.org/docs/latest/blueprints @app.route("/") def index(): return render_template('index.html') @app.route("/api/search/<query>/<total_docs>") def search_query(query, total_docs): docs = Searcher.search_query(unquote(query)) returned_docs = [] total_docs = int(total_docs) returned_docs.append({'process_time': docs['process_time']}) for key in docs: if key == 'process_time': continue if total_docs > 0: total_docs -= 1 returned_docs.append(Searcher.get_article(key)) else: break return jsonify(returned_docs) @app.route("/api/document/<doc_name>") def get_document(doc_name): return doc_name return app
def test_compare(self, min_sentence_set, min_content_set, valid_result): result = Search().compare(sentence=min_sentence_set, content=min_content_set) assert result == valid_result
def test_minimal(self, min_sentence, min_query): result = Search().toSet(min_sentence) assert min_query == result
class Graph(object): """ Base class which represents the heterogeneous textual graph (undirected graph). G = <V, E>. V is the set of nodes (objects), including 3 types of objects (i.e. new entites, known entities, and contextual words). Entities are words (with label: PERSON, LOCATION, and ORGANIZATION) whereas contextual words are the remaining uni-gram words. New entities are the entities not in DBpedia, and Know entities are the entities in the DBpedia. E is a set of edges (co-occurrences) of entity-entity, entity-word, and word-word corrences. Words within every 5-word sliding window in a news sentence are considered to be co-occuring with each other. The weights are represented by adjacency matrix using dataframe. Attributes: nodes: dictionary of nodes {"N (new entity)": [(word, label)], "K (Known entity)": [(word, label)], "C (Contextual word)": [(word, label)]} in the graph; Includes 3 types of objects (i.e. e new entites, known entities, and contextual words). edges: set contains the tuples. e.g. ("A", "B") indicates a link between node "A" and node "B". weights: The weights are represented by adjacency matrix using dataframe. news: list of news articles (articles are string type). """ def __init__(self, lst_news, window_size=5): """Inits Graph Args: lst_news: list of string. list of news articles. """ self.window_size = window_size self.news = list(lst_news) self.reader = Reader() self.search = Search() self.nodes = self.__create_nodes() self.edges = self.__create_edges() self.edge_weights = self.__create_weights() def __create_nodes(self): """Private class method Takes in a list of news articles (articles are string types): 1) tokenize the articles 2) remove stopwords 3) label words with 3 labels (i.e. PERSON, ORGANIZATION, LOCATION) 4) Match entities (i.e. person, org, loc) against DBpedia Returns: Returns a dictionary contains 3 types of objects (i.e. new entites, known entities, and contextual words). E.g. {"N": [("Washington", "LOCATION")], "K":[("Trump", "PERSON"), ("Hua Wei", "ORGANIZATION")], "C": [("the", "O"), ("am", "O")]} """ # parse news articles tagged_words = self.reader.parse_news(self.news) # seperate entities from contextual words entities, cwords = self.__entities_words(tagged_words) new_e, known_e = self.search.query(entities) ret = dict() ret["N"] = list(set(new_e)) ret["K"] = list(set(known_e)) ret["C"] = list(set(cwords)) return dict(ret) def get_nodes(self): """ Getter method which returns all nodes from self.nodes. """ ret = set() for i in self.nodes["N"]: ret.add(i[0]) for i in self.nodes["K"]: ret.add(i[0]) for i in self.nodes["C"]: ret.add(i[0]) return list(ret) def get_entities(self): """ Getter method which returns a list of entities (i.e. word tagged with "PERSON", "LOCATION", "ORGANIZATION") from self.nodes. """ ret = set() for i in self.nodes["N"]: ret.add(i[0]) for i in self.nodes["K"]: ret.add(i[0]) return list(ret) def get_words(self): """ Getter method which returns a list of contextual words from self.nodes. """ ret = set() for i in self.nodes["C"]: ret.add(i[0]) return list(ret) def __create_edges(self, window_size=5): """Private class method Takes in a list of news articles, and extract the co-occurring links between nodes. Nodes within 5-word sliding window in a news sentence are considered to be co-occuring with each other. The frequncies of nodes co-appearing in news sentences as weights of these links. Returns: Returns a set of links between nodes. """ e = set() for article in self.news: self.tokenized_text = word_tokenize(article) self.tokenized_text = self.reader.filter_stop_words( self.tokenized_text) generator = self.sliding_window(self.tokenized_text, self.window_size) for t in generator: e = e.union(set(itertools.combinations(t, 2))) return set(e) def get_edges(self): """ Getter method which returns a set of edges from self.edges. """ return set(self.edges) def sliding_window(self, seq, n=5): """ Returns a sliding window (of width n) over data from the iterable s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... Args: seq: list of words; This is one news article splitted into a list of words. n: int; size of the sliding window Returns: An iterator contains all the sliced window. See the test case in `tests/test_graph.py` for more details. """ it = iter(seq) result = tuple(islice(it, n)) if len(result) <= n: yield result for elem in it: result = result[1:] + (elem, ) yield result def __create_weights(self): """Private class method Create weights matrix using pandas dataframe. The value at ith row and jth row is the counts of links (undirected) between node i and node j. Returns: Return a copy of dataframe representing the weights matrix. """ words = self.get_nodes() df = pd.DataFrame(index=words, columns=words).fillna(0) for article in self.news: self.tokenized_text = word_tokenize(article) self.tokenized_text = self.reader.filter_stop_words( self.tokenized_text) generator = self.sliding_window(self.tokenized_text, self.window_size) for t in generator: for tup in set(itertools.combinations(t, 2)): if tup[0] != tup[1]: df.loc[tup[0], tup[1]] += 1 df.loc[tup[1], tup[0]] += 1 return df.copy() def get_weights(self): return self.edge_weights.copy() def __entities_words(self, tagged_words): """Private class method Seperate the entity words from the comtextual words. Args: tagged_words: list of strings; a list of tuples (word, label) Returns: entities: words tagged with "PERSON", "LOCATION", "ORGANIZATION". cwords: words tagged with "O" """ entities = list() cwords = list() for word in tagged_words: if word[1] == "O": # contextual words cwords.append(word) else: entities.append(word) assert len(entities) + len(cwords) == len(tagged_words) return entities, cwords def update_weight(self, e, w): """ Update the edge weight in the enternal weight matrix. Args: e: tuple; a tuple contains two nodes, e.g. ("A", "B") w: int; The new weight associated with e """ if e[0] not in set(self.get_nodes()): raise ValueError("Node {} is not in the graph".format(str(e[0]))) if e[1] not in set(self.get_nodes()): raise ValueError("Node {} is not in the graph".format(str(e[1]))) if e in self.edges and w <= 0: self.edge_weights.loc[e[0], e[1]] = w self.edge_weights.loc[e[1], e[0]] = w self.edges.remove(e) self.edges.remove((e[1], e[0])) elif e in self.edges and w > 0: self.edge_weights.loc[e[0], e[1]] = w self.edge_weights.loc[e[1], e[0]] = w else: self.edge_weights.loc[e[0], e[1]] = w self.edge_weights.loc[e[1], e[0]] = w self.edges.add(e)
def test_score(self, min_content, min_sentence, valid_score): result = Search(sentence=min_sentence, content=min_content).score() assert result == valid_score
# bot.py import os import discord from dotenv import load_dotenv from src.search import Search from tests.fake_database import FakeDatabase # Fake initialization - use a fake database to have your bot working from src.utilities_interface import UtilitiesInterface # Fake initialization - you need to extend the Interface classes to fit your database load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') # Fake initialization - use a fake database to have your bot working db = FakeDatabase() utilities = UtilitiesInterface # / TEMP - you will need to extend the Interfaces in order to interact with your own database searcher = Search(db, utilities) client = discord.Client() @client.event async def on_ready(): # Feedback to ensure the bot is on print(f'{client.user.name} has connected to Discord!') @client.event async def on_message(message): if message.author == client.user: return # avoid recursion cards = searcher.find_cards(message.content)
async def dir(self, ctx, *, args=None): """ Search for a directory to upload """ search_terms = args parser = Args(config) try: input_string = args dict, parser, before_args = parser.parse( tuple(input_string.split(' ')), {}) search_terms = " ".join(before_args) args = args.replace(search_terms, '') while args.startswith(" "): args = args[1:] except SystemExit as error: await ctx.send( f"Invalid argument detected, use `{config['DISCORD']['command_prefix']}args` for list of valid args" ) return if ctx.channel.id != int(config['DISCORD']['discord_channel_id']): return search = Search(config=config) if search_terms == None: await ctx.send("Missing search term(s)") return folders_total = await search.searchFolder(search_terms) if folders_total == []: await ctx.send("Nothing Found") return folders = "\n\n• ".join(folders_total) if not folders_total: embed = discord.Embed(description="No files found") elif len(folders_total) >= 2: embed = discord.Embed( title=f"Directory search results for: `{search_terms}`", color=0x00ff40, description=f"```• {folders}```") embed.add_field( name="What Now?", value= f"Please be more specific or use `{config['DISCORD']['command_prefix']}search dir` to find a directory" ) await ctx.send(embed=embed) return elif len(folders_total) == 1: embed = discord.Embed( title=f"Directory search results for: {search_terms}", color=0x00ff40, description=f"```{folders}```") embed.set_footer( text= f"{config['DISCORD']['discord_emojis']['UPLOAD']} to Upload") message = await ctx.send(embed=embed) await message.add_reaction( config['DISCORD']['discord_emojis']['UPLOAD']) channel = message.channel def check(reaction, user): if reaction.message.id == message.id: if str(user.id) == config['DISCORD']['admin_id']: if str( reaction.emoji ) == config['DISCORD']['discord_emojis']['UPLOAD']: return reaction try: await self.bot.wait_for("reaction_add", timeout=120, check=check) except asyncio.TimeoutError: await channel.send(f"Search: `{search_terms}`timed out") else: await self.upload(ctx, path=folders_total[0], search_args=tuple(args.split(" ")), message_id=message.id) # await ctx.send(folders_total) return
def setUp(self): self.tmp_dir = 'tmp_test' self.to_remove = [] os.mkdir(self.tmp_dir) self.search = Search()
class TestSearch(unittest.TestCase): def setUp(self): self.tmp_dir = 'tmp_test' self.to_remove = [] os.mkdir(self.tmp_dir) self.search = Search() def test_extract_vehicle_details(self): data = [{'name': 'nm1'}, {'name': 'nm2'}, {'name': 'nm3'}] expected = ['nm1', 'nm2', 'nm3'] actual = self.search.extract_key_names(data) self.assertEqual(expected, actual) def test_set_api_key(self): expected = 'abc123' key_file = 'tmp_test/test.key' self.to_remove.append(key_file) self.search.config.store_api_key(key_file, expected) key = self.search.config.get_api_key(key_file) self.assertEqual(expected, key) def test_squeeze(self): data = [{'name': 'test1', 'value': 1}, {'name': 'test2', 'value': 2}] expected = {'test1': 1, 'test2': 2} actual = self.search.squeeze(data) self.assertEqual(expected, actual) def test_sort_elements(self): data = {'b': 2, 'c': 3, 'a': 1} expected = [('a', 1), ('b', 2), ('c', 3)] actual = self.search.sort_elements(data.items()) self.assertEqual(expected, actual) def test_get_all_makes(self): actual = subprocess.check_output(['python3', 'run.py', '-get', 'all-makes', '-v']) self.assertTrue(actual) def test_get_all_models(self): actual = subprocess.check_output(['python3', 'run.py', '-get', 'all-models', '-m', 'Ford', '-v']) self.assertTrue(actual) def test_get_all_bodies(self): actual = subprocess.check_output(['python3', 'run.py', '-get', 'all-bodies', '-v']) self.assertTrue(actual) def test_invalid_make(self): actual = subprocess.check_output(['python3', 'run.py', '-m', 'Abc', '-v']) self.assertTrue(actual) def test_invalid_model(self): actual = subprocess.check_output(['python3', 'run.py', '-m', 'Ford', '-M', 'Abc' '-v']) self.assertTrue(actual) def test_invalid_body(self): actual = subprocess.check_output(['python3', 'run.py', '-m', 'Ford', '--body', 'abc', '-v']) self.assertTrue(actual) def test_invalid_gearbox(self): actual = subprocess.check_output(['python3', 'run.py', '-m', 'Ford', '--gearbox', 'abc', '-v']) self.assertTrue(actual) def tearDown(self): for f in self.to_remove: os.remove(f) os.rmdir(self.tmp_dir)
import warnings warnings.filterwarnings('ignore') # Load Search Models folder_gens = {'akiwi_50': AkiwiFeatureGenerator(50), 'resnet': ResnetFeatureGenerator()} dress_imgs = './data/images/fashion/dresses/' model_imgs = './data/images/fashion_models/dresses_clustered/' dress_feats = './data/features/fashion/dresses/' model_feats = './data/features/fashion_models/dresses/' dress_search = {} for dir_name, gen in folder_gens.items(): dress_search[dir_name] = Search(dress_imgs, os.path.join(dress_feats, dir_name), gen) model_search = {} for dir_name, gen in folder_gens.items(): model_search[dir_name] = Search(model_imgs, os.path.join(model_feats, dir_name), gen) # combined search dress_resnet50 = CombinedSearch([dress_search['akiwi_50'], dress_search['resnet']], factors=[2, 1]) model_resnet50 = CombinedSearch([model_search['akiwi_50'], model_search['resnet']], factors=[2, 1]) # FashionGAN Search modifier = Modifier('./data/models/') app = FashionGANApp(modifier, dress_resnet50, model_resnet50) test_img = Image.open('./data/images/fashion/dresses/9815337.jpg')
'akiwi_114': AkiwiFeatureGenerator(114), 'resnet': ResnetFeatureGenerator(), 'resnet_retrained': ResnetFeatureGenerator('../data/models/resnet152_retrained.pth') } # Products product_imgs = '../data/images/fashion/dresses/' product_feats_root = '../data/features/fashion/dresses/' searches = {} for dir_name, gen in folder_gens.items(): searches[dir_name] = Search(product_imgs, os.path.join(product_feats_root, dir_name), gen) searches['resnet+akiwi50 (1:1)'] = CombinedSearch( [searches['akiwi_50'], searches['resnet']], factors=[1, 1]) searches['resnet+akiwi50 (1:2)'] = CombinedSearch( [searches['akiwi_50'], searches['resnet']], factors=[2, 1]) searches['resnet+akiwi50 (1:3)'] = CombinedSearch( [searches['akiwi_50'], searches['resnet']], factors=[3, 1]) # Models model_imgs = '../data/images/fashion_models/dresses_clustered/' model_feats_root = '../data/features/fashion_models/dresses/' searches_m = {}
def setUp(self): self.search = Search()