John Yang commited on
Commit
11d1f29
1 Parent(s): 72eae12

Add sim-to-real transfer

Browse files
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ *.pyc
app.py CHANGED
@@ -2,6 +2,9 @@ import gradio as gr
2
  import torch
3
  from transformers import BartTokenizer, BartForConditionalGeneration, AutoModel, AutoTokenizer
4
 
 
 
 
5
  # load IL models
6
  bart_tokenizer = BartTokenizer.from_pretrained('facebook/bart-large')
7
  bart_model = BartForConditionalGeneration.from_pretrained('webshop/il_search_bart')
@@ -85,17 +88,142 @@ def predict(obs, info):
85
  if valid_acts[0].startswith('click['):
86
  return bert_predict(obs, info)
87
  else:
88
- return bart_predict(process_goal(obs))
89
 
 
90
 
91
- def run_episode(goal):
92
  """
93
  Interact with amazon to find a product given input goal.
94
  Input: text goal
95
  Output: a url of found item on amazon.
96
  """
97
- return bart_predict(goal) # TODO: implement run_episode
98
-
99
-
100
- gr.Interface(fn=run_episode, inputs=gr.inputs.Textbox(
101
- lines=7, label="Input Text"), outputs="text").launch(inline=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import torch
3
  from transformers import BartTokenizer, BartForConditionalGeneration, AutoModel, AutoTokenizer
4
 
5
+ from webshop_lite import dict_to_fake_html
6
+ from predict_help import convert_dict_to_actions, convert_html_to_text, parse_results, parse_item_page, Page
7
+
8
  # load IL models
9
  bart_tokenizer = BartTokenizer.from_pretrained('facebook/bart-large')
10
  bart_model = BartForConditionalGeneration.from_pretrained('webshop/il_search_bart')
 
88
  if valid_acts[0].startswith('click['):
89
  return bert_predict(obs, info)
90
  else:
91
+ return "search[" + bart_predict(process_goal(obs)) + "]"
92
 
93
+ NUM_PROD_LIMIT = 10
94
 
95
+ def run_episode(goal, verbose=True):
96
  """
97
  Interact with amazon to find a product given input goal.
98
  Input: text goal
99
  Output: a url of found item on amazon.
100
  """
101
+ obs = "Amazon Shopping Game\nInstruction:" + goal + "\n[button] search [button]"
102
+ info = {'valid': ['search[stuff]'], 'image_feat': torch.zeros(512)}
103
+ product_map = {}
104
+ page_to_product_map_memo = {}
105
+ visited_asins, clicked_options = set(), set()
106
+ arg, sub_page_type, page_type, page_num = None, None, None, None
107
+ search_terms, prod_title, asin, num_prods, = None, None, None, None
108
+ options = {}
109
+
110
+ for i in range(100):
111
+ # Run prediction
112
+ action = predict(obs, info)
113
+ if verbose:
114
+ print("====")
115
+ print(action)
116
+
117
+ # Previous Page Type, Action -> Next Page Type
118
+ action_content = action[action.find("[")+1:action.find("]")]
119
+ prev_page_type = page_type
120
+ if action.startswith('search['):
121
+ page_type = Page.RESULTS
122
+ search_terms = action_content
123
+ page_num = 1
124
+ elif action.startswith('click['):
125
+ if action.startswith('click[item -'):
126
+ prod_title = action_content[len("item -"):].strip()
127
+ found = False
128
+ for value in product_map.values():
129
+ if prod_title == value["Title"]:
130
+ asin = value["asin"]
131
+ page_type = Page.ITEM_PAGE
132
+ visited_asins.add(asin)
133
+ found = True
134
+ break
135
+ if not found:
136
+ raise Exception("Product to click not found")
137
+
138
+ elif any(x.value in action for x in [Page.DESC, Page.FEATURES, Page.REVIEWS]):
139
+ page_type = Page.SUB_PAGE
140
+ sub_page_type = Page(action_content.lower())
141
+
142
+ elif action == 'click[< prev]':
143
+ if sub_page_type is not None:
144
+ page_type, sub_page_type = Page.ITEM_PAGE, None
145
+ elif prev_page_type == Page.ITEM_PAGE:
146
+ page_type = Page.RESULTS
147
+ options, clicked_options = {}, set()
148
+ elif prev_page_type == Page.RESULTS and page_num > 1:
149
+ page_type = Page.RESULTS
150
+ page_num -= 1
151
+
152
+ elif action == 'click[next >]':
153
+ page_type = Page.RESULTS
154
+ page_num += 1
155
+
156
+ elif action.lower() == 'click[back to search]':
157
+ page_type = Page.SEARCH
158
+
159
+ elif action == 'click[buy now]':
160
+ return asin
161
+
162
+ elif prev_page_type == Page.ITEM_PAGE:
163
+ found = False
164
+ for opt_name, opt_values in product_map[asin]["options"].items():
165
+ if action_content in opt_values:
166
+ options[opt_name] = action_content
167
+ page_type = Page.ITEM_PAGE
168
+ clicked_options.add(action_content)
169
+ found = True
170
+ break
171
+ if not found:
172
+ raise Exception("Unrecognized action: " + action)
173
+ else:
174
+ raise Exception("Unrecognized action:" + action)
175
+
176
+ if verbose:
177
+ print(f"Parsing {page_type.value} page...")
178
+
179
+ # URL -> Real HTML -> Dict of Info
180
+ if page_type == Page.RESULTS:
181
+ if search_terms not in page_to_product_map_memo or page_num not in page_to_product_map_memo[search_terms]:
182
+ product_map = {}
183
+ asins = parse_results(search_terms, page_num)
184
+ num_prods = len(asins)
185
+ for asin_ in asins[:NUM_PROD_LIMIT]:
186
+ product_map[asin_] = parse_item_page(asin_)
187
+ if search_terms not in page_to_product_map_memo:
188
+ page_to_product_map_memo[search_terms] = {}
189
+ page_to_product_map_memo[search_terms][page_num] = product_map
190
+ else:
191
+ if verbose:
192
+ print("Loaded memoized search results (" + str(page_num) + ")...")
193
+ product_map = page_to_product_map_memo[search_terms][page_num]
194
+ if verbose:
195
+ print("Product Map Length:", len(product_map))
196
+ data = list(product_map.values())
197
+ elif page_type == Page.ITEM_PAGE or page_type == Page.SUB_PAGE:
198
+ data = product_map
199
+ elif page_type == Page.SEARCH:
200
+ if verbose:
201
+ print("Executing search")
202
+ obs = "Amazon Shopping Game\nInstruction:" + goal + "\n[button] search [button]"
203
+ info = {'valid': ['search[stuff]'], 'image_feat': torch.zeros(512)}
204
+ continue
205
+ else:
206
+ raise Exception("Page of type `", page_type,value, "` not found")
207
+
208
+ # Dict of Info -> Fake HTML -> Text Observation
209
+ html_str = dict_to_fake_html(data, page_type, asin, sub_page_type, options, product_map, goal)
210
+ obs = convert_html_to_text(html_str, simple=False, clicked_options=clicked_options, visited_asins=visited_asins)
211
+
212
+ # Dict of Info -> Valid Action State (Info)
213
+ info = convert_dict_to_actions(page_type, data, asin, page_num, num_prods)
214
+
215
+ if i == 99:
216
+ return asin
217
+
218
+ gr.Interface(fn=run_episode,\
219
+ inputs=gr.inputs.Textbox(lines=7, label="Input Text"),\
220
+ outputs="text",\
221
+ examples=[
222
+ "I am looking for a high power sound column subwoofer, that uses bluetooth and is also a 3d surround sound system, and price lower than 650.00 dollars",
223
+ "Please select a 1 pound, certified organic sea salt shaker in the flavor triple blend flakes, and price lower than 40.00 dollars",
224
+ "I want to find a gold floor lamp with a glass shade and a nickel finish that i can use for my living room, and price lower than 270.00 dollars"
225
+ ],\
226
+ title="WebShop",\
227
+ article="<p style='padding-top:15px;text-align:center;'>To learn more about this project, check out the <a href='https://webshop-pnlp.github.io/' target='_blank'>project page</a>!</p>",\
228
+ description="<p style='text-align:center;'>Imitation Learning agent that searches for the desired product on Amazon from any natural language query!</p>",\
229
+ ).launch(inline=False)
predict_help.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from bs4 import BeautifulSoup
2
+ from bs4.element import Comment
3
+ from enum import Enum
4
+ from urllib.parse import urlencode
5
+
6
+ import json, requests, torch
7
+
8
+ class Page(Enum):
9
+ DESC = "description"
10
+ FEATURES = "features"
11
+ ITEM_PAGE = "item_page"
12
+ RESULTS = "results"
13
+ REVIEWS = "reviews"
14
+ SEARCH = "search"
15
+ SUB_PAGE = "item_sub_page"
16
+
17
+ HEADER_ = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36'
18
+ DEBUG_HTML = "temp.html"
19
+ VERBOSE = True
20
+
21
+ API = '85956985fae328bfe5a759a2984448d2'
22
+ def get_url(url):
23
+ payload = {'api_key': API, 'url': url , 'country_code': 'us'}
24
+ proxy_url = 'http://api.scraperapi.com/?' + urlencode(payload)
25
+ return proxy_url
26
+
27
+ # Query -> Search Result ASINs
28
+ def parse_results(query, page_num=None):
29
+ url = 'https://www.amazon.com/s?k=' + query.replace(" ", "+")
30
+ if page_num is not None:
31
+ url += "&page=" + str(page_num)
32
+ if VERBOSE:
33
+ print("Action URL: ", get_url(url))
34
+ webpage = requests.get(get_url(url), headers={'User-Agent': HEADER_, 'Accept-Language': 'en-US, en;q=0.5'})
35
+ asins = []
36
+ soup = BeautifulSoup(webpage.content, 'html.parser')
37
+ products = soup.findAll('div', {'data-component-type': 's-search-result'})
38
+ if products is None:
39
+ temp = open(DEBUG_HTML, "w")
40
+ temp.write(str(soup))
41
+ temp.close()
42
+ raise Exception("Couldn't find search results page, outputted html for inspection")
43
+ for product in products:
44
+ asins.append(product['data-asin'])
45
+ if VERBOSE:
46
+ print("Scraped", len(asins), "products")
47
+ return asins
48
+
49
+ # Scrape information of each product
50
+ def parse_item_page(asin):
51
+ product_dict = {}
52
+ product_dict["asin"] = asin
53
+
54
+ url = f"https://www.amazon.com/dp/{asin}"
55
+ webpage = requests.get(get_url(url), headers={'User-Agent': HEADER_, 'Accept-Language': 'en-US, en;q=0.5'})
56
+ soup = BeautifulSoup(webpage.content, "html.parser")
57
+
58
+ # Title
59
+ try:
60
+ title = soup.find("span", attrs={"id": 'productTitle'})
61
+ title = title.string.strip().replace(',', '')
62
+ except AttributeError:
63
+ title = "N/A"
64
+ product_dict["Title"] = title
65
+
66
+ # Price
67
+ try:
68
+ parent_price_span = soup.find(name="span", class_="apexPriceToPay")
69
+ price_span = parent_price_span.find(name="span", class_="a-offscreen")
70
+ price = float(price_span.getText().replace("$", ""))
71
+ except AttributeError:
72
+ price = "N/A"
73
+ product_dict["Price"] = price
74
+
75
+ # Rating
76
+ try:
77
+ rating = soup.find(name="span", attrs={"id": "acrPopover"})
78
+ if rating is None:
79
+ rating = "N/A"
80
+ else:
81
+ rating = rating.text
82
+ except AttributeError:
83
+ rating = "N/A"
84
+ product_dict["Rating"] = rating.strip("\n").strip()
85
+
86
+ # Features
87
+ try:
88
+ features = soup.find(name="div", attrs={"id": "feature-bullets"}).text
89
+ except AttributeError:
90
+ features = "N/A"
91
+ product_dict["BulletPoints"] = features
92
+
93
+ # Description
94
+ try:
95
+ desc_body = soup.find(name="div", attrs={"id": "productDescription_feature_div"})
96
+ desc_div = desc_body.find(name="div", attrs={"id": "productDescription"})
97
+ desc_ps = desc_div.findAll(name="p")
98
+ desc = " ".join([p.text for p in desc_ps])
99
+
100
+ except AttributeError:
101
+ desc = "N/A"
102
+ product_dict["Description"] = desc.strip()
103
+
104
+ # Main Image
105
+ try:
106
+ body = soup.find("body")
107
+ imgtag = soup.find("img", {"id":"landingImage"})
108
+ imageurl = dict(imgtag.attrs)["src"]
109
+ except AttributeError:
110
+ imageurl = ""
111
+ product_dict["MainImage"] = imageurl
112
+
113
+ # Options
114
+ options, options_to_image = {}, {}
115
+ try:
116
+ option_body = soup.find(name='div', attrs={"id": "softlinesTwister_feature_div"})
117
+ if option_body is None:
118
+ option_body = soup.find(name='div', attrs={"id": "twister_feature_div"})
119
+ option_blocks = option_body.findAll(name='ul')
120
+ for block in option_blocks:
121
+ name = json.loads(block["data-a-button-group"])["name"]
122
+ # Options
123
+ opt_list = []
124
+ for li in block.findAll("li"):
125
+ img = li.find(name="img")
126
+ if img is not None:
127
+ opt = img["alt"].strip()
128
+ opt_img = img["src"]
129
+ if len(opt) > 0:
130
+ options_to_image[opt] = opt_img
131
+ else:
132
+ opt = li.text.strip()
133
+ if len(opt) > 0:
134
+ opt_list.append(opt)
135
+ options[name.replace("_name", "").replace("twister_", "")] = opt_list
136
+ except AttributeError:
137
+ options = {}
138
+ product_dict["options"], product_dict["option_to_image"] = options, options_to_image
139
+ return product_dict
140
+
141
+ # Get text observation from html
142
+ def convert_html_to_text(html, simple=False, clicked_options=None, visited_asins=None):
143
+ def tag_visible(element):
144
+ ignore = {'style', 'script', 'head', 'title', 'meta', '[document]'}
145
+ return (
146
+ element.parent.name not in ignore and not isinstance(element, Comment)
147
+ )
148
+ html_obj = BeautifulSoup(html, 'html.parser')
149
+ texts = html_obj.findAll(text=True)
150
+ visible_texts = filter(tag_visible, texts)
151
+ if simple:
152
+ return ' [SEP] '.join(t.strip() for t in visible_texts if t != '\n')
153
+ else:
154
+ observation = ''
155
+ for t in visible_texts:
156
+ if t == '\n': continue
157
+ if t.parent.name == 'button': # button
158
+ processed_t = f'[button] {t} [button]'
159
+ elif t.parent.name == 'label': # options
160
+ if f'{t}' in clicked_options:
161
+ processed_t = f' [clicked button] {t} [clicked button]'
162
+ observation = f'You have clicked {t}.\n' + observation
163
+ else:
164
+ processed_t = f' [button] {t} [button]'
165
+ elif t.parent.get('class') == ["product-link"]: # asins
166
+ if f'{t}' in visited_asins:
167
+ processed_t = f'\n[clicked button] {t} [clicked button]'
168
+ else:
169
+ processed_t = f'\n[button] {t} [button]'
170
+ else: # regular, unclickable text
171
+ processed_t = str(t)
172
+ observation += processed_t + '\n'
173
+ return observation
174
+
175
+ # Get action from dict
176
+ def convert_dict_to_actions(page_type, products=None, asin=None, page_num=None, num_prods=None) -> dict:
177
+ info = {"valid": []}
178
+ if page_type == Page.RESULTS:
179
+ info["valid"] = ['click[back to search]']
180
+ if products is None or page_num is None or num_prods is None:
181
+ print(page_num)
182
+ print(num_prods)
183
+ print(products)
184
+ raise Exception('Provide `products`, `num_prods`, `page_num` to get `results` valid actions')
185
+ # Decide whether to add `next >` as clickable based on # of search results
186
+ if num_prods > 10:
187
+ info["valid"].append('click[next >]')
188
+ # Add `< prev` as clickable if not first page of search results
189
+ if page_num > 1:
190
+ info["valid"].append('click[< prev]')
191
+ for product in products:
192
+ info["valid"].append("click[item - " + product["Title"] + "]")
193
+ if page_type == Page.ITEM_PAGE:
194
+ if products is None or asin is None:
195
+ raise Exception('Provide `products` and `asin` to get `item_page` valid actions')
196
+ info["valid"] = ['click[back to search]', 'click[< prev]', 'click[description]',\
197
+ 'click[features]', 'click[buy now]'] # To do: reviews
198
+ if "options" in products[asin]:
199
+ for key, values in products[asin]["options"].items():
200
+ for value in values:
201
+ info["valid"].append("click[" + value + "]")
202
+ if page_type == Page.SUB_PAGE:
203
+ info["valid"] = ['click[back to search]', 'click[< prev]']
204
+ info['image_feat'] = torch.zeros(512)
205
+ return info
requirements.txt CHANGED
@@ -1,2 +1,5 @@
 
 
 
1
  torch
2
  transformers
 
1
+ bs4
2
+ flask
3
+ requests
4
  torch
5
  transformers
templates/attributes_page.html ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
5
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
6
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.css ">
7
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
8
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
9
+ <link rel="icon" href="data:,">
10
+ </head>
11
+ <body>
12
+ <div class="container py-5">
13
+ <div class="row top-buffer">
14
+ <div class="col-sm-6">
15
+ <div id="instruction-text" class="text-center">
16
+ <h4>Instruction:<br>{{ instruction_text }}</h4>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <div class="row top-buffer">
21
+ <form method="post" action="{{url_for('index', session_id=session_id)}}">
22
+ <button type="submit" class="btn btn-success">Back to Search</button>
23
+ </form>
24
+ </div>
25
+ <div class="row top-buffer">
26
+ <form method="post" action="{{url_for('item_page', session_id=session_id, asin=asin, keywords=keywords, page=page, options=options)}}">
27
+ <button type="submit" class="btn btn-primary">&lt; Prev</button>
28
+ </form>
29
+ </div>
30
+ <div class="row top-buffer">
31
+ <div class="col-md-12">
32
+ <div class="row top-buffer">
33
+ <div class="col-sm-6" name="description">
34
+ <div class="card card-body">
35
+ <ul>
36
+ {% for attribute in product_info.Attributes %}
37
+ <li><p class="attribute"> {{attribute}}</p></li>
38
+ {% endfor %}
39
+ </ul>
40
+ </div>
41
+ </div>
42
+ <div class="col-sm-6" name="description">
43
+ <div class="d-flex align-items-center justify-content-between mt-1">
44
+ <h5 class="font-weight-bold my-2 product-category">{{product_info.category}}</h5>
45
+ </div>
46
+ <div class="d-flex align-items-center justify-content-between mt-1">
47
+ <h5 class="font-weight-bold my-2 product-query">{{product_info.query}}</h5>
48
+ </div>
49
+ <div class="d-flex align-items-center justify-content-between mt-1">
50
+ <h5 class="font-weight-bold my-2 product-product_category">{{product_info.product_category}}</h5>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </body>
58
+ </html>
templates/description_page.html ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
5
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
6
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.css ">
7
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
8
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
9
+ <link rel="icon" href="data:,">
10
+ </head>
11
+ <body>
12
+ <div class="container py-5">
13
+ <div class="row top-buffer">
14
+ <div class="col-sm-6">
15
+ <div id="instruction-text" class="text-center">
16
+ <h4>Instruction:<br>{{ instruction_text }}</h4>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <div class="row top-buffer">
21
+ <form method="post" action="{{url_for('index', session_id=session_id)}}">
22
+ <button type="submit" class="btn btn-success">Back to Search</button>
23
+ </form>
24
+ </div>
25
+ <div class="row top-buffer">
26
+ <form method="post" action="{{url_for('item_page', session_id=session_id, asin=asin, keywords=keywords, page=page, options=options)}}">
27
+ <button type="submit" class="btn btn-primary">&lt; Prev</button>
28
+ </form>
29
+ </div>
30
+ <div class="row top-buffer">
31
+ <div class="col-md-12">
32
+ <div class="row top-buffer">
33
+ <div class="col-sm-6" name="description">
34
+ <div class="card card-body">
35
+ <p class="product-info">{{product_info.Description}}</p>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </body>
43
+ </html>
templates/done_page.html ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
5
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
6
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
7
+ <link rel="icon" href="data:,">
8
+ </head>
9
+ <body>
10
+ <!-- Code reference: https://bootsnipp.com/snippets/m13mN -->
11
+ <div class="container" style="margin-top: 8%;">
12
+ <div class="col-md-6 col-md-offset-3">
13
+ <div class="row">
14
+ <div id="thankyou" class="text-center">
15
+ <h1>Thank you for shopping with us!</h1>
16
+ </div>
17
+ <div id="stats" class="text-center">
18
+ <h3 align="mturk_code">Your code: </h3>
19
+ <p><pre>{{ mturk_code }}</pre> (Paste it in your MTurk interface.)</p>
20
+ <div style="display:none">
21
+ <h2 align="left">Purchased</h2>
22
+ <hr class="solid">
23
+ <h4 id="asin">asin<pre>{{ asin }}</pre></p>
24
+ <h4 id="options">options<pre>{{ options | tojson }}</pre></h4>
25
+ <h4 id="purchased_attrs">attrs<pre>{{ purchased_attrs }}</pre></h4>
26
+ <h4 id="purchased-category">category<pre>{{ category }}</pre></h4>
27
+ <h4 id="purchased-query">query<pre>{{ query }}</pre></h4>
28
+ <h4 id="purchased-pc">product category<pre>{{ product_category }}</pre></h4>
29
+ <h2 align="left">Target</h2>
30
+ <hr class="solid">
31
+ <h4 id="goal-asin">asin<pre>{{ goal.asin }}</pre></p>
32
+ <h4 id="goal-options">options<pre>{{ goal.goal_options }}</pre></h4>
33
+ <h4 id="goal-attrs">attrs<pre>{{ goal.attributes }}</pre></h4>
34
+ <h4 id="goal-price">price upper<pre>{{ goal.price_upper }}</pre></h4>
35
+ <h4 id="goal-instruction-text">instuction text<pre>{{ goal.instruction_text }}</pre></h4>
36
+ <h4 id="goal-category">category<pre>{{ goal.category }}</pre></h4>
37
+ <h4 id="goal-pc">product category<pre>{{ goal.product_category }}</pre></h4>
38
+ <h4 id="goal-query">query<pre>{{ goal.query }}</pre></h4>
39
+ <h4>Goal <div id="goal"><pre>{{ goal | pprint }}</pre></div></h4>
40
+ <h2 align="left">Reward</h2>
41
+ </div>
42
+ <hr class="solid">
43
+ <h3 id="reward">Your score (min 0.0, max 1.0)<pre>{{ reward }}</pre></h3>
44
+ <h4 hidden>Reward Details <div id="reward_info"><pre>{{ reward_info | pprint }}</pre></div></h4>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </body>
50
+ </html>
templates/features_page.html ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
5
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
6
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.css ">
7
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
8
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
9
+ <link rel="icon" href="data:,">
10
+ </head>
11
+ <body>
12
+ <div class="container py-5">
13
+ <div class="row top-buffer">
14
+ <div class="col-sm-6">
15
+ <div id="instruction-text" class="text-center">
16
+ <h4>Instruction:<br>{{ instruction_text }}</h4>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <div class="row top-buffer">
21
+ <form method="post" action="{{url_for('index', session_id=session_id)}}">
22
+ <button type="submit" class="btn btn-success">Back to Search</button>
23
+ </form>
24
+ </div>
25
+ <div class="row top-buffer">
26
+ <form method="post" action="{{url_for('item_page', session_id=session_id, asin=asin, keywords=keywords, page=page, options=options)}}">
27
+ <button type="submit" class="btn btn-primary">&lt; Prev</button>
28
+ </form>
29
+ </div>
30
+ <div class="row top-buffer">
31
+ <div class="col-md-12">
32
+ <div class="row top-buffer">
33
+ <div class="col-sm-6" name="bulletpoints">
34
+ <div class="card card-body">
35
+ <ul>
36
+ {% for bulletpoint in product_info.BulletPoints %}
37
+ <li><p class="product-info"> {{bulletpoint}}</p></li>
38
+ {% endfor %}
39
+ </ul>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </body>
47
+ </html>
templates/item_page.html ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
5
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
6
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.css ">
7
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
8
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
9
+ <link rel="icon" href="data:,">
10
+ </head>
11
+ <body>
12
+ <div class="container py-5">
13
+ <div class="row top-buffer">
14
+ <div class="col-sm-6">
15
+ <div id="instruction-text" class="text-center">
16
+ <h4>Instruction:<br>{{ instruction_text }}</h4>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <div class="row top-buffer">
21
+ <form method="post" action="{{url_for('index', session_id=session_id)}}">
22
+ <button type="submit" class="btn btn-success">Back to Search</button>
23
+ </form>
24
+ </div>
25
+ <div class="row top-buffer">
26
+ <form method="post" action="{{url_for('search_results', session_id=session_id, keywords=keywords, page=page)}}">
27
+ <button type="submit" class="btn btn-primary">&lt; Prev</button>
28
+ </form>
29
+ </div>
30
+ <div class="row top-buffer">
31
+ <div class="col-md-4 mb-4 mb-md-0">
32
+ <div class="row top-buffer">
33
+ <img id="product-image" src="{{product_info.MainImage}}" class="item-page-img">
34
+ </div>
35
+ {% for option_name, option_contents in product_info.options.items() %}
36
+ <div class="row top-buffer">
37
+ <h4>{{ option_name }}</h4>
38
+ <div class="radio-toolbar">
39
+ {% for option_content in option_contents %}
40
+ {% set current_options = options.copy() %}
41
+ {% set _ = current_options.update({option_name: option_content}) %}
42
+ {% set url = url_for('item_page', session_id=session_id, asin=asin, keywords=keywords, page=page, options=current_options) %}
43
+ <input type="radio" id="radio_{{ option_name }}{{ loop.index0 }}" name="{{ option_name }}" value="{{ option_content }}" onclick="window.location.href='{{ url }}';">
44
+ <label for="radio_{{ option_name }}{{ loop.index0 }}">{{ option_content }}</label>
45
+ {% endfor %}
46
+ </div>
47
+ </div>
48
+ {% endfor %}
49
+ </div>
50
+ <div class="col-md-6">
51
+ <h2>{{product_info.Title}}</h2>
52
+ <h4>Price: {{product_info.Price}}</h4>
53
+ <h4>Rating: {{product_info.Rating}}</h4>
54
+ <div class="row top-buffer">
55
+ <div class="col-sm-3" name="description">
56
+ <form method="post" action="{{ url_for('item_sub_page', session_id=session_id, asin=asin, keywords=keywords, page=page, sub_page='Description', options=options) }}">
57
+ <button class="btn btn-primary" type="submit">Description</button>
58
+ </form>
59
+ </div>
60
+ <div class="col-sm-3" name="bulletpoints">
61
+ <form method="post" action="{{ url_for('item_sub_page', session_id=session_id, asin=asin, keywords=keywords, page=page, sub_page='Features', options=options) }}">
62
+ <button class="btn btn-primary" type="submit">Features</button>
63
+ </form>
64
+ </div>
65
+ <div class="col-sm-3" name="reviews">
66
+ <form method="post" action="{{ url_for('item_sub_page', session_id=session_id, asin=asin, keywords=keywords, page=page, sub_page='Reviews', options=options) }}">
67
+ <button class="btn btn-primary" type="submit">Reviews</button>
68
+ </form>
69
+ </div>
70
+ <!--
71
+ <div class="col-sm-3" name="attributes">
72
+ <form method="post" action="{{ url_for('item_sub_page', session_id=session_id, asin=asin, keywords=keywords, page=page, sub_page='Attributes', options=options) }}">
73
+ <button class="btn btn-primary" type="submit">Attributes</button>
74
+ </form>
75
+ </div>
76
+ -->
77
+ </div>
78
+ </div>
79
+ <div class="col-sm-2">
80
+ <div class="row top-buffer">
81
+ <form method="post" action="{{url_for('done', session_id=session_id, asin=asin, options=options )}}">
82
+ <button type="submit" class="btn btn-lg purchase">Buy Now</button>
83
+ </form>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ </body>
89
+ <script>
90
+ $(document).ready(function() {
91
+ $('input:radio').each(function() {
92
+ //console.log($(this).val());
93
+ let options = JSON.parse(`{{ options | tojson }}`);
94
+ let optionValues = $.map(options, function(value, key) { return value });
95
+ //console.log(optionValues);
96
+ if (optionValues.includes($(this).val())) {
97
+ $(this).prop('checked', true);
98
+
99
+ let option_to_image = JSON.parse(`{{ product_info.option_to_image | tojson }}`);
100
+ // console.log($(this).val());
101
+ // console.log(options);
102
+ // console.log(option_to_image);
103
+ let image_url = option_to_image[$(this).val()];
104
+
105
+ console.log(image_url);
106
+ if (image_url) {
107
+ $("#product-image").attr("src", image_url);
108
+ }
109
+ }
110
+ });
111
+ });
112
+ </script>
113
+ </html>
templates/results_page.html ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
5
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
6
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.css ">
7
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
8
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
9
+ <link rel="icon" href="data:,">
10
+ </head>
11
+ <body>
12
+ <div class="container py-5">
13
+ <div class="row top-buffer">
14
+ <div class="col-sm-6">
15
+ <div id="instruction-text" class="text-center">
16
+ <h4>Instruction:<br>{{ instruction_text }}</h4>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <div class="row top-buffer">
21
+ <form method="post" action="{{ url_for('index', session_id=session_id) }}">
22
+ <button type="submit" class="btn btn-success">Back to Search</button>
23
+ </form>
24
+ </div>
25
+ <div class="row top-buffer">
26
+ <div class="col-sm-8">
27
+ <div class="display-4">
28
+ <h3>Page {{page}} (Total results: {{total}})</h3>
29
+ </div>
30
+ {% if page > 1 %}
31
+ <div class="col-sm-2">
32
+ <form method="post" action="{{url_for('search_results', session_id=session_id, keywords=keywords, page=page - 1)}}">
33
+ <button type="submit" class="btn btn-primary">&lt; Prev</button>
34
+ </form>
35
+ </div>
36
+ <div class="col-sm-2">
37
+ <form method="post" action="{{url_for('search_results', session_id=session_id, keywords=keywords, page=page + 1)}}">
38
+ <button type="submit" class="btn btn-primary">Next &gt;</button>
39
+ </form>
40
+ </div>
41
+ {% else %}
42
+ <div class="col-sm-2">
43
+ </div>
44
+ <div class="col-sm-2">
45
+ <form method="post" action="{{url_for('search_results', session_id=session_id, keywords=keywords, page=page + 1)}}">
46
+ <button type="submit" class="btn btn-primary">Next &gt;</button>
47
+ </form>
48
+ </div>
49
+ {% endif %}
50
+ </div>
51
+ </div>
52
+
53
+ <div class="row top-buffer">
54
+ {% for item in products %}
55
+ <div class="col-lg-12 mx-auto list-group-item">
56
+ <div class="col-lg-4">
57
+ <img src="{{item.MainImage}}" class="result-img">
58
+ </div>
59
+ <div class="col-lg-8">
60
+ <ul class="list-group shadow">
61
+ <div class="media align-items-lg-center flex-column flex-lg-row p-3">
62
+ <div class="media-body order-2 order-lg-1 searched-product">
63
+ {% set item_page_url = url_for('item_page', session_id=session_id, asin=item.asin, keywords=keywords, page=page, options=dict() ) %}
64
+ <h4 class="mt-0 font-weight-bold mb-2 product-asin"><a class="product-link" href="{{ item_page_url }}">{{item.asin}}</a></h5>
65
+ <h4 class="mt-0 font-weight-bold mb-2 product-title">{{item.Title}}</h5>
66
+ <div class="d-flex align-items-center justify-content-between mt-1">
67
+ <h5 class="font-weight-bold my-2 product-price">{{item.Price}}</h6>
68
+ </div>
69
+ <!--
70
+ <div class="d-flex align-items-center justify-content-between mt-1">
71
+ <h5 class="font-weight-bold my-2 product-category">{{item.category}}</h5>
72
+ </div>
73
+ <div class="d-flex align-items-center justify-content-between mt-1">
74
+ <h5 class="font-weight-bold my-2 product-query">{{item.query}}</h5>
75
+ </div>
76
+ <div class="d-flex align-items-center justify-content-between mt-1">
77
+ <h5 class="font-weight-bold my-2 product-product_category">{{item.product_category}}</h5>
78
+ </div>
79
+ -->
80
+ </div>
81
+ </div>
82
+ </ul>
83
+ </div>
84
+ </div>
85
+ {% endfor %}
86
+ </div>
87
+ </div>
88
+ </body>
89
+ </html>
templates/review_page.html ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
5
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
6
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.css ">
7
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
8
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
9
+ <link rel="icon" href="data:,">
10
+ </head>
11
+ <body>
12
+ <div class="container py-5">
13
+ <div class="row top-buffer">
14
+ <div class="col-sm-6">
15
+ <div id="instruction-text" class="text-center">
16
+ <h4>Instruction:<br>{{ instruction_text }}</h4>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <div class="row top-buffer">
21
+ <form method="post" action="{{url_for('index', session_id=session_id)}}">
22
+ <button type="submit" class="btn btn-success">Back to Search</button>
23
+ </form>
24
+ </div>
25
+ <div class="row top-buffer">
26
+ <form method="post" action="{{url_for('item_page', session_id=session_id, asin=asin, keywords=keywords, page=page, options=options)}}">
27
+ <button type="submit" class="btn btn-primary">&lt; Prev</button>
28
+ </form>
29
+ </div>
30
+ <div class="row top-buffer">
31
+ <div class="col-md-12">
32
+ <div class="row top-buffer">
33
+ <div class="col-sm-6" name="reviews">
34
+ <div class="card card-body">
35
+ {% for review in product_info.Reviews %}
36
+ <div class="card">
37
+ <div class="row text-left">
38
+ <h4 class="blue-text mt-3">"{{review.title}}"</h4>
39
+ <p class="text-left">
40
+ <span>{{review.score}}</span>
41
+ {% for i in range(review.score | int) %}
42
+ <span class="fa fa-star star-active"></span>
43
+ {% endfor %}
44
+ {% for i in range(5 - review.score | int) %}
45
+ <span class="fa fa-star star-inactive"></span>
46
+ {% endfor %}
47
+ </p>
48
+ <p class="content">{{review.body}}</p>
49
+ </div>
50
+ </div>
51
+ {% endfor %}
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </body>
59
+ </html>
templates/search_page.html ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
5
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
6
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
7
+ <link rel="icon" href="data:,">
8
+ </head>
9
+ <body>
10
+ <!-- Code reference: https://bootsnipp.com/snippets/m13mN -->
11
+ <div class="container" style="margin-top: 8%;">
12
+ <div class="col-md-6 col-md-offset-3">
13
+ <div class="row">
14
+ <div id="logo" class="text-center">
15
+ <h2>Amazon Shopping Game</h2>
16
+ </div>
17
+ <div id="instruction-text" class="text-center">
18
+ <h4>Instruction: <br>{{ instruction_text }}</h4>
19
+ </div>
20
+ <form role="form" id="form-buscar" method="post" action="{{url_for('index', session_id=session_id)}}">
21
+ <div class="form-group">
22
+ <div class="input-group">
23
+ <input id="search_input" class="form-control" type="text" name="search_query" placeholder="Search..." required/>
24
+ <span class="input-group-btn">
25
+ <button class="btn btn-success" type="submit"><i class="glyphicon glyphicon-search" aria-hidden="true"></i>Search</button>
26
+ </span>
27
+ </div>
28
+ </div>
29
+ </form>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </body>
34
+ </html>
webshop_lite.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ from flask import render_template_string, Flask
4
+ from predict_help import Page
5
+
6
+ app=Flask(__name__)
7
+ app.debug=True
8
+
9
+ SESSION_ID = "ABC"
10
+ TEMPLATE_DIR = "templates/"
11
+ KEYWORDS = ["placeholder (not needed)"] # To Do: Does this matter?
12
+ QUERY = ""
13
+ product_map = {}
14
+
15
+ def read_html_template(path):
16
+ with open(path) as f:
17
+ template = f.read()
18
+ return template
19
+
20
+ @app.route('/', methods=['GET', 'POST'])
21
+ def index(session_id, **kwargs):
22
+ print("Hello world")
23
+
24
+ @app.route('/', methods=['GET', 'POST'])
25
+ def search_results(data):
26
+ path = os.path.join(TEMPLATE_DIR, 'results_page.html')
27
+ html = render_template_string(
28
+ read_html_template(path=path),
29
+ session_id=SESSION_ID,
30
+ products=data,
31
+ keywords=KEYWORDS,
32
+ page=1,
33
+ total=len(data),
34
+ instruction_text=QUERY,
35
+ )
36
+ return html
37
+
38
+ @app.route('/', methods=['GET', 'POST'])
39
+ def item_page(session_id, asin, keywords, page, options):
40
+ path = os.path.join(TEMPLATE_DIR, 'item_page.html')
41
+ html = render_template_string(
42
+ read_html_template(path=path),
43
+ session_id=session_id,
44
+ product_info=product_map[asin],
45
+ keywords=keywords,
46
+ page=page,
47
+ asin=asin,
48
+ options=options,
49
+ instruction_text=QUERY
50
+ )
51
+ return html
52
+
53
+ @app.route('/', methods=['GET', 'POST'])
54
+ def item_sub_page(session_id, asin, keywords, page, sub_page, options):
55
+ path = os.path.join(TEMPLATE_DIR, sub_page.value.lower() + "_page.html")
56
+ html = render_template_string(
57
+ read_html_template(path),
58
+ session_id=session_id,
59
+ product_info=product_map[asin],
60
+ keywords=keywords,
61
+ page=page,
62
+ asin=asin,
63
+ options=options,
64
+ instruction_text=QUERY
65
+ )
66
+ return html
67
+
68
+ @app.route('/', methods=['GET', 'POST'])
69
+ def done(asin, options, session_id, **kwargs):
70
+ path = os.path.join(TEMPLATE_DIR, 'done_page.html')
71
+ html = render_template_string(
72
+ read_html_template(path),
73
+ session_id=session_id,
74
+ reward=1,
75
+ asin=asin,
76
+ options=product_map[asin]["options"],
77
+ reward_info=kwargs.get('reward_info'),
78
+ goal_attrs=kwargs.get('goal_attrs'),
79
+ purchased_attrs=kwargs.get('purchased_attrs'),
80
+ goal=kwargs.get('goal'),
81
+ mturk_code=kwargs.get('mturk_code'),
82
+ query=kwargs.get('query'),
83
+ category=kwargs.get('category'),
84
+ product_category=kwargs.get('product_category'),
85
+ )
86
+ return html
87
+
88
+ # Project Dictionary Information onto Fake Amazon
89
+ def dict_to_fake_html(data, page_type, asin=None, sub_page_type=None, options=None, prod_map={}, query=""):
90
+ global QUERY, product_map
91
+ QUERY = query
92
+ product_map = prod_map
93
+ with app.app_context(), app.test_request_context():
94
+ if page_type == Page.RESULTS:
95
+ return search_results(data)
96
+ if page_type == Page.ITEM_PAGE:
97
+ return item_page(SESSION_ID, asin, KEYWORDS, 1, options)
98
+ if page_type == Page.SUB_PAGE:
99
+ if sub_page_type is not None:
100
+ return item_sub_page(SESSION_ID, asin, KEYWORDS, 1, sub_page_type, options)
101
+ else:
102
+ raise Exception("Sub page of type", sub_page_type, "unrecognized")