forked from zaclys/searxng
		
	Merge pull request #513 from a01200356/wolframalpha
WolframAlpha infobox
This commit is contained in:
		
						commit
						308613e586
					
				
					 11 changed files with 450 additions and 315 deletions
				
			
		|  | @ -1,43 +1,60 @@ | |||
| # Wolfram Alpha (Maths) | ||||
| # Wolfram Alpha (Science) | ||||
| # | ||||
| # @website     http://www.wolframalpha.com | ||||
| # @provide-api yes (http://api.wolframalpha.com/v2/) | ||||
| # @website     https://www.wolframalpha.com | ||||
| # @provide-api yes (https://api.wolframalpha.com/v2/) | ||||
| # | ||||
| # @using-api   yes | ||||
| # @results     XML | ||||
| # @stable      yes | ||||
| # @parse       result | ||||
| # @parse       url, infobox | ||||
| 
 | ||||
| from urllib import urlencode | ||||
| from lxml import etree | ||||
| from re import search | ||||
| 
 | ||||
| # search-url | ||||
| base_url = 'http://api.wolframalpha.com/v2/query' | ||||
| search_url = base_url + '?appid={api_key}&{query}&format=plaintext' | ||||
| site_url = 'http://www.wolframalpha.com/input/?{query}' | ||||
| search_url = 'https://api.wolframalpha.com/v2/query?appid={api_key}&{query}' | ||||
| site_url = 'https://www.wolframalpha.com/input/?{query}' | ||||
| api_key = ''  # defined in settings.yml | ||||
| 
 | ||||
| # xpath variables | ||||
| failure_xpath = '/queryresult[attribute::success="false"]' | ||||
| answer_xpath = '//pod[attribute::primary="true"]/subpod/plaintext' | ||||
| input_xpath = '//pod[starts-with(attribute::title, "Input")]/subpod/plaintext' | ||||
| input_xpath = '//pod[starts-with(attribute::id, "Input")]/subpod/plaintext' | ||||
| pods_xpath = '//pod' | ||||
| subpods_xpath = './subpod' | ||||
| pod_id_xpath = './@id' | ||||
| pod_title_xpath = './@title' | ||||
| plaintext_xpath = './plaintext' | ||||
| image_xpath = './img' | ||||
| img_src_xpath = './@src' | ||||
| img_alt_xpath = './@alt' | ||||
| 
 | ||||
| # pods to display as image in infobox | ||||
| # this pods do return a plaintext, but they look better and are more useful as images | ||||
| image_pods = {'VisualRepresentation', | ||||
|               'Illustration'} | ||||
| 
 | ||||
| 
 | ||||
| # do search-request | ||||
| def request(query, params): | ||||
|     params['url'] = search_url.format(query=urlencode({'input': query}), | ||||
|                                       api_key=api_key) | ||||
|     params['headers']['Referer'] = site_url.format(query=urlencode({'i': query})) | ||||
| 
 | ||||
|     return params | ||||
| 
 | ||||
| 
 | ||||
| # replace private user area characters to make text legible | ||||
| def replace_pua_chars(text): | ||||
|     pua_chars = {u'\uf74c': 'd', | ||||
|                  u'\uf74d': u'\u212f', | ||||
|                  u'\uf74e': 'i', | ||||
|                  u'\uf7d9': '='} | ||||
|     pua_chars = {u'\uf522': u'\u2192',  # rigth arrow | ||||
|                  u'\uf7b1': u'\u2115',  # set of natural numbers | ||||
|                  u'\uf7b4': u'\u211a',  # set of rational numbers | ||||
|                  u'\uf7b5': u'\u211d',  # set of real numbers | ||||
|                  u'\uf7bd': u'\u2124',  # set of integer numbers | ||||
|                  u'\uf74c': 'd',        # differential | ||||
|                  u'\uf74d': u'\u212f',  # euler's number | ||||
|                  u'\uf74e': 'i',        # imaginary number | ||||
|                  u'\uf7d9': '='}        # equals sign | ||||
| 
 | ||||
|     for k, v in pua_chars.iteritems(): | ||||
|         text = text.replace(k, v) | ||||
|  | @ -55,23 +72,51 @@ def response(resp): | |||
|     if search_results.xpath(failure_xpath): | ||||
|         return [] | ||||
| 
 | ||||
|     # parse answers | ||||
|     answers = search_results.xpath(answer_xpath) | ||||
|     if answers: | ||||
|         for answer in answers: | ||||
|             answer = replace_pua_chars(answer.text) | ||||
| 
 | ||||
|             results.append({'answer': answer}) | ||||
| 
 | ||||
|     # if there's no input section in search_results, check if answer has the input embedded (before their "=" sign) | ||||
|     try: | ||||
|         query_input = search_results.xpath(input_xpath)[0].text | ||||
|     except IndexError: | ||||
|         query_input = search(u'([^\uf7d9]+)', answers[0].text).group(1) | ||||
|         infobox_title = search_results.xpath(input_xpath)[0].text | ||||
|     except: | ||||
|         infobox_title = None | ||||
| 
 | ||||
|     pods = search_results.xpath(pods_xpath) | ||||
|     result_chunks = [] | ||||
|     for pod in pods: | ||||
|         pod_id = pod.xpath(pod_id_xpath)[0] | ||||
|         pod_title = pod.xpath(pod_title_xpath)[0] | ||||
| 
 | ||||
|         subpods = pod.xpath(subpods_xpath) | ||||
|         if not subpods: | ||||
|             continue | ||||
| 
 | ||||
|         # Appends either a text or an image, depending on which one is more suitable | ||||
|         for subpod in subpods: | ||||
|             content = subpod.xpath(plaintext_xpath)[0].text | ||||
|             image = subpod.xpath(image_xpath) | ||||
| 
 | ||||
|             if content and pod_id not in image_pods: | ||||
| 
 | ||||
|                 # if no input pod was found, title is first plaintext pod | ||||
|                 if not infobox_title: | ||||
|                     infobox_title = content | ||||
| 
 | ||||
|                 content = replace_pua_chars(content) | ||||
|                 result_chunks.append({'label': pod_title, 'value': content}) | ||||
| 
 | ||||
|             elif image: | ||||
|                 result_chunks.append({'label': pod_title, | ||||
|                                       'image': {'src': image[0].xpath(img_src_xpath)[0], | ||||
|                                                 'alt': image[0].xpath(img_alt_xpath)[0]}}) | ||||
| 
 | ||||
|     if not result_chunks: | ||||
|         return [] | ||||
| 
 | ||||
|     # append infobox | ||||
|     results.append({'infobox': infobox_title, | ||||
|                     'attributes': result_chunks, | ||||
|                     'urls': [{'title': 'Wolfram|Alpha', 'url': resp.request.headers['Referer'].decode('utf8')}]}) | ||||
| 
 | ||||
|     # append link to site | ||||
|     result_url = site_url.format(query=urlencode({'i': query_input.encode('utf-8')})) | ||||
|     results.append({'url': result_url, | ||||
|                     'title': query_input + " - Wolfram|Alpha"}) | ||||
|     results.append({'url': resp.request.headers['Referer'].decode('utf8'), | ||||
|                     'title': 'Wolfram|Alpha', | ||||
|                     'content': infobox_title}) | ||||
| 
 | ||||
|     return results | ||||
|  |  | |||
|  | @ -1,26 +1,26 @@ | |||
| # WolframAlpha (Maths) | ||||
| # Wolfram|Alpha (Science) | ||||
| # | ||||
| # @website     http://www.wolframalpha.com/ | ||||
| # @provide-api yes (http://api.wolframalpha.com/v2/) | ||||
| # @website     https://www.wolframalpha.com/ | ||||
| # @provide-api yes (https://api.wolframalpha.com/v2/) | ||||
| # | ||||
| # @using-api   no | ||||
| # @results     HTML | ||||
| # @results     JSON | ||||
| # @stable      no | ||||
| # @parse       answer | ||||
| # @parse       url, infobox | ||||
| 
 | ||||
| from cgi import escape | ||||
| from json import loads | ||||
| from time import time | ||||
| from urllib import urlencode | ||||
| from lxml.etree import XML | ||||
| 
 | ||||
| from searx.poolrequests import get as http_get | ||||
| 
 | ||||
| # search-url | ||||
| url = 'https://www.wolframalpha.com/' | ||||
| search_url = url + 'input/?{query}' | ||||
| 
 | ||||
| search_url = url + 'input/json.jsp'\ | ||||
|     '?async=true'\ | ||||
|     '?async=false'\ | ||||
|     '&banners=raw'\ | ||||
|     '&debuggingdata=false'\ | ||||
|     '&format=image,plaintext,imagemap,minput,moutput'\ | ||||
|  | @ -33,13 +33,17 @@ search_url = url + 'input/json.jsp'\ | |||
|     '&sponsorcategories=true'\ | ||||
|     '&statemethod=deploybutton' | ||||
| 
 | ||||
| # xpath variables | ||||
| scripts_xpath = '//script' | ||||
| title_xpath = '//title' | ||||
| failure_xpath = '//p[attribute::class="pfail"]' | ||||
| referer_url = url + 'input/?{query}' | ||||
| 
 | ||||
| token = {'value': '', | ||||
|          'last_updated': None} | ||||
| 
 | ||||
| # pods to display as image in infobox | ||||
| # this pods do return a plaintext, but they look better and are more useful as images | ||||
| image_pods = {'VisualRepresentation', | ||||
|               'Illustration', | ||||
|               'Symbol'} | ||||
| 
 | ||||
| 
 | ||||
| # seems, wolframalpha resets its token in every hour | ||||
| def obtain_token(): | ||||
|  | @ -62,13 +66,15 @@ def request(query, params): | |||
|     if time() - token['last_updated'] > 3600: | ||||
|         obtain_token() | ||||
|     params['url'] = search_url.format(query=urlencode({'input': query}), token=token['value']) | ||||
|     params['headers']['Referer'] = 'https://www.wolframalpha.com/input/?i=' + query | ||||
|     params['headers']['Referer'] = referer_url.format(query=urlencode({'i': query})) | ||||
| 
 | ||||
|     return params | ||||
| 
 | ||||
| 
 | ||||
| # get response from search-request | ||||
| def response(resp): | ||||
|     results = [] | ||||
| 
 | ||||
|     resp_json = loads(resp.text) | ||||
| 
 | ||||
|     if not resp_json['queryresult']['success']: | ||||
|  | @ -76,20 +82,35 @@ def response(resp): | |||
| 
 | ||||
|     # TODO handle resp_json['queryresult']['assumptions'] | ||||
|     result_chunks = [] | ||||
|     infobox_title = None | ||||
|     for pod in resp_json['queryresult']['pods']: | ||||
|         pod_id = pod.get('id', '') | ||||
|         pod_title = pod.get('title', '') | ||||
| 
 | ||||
|         if 'subpods' not in pod: | ||||
|             continue | ||||
| 
 | ||||
|         if pod_id == 'Input' or not infobox_title: | ||||
|             infobox_title = pod['subpods'][0]['plaintext'] | ||||
| 
 | ||||
|         for subpod in pod['subpods']: | ||||
|             if 'img' in subpod: | ||||
|                 result_chunks.append(u'<p>{0}<br /><img src="{1}" alt="{2}" /></p>' | ||||
|                                      .format(escape(pod_title or subpod['img']['alt']), | ||||
|                                              escape(subpod['img']['src']), | ||||
|                                              escape(subpod['img']['alt']))) | ||||
|             if subpod['plaintext'] != '' and pod_id not in image_pods: | ||||
|                 # append unless it's not an actual answer | ||||
|                 if subpod['plaintext'] != '(requires interactivity)': | ||||
|                     result_chunks.append({'label': pod_title, 'value': subpod['plaintext']}) | ||||
| 
 | ||||
|             elif 'img' in subpod: | ||||
|                 result_chunks.append({'label': pod_title, 'image': subpod['img']}) | ||||
| 
 | ||||
|     if not result_chunks: | ||||
|         return [] | ||||
| 
 | ||||
|     return [{'url': resp.request.headers['Referer'].decode('utf-8'), | ||||
|              'title': 'Wolframalpha', | ||||
|              'content': ''.join(result_chunks)}] | ||||
|     results.append({'infobox': infobox_title, | ||||
|                     'attributes': result_chunks, | ||||
|                     'urls': [{'title': 'Wolfram|Alpha', 'url': resp.request.headers['Referer'].decode('utf8')}]}) | ||||
| 
 | ||||
|     results.append({'url': resp.request.headers['Referer'].decode('utf8'), | ||||
|                     'title': 'Wolfram|Alpha', | ||||
|                     'content': infobox_title}) | ||||
| 
 | ||||
|     return results | ||||
|  |  | |||
|  | @ -310,8 +310,8 @@ engines: | |||
|     shortcut : wa | ||||
|     # You can use the engine using the official stable API, but you need an API key | ||||
|     # See : http://products.wolframalpha.com/api/ | ||||
|     #    engine : wolframalpha_api | ||||
|     #    api_key: 'apikey' # required! | ||||
|     # engine : wolframalpha_api | ||||
|     # api_key: '' # required! | ||||
|     engine : wolframalpha_noapi | ||||
|     timeout: 6.0 | ||||
|     categories : science | ||||
|  |  | |||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -476,6 +476,7 @@ color: @color-font-light; | |||
| 	   margin: 0px 2px 5px 5px; | ||||
| 	   padding: 0px 2px 2px; | ||||
| 	   max-width: 21em; | ||||
|        word-wrap: break-word; | ||||
| 
 | ||||
| 	   .infobox { | ||||
| 	   	    margin: 10px 0 10px; | ||||
|  | @ -485,7 +486,7 @@ color: @color-font-light; | |||
|                     /* box-shadow: 0px 0px 5px #CCC; */ | ||||
| 
 | ||||
| 	   	    img { | ||||
| 		    	max-width: 20em; | ||||
| 		    	max-width: 90%; | ||||
| 			max-heigt: 12em; | ||||
| 			display: block; | ||||
| 			margin: 5px; | ||||
|  | @ -497,7 +498,7 @@ color: @color-font-light; | |||
| 		    } | ||||
| 
 | ||||
| 		    table { | ||||
| 		    	  width: auto; | ||||
|                   table-layout: fixed; | ||||
| 
 | ||||
| 			  td { | ||||
| 		       	     vertical-align: top; | ||||
|  |  | |||
							
								
								
									
										2
									
								
								searx/static/themes/oscar/css/oscar.min.css
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								searx/static/themes/oscar/css/oscar.min.css
									
										
									
									
										vendored
									
									
								
							|  | @ -17,7 +17,7 @@ input[type=checkbox]:not(:checked)+.label_hide_if_not_checked,input[type=checkbo | |||
| .result_download{margin-right:5px} | ||||
| #pagination{margin-top:30px;padding-bottom:50px} | ||||
| .label-default{color:#aaa;background:#fff} | ||||
| .infobox .infobox_part{margin-bottom:20px;word-wrap:break-word} | ||||
| .infobox .infobox_part{margin-bottom:20px;word-wrap:break-word;table-layout:fixed} | ||||
| .infobox .infobox_part:last-child{margin-bottom:0} | ||||
| .search_categories{margin:10px 0;text-transform:capitalize} | ||||
| .cursor-text{cursor:text !important} | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| .infobox { | ||||
|     .infobox_part { | ||||
|         margin-bottom: 20px; | ||||
|          word-wrap: break-word; | ||||
|         word-wrap: break-word; | ||||
|         table-layout: fixed; | ||||
|     } | ||||
|      | ||||
|     .infobox_part:last-child { | ||||
|  |  | |||
|  | @ -7,7 +7,14 @@ | |||
|     <div class="attributes"> | ||||
|         <table> | ||||
|             {% for attribute in infobox.attributes %} | ||||
|             <tr><td>{{ attribute.label }}</td><td>{{ attribute.value }}</td></tr> | ||||
|             <tr> | ||||
|                 <td>{{ attribute.label }}</td> | ||||
|                 {% if attribute.image %} | ||||
|                 <td><img src="{{ image_proxify(attribute.image.src) }}" alt="{{ attribute.image.alt }}" /></td> | ||||
|                 {% else %} | ||||
|                 <td>{{ attribute.value }}</td> | ||||
|                 {% endif %} | ||||
|             </tr> | ||||
|             {% endfor %} | ||||
|         </table> | ||||
|     </div> | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| <div class="panel panel-default infobox"> | ||||
|     <div class="panel-heading"> | ||||
|         <h4 class="panel-title">{{ infobox.infobox }}</h4> | ||||
|         <h4 class="panel-title infobox_part">{{ infobox.infobox }}</h4> | ||||
|     </div> | ||||
|     <div class="panel-body"> | ||||
|         {% if infobox.img_src %}<img class="img-responsive center-block infobox_part" src="{{ image_proxify(infobox.img_src) }}" alt="{{ infobox.infobox }}" />{% endif %} | ||||
|  | @ -11,7 +11,11 @@ | |||
|             {% for attribute in infobox.attributes %} | ||||
|             <tr> | ||||
|                 <td>{{ attribute.label }}</td> | ||||
|                 {% if attribute.image %} | ||||
|                 <td><img class="img-responsive" src="{{ image_proxify(attribute.image.src) }}" alt="{{ attribute.image.alt }}" /></td> | ||||
|                 {% else %} | ||||
|                 <td>{{ attribute.value }}</td> | ||||
|                 {% endif %} | ||||
|             </tr> | ||||
|             {% endfor %} | ||||
|         </table> | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from collections import defaultdict | ||||
| import mock | ||||
| from requests import Request | ||||
| from searx.engines import wolframalpha_api | ||||
| from searx.testing import SearxTestCase | ||||
| 
 | ||||
|  | @ -9,17 +10,17 @@ class TestWolframAlphaAPIEngine(SearxTestCase): | |||
| 
 | ||||
|     def test_request(self): | ||||
|         query = 'test_query' | ||||
|         api_key = 'XXXXXX-XXXXXXXXXX' | ||||
|         dicto = defaultdict(dict) | ||||
|         dicto['api_key'] = api_key | ||||
|         params = wolframalpha_api.request(query, dicto) | ||||
| 
 | ||||
|         # TODO: test api_key | ||||
|         self.assertIn('url', params) | ||||
|         self.assertIn('https://api.wolframalpha.com/v2/query?', params['url']) | ||||
|         self.assertIn(query, params['url']) | ||||
|         self.assertIn('wolframalpha.com', params['url']) | ||||
|         self.assertEqual('https://www.wolframalpha.com/input/?i=test_query', params['headers']['Referer']) | ||||
| 
 | ||||
|         self.assertIn('api_key', params) | ||||
|         self.assertIn(api_key, params['api_key']) | ||||
|     def test_replace_pua_chars(self): | ||||
|         self.assertEqual('i', wolframalpha_api.replace_pua_chars(u'\uf74e')) | ||||
| 
 | ||||
|     def test_response(self): | ||||
|         self.assertRaises(AttributeError, wolframalpha_api.response, None) | ||||
|  | @ -27,281 +28,137 @@ class TestWolframAlphaAPIEngine(SearxTestCase): | |||
|         self.assertRaises(AttributeError, wolframalpha_api.response, '') | ||||
|         self.assertRaises(AttributeError, wolframalpha_api.response, '[]') | ||||
| 
 | ||||
|         referer_url = 'referer_url' | ||||
|         request = Request(headers={'Referer': referer_url}) | ||||
| 
 | ||||
|         # test failure | ||||
|         xml = '''<?xml version='1.0' encoding='UTF-8'?> | ||||
|         <queryresult success='false' error='false' /> | ||||
|         ''' | ||||
|         # test failure | ||||
|         response = mock.Mock(content=xml) | ||||
|         self.assertEqual(wolframalpha_api.response(response), []) | ||||
| 
 | ||||
|         # test basic case | ||||
|         xml = """<?xml version='1.0' encoding='UTF-8'?> | ||||
|         <queryresult success='true' | ||||
|             error='false' | ||||
|             numpods='6' | ||||
|             datatypes='' | ||||
|             timedout='' | ||||
|             timedoutpods='' | ||||
|             timing='0.684' | ||||
|             parsetiming='0.138' | ||||
|             parsetimedout='false' | ||||
|             recalculate='' | ||||
|             id='MSPa416020a7966dachc463600000f9c66cc21444cfg' | ||||
|             host='http://www3.wolframalpha.com' | ||||
|             server='6' | ||||
|             related='http://www3.wolframalpha.com/api/v2/relatedQueries.jsp?...' | ||||
|             numpods='3' | ||||
|             datatypes='Math' | ||||
|             id='queryresult_id' | ||||
|             host='http://www4c.wolframalpha.com' | ||||
|             related='related_url' | ||||
|             version='2.6'> | ||||
|          <pod title='Input' | ||||
|              scanner='Identity' | ||||
|              id='Input' | ||||
|              position='100' | ||||
|              error='false' | ||||
|              numsubpods='1'> | ||||
|           <subpod title=''> | ||||
|            <plaintext>sqrt(-1)</plaintext> | ||||
|           </subpod> | ||||
|          </pod> | ||||
|          <pod title='Result' | ||||
|              scanner='Simplification' | ||||
|              id='Result' | ||||
|              position='200' | ||||
|              error='false' | ||||
|              numsubpods='1' | ||||
|              primary='true'> | ||||
|           <subpod title=''> | ||||
|            <plaintext></plaintext> | ||||
|           </subpod> | ||||
|           <states count='1'> | ||||
|            <state name='Step-by-step solution' | ||||
|                input='Result__Step-by-step solution' /> | ||||
|           </states> | ||||
|          </pod> | ||||
|          <pod title='Polar coordinates' | ||||
|              scanner='Numeric' | ||||
|              id='PolarCoordinates' | ||||
|              position='300' | ||||
|              error='false' | ||||
|              numsubpods='1'> | ||||
|           <subpod title=''> | ||||
|            <plaintext>r1 (radius), θ90° (angle)</plaintext> | ||||
|           </subpod> | ||||
|          </pod> | ||||
|          <pod title='Position in the complex plane' | ||||
|              scanner='Numeric' | ||||
|              id='PositionInTheComplexPlane' | ||||
|              position='400' | ||||
|              error='false' | ||||
|              numsubpods='1'> | ||||
|           <subpod title=''> | ||||
|            <plaintext></plaintext> | ||||
|           </subpod> | ||||
|          </pod> | ||||
|          <pod title='All 2nd roots of -1' | ||||
|              scanner='RootsOfUnity' | ||||
|              id='' | ||||
|              position='500' | ||||
|              error='false' | ||||
|              numsubpods='2'> | ||||
|           <subpod title=''> | ||||
|            <plaintext>  (principal root)</plaintext> | ||||
|           </subpod> | ||||
|           <subpod title=''> | ||||
|            <plaintext>-</plaintext> | ||||
|           </subpod> | ||||
|          </pod> | ||||
|          <pod title='Plot of all roots in the complex plane' | ||||
|              scanner='RootsOfUnity' | ||||
|              id='PlotOfAllRootsInTheComplexPlane' | ||||
|              position='600' | ||||
|              error='false' | ||||
|              numsubpods='1'> | ||||
|           <subpod title=''> | ||||
|            <plaintext></plaintext> | ||||
|           </subpod> | ||||
|          </pod> | ||||
|         </queryresult> | ||||
|         """ | ||||
|         # test private user area char in response | ||||
|         response = mock.Mock(content=xml) | ||||
|         results = wolframalpha_api.response(response) | ||||
|         self.assertEqual(type(results), list) | ||||
|         self.assertEqual(len(results), 2) | ||||
|         self.assertIn('i', results[0]['answer']) | ||||
|         self.assertIn('sqrt(-1) - Wolfram|Alpha', results[1]['title']) | ||||
|         self.assertEquals('http://www.wolframalpha.com/input/?i=sqrt%28-1%29', results[1]['url']) | ||||
| 
 | ||||
|         xml = """<?xml version='1.0' encoding='UTF-8'?> | ||||
|             <queryresult success='true' | ||||
|                 error='false' | ||||
|                 numpods='2' | ||||
|                 datatypes='' | ||||
|                 timedout='' | ||||
|                 timedoutpods='' | ||||
|                 timing='1.286' | ||||
|                 parsetiming='0.255' | ||||
|                 parsetimedout='false' | ||||
|                 recalculate='' | ||||
|                 id='MSPa195222ad740ede5214h30000480ca61h003d3gd6' | ||||
|                 host='http://www3.wolframalpha.com' | ||||
|                 server='20' | ||||
|                 related='http://www3.wolframalpha.com/api/v2/relatedQueries.jsp?id=...' | ||||
|                 version='2.6'> | ||||
|              <pod title='Indefinite integral' | ||||
|                  scanner='Integral' | ||||
|                  id='IndefiniteIntegral' | ||||
|                  position='100' | ||||
|                  error='false' | ||||
|             <pod title='Input' | ||||
|                  scanner='Identity' | ||||
|                  id='Input' | ||||
|                  numsubpods='1'> | ||||
|                   <subpod title=''> | ||||
|                        <img src='input_img_src.gif' | ||||
|                            alt='input_img_alt' | ||||
|                            title='input_img_title' /> | ||||
|                        <plaintext>input_plaintext</plaintext> | ||||
|                   </subpod> | ||||
|              </pod> | ||||
|              <pod title='Result' | ||||
|                  scanner='Simplification' | ||||
|                  id='Result' | ||||
|                  numsubpods='1' | ||||
|                  primary='true'> | ||||
|               <subpod title=''> | ||||
|                <plaintext>∫1/xxlog(x)+constant</plaintext> | ||||
|               </subpod> | ||||
|               <states count='1'> | ||||
|                <state name='Step-by-step solution' | ||||
|                    input='IndefiniteIntegral__Step-by-step solution' /> | ||||
|               </states> | ||||
|               <infos count='1'> | ||||
|                <info text='log(x) is the natural logarithm'> | ||||
|                 <link url='http://reference.wolfram.com/mathematica/ref/Log.html' | ||||
|                     text='Documentation' | ||||
|                     title='Mathematica' /> | ||||
|                 <link url='http://functions.wolfram.com/ElementaryFunctions/Log' | ||||
|                     text='Properties' | ||||
|                     title='Wolfram Functions Site' /> | ||||
|                 <link url='http://mathworld.wolfram.com/NaturalLogarithm.html' | ||||
|                     text='Definition' | ||||
|                     title='MathWorld' /> | ||||
|                </info> | ||||
|               </infos> | ||||
|                   <subpod title=''> | ||||
|                        <img src='result_img_src.gif' | ||||
|                            alt='result_img_alt' | ||||
|                            title='result_img_title' /> | ||||
|                        <plaintext>result_plaintext</plaintext> | ||||
|                   </subpod> | ||||
|              </pod> | ||||
|              <pod title='Plots of the integral' | ||||
|                  scanner='Integral' | ||||
|                  id='Plot' | ||||
|                  position='200' | ||||
|                  error='false' | ||||
|                  numsubpods='2'> | ||||
|               <subpod title=''> | ||||
|                <plaintext></plaintext> | ||||
|                <states count='1'> | ||||
|                 <statelist count='2' | ||||
|                     value='Complex-valued plot' | ||||
|                     delimiters=''> | ||||
|                  <state name='Complex-valued plot' | ||||
|                      input='Plot__1_Complex-valued plot' /> | ||||
|                  <state name='Real-valued plot' | ||||
|                      input='Plot__1_Real-valued plot' /> | ||||
|                 </statelist> | ||||
|                </states> | ||||
|               </subpod> | ||||
|               <subpod title=''> | ||||
|                <plaintext></plaintext> | ||||
|                <states count='1'> | ||||
|                 <statelist count='2' | ||||
|                     value='Complex-valued plot' | ||||
|                     delimiters=''> | ||||
|                  <state name='Complex-valued plot' | ||||
|                      input='Plot__2_Complex-valued plot' /> | ||||
|                  <state name='Real-valued plot' | ||||
|                      input='Plot__2_Real-valued plot' /> | ||||
|                 </statelist> | ||||
|                </states> | ||||
|               </subpod> | ||||
|              <pod title='Manipulatives illustration' | ||||
|                  scanner='Arithmetic' | ||||
|                  id='Illustration' | ||||
|                  numsubpods='1'> | ||||
|                   <subpod title=''> | ||||
|                        <img src='illustration_img_src.gif' | ||||
|                            alt='illustration_img_alt' /> | ||||
|                        <plaintext>illustration_plaintext</plaintext> | ||||
|                   </subpod> | ||||
|              </pod> | ||||
|              <assumptions count='1'> | ||||
|               <assumption type='Clash' | ||||
|                   word='integral' | ||||
|                   template='Assuming "${word}" is ${desc1}. Use as ${desc2} instead' | ||||
|                   count='2'> | ||||
|                <value name='IntegralsWord' | ||||
|                    desc='an integral' | ||||
|                    input='*C.integral-_*IntegralsWord-' /> | ||||
|                <value name='MathematicalFunctionIdentityPropertyClass' | ||||
|                    desc='a function property' | ||||
|                    input='*C.integral-_*MathematicalFunctionIdentityPropertyClass-' /> | ||||
|               </assumption> | ||||
|              </assumptions> | ||||
|             </queryresult> | ||||
|         </queryresult> | ||||
|         """ | ||||
|         # test integral | ||||
|         response = mock.Mock(content=xml) | ||||
|         response = mock.Mock(content=xml, request=request) | ||||
|         results = wolframalpha_api.response(response) | ||||
|         self.assertEqual(type(results), list) | ||||
|         self.assertEqual(len(results), 2) | ||||
|         self.assertIn('log(x)+c', results[0]['answer']) | ||||
|         self.assertIn('∫1/xx - Wolfram|Alpha'.decode('utf-8'), results[1]['title']) | ||||
|         self.assertEquals('http://www.wolframalpha.com/input/?i=%E2%88%AB1%2Fx%EF%9D%8Cx', results[1]['url']) | ||||
|         self.assertEqual('input_plaintext', results[0]['infobox']) | ||||
| 
 | ||||
|         self.assertEqual(len(results[0]['attributes']), 3) | ||||
|         self.assertEqual('Input', results[0]['attributes'][0]['label']) | ||||
|         self.assertEqual('input_plaintext', results[0]['attributes'][0]['value']) | ||||
|         self.assertEqual('Result', results[0]['attributes'][1]['label']) | ||||
|         self.assertEqual('result_plaintext', results[0]['attributes'][1]['value']) | ||||
|         self.assertEqual('Manipulatives illustration', results[0]['attributes'][2]['label']) | ||||
|         self.assertEqual('illustration_img_src.gif', results[0]['attributes'][2]['image']['src']) | ||||
|         self.assertEqual('illustration_img_alt', results[0]['attributes'][2]['image']['alt']) | ||||
| 
 | ||||
|         self.assertEqual(len(results[0]['urls']), 1) | ||||
| 
 | ||||
|         self.assertEqual(referer_url, results[0]['urls'][0]['url']) | ||||
|         self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title']) | ||||
|         self.assertEqual(referer_url, results[1]['url']) | ||||
|         self.assertEqual('Wolfram|Alpha', results[1]['title']) | ||||
| 
 | ||||
|         # test calc | ||||
|         xml = """<?xml version='1.0' encoding='UTF-8'?> | ||||
|         <queryresult success='true' | ||||
|             error='false' | ||||
|             numpods='4' | ||||
|             datatypes='Solve' | ||||
|             timedout='' | ||||
|             timedoutpods='' | ||||
|             timing='0.79' | ||||
|             parsetiming='0.338' | ||||
|             numpods='2' | ||||
|             datatypes='' | ||||
|             parsetimedout='false' | ||||
|             recalculate='' | ||||
|             id='MSPa7481f7i06d25h3deh2900004810i3a78d9b4fdc' | ||||
|             id='queryresult_id' | ||||
|             host='http://www5b.wolframalpha.com' | ||||
|             server='23' | ||||
|             related='http://www5b.wolframalpha.com/api/v2/relatedQueries.jsp?id=...' | ||||
|             version='2.6'> | ||||
|          <pod title='Input interpretation' | ||||
|              scanner='Identity' | ||||
|              id='Input' | ||||
|              position='100' | ||||
|              error='false' | ||||
|              numsubpods='1'> | ||||
|           <subpod title=''> | ||||
|            <plaintext>solve x^2+x0</plaintext> | ||||
|           </subpod> | ||||
|          </pod> | ||||
|          <pod title='Results' | ||||
|              scanner='Solve' | ||||
|              id='Result' | ||||
|              position='200' | ||||
|              error='false' | ||||
|              numsubpods='2' | ||||
|              primary='true'> | ||||
|           <subpod title=''> | ||||
|            <plaintext>x-1</plaintext> | ||||
|           </subpod> | ||||
|           <subpod title=''> | ||||
|            <plaintext>x0</plaintext> | ||||
|           </subpod> | ||||
|           <states count='1'> | ||||
|            <state name='Step-by-step solution' | ||||
|                input='Result__Step-by-step solution' /> | ||||
|           </states> | ||||
|          </pod> | ||||
|          <pod title='Root plot' | ||||
|              scanner='Solve' | ||||
|              id='RootPlot' | ||||
|              position='300' | ||||
|              error='false' | ||||
|              numsubpods='1'> | ||||
|           <subpod title=''> | ||||
|            <plaintext></plaintext> | ||||
|           </subpod> | ||||
|          </pod> | ||||
|          <pod title='Number line' | ||||
|              scanner='Solve' | ||||
|              id='NumberLine' | ||||
|              position='400' | ||||
|              error='false' | ||||
|              numsubpods='1'> | ||||
|           <subpod title=''> | ||||
|            <plaintext></plaintext> | ||||
|           </subpod> | ||||
|          </pod> | ||||
|             related='related_url' | ||||
|             version='2.6' > | ||||
|             <pod title='Indefinite integral' | ||||
|                 scanner='Integral' | ||||
|                 id='IndefiniteIntegral' | ||||
|                 error='false' | ||||
|                 numsubpods='1' | ||||
|                 primary='true'> | ||||
|                 <subpod title=''> | ||||
|                     <img src='integral_image.gif' | ||||
|                         alt='integral_img_alt' | ||||
|                         title='integral_img_title' /> | ||||
|                     <plaintext>integral_plaintext</plaintext> | ||||
|                 </subpod> | ||||
|             </pod> | ||||
|             <pod title='Plot of the integral' | ||||
|                 scanner='Integral' | ||||
|                 id='Plot' | ||||
|                 error='false' | ||||
|                 numsubpods='1'> | ||||
|                 <subpod title=''> | ||||
|                     <img src='plot.gif' | ||||
|                         alt='plot_alt' | ||||
|                         title='' /> | ||||
|                     <plaintext></plaintext> | ||||
|                 </subpod> | ||||
|             </pod> | ||||
|         </queryresult> | ||||
|         """ | ||||
|         # test ecuation with multiple answers | ||||
|         response = mock.Mock(content=xml) | ||||
|         response = mock.Mock(content=xml, request=request) | ||||
|         results = wolframalpha_api.response(response) | ||||
|         self.assertEqual(type(results), list) | ||||
|         self.assertEqual(len(results), 3) | ||||
|         self.assertIn('x=-1', results[0]['answer']) | ||||
|         self.assertIn('x=0', results[1]['answer']) | ||||
|         self.assertIn('solve x^2+x0 - Wolfram|Alpha'.decode('utf-8'), results[2]['title']) | ||||
|         self.assertEquals('http://www.wolframalpha.com/input/?i=solve+x%5E2%2Bx%EF%9F%990', results[2]['url']) | ||||
|         self.assertEqual(len(results), 2) | ||||
|         self.assertEqual('integral_plaintext', results[0]['infobox']) | ||||
| 
 | ||||
|         self.assertEqual(len(results[0]['attributes']), 2) | ||||
|         self.assertEqual('Indefinite integral', results[0]['attributes'][0]['label']) | ||||
|         self.assertEqual('integral_plaintext', results[0]['attributes'][0]['value']) | ||||
|         self.assertEqual('Plot of the integral', results[0]['attributes'][1]['label']) | ||||
|         self.assertEqual('plot.gif', results[0]['attributes'][1]['image']['src']) | ||||
|         self.assertEqual('plot_alt', results[0]['attributes'][1]['image']['alt']) | ||||
| 
 | ||||
|         self.assertEqual(len(results[0]['urls']), 1) | ||||
| 
 | ||||
|         self.assertEqual(referer_url, results[0]['urls'][0]['url']) | ||||
|         self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title']) | ||||
|         self.assertEqual(referer_url, results[1]['url']) | ||||
|         self.assertEqual('Wolfram|Alpha', results[1]['title']) | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from collections import defaultdict | ||||
| import mock | ||||
| from requests import Request | ||||
| from searx.engines import wolframalpha_noapi | ||||
| from searx.testing import SearxTestCase | ||||
| 
 | ||||
|  | @ -9,15 +11,212 @@ class TestWolframAlphaNoAPIEngine(SearxTestCase): | |||
|     def test_request(self): | ||||
|         query = 'test_query' | ||||
|         dicto = defaultdict(dict) | ||||
|         dicto['pageno'] = 1 | ||||
|         params = wolframalpha_noapi.request(query, dicto) | ||||
| 
 | ||||
|         self.assertIn('url', params) | ||||
|         self.assertIn('https://www.wolframalpha.com/input/json.jsp', params['url']) | ||||
|         self.assertIn(query, params['url']) | ||||
|         self.assertIn('wolframalpha.com', params['url']) | ||||
|         self.assertEqual('https://www.wolframalpha.com/input/?i=test_query', params['headers']['Referer']) | ||||
| 
 | ||||
|     def test_response(self): | ||||
|         self.assertRaises(AttributeError, wolframalpha_noapi.response, None) | ||||
|         self.assertRaises(AttributeError, wolframalpha_noapi.response, []) | ||||
|         self.assertRaises(AttributeError, wolframalpha_noapi.response, '') | ||||
|         self.assertRaises(AttributeError, wolframalpha_noapi.response, '[]') | ||||
|         # TODO | ||||
| 
 | ||||
|         referer_url = 'referer_url' | ||||
|         request = Request(headers={'Referer': referer_url}) | ||||
| 
 | ||||
|         # test failure | ||||
|         json = ''' | ||||
|         {"queryresult" : { | ||||
|             "success" : false, | ||||
|             "error" : false, | ||||
|             "numpods" : 0, | ||||
|             "id" : "", | ||||
|             "host" : "https:\/\/www5a.wolframalpha.com", | ||||
|             "didyoumeans" : {} | ||||
|         }} | ||||
|         ''' | ||||
|         response = mock.Mock(text=json, request=request) | ||||
|         self.assertEqual(wolframalpha_noapi.response(response), []) | ||||
| 
 | ||||
|         # test basic case | ||||
|         json = ''' | ||||
|         {"queryresult" : { | ||||
|             "success" : true, | ||||
|             "error" : false, | ||||
|             "numpods" : 6, | ||||
|             "datatypes" : "Math", | ||||
|             "id" : "queryresult_id", | ||||
|             "host" : "https:\/\/www5b.wolframalpha.com", | ||||
|             "related" : "related_url", | ||||
|             "version" : "2.6", | ||||
|             "pods" : [ | ||||
|                 { | ||||
|                     "title" : "Input", | ||||
|                     "scanners" : [ | ||||
|                         "Identity" | ||||
|                     ], | ||||
|                     "id" : "Input", | ||||
|                     "error" : false, | ||||
|                     "numsubpods" : 1, | ||||
|                     "subpods" : [ | ||||
|                         { | ||||
|                             "title" : "", | ||||
|                             "img" : { | ||||
|                                 "src" : "input_img_src.gif", | ||||
|                                 "alt" : "input_img_alt", | ||||
|                                 "title" : "input_img_title" | ||||
|                             }, | ||||
|                             "plaintext" : "input_plaintext", | ||||
|                             "minput" : "input_minput" | ||||
|                         } | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "title" : "Result", | ||||
|                     "scanners" : [ | ||||
|                         "Simplification" | ||||
|                     ], | ||||
|                     "id" : "Result", | ||||
|                     "error" : false, | ||||
|                     "numsubpods" : 1, | ||||
|                     "primary" : true, | ||||
|                     "subpods" : [ | ||||
|                         { | ||||
|                             "title" : "", | ||||
|                             "img" : { | ||||
|                                 "src" : "result_img_src.gif", | ||||
|                                 "alt" : "result_img_alt", | ||||
|                                 "title" : "result_img_title" | ||||
|                             }, | ||||
|                             "plaintext" : "result_plaintext", | ||||
|                             "moutput" : "result_moutput" | ||||
|                         } | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "title" : "Manipulatives illustration", | ||||
|                     "scanners" : [ | ||||
|                         "Arithmetic" | ||||
|                     ], | ||||
|                     "id" : "Illustration", | ||||
|                     "error" : false, | ||||
|                     "numsubpods" : 1, | ||||
|                     "subpods" : [ | ||||
|                         { | ||||
|                             "title" : "", | ||||
|                             "CDFcontent" : "Resizeable", | ||||
|                             "img" : { | ||||
|                                 "src" : "illustration_img_src.gif", | ||||
|                                 "alt" : "illustration_img_alt", | ||||
|                                 "title" : "illustration_img_title" | ||||
|                             }, | ||||
|                             "plaintext" : "illustration_img_plaintext" | ||||
|                         } | ||||
|                     ] | ||||
|                 } | ||||
|             ] | ||||
|         }} | ||||
|         ''' | ||||
|         response = mock.Mock(text=json, request=request) | ||||
|         results = wolframalpha_noapi.response(response) | ||||
|         self.assertEqual(type(results), list) | ||||
|         self.assertEqual(len(results), 2) | ||||
|         self.assertEqual('input_plaintext', results[0]['infobox']) | ||||
| 
 | ||||
|         self.assertEqual(len(results[0]['attributes']), 3) | ||||
|         self.assertEqual('Input', results[0]['attributes'][0]['label']) | ||||
|         self.assertEqual('input_plaintext', results[0]['attributes'][0]['value']) | ||||
|         self.assertEqual('Result', results[0]['attributes'][1]['label']) | ||||
|         self.assertEqual('result_plaintext', results[0]['attributes'][1]['value']) | ||||
|         self.assertEqual('Manipulatives illustration', results[0]['attributes'][2]['label']) | ||||
|         self.assertEqual('illustration_img_src.gif', results[0]['attributes'][2]['image']['src']) | ||||
|         self.assertEqual('illustration_img_alt', results[0]['attributes'][2]['image']['alt']) | ||||
| 
 | ||||
|         self.assertEqual(len(results[0]['urls']), 1) | ||||
| 
 | ||||
|         self.assertEqual(referer_url, results[0]['urls'][0]['url']) | ||||
|         self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title']) | ||||
|         self.assertEqual(referer_url, results[1]['url']) | ||||
|         self.assertEqual('Wolfram|Alpha', results[1]['title']) | ||||
| 
 | ||||
|         # test calc | ||||
|         json = """ | ||||
|         {"queryresult" : { | ||||
|             "success" : true, | ||||
|             "error" : false, | ||||
|             "numpods" : 2, | ||||
|             "datatypes" : "", | ||||
|             "id" : "queryresult_id", | ||||
|             "host" : "https:\/\/www4b.wolframalpha.com", | ||||
|             "related" : "related_url", | ||||
|             "version" : "2.6", | ||||
|             "pods" : [ | ||||
|                 { | ||||
|                     "title" : "Indefinite integral", | ||||
|                     "scanners" : [ | ||||
|                         "Integral" | ||||
|                     ], | ||||
|                     "id" : "IndefiniteIntegral", | ||||
|                     "error" : false, | ||||
|                     "numsubpods" : 1, | ||||
|                     "primary" : true, | ||||
|                     "subpods" : [ | ||||
|                         { | ||||
|                             "title" : "", | ||||
|                             "img" : { | ||||
|                                 "src" : "integral_img_src.gif", | ||||
|                                 "alt" : "integral_img_alt", | ||||
|                                 "title" : "integral_img_title" | ||||
|                             }, | ||||
|                             "plaintext" : "integral_plaintext", | ||||
|                             "minput" : "integral_minput", | ||||
|                             "moutput" : "integral_moutput" | ||||
|                         } | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "title" : "Plot of the integral", | ||||
|                     "scanners" : [ | ||||
|                         "Integral" | ||||
|                     ], | ||||
|                     "id" : "Plot", | ||||
|                     "error" : false, | ||||
|                     "numsubpods" : 1, | ||||
|                     "subpods" : [ | ||||
|                         { | ||||
|                             "title" : "", | ||||
|                             "img" : { | ||||
|                                 "src" : "plot.gif", | ||||
|                                 "alt" : "plot_alt", | ||||
|                                 "title" : "plot_title" | ||||
|                             }, | ||||
|                             "plaintext" : "", | ||||
|                             "minput" : "plot_minput" | ||||
|                         } | ||||
|                     ] | ||||
|                 } | ||||
|             ] | ||||
|         }} | ||||
|         """ | ||||
|         response = mock.Mock(text=json, request=request) | ||||
|         results = wolframalpha_noapi.response(response) | ||||
|         self.assertEqual(type(results), list) | ||||
|         self.assertEqual(len(results), 2) | ||||
|         self.assertEqual('integral_plaintext', results[0]['infobox']) | ||||
| 
 | ||||
|         self.assertEqual(len(results[0]['attributes']), 2) | ||||
|         self.assertEqual('Indefinite integral', results[0]['attributes'][0]['label']) | ||||
|         self.assertEqual('integral_plaintext', results[0]['attributes'][0]['value']) | ||||
|         self.assertEqual('Plot of the integral', results[0]['attributes'][1]['label']) | ||||
|         self.assertEqual('plot.gif', results[0]['attributes'][1]['image']['src']) | ||||
|         self.assertEqual('plot_alt', results[0]['attributes'][1]['image']['alt']) | ||||
| 
 | ||||
|         self.assertEqual(len(results[0]['urls']), 1) | ||||
| 
 | ||||
|         self.assertEqual(referer_url, results[0]['urls'][0]['url']) | ||||
|         self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title']) | ||||
|         self.assertEqual(referer_url, results[1]['url']) | ||||
|         self.assertEqual('Wolfram|Alpha', results[1]['title']) | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Adam Tauber
						Adam Tauber