Przeglądaj źródła

[enh] unit tests for wolframalpha

a01200356 9 lat temu
rodzic
commit
4d8996eb4d

+ 27
- 20
searx/engines/wolframalpha_api.py Wyświetl plik

19
 # xpath variables
19
 # xpath variables
20
 failure_xpath = '/queryresult[attribute::success="false"]'
20
 failure_xpath = '/queryresult[attribute::success="false"]'
21
 answer_xpath = '//pod[attribute::primary="true"]/subpod/plaintext'
21
 answer_xpath = '//pod[attribute::primary="true"]/subpod/plaintext'
22
-input_xpath = '//pod[starts-with(attribute::title, "Input")]/subpod/plaintext'
22
+input_xpath = '//pod[starts-with(attribute::id, "Input")]/subpod/plaintext'
23
 pods_xpath = '//pod'
23
 pods_xpath = '//pod'
24
 subpods_xpath = './subpod'
24
 subpods_xpath = './subpod'
25
+pod_id_xpath = './@id'
25
 pod_title_xpath = './@title'
26
 pod_title_xpath = './@title'
26
 plaintext_xpath = './plaintext'
27
 plaintext_xpath = './plaintext'
27
 image_xpath = './img'
28
 image_xpath = './img'
30
 
31
 
31
 # pods to display as image in infobox
32
 # pods to display as image in infobox
32
 # this pods do return a plaintext, but they look better and are more useful as images
33
 # this pods do return a plaintext, but they look better and are more useful as images
33
-image_pods = {'Visual representation',
34
-              'Manipulatives illustration'}
34
+image_pods = {'VisualRepresentation',
35
+              'Illustration'}
35
 
36
 
36
 
37
 
37
 # do search-request
38
 # do search-request
45
 
46
 
46
 # replace private user area characters to make text legible
47
 # replace private user area characters to make text legible
47
 def replace_pua_chars(text):
48
 def replace_pua_chars(text):
48
-    pua_chars = {u'\uf522': u'\u2192',
49
-                 u'\uf7b1': u'\u2115',
50
-                 u'\uf7b4': u'\u211a',
51
-                 u'\uf7b5': u'\u211d',
52
-                 u'\uf7bd': u'\u2124',
53
-                 u'\uf74c': 'd',
54
-                 u'\uf74d': u'\u212f',
55
-                 u'\uf74e': 'i',
56
-                 u'\uf7d9': '='}
49
+    pua_chars = {u'\uf522': u'\u2192',  # rigth arrow
50
+                 u'\uf7b1': u'\u2115',  # set of natural numbers
51
+                 u'\uf7b4': u'\u211a',  # set of rational numbers
52
+                 u'\uf7b5': u'\u211d',  # set of real numbers
53
+                 u'\uf7bd': u'\u2124',  # set of integer numbers
54
+                 u'\uf74c': 'd',        # differential
55
+                 u'\uf74d': u'\u212f',  # euler's number
56
+                 u'\uf74e': 'i',        # imaginary number
57
+                 u'\uf7d9': '='}        # equals sign
57
 
58
 
58
     for k, v in pua_chars.iteritems():
59
     for k, v in pua_chars.iteritems():
59
         text = text.replace(k, v)
60
         text = text.replace(k, v)
71
     if search_results.xpath(failure_xpath):
72
     if search_results.xpath(failure_xpath):
72
         return []
73
         return []
73
 
74
 
74
-    infobox_title = search_results.xpath(input_xpath)
75
-    if infobox_title:
76
-        infobox_title = replace_pua_chars(infobox_title[0].text)
75
+    try:
76
+        infobox_title = search_results.xpath(input_xpath)[0].text
77
+    except:
78
+        infobox_title = None
77
 
79
 
78
     pods = search_results.xpath(pods_xpath)
80
     pods = search_results.xpath(pods_xpath)
79
     result_chunks = []
81
     result_chunks = []
80
     for pod in pods:
82
     for pod in pods:
81
-        pod_title = replace_pua_chars(pod.xpath(pod_title_xpath)[0])
83
+        pod_id = pod.xpath(pod_id_xpath)[0]
84
+        pod_title = pod.xpath(pod_title_xpath)[0]
82
 
85
 
83
         subpods = pod.xpath(subpods_xpath)
86
         subpods = pod.xpath(subpods_xpath)
84
         if not subpods:
87
         if not subpods:
85
             continue
88
             continue
86
 
89
 
90
+        # Appends either a text or an image, depending on which one is more suitable
87
         for subpod in subpods:
91
         for subpod in subpods:
88
             content = subpod.xpath(plaintext_xpath)[0].text
92
             content = subpod.xpath(plaintext_xpath)[0].text
89
             image = subpod.xpath(image_xpath)
93
             image = subpod.xpath(image_xpath)
90
-            if content and pod_title not in image_pods:
91
-                content = replace_pua_chars(content)
92
-                result_chunks.append({'label': pod_title, 'value': content})
93
 
94
 
94
-                # if there's no input pod, infobox_title is content of first pod
95
+            if content and pod_id not in image_pods:
96
+
97
+                # if no input pod was found, title is first plaintext pod
95
                 if not infobox_title:
98
                 if not infobox_title:
96
                     infobox_title = content
99
                     infobox_title = content
97
 
100
 
101
+                content = replace_pua_chars(content)
102
+                result_chunks.append({'label': pod_title, 'value': content})
103
+
98
             elif image:
104
             elif image:
99
                 result_chunks.append({'label': pod_title,
105
                 result_chunks.append({'label': pod_title,
100
                                       'image': {'src': image[0].xpath(img_src_xpath)[0],
106
                                       'image': {'src': image[0].xpath(img_src_xpath)[0],
103
     if not result_chunks:
109
     if not result_chunks:
104
         return []
110
         return []
105
 
111
 
112
+    # append infobox
106
     results.append({'infobox': infobox_title,
113
     results.append({'infobox': infobox_title,
107
                     'attributes': result_chunks,
114
                     'attributes': result_chunks,
108
                     'urls': [{'title': 'Wolfram|Alpha', 'url': resp.request.headers['Referer']}]})
115
                     'urls': [{'title': 'Wolfram|Alpha', 'url': resp.request.headers['Referer']}]})

+ 34
- 27
searx/engines/wolframalpha_noapi.py Wyświetl plik

48
 
48
 
49
 # pods to display as image in infobox
49
 # pods to display as image in infobox
50
 # this pods do return a plaintext, but they look better and are more useful as images
50
 # this pods do return a plaintext, but they look better and are more useful as images
51
-image_pods = {'Visual representation',
52
-              'Manipulatives illustration',
51
+image_pods = {'VisualRepresentation',
52
+              'Illustration',
53
               'Symbol'}
53
               'Symbol'}
54
 
54
 
55
 
55
 
82
 # get additional pod
82
 # get additional pod
83
 # NOTE: this makes an additional requests to server, so the response will take longer and might reach timeout
83
 # NOTE: this makes an additional requests to server, so the response will take longer and might reach timeout
84
 def get_async_pod(url):
84
 def get_async_pod(url):
85
-    pod = {'subpods': []}
86
-
87
     try:
85
     try:
88
         resp = http_get(url, timeout=2.0)
86
         resp = http_get(url, timeout=2.0)
89
-
90
-        resp_pod = XML(resp.content)
91
-        if resp_pod.xpath(success_xpath):
92
-
93
-            for subpod in resp_pod:
94
-                plaintext = subpod.xpath(plaintext_xpath)[0].text
95
-                if plaintext:
96
-                    pod['subpods'].append({'title': subpod.xpath(title_xpath)[0],
97
-                                           'plaintext': plaintext})
98
-                elif subpod.xpath(image_xpath):
99
-                    pod['subpods'].append({'title': subpod.xpath(title_xpath)[0],
100
-                                           'plaintext': '',
101
-                                           'img': {'src': subpod.xpath(img_src_xpath)[0],
102
-                                                   'alt': subpod.xpath(img_alt_xpath)[0]}})
103
     except:
87
     except:
104
-        pass
88
+        return None
89
+
90
+    if resp:
91
+        return parse_async_pod(resp)
92
+
93
+
94
+def parse_async_pod(resp):
95
+    pod = {'subpods': []}
96
+
97
+    resp_pod = XML(resp.content)
98
+
99
+    if resp_pod.xpath(success_xpath):
100
+        for subpod in resp_pod:
101
+            new_subpod = {'title': subpod.xpath(title_xpath)[0]}
102
+
103
+            plaintext = subpod.xpath(plaintext_xpath)[0].text
104
+            if plaintext:
105
+                new_subpod['plaintext'] = plaintext
106
+            else:
107
+                new_subpod['plaintext'] = ''
108
+
109
+            if subpod.xpath(image_xpath):
110
+                new_subpod['img'] = {'src': subpod.xpath(img_src_xpath)[0],
111
+                                     'alt': subpod.xpath(img_alt_xpath)[0]}
112
+
113
+            pod['subpods'].append(new_subpod)
105
 
114
 
106
     return pod
115
     return pod
107
 
116
 
119
     result_chunks = []
128
     result_chunks = []
120
     infobox_title = None
129
     infobox_title = None
121
     for pod in resp_json['queryresult']['pods']:
130
     for pod in resp_json['queryresult']['pods']:
131
+        pod_id = pod.get('id', '')
122
         pod_title = pod.get('title', '')
132
         pod_title = pod.get('title', '')
123
 
133
 
124
         if 'subpods' not in pod:
134
         if 'subpods' not in pod:
127
                 result = get_async_pod(pod['async'])
137
                 result = get_async_pod(pod['async'])
128
                 if result:
138
                 if result:
129
                     pod = result
139
                     pod = result
140
+                else:
141
+                    continue
130
             else:
142
             else:
131
                 continue
143
                 continue
132
 
144
 
133
-        # infobox title is input or text content on first pod
134
-        if pod_title.startswith('Input') or not infobox_title:
135
-            try:
136
-                infobox_title = pod['subpods'][0]['plaintext']
137
-            except:
138
-                infobox_title = ''
139
-                pass
145
+        if pod_id == 'Input' or not infobox_title:
146
+            infobox_title = pod['subpods'][0]['plaintext']
140
 
147
 
141
         for subpod in pod['subpods']:
148
         for subpod in pod['subpods']:
142
-            if subpod['plaintext'] != '' and pod_title not in image_pods:
149
+            if subpod['plaintext'] != '' and pod_id not in image_pods:
143
                 # append unless it's not an actual answer
150
                 # append unless it's not an actual answer
144
                 if subpod['plaintext'] != '(requires interactivity)':
151
                 if subpod['plaintext'] != '(requires interactivity)':
145
                     result_chunks.append({'label': pod_title, 'value': subpod['plaintext']})
152
                     result_chunks.append({'label': pod_title, 'value': subpod['plaintext']})

+ 2
- 2
searx/settings.yml Wyświetl plik

311
     # You can use the engine using the official stable API, but you need an API key
311
     # You can use the engine using the official stable API, but you need an API key
312
     # See : http://products.wolframalpha.com/api/
312
     # See : http://products.wolframalpha.com/api/
313
     # engine : wolframalpha_api
313
     # engine : wolframalpha_api
314
-    # api_key: '5952JX-X52L3VKWT8' # required!
314
+    # api_key: '' # required!
315
     engine : wolframalpha_noapi
315
     engine : wolframalpha_noapi
316
-    timeout: 10.0
316
+    timeout: 6.0
317
     categories : science
317
     categories : science
318
 
318
 
319
 #The blekko technology and team have joined IBM Watson! -> https://blekko.com/
319
 #The blekko technology and team have joined IBM Watson! -> https://blekko.com/

+ 112
- 255
tests/unit/engines/test_wolframalpha_api.py Wyświetl plik

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 from collections import defaultdict
2
 from collections import defaultdict
3
 import mock
3
 import mock
4
+from requests import Request
4
 from searx.engines import wolframalpha_api
5
 from searx.engines import wolframalpha_api
5
 from searx.testing import SearxTestCase
6
 from searx.testing import SearxTestCase
6
 
7
 
9
 
10
 
10
     def test_request(self):
11
     def test_request(self):
11
         query = 'test_query'
12
         query = 'test_query'
12
-        api_key = 'XXXXXX-XXXXXXXXXX'
13
         dicto = defaultdict(dict)
13
         dicto = defaultdict(dict)
14
-        dicto['api_key'] = api_key
15
         params = wolframalpha_api.request(query, dicto)
14
         params = wolframalpha_api.request(query, dicto)
16
 
15
 
16
+        # TODO: test api_key
17
         self.assertIn('url', params)
17
         self.assertIn('url', params)
18
+        self.assertIn('https://api.wolframalpha.com/v2/query?', params['url'])
18
         self.assertIn(query, params['url'])
19
         self.assertIn(query, params['url'])
19
-        self.assertIn('wolframalpha.com', params['url'])
20
+        self.assertEqual('https://www.wolframalpha.com/input/?i=test_query', params['headers']['Referer'])
20
 
21
 
21
-        self.assertIn('api_key', params)
22
-        self.assertIn(api_key, params['api_key'])
22
+    def test_replace_pua_chars(self):
23
+        self.assertEqual('i', wolframalpha_api.replace_pua_chars(u'\uf74e'))
23
 
24
 
24
     def test_response(self):
25
     def test_response(self):
25
         self.assertRaises(AttributeError, wolframalpha_api.response, None)
26
         self.assertRaises(AttributeError, wolframalpha_api.response, None)
27
         self.assertRaises(AttributeError, wolframalpha_api.response, '')
28
         self.assertRaises(AttributeError, wolframalpha_api.response, '')
28
         self.assertRaises(AttributeError, wolframalpha_api.response, '[]')
29
         self.assertRaises(AttributeError, wolframalpha_api.response, '[]')
29
 
30
 
31
+        referer_url = 'referer_url'
32
+        request = Request(headers={'Referer': referer_url})
33
+
34
+        # test failure
30
         xml = '''<?xml version='1.0' encoding='UTF-8'?>
35
         xml = '''<?xml version='1.0' encoding='UTF-8'?>
31
         <queryresult success='false' error='false' />
36
         <queryresult success='false' error='false' />
32
         '''
37
         '''
33
-        # test failure
34
         response = mock.Mock(content=xml)
38
         response = mock.Mock(content=xml)
35
         self.assertEqual(wolframalpha_api.response(response), [])
39
         self.assertEqual(wolframalpha_api.response(response), [])
36
 
40
 
41
+        # test basic case
37
         xml = """<?xml version='1.0' encoding='UTF-8'?>
42
         xml = """<?xml version='1.0' encoding='UTF-8'?>
38
         <queryresult success='true'
43
         <queryresult success='true'
39
             error='false'
44
             error='false'
40
-            numpods='6'
41
-            datatypes=''
42
-            timedout=''
43
-            timedoutpods=''
44
-            timing='0.684'
45
-            parsetiming='0.138'
46
-            parsetimedout='false'
47
-            recalculate=''
48
-            id='MSPa416020a7966dachc463600000f9c66cc21444cfg'
49
-            host='http://www3.wolframalpha.com'
50
-            server='6'
51
-            related='http://www3.wolframalpha.com/api/v2/relatedQueries.jsp?...'
45
+            numpods='3'
46
+            datatypes='Math'
47
+            id='queryresult_id'
48
+            host='http://www4c.wolframalpha.com'
49
+            related='related_url'
52
             version='2.6'>
50
             version='2.6'>
53
-         <pod title='Input'
54
-             scanner='Identity'
55
-             id='Input'
56
-             position='100'
57
-             error='false'
58
-             numsubpods='1'>
59
-          <subpod title=''>
60
-           <plaintext>sqrt(-1)</plaintext>
61
-          </subpod>
62
-         </pod>
63
-         <pod title='Result'
64
-             scanner='Simplification'
65
-             id='Result'
66
-             position='200'
67
-             error='false'
68
-             numsubpods='1'
69
-             primary='true'>
70
-          <subpod title=''>
71
-           <plaintext></plaintext>
72
-          </subpod>
73
-          <states count='1'>
74
-           <state name='Step-by-step solution'
75
-               input='Result__Step-by-step solution' />
76
-          </states>
77
-         </pod>
78
-         <pod title='Polar coordinates'
79
-             scanner='Numeric'
80
-             id='PolarCoordinates'
81
-             position='300'
82
-             error='false'
83
-             numsubpods='1'>
84
-          <subpod title=''>
85
-           <plaintext>r1 (radius), θ90° (angle)</plaintext>
86
-          </subpod>
87
-         </pod>
88
-         <pod title='Position in the complex plane'
89
-             scanner='Numeric'
90
-             id='PositionInTheComplexPlane'
91
-             position='400'
92
-             error='false'
93
-             numsubpods='1'>
94
-          <subpod title=''>
95
-           <plaintext></plaintext>
96
-          </subpod>
97
-         </pod>
98
-         <pod title='All 2nd roots of -1'
99
-             scanner='RootsOfUnity'
100
-             id=''
101
-             position='500'
102
-             error='false'
103
-             numsubpods='2'>
104
-          <subpod title=''>
105
-           <plaintext>  (principal root)</plaintext>
106
-          </subpod>
107
-          <subpod title=''>
108
-           <plaintext>-</plaintext>
109
-          </subpod>
110
-         </pod>
111
-         <pod title='Plot of all roots in the complex plane'
112
-             scanner='RootsOfUnity'
113
-             id='PlotOfAllRootsInTheComplexPlane'
114
-             position='600'
115
-             error='false'
116
-             numsubpods='1'>
117
-          <subpod title=''>
118
-           <plaintext></plaintext>
119
-          </subpod>
120
-         </pod>
121
-        </queryresult>
122
-        """
123
-        # test private user area char in response
124
-        response = mock.Mock(content=xml)
125
-        results = wolframalpha_api.response(response)
126
-        self.assertEqual(type(results), list)
127
-        self.assertEqual(len(results), 2)
128
-        self.assertIn('i', results[0]['answer'])
129
-        self.assertIn('sqrt(-1) - Wolfram|Alpha', results[1]['title'])
130
-        self.assertEquals('http://www.wolframalpha.com/input/?i=sqrt%28-1%29', results[1]['url'])
131
-
132
-        xml = """<?xml version='1.0' encoding='UTF-8'?>
133
-            <queryresult success='true'
134
-                error='false'
135
-                numpods='2'
136
-                datatypes=''
137
-                timedout=''
138
-                timedoutpods=''
139
-                timing='1.286'
140
-                parsetiming='0.255'
141
-                parsetimedout='false'
142
-                recalculate=''
143
-                id='MSPa195222ad740ede5214h30000480ca61h003d3gd6'
144
-                host='http://www3.wolframalpha.com'
145
-                server='20'
146
-                related='http://www3.wolframalpha.com/api/v2/relatedQueries.jsp?id=...'
147
-                version='2.6'>
148
-             <pod title='Indefinite integral'
149
-                 scanner='Integral'
150
-                 id='IndefiniteIntegral'
151
-                 position='100'
152
-                 error='false'
51
+            <pod title='Input'
52
+                 scanner='Identity'
53
+                 id='Input'
54
+                 numsubpods='1'>
55
+                  <subpod title=''>
56
+                       <img src='input_img_src.gif'
57
+                           alt='input_img_alt'
58
+                           title='input_img_title' />
59
+                       <plaintext>input_plaintext</plaintext>
60
+                  </subpod>
61
+             </pod>
62
+             <pod title='Result'
63
+                 scanner='Simplification'
64
+                 id='Result'
153
                  numsubpods='1'
65
                  numsubpods='1'
154
                  primary='true'>
66
                  primary='true'>
155
-              <subpod title=''>
156
-               <plaintext>∫1/xxlog(x)+constant</plaintext>
157
-              </subpod>
158
-              <states count='1'>
159
-               <state name='Step-by-step solution'
160
-                   input='IndefiniteIntegral__Step-by-step solution' />
161
-              </states>
162
-              <infos count='1'>
163
-               <info text='log(x) is the natural logarithm'>
164
-                <link url='http://reference.wolfram.com/mathematica/ref/Log.html'
165
-                    text='Documentation'
166
-                    title='Mathematica' />
167
-                <link url='http://functions.wolfram.com/ElementaryFunctions/Log'
168
-                    text='Properties'
169
-                    title='Wolfram Functions Site' />
170
-                <link url='http://mathworld.wolfram.com/NaturalLogarithm.html'
171
-                    text='Definition'
172
-                    title='MathWorld' />
173
-               </info>
174
-              </infos>
67
+                  <subpod title=''>
68
+                       <img src='result_img_src.gif'
69
+                           alt='result_img_alt'
70
+                           title='result_img_title' />
71
+                       <plaintext>result_plaintext</plaintext>
72
+                  </subpod>
175
              </pod>
73
              </pod>
176
-             <pod title='Plots of the integral'
177
-                 scanner='Integral'
178
-                 id='Plot'
179
-                 position='200'
180
-                 error='false'
181
-                 numsubpods='2'>
182
-              <subpod title=''>
183
-               <plaintext></plaintext>
184
-               <states count='1'>
185
-                <statelist count='2'
186
-                    value='Complex-valued plot'
187
-                    delimiters=''>
188
-                 <state name='Complex-valued plot'
189
-                     input='Plot__1_Complex-valued plot' />
190
-                 <state name='Real-valued plot'
191
-                     input='Plot__1_Real-valued plot' />
192
-                </statelist>
193
-               </states>
194
-              </subpod>
195
-              <subpod title=''>
196
-               <plaintext></plaintext>
197
-               <states count='1'>
198
-                <statelist count='2'
199
-                    value='Complex-valued plot'
200
-                    delimiters=''>
201
-                 <state name='Complex-valued plot'
202
-                     input='Plot__2_Complex-valued plot' />
203
-                 <state name='Real-valued plot'
204
-                     input='Plot__2_Real-valued plot' />
205
-                </statelist>
206
-               </states>
207
-              </subpod>
74
+             <pod title='Manipulatives illustration'
75
+                 scanner='Arithmetic'
76
+                 id='Illustration'
77
+                 numsubpods='1'>
78
+                  <subpod title=''>
79
+                       <img src='illustration_img_src.gif'
80
+                           alt='illustration_img_alt' />
81
+                       <plaintext>illustration_plaintext</plaintext>
82
+                  </subpod>
208
              </pod>
83
              </pod>
209
-             <assumptions count='1'>
210
-              <assumption type='Clash'
211
-                  word='integral'
212
-                  template='Assuming &quot;${word}&quot; is ${desc1}. Use as ${desc2} instead'
213
-                  count='2'>
214
-               <value name='IntegralsWord'
215
-                   desc='an integral'
216
-                   input='*C.integral-_*IntegralsWord-' />
217
-               <value name='MathematicalFunctionIdentityPropertyClass'
218
-                   desc='a function property'
219
-                   input='*C.integral-_*MathematicalFunctionIdentityPropertyClass-' />
220
-              </assumption>
221
-             </assumptions>
222
-            </queryresult>
84
+        </queryresult>
223
         """
85
         """
224
-        # test integral
225
-        response = mock.Mock(content=xml)
86
+        response = mock.Mock(content=xml, request=request)
226
         results = wolframalpha_api.response(response)
87
         results = wolframalpha_api.response(response)
227
         self.assertEqual(type(results), list)
88
         self.assertEqual(type(results), list)
228
         self.assertEqual(len(results), 2)
89
         self.assertEqual(len(results), 2)
229
-        self.assertIn('log(x)+c', results[0]['answer'])
230
-        self.assertIn('∫1/xx - Wolfram|Alpha'.decode('utf-8'), results[1]['title'])
231
-        self.assertEquals('http://www.wolframalpha.com/input/?i=%E2%88%AB1%2Fx%EF%9D%8Cx', results[1]['url'])
90
+        self.assertIn('input_plaintext', results[0]['infobox'])
91
+
92
+        self.assertEqual(len(results[0]['attributes']), 3)
93
+        self.assertIn('Input', results[0]['attributes'][0]['label'])
94
+        self.assertIn('input_plaintext', results[0]['attributes'][0]['value'])
95
+        self.assertIn('Result', results[0]['attributes'][1]['label'])
96
+        self.assertIn('result_plaintext', results[0]['attributes'][1]['value'])
97
+        self.assertIn('Manipulatives illustration', results[0]['attributes'][2]['label'])
98
+        self.assertIn('illustration_img_src.gif', results[0]['attributes'][2]['image']['src'])
99
+        self.assertIn('illustration_img_alt', results[0]['attributes'][2]['image']['alt'])
100
+
101
+        self.assertEqual(len(results[0]['urls']), 1)
232
 
102
 
103
+        self.assertEqual(referer_url, results[0]['urls'][0]['url'])
104
+        self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
105
+        self.assertEqual(referer_url, results[1]['url'])
106
+        self.assertEqual('Wolfram|Alpha', results[1]['title'])
107
+
108
+        # test calc
233
         xml = """<?xml version='1.0' encoding='UTF-8'?>
109
         xml = """<?xml version='1.0' encoding='UTF-8'?>
234
         <queryresult success='true'
110
         <queryresult success='true'
235
             error='false'
111
             error='false'
236
-            numpods='4'
237
-            datatypes='Solve'
238
-            timedout=''
239
-            timedoutpods=''
240
-            timing='0.79'
241
-            parsetiming='0.338'
112
+            numpods='2'
113
+            datatypes=''
242
             parsetimedout='false'
114
             parsetimedout='false'
243
-            recalculate=''
244
-            id='MSPa7481f7i06d25h3deh2900004810i3a78d9b4fdc'
115
+            id='queryresult_id'
245
             host='http://www5b.wolframalpha.com'
116
             host='http://www5b.wolframalpha.com'
246
-            server='23'
247
-            related='http://www5b.wolframalpha.com/api/v2/relatedQueries.jsp?id=...'
248
-            version='2.6'>
249
-         <pod title='Input interpretation'
250
-             scanner='Identity'
251
-             id='Input'
252
-             position='100'
253
-             error='false'
254
-             numsubpods='1'>
255
-          <subpod title=''>
256
-           <plaintext>solve x^2+x0</plaintext>
257
-          </subpod>
258
-         </pod>
259
-         <pod title='Results'
260
-             scanner='Solve'
261
-             id='Result'
262
-             position='200'
263
-             error='false'
264
-             numsubpods='2'
265
-             primary='true'>
266
-          <subpod title=''>
267
-           <plaintext>x-1</plaintext>
268
-          </subpod>
269
-          <subpod title=''>
270
-           <plaintext>x0</plaintext>
271
-          </subpod>
272
-          <states count='1'>
273
-           <state name='Step-by-step solution'
274
-               input='Result__Step-by-step solution' />
275
-          </states>
276
-         </pod>
277
-         <pod title='Root plot'
278
-             scanner='Solve'
279
-             id='RootPlot'
280
-             position='300'
281
-             error='false'
282
-             numsubpods='1'>
283
-          <subpod title=''>
284
-           <plaintext></plaintext>
285
-          </subpod>
286
-         </pod>
287
-         <pod title='Number line'
288
-             scanner='Solve'
289
-             id='NumberLine'
290
-             position='400'
291
-             error='false'
292
-             numsubpods='1'>
293
-          <subpod title=''>
294
-           <plaintext></plaintext>
295
-          </subpod>
296
-         </pod>
117
+            related='related_url'
118
+            version='2.6' >
119
+            <pod title='Indefinite integral'
120
+                scanner='Integral'
121
+                id='IndefiniteIntegral'
122
+                error='false'
123
+                numsubpods='1'
124
+                primary='true'>
125
+                <subpod title=''>
126
+                    <img src='integral_image.gif'
127
+                        alt='integral_img_alt'
128
+                        title='integral_img_title' />
129
+                    <plaintext>integral_plaintext</plaintext>
130
+                </subpod>
131
+            </pod>
132
+            <pod title='Plot'
133
+                scanner='Plotter'
134
+                id='Plot'
135
+                error='false'
136
+                numsubpods='1'>
137
+                <subpod title=''>
138
+                    <img src='plot.gif'
139
+                        alt='plot_alt'
140
+                        title='' />
141
+                    <plaintext></plaintext>
142
+                </subpod>
143
+            </pod>
297
         </queryresult>
144
         </queryresult>
298
         """
145
         """
299
-        # test ecuation with multiple answers
300
-        response = mock.Mock(content=xml)
146
+        response = mock.Mock(content=xml, request=request)
301
         results = wolframalpha_api.response(response)
147
         results = wolframalpha_api.response(response)
302
         self.assertEqual(type(results), list)
148
         self.assertEqual(type(results), list)
303
-        self.assertEqual(len(results), 3)
304
-        self.assertIn('x=-1', results[0]['answer'])
305
-        self.assertIn('x=0', results[1]['answer'])
306
-        self.assertIn('solve x^2+x0 - Wolfram|Alpha'.decode('utf-8'), results[2]['title'])
307
-        self.assertEquals('http://www.wolframalpha.com/input/?i=solve+x%5E2%2Bx%EF%9F%990', results[2]['url'])
149
+        self.assertEqual(len(results), 2)
150
+        self.assertIn('integral_plaintext', results[0]['infobox'])
151
+
152
+        self.assertEqual(len(results[0]['attributes']), 2)
153
+        self.assertIn('Indefinite integral', results[0]['attributes'][0]['label'])
154
+        self.assertIn('integral_plaintext', results[0]['attributes'][0]['value'])
155
+        self.assertIn('Plot', results[0]['attributes'][1]['label'])
156
+        self.assertIn('plot.gif', results[0]['attributes'][1]['image']['src'])
157
+        self.assertIn('plot_alt', results[0]['attributes'][1]['image']['alt'])
158
+
159
+        self.assertEqual(len(results[0]['urls']), 1)
160
+
161
+        self.assertEqual(referer_url, results[0]['urls'][0]['url'])
162
+        self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
163
+        self.assertEqual(referer_url, results[1]['url'])
164
+        self.assertEqual('Wolfram|Alpha', results[1]['title'])

+ 218
- 3
tests/unit/engines/test_wolframalpha_noapi.py Wyświetl plik

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 from collections import defaultdict
2
 from collections import defaultdict
3
+import mock
4
+from requests import Request
3
 from searx.engines import wolframalpha_noapi
5
 from searx.engines import wolframalpha_noapi
4
 from searx.testing import SearxTestCase
6
 from searx.testing import SearxTestCase
5
 
7
 
9
     def test_request(self):
11
     def test_request(self):
10
         query = 'test_query'
12
         query = 'test_query'
11
         dicto = defaultdict(dict)
13
         dicto = defaultdict(dict)
12
-        dicto['pageno'] = 1
13
         params = wolframalpha_noapi.request(query, dicto)
14
         params = wolframalpha_noapi.request(query, dicto)
15
+
14
         self.assertIn('url', params)
16
         self.assertIn('url', params)
17
+        self.assertIn('https://www.wolframalpha.com/input/json.jsp', params['url'])
15
         self.assertIn(query, params['url'])
18
         self.assertIn(query, params['url'])
16
-        self.assertIn('wolframalpha.com', params['url'])
19
+        self.assertEqual('https://www.wolframalpha.com/input/?i=test_query', params['headers']['Referer'])
17
 
20
 
18
     def test_response(self):
21
     def test_response(self):
19
         self.assertRaises(AttributeError, wolframalpha_noapi.response, None)
22
         self.assertRaises(AttributeError, wolframalpha_noapi.response, None)
20
         self.assertRaises(AttributeError, wolframalpha_noapi.response, [])
23
         self.assertRaises(AttributeError, wolframalpha_noapi.response, [])
21
         self.assertRaises(AttributeError, wolframalpha_noapi.response, '')
24
         self.assertRaises(AttributeError, wolframalpha_noapi.response, '')
22
         self.assertRaises(AttributeError, wolframalpha_noapi.response, '[]')
25
         self.assertRaises(AttributeError, wolframalpha_noapi.response, '[]')
23
-        # TODO
26
+
27
+        referer_url = 'referer_url'
28
+        request = Request(headers={'Referer': referer_url})
29
+
30
+        # test failure
31
+        json = '''
32
+        {"queryresult" : {
33
+            "success" : false,
34
+            "error" : false,
35
+            "numpods" : 0,
36
+            "id" : "",
37
+            "host" : "https:\/\/www5a.wolframalpha.com",
38
+            "didyoumeans" : {}
39
+        }}
40
+        '''
41
+        response = mock.Mock(text=json, request=request)
42
+        self.assertEqual(wolframalpha_noapi.response(response), [])
43
+
44
+        # test basic case
45
+        json = '''
46
+        {"queryresult" : {
47
+            "success" : true,
48
+            "error" : false,
49
+            "numpods" : 6,
50
+            "datatypes" : "Math",
51
+            "id" : "queryresult_id",
52
+            "host" : "https:\/\/www5b.wolframalpha.com",
53
+            "related" : "related_url",
54
+            "version" : "2.6",
55
+            "pods" : [
56
+                {
57
+                    "title" : "Input",
58
+                    "scanners" : [
59
+                        "Identity"
60
+                    ],
61
+                    "id" : "Input",
62
+                    "error" : false,
63
+                    "numsubpods" : 1,
64
+                    "subpods" : [
65
+                        {
66
+                            "title" : "",
67
+                            "img" : {
68
+                                "src" : "input_img_src.gif",
69
+                                "alt" : "input_img_alt",
70
+                                "title" : "input_img_title"
71
+                            },
72
+                            "plaintext" : "input_plaintext",
73
+                            "minput" : "input_minput"
74
+                        }
75
+                    ]
76
+                },
77
+                {
78
+                    "title" : "Result",
79
+                    "scanners" : [
80
+                        "Simplification"
81
+                    ],
82
+                    "id" : "Result",
83
+                    "error" : false,
84
+                    "numsubpods" : 1,
85
+                    "primary" : true,
86
+                    "subpods" : [
87
+                        {
88
+                            "title" : "",
89
+                            "img" : {
90
+                                "src" : "result_img_src.gif",
91
+                                "alt" : "result_img_alt",
92
+                                "title" : "result_img_title"
93
+                            },
94
+                            "plaintext" : "result_plaintext",
95
+                            "moutput" : "result_moutput"
96
+                        }
97
+                    ]
98
+                },
99
+                {
100
+                    "title" : "Manipulatives illustration",
101
+                    "scanners" : [
102
+                        "Arithmetic"
103
+                    ],
104
+                    "id" : "Illustration",
105
+                    "error" : false,
106
+                    "numsubpods" : 1,
107
+                    "subpods" : [
108
+                        {
109
+                            "title" : "",
110
+                            "CDFcontent" : "Resizeable",
111
+                            "img" : {
112
+                                "src" : "illustration_img_src.gif",
113
+                                "alt" : "illustration_img_alt",
114
+                                "title" : "illustration_img_title"
115
+                            },
116
+                            "plaintext" : "illustration_img_plaintext"
117
+                        }
118
+                    ]
119
+                }
120
+            ]
121
+        }}
122
+        '''
123
+        response = mock.Mock(text=json, request=request)
124
+        results = wolframalpha_noapi.response(response)
125
+        self.assertEqual(type(results), list)
126
+        self.assertEqual(len(results), 2)
127
+        self.assertIn('input_plaintext', results[0]['infobox'])
128
+
129
+        self.assertEqual(len(results[0]['attributes']), 3)
130
+        self.assertIn('Input', results[0]['attributes'][0]['label'])
131
+        self.assertIn('input_plaintext', results[0]['attributes'][0]['value'])
132
+        self.assertIn('Result', results[0]['attributes'][1]['label'])
133
+        self.assertIn('result_plaintext', results[0]['attributes'][1]['value'])
134
+        self.assertIn('Manipulatives illustration', results[0]['attributes'][2]['label'])
135
+        self.assertIn('illustration_img_src.gif', results[0]['attributes'][2]['image']['src'])
136
+        self.assertIn('illustration_img_alt', results[0]['attributes'][2]['image']['alt'])
137
+
138
+        self.assertEqual(len(results[0]['urls']), 1)
139
+
140
+        self.assertEqual(referer_url, results[0]['urls'][0]['url'])
141
+        self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
142
+        self.assertEqual(referer_url, results[1]['url'])
143
+        self.assertEqual('Wolfram|Alpha', results[1]['title'])
144
+
145
+        # test calc
146
+        json = """
147
+        {"queryresult" : {
148
+            "success" : true,
149
+            "error" : false,
150
+            "numpods" : 2,
151
+            "datatypes" : "",
152
+            "id" : "queryresult_id",
153
+            "host" : "https:\/\/www4b.wolframalpha.com",
154
+            "related" : "related_url",
155
+            "version" : "2.6",
156
+            "pods" : [
157
+                {
158
+                    "title" : "Indefinite integral",
159
+                    "scanners" : [
160
+                        "Integral"
161
+                    ],
162
+                    "id" : "IndefiniteIntegral",
163
+                    "error" : false,
164
+                    "numsubpods" : 1,
165
+                    "primary" : true,
166
+                    "subpods" : [
167
+                        {
168
+                            "title" : "",
169
+                            "img" : {
170
+                                "src" : "integral_img_src.gif",
171
+                                "alt" : "integral_img_alt",
172
+                                "title" : "integral_img_title"
173
+                            },
174
+                            "plaintext" : "integral_plaintext",
175
+                            "minput" : "integral_minput",
176
+                            "moutput" : "integral_moutput"
177
+                        }
178
+                    ]
179
+                },
180
+                {
181
+                    "title" : "Plot of the integral",
182
+                    "scanners" : [
183
+                        "Integral"
184
+                    ],
185
+                    "id" : "Plot",
186
+                    "error" : false,
187
+                    "numsubpods" : 0,
188
+                    "async" : "invalid_async_url"
189
+                }
190
+            ]
191
+        }}
192
+        """
193
+        response = mock.Mock(text=json, request=request)
194
+        results = wolframalpha_noapi.response(response)
195
+        self.assertEqual(type(results), list)
196
+        self.assertEqual(len(results), 2)
197
+        self.assertIn('integral_plaintext', results[0]['infobox'])
198
+
199
+        self.assertEqual(len(results[0]['attributes']), 1)
200
+        self.assertIn('Indefinite integral', results[0]['attributes'][0]['label'])
201
+        self.assertIn('integral_plaintext', results[0]['attributes'][0]['value'])
202
+
203
+        self.assertEqual(len(results[0]['urls']), 1)
204
+
205
+        self.assertEqual(referer_url, results[0]['urls'][0]['url'])
206
+        self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
207
+        self.assertEqual(referer_url, results[1]['url'])
208
+        self.assertEqual('Wolfram|Alpha', results[1]['title'])
209
+
210
+    def test_parse_async_pod(self):
211
+        self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, None)
212
+        self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, [])
213
+        self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, '')
214
+        self.assertRaises(AttributeError, wolframalpha_noapi.parse_async_pod, '[]')
215
+
216
+        # test plot
217
+        xml = '''<?xml version='1.0' encoding='UTF-8'?>
218
+        <pod title='Plot'
219
+            scanner='Plot'
220
+            id='Plot'
221
+            error='false'
222
+            numsubpods='1'>
223
+            <subpod title=''>
224
+                <img src='plot_img_src.gif'
225
+                    alt='plot_img_alt'
226
+                    title='plot_img_title' />
227
+                <plaintext>plot_plaintext</plaintext>
228
+                <minput>plot_minput</minput>
229
+            </subpod>
230
+        </pod>
231
+        '''
232
+        response = mock.Mock(content=xml)
233
+        pod = wolframalpha_noapi.parse_async_pod(response)
234
+        self.assertEqual(len(pod['subpods']), 1)
235
+        self.assertEqual('', pod['subpods'][0]['title'])
236
+        self.assertEqual('plot_plaintext', pod['subpods'][0]['plaintext'])
237
+        self.assertEqual('plot_img_src.gif', pod['subpods'][0]['img']['src'])
238
+        self.assertEqual('plot_img_alt', pod['subpods'][0]['img']['alt'])