Richdynamix_SimilarProducts - Version 1.0.0

Version Notes

Latest release 3 month after Gihub release

Download this release

Release Info

Developer Steven Richardson
Extension Richdynamix_SimilarProducts
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

app/code/local/Richdynamix/.DS_Store ADDED
Binary file
app/code/local/Richdynamix/SimilarProducts/.DS_Store ADDED
Binary file
app/code/local/Richdynamix/SimilarProducts/Block/Catalog/Product/List/Upsell.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Upsell Block class for Similarity module
4
+ * Replacing upsell data with PredictionIO data
5
+ *
6
+ * @category Richdynamix
7
+ * @package Richdynamix_SimilarProducts
8
+ * @author Steven Richardson (steven@richdynamix.com) @troongizmo
9
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
10
+ */
11
+ class Richdynamix_SimilarProducts_Block_Catalog_Product_List_Upsell extends Mage_Catalog_Block_Product_List_Upsell
12
+ {
13
+ /**
14
+ * Rewrite of parent::_prepareData() if
15
+ * module enabled and has data relating to current product and
16
+ * customer is logged in.
17
+ * @return mixed _itemCollection or parent::_prepareData()
18
+ */
19
+ protected function _prepareData()
20
+ {
21
+ $_helper = Mage::helper('similarproducts');
22
+ $product = Mage::registry('product');
23
+
24
+ if ($_helper->isEnabled() && Mage::getSingleton('customer/session')->isLoggedIn()) {
25
+ if ($similarproducts = $_helper->getSimilarProducts($product)) {
26
+
27
+ $collection = Mage::getResourceModel('catalog/product_collection');
28
+ Mage::getModel('catalog/layer')->prepareProductCollection($collection);
29
+ $collection->addAttributeToFilter('entity_id', array('in' => $similarproducts));
30
+
31
+ $this->_itemCollection = $collection;
32
+
33
+ return $this->_itemCollection;
34
+
35
+ } else {
36
+ return parent::_prepareData();
37
+ }
38
+ } else {
39
+ return parent::_prepareData();
40
+ }
41
+ }
42
+ }
app/code/local/Richdynamix/SimilarProducts/Helper/Data.php ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Helper class for Similarity module
4
+ * handling most data transactions via cURL
5
+ *
6
+ * @category Richdynamix
7
+ * @package Richdynamix_SimilarProducts
8
+ * @author Steven Richardson (steven@richdynamix.com) @troongizmo
9
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
10
+ */
11
+ class Richdynamix_SimilarProducts_Helper_Data extends Mage_Core_Helper_Abstract
12
+ {
13
+
14
+ /**
15
+ * API Endpoint for users
16
+ * @var string
17
+ */
18
+ protected $_userUrl = 'users.json';
19
+
20
+ /**
21
+ * API Endpoint for items
22
+ * @var string
23
+ */
24
+ protected $_itemsUrl = 'items.json';
25
+
26
+ /**
27
+ * API Endpoint for users-to-item actions
28
+ * @var string
29
+ */
30
+ protected $_actionsUrl = 'actions/u2i.json';
31
+
32
+ /**
33
+ * API Endpoint for similarity engine.
34
+ * {engine} will be string replaced by the engine name
35
+ * you specify in the CMS configuration
36
+ * @var string
37
+ */
38
+ protected $_engineUrl = 'engines/itemsim/{engine}/topn.json';
39
+
40
+ /**
41
+ * Sets up cURL request paramaters for adding a customer
42
+ * @param int $customerId Customer ID of loggedin customer
43
+ */
44
+ public function _addCustomer($customerId) {
45
+
46
+ $fields_string = 'pio_appkey='.$this->getEngineKey().'&';
47
+ $fields_string .= 'pio_uid='.$customerId;
48
+ $this->sendData($this->getApiHost().':'.$this->getApiPort().'/'.$this->_userUrl, $fields_string);
49
+ }
50
+
51
+ /**
52
+ * Sets up cURL request paramaters for adding a product
53
+ * @param Mage_Catalog_Model_Product $product Instance of Product Model
54
+ */
55
+ public function _addItem(Mage_Catalog_Model_Product $product) {
56
+
57
+ $cats = $this->getCategories($product);
58
+ $fields_string = 'pio_appkey='.$this->getEngineKey().'&';
59
+ $fields_string .= 'pio_iid='.$product->getId().'&';
60
+ $fields_string .= 'pio_itypes='.$cats;
61
+ $this->sendData($this->getApiHost().':'.$this->getApiPort().'/'.$this->_itemsUrl, $fields_string);
62
+ }
63
+
64
+ /**
65
+ * Sets up cURL request paramaters for adding a parent
66
+ * item of ordered product (Since Upsells can only be shown on parents)
67
+ * @param int $productid Product ID of purchased item
68
+ */
69
+ public function _addItems($productid) {
70
+
71
+ $product = Mage::getModel('catalog/product')->load($productid);
72
+ $cats = $this->getCategories($product);
73
+ if($product->getTypeId() == "simple"){
74
+ $parentIds = Mage::getModel('catalog/product_type_grouped')->getParentIdsByChild($product->getId());
75
+ if(!$parentIds)
76
+ $parentIds = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($product->getId());
77
+ if(isset($parentIds[0])){
78
+ $_productId = $parentIds[0];
79
+ } else {
80
+ $_productId = $product->getId();
81
+ }
82
+ }
83
+
84
+ $fields_string = 'pio_appkey='.$this->getEngineKey().'&';
85
+ $fields_string .= 'pio_iid='.$_productId.'&';
86
+ $fields_string .= 'pio_itypes='.$cats;
87
+ $this->sendData($this->getApiHost().':'.$this->getApiPort().'/'.$this->_itemsUrl, $fields_string);
88
+
89
+ }
90
+
91
+ /**
92
+ * Sets up cURL request paramaters for adding a user-to-item action
93
+ * @param int $productid Product ID of item to action
94
+ * @param int $customerId Customer ID of loggedin customer
95
+ */
96
+ public function _addAction($productId, $customerId, $action, $rate = null) {
97
+
98
+ $fields_string = 'pio_appkey='.$this->getEngineKey().'&';
99
+ $fields_string .= 'pio_uid='.$customerId.'&';
100
+ $fields_string .= 'pio_iid='.$productId.'&';
101
+ if ($rate != null) {
102
+ $fields_string .= 'pio_rate='.$rate.'&';
103
+ }
104
+ $fields_string .= 'pio_action='.$action;
105
+ $this->sendData($this->getApiHost().':'.$this->getApiPort().'/'.$this->_actionsUrl, $fields_string);
106
+ }
107
+
108
+ /**
109
+ * Gets comma seperated list of categories
110
+ * belonging to product, used for pio_itypes in PredictionIO
111
+ * @param Mage_Catalog_Model_Product $product Instance of Product Model
112
+ * @return string Comma seperated categories
113
+ */
114
+ public function getCategories(Mage_Catalog_Model_Product $product) {
115
+
116
+ if ($product->getId()) {
117
+ $categoryIds = $product->getCategoryIds();
118
+ if (is_array($categoryIds) and count($categoryIds) >= 1) {
119
+ $catsString = '';
120
+ foreach ($categoryIds as $id) {
121
+ $cat = Mage::getModel('catalog/category')->load($id);
122
+ $catsString .= $cat->getName().',';
123
+ }
124
+ $cats = rtrim($catsString, ",");
125
+ return $cats;
126
+ }
127
+ return '';
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Sets up cURL request paramaters for getting similar products
133
+ * from PredictionIO after data is trained
134
+ * @param Mage_Catalog_Model_Product $product Instance of Product Model
135
+ * @return mixed (Array of product ID's or NULL when empty)
136
+ */
137
+ public function getSimilarProducts (Mage_Catalog_Model_Product $product)
138
+ {
139
+
140
+ $engineUrl = str_replace('{engine}', $this->getEngineName(), $this->_engineUrl);
141
+
142
+ $_currentProduct = 'pio_iid='.$product->getId();
143
+
144
+ $_maxProductCount = 'pio_n='.$this->getProductCount();
145
+ $_key = 'pio_appkey='.$this->getEngineKey();
146
+
147
+ $cats = '';
148
+ if ($this->isCategoryResults()) {
149
+ $cats = '&pio_itypes='.$this->getCategories($product);
150
+ }
151
+
152
+ $url = $this->getApiHost() . ':' . $this->getApiPort() . '/' . $engineUrl;
153
+ $query = '?' . $_currentProduct. '&' . $_maxProductCount. '&' . $_key.$cats;
154
+
155
+ $content = json_decode($this->getData($url, $query));
156
+
157
+ if (isset($content->pio_iids)) {
158
+ return $content->pio_iids;
159
+ } else {
160
+ return null;
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Perform the cURL GET Request
166
+ * @param string $url URL of PredictionIO API
167
+ * @param string $query Query params to get data
168
+ * @return string string version of JSON object to be parsed.
169
+ */
170
+ public function getData($url, $query) {
171
+
172
+ $ch = curl_init();
173
+
174
+ // Set query data here with the URL
175
+ curl_setopt($ch, CURLOPT_URL, $url . $query);
176
+
177
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
178
+ curl_setopt($ch, CURLOPT_TIMEOUT, '3');
179
+
180
+ $content = trim(curl_exec($ch));
181
+
182
+ curl_close($ch);
183
+
184
+ return $content;
185
+ }
186
+
187
+ /**
188
+ * Perform the cURL POST Request
189
+ * @param string $url URL of PredictionIO API
190
+ * @param string $fields_string Query params for POST data
191
+ */
192
+ public function sendData($url, $fields_string) {
193
+ //open connection
194
+ $ch = curl_init();
195
+
196
+ //set the url, number of POST vars, POST data
197
+ curl_setopt($ch,CURLOPT_URL, $url);
198
+ curl_setopt($ch,CURLOPT_POST, 1);
199
+ curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
200
+ curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
201
+ curl_setopt($ch,CURLOPT_VERBOSE, 1);
202
+
203
+ //execute post
204
+ $result = curl_exec($ch);
205
+
206
+ //close connection
207
+ curl_close($ch);
208
+ }
209
+
210
+ /**
211
+ * Similarity Engine Key, Defined in PredictionIO
212
+ * @return string
213
+ */
214
+ public function getEngineKey(){
215
+ return Mage::getStoreConfig('similarproducts/settings/predict_key');
216
+ }
217
+
218
+ /**
219
+ * Similarity Engine Name, Defined in PredictionIO
220
+ * @return string
221
+ */
222
+ public function getEngineName(){
223
+ return Mage::getStoreConfig('similarproducts/settings/engine_name');
224
+ }
225
+
226
+ /**
227
+ * PredictionIO URL
228
+ * @return string
229
+ */
230
+ public function getApiHost(){
231
+ return Mage::getStoreConfig('similarproducts/settings/predict_host');
232
+ }
233
+
234
+ /**
235
+ * PredictionIO API Port, Default is 8000 but needs to be defined
236
+ * @return string
237
+ */
238
+ public function getApiPort(){
239
+ return Mage::getStoreConfig('similarproducts/settings/predict_port');
240
+ }
241
+
242
+ /**
243
+ * Module ON/OFF Switch
244
+ * @return bool
245
+ */
246
+ public function isEnabled(){
247
+ return Mage::getStoreConfig('similarproducts/settings/enabled');
248
+ }
249
+
250
+ /**
251
+ * Determine if the results should be based on similar categories
252
+ * @return bool
253
+ */
254
+ public function isCategoryResults(){
255
+ return Mage::getStoreConfig('similarproducts/settings/category_results');
256
+ }
257
+
258
+ /**
259
+ * Get maximum returned products
260
+ * @return int
261
+ */
262
+ public function getProductCount(){
263
+ return Mage::getStoreConfig('similarproducts/settings/product_count');
264
+ }
265
+
266
+ }
267
+
app/code/local/Richdynamix/SimilarProducts/Model/Observer.php ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Observer class for Similarity module
4
+ * handling all event hooks
5
+ *
6
+ * @category Richdynamix
7
+ * @package Richdynamix_SimilarProducts
8
+ * @author Steven Richardson (steven@richdynamix.com) @troongizmo
9
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
10
+ */
11
+ class Richdynamix_SimilarProducts_Model_Observer
12
+ {
13
+
14
+ /**
15
+ * Define the helper object
16
+ * @var NULL
17
+ */
18
+ protected $_helper;
19
+
20
+ /**
21
+ * Construct for assigning helper class to helper object
22
+ */
23
+ public function __construct() {
24
+ $this->_helper = Mage::helper('similarproducts');
25
+ }
26
+
27
+ /**
28
+ * When the customer is not logged in we should still capture
29
+ * data in case they login at basket after viewing several
30
+ * products. We log items to the session then extract later
31
+ * when they login.
32
+ *
33
+ * @param string $action Define the action to watch
34
+ * @param Mage_Catalog_Model_Product $product [description]
35
+ */
36
+ public function logGuestActions($action, Mage_Catalog_Model_Product $product, $rating = null)
37
+ {
38
+
39
+ $guestActions = Mage::getSingleton('core/session')->getGuestActions();
40
+
41
+ if (isset($guestActions) && $guestActions != NULL) {
42
+ if ($action === 'view') {
43
+ array_push($guestActions['product_view'], $product->getId());
44
+ }
45
+ if ($action === 'rate') {
46
+ $guestActions['product_rate'][$product->getId()] = $rating;
47
+ }
48
+ Mage::getSingleton('core/session')->setGuestActions($guestActions);
49
+ } else {
50
+ switch ($action) {
51
+ case 'view':
52
+ $guestActions['product_view'][] = $product->getId();
53
+ break;
54
+ case 'rate':
55
+ $guestActions['product_rate'][$product->getId()] = $rating;
56
+ break;
57
+ }
58
+ Mage::getSingleton('core/session')->setGuestActions($guestActions);
59
+ }
60
+
61
+ // Mage::getSingleton('core/session')->unsGuestActions();
62
+ // var_dump($guestActions);
63
+
64
+ }
65
+
66
+ /**
67
+ * Event to fire when the customer logs in
68
+ * @param Varien_Event_Observer $observer customer_login
69
+ */
70
+ public function addCustomer(Varien_Event_Observer $observer)
71
+ {
72
+ if ($this->_helper->isEnabled() && Mage::getSingleton('customer/session')->isLoggedIn()) {
73
+ $customer = $observer->getEvent()->getCustomer();
74
+ $this->_helper->_addCustomer($customer->getId());
75
+
76
+ // Check if there is a guest actions log
77
+ $guestActions = Mage::getSingleton('core/session')->getGuestActions();
78
+ if (isset($guestActions)) {
79
+ // there is actions been logged prior to login, lets process them
80
+ $this->processGuestActions($guestActions, $customer->getId());
81
+ }
82
+
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Method used to do the guest action logging.
88
+ * @param string $guestActions type of action being defined
89
+ * @param int $customerId Customer ID of logged in customer
90
+ */
91
+ protected function processGuestActions($guestActions, $customerId)
92
+ {
93
+ if (isset($guestActions['product_view'])) {
94
+ foreach ($guestActions['product_view'] as $item) {
95
+ $product = Mage::getModel('catalog/product')->load($item);
96
+ $this->_helper->_addItem($product);
97
+ $this->_helper->_addAction($product->getId(), $customerId, 'view');
98
+ }
99
+ }
100
+ if (isset($guestActions['product_rate'])) {
101
+ foreach ($guestActions['product_rate'] as $product_id => $rating) {
102
+ $product = Mage::getModel('catalog/product')->load($product_id);
103
+ $this->_helper->_addItem($produproductct_id);
104
+ $this->_helper->_addAction($product_id, $customerId, 'rate', $rating);
105
+ }
106
+ }
107
+ Mage::getSingleton('core/session')->unsGuestActions();
108
+ }
109
+
110
+ /**
111
+ * Event to fire when the customer views a product
112
+ * @param Varien_Event_Observer $observer [description]
113
+ */
114
+ public function productView(Varien_Event_Observer $observer)
115
+ {
116
+
117
+ if ($this->_helper->isEnabled() && Mage::getSingleton('customer/session')->isLoggedIn()) {
118
+ $product = Mage::registry('current_product');
119
+ $customer = Mage::getSingleton('customer/session')->getCustomer();
120
+ $this->_helper->_addItem($product);
121
+ $this->_helper->_addAction($product->getId(), $customer->getId(), 'view');
122
+ } else {
123
+ $this->logGuestActions('view', Mage::registry('current_product'));
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Event to fire when the customer reviews a product
129
+ * @param Varien_Event_Observer $observer [description]
130
+ */
131
+ public function productRate(Varien_Event_Observer $observer)
132
+ {
133
+ if ($this->_helper->isEnabled()) {
134
+ $customer = Mage::getSingleton('customer/session')->getCustomer();
135
+ $product = Mage::registry('current_product');
136
+
137
+ $object = $observer->getEvent()->getObject();
138
+ $data = $object->getData();
139
+
140
+ $newSumRatings = 0;
141
+ foreach($data['ratings'] as $r) {
142
+ $value = $r % 5;
143
+ $newSumRatings += ($value) ? $value : 5;
144
+ }
145
+ $rating = $newSumRatings/count($data['ratings']);
146
+
147
+ if (Mage::getSingleton('customer/session')->isLoggedIn()) {
148
+ $this->_helper->_addItem($product);
149
+ $this->_helper->_addAction($product->getId(), $customer->getId(), 'rate', $rating);
150
+ } else {
151
+ $this->logGuestActions('rate', Mage::registry('current_product'), $rating);
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Event to fire when the customer buys a product
158
+ * @param Varien_Event_Observer $observer [description]
159
+ */
160
+ public function productSale(Varien_Event_Observer $observer)
161
+ {
162
+ if ($this->_helper->isEnabled() && Mage::getSingleton('customer/session')->isLoggedIn()) {
163
+ $customer = Mage::getSingleton('customer/session')->getCustomer();
164
+
165
+ $order = $observer->getEvent()->getOrder();
166
+ $items = $order->getItemsCollection();
167
+
168
+ foreach ($items as $item) {
169
+ $this->_helper->_addItems($item->getProductId());
170
+ $this->_helper->_addAction($item->getProductId(), $customer->getId(), 'conversion');
171
+ }
172
+ }
173
+ }
174
+
175
+ }
app/code/local/Richdynamix/SimilarProducts/controllers/Frontend/Review/ProductController.php ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Need to extend the review section so we can get the passed rating,
5
+ * pretty much overkill to copy the whol controller but Magento does
6
+ * not allow access to thre reviews data via the observer :(
7
+ */
8
+
9
+ require_once Mage::getModuleDir('controllers', 'Mage_Review').DS.'ProductController.php';
10
+ class Richdynamix_SimilarProducts_Frontend_Review_ProductController extends Mage_Review_ProductController
11
+ {
12
+ /**
13
+ * Action list where need check enabled cookie
14
+ *
15
+ * @var array
16
+ */
17
+ protected $_cookieCheckActions = array('post');
18
+
19
+ public function preDispatch()
20
+ {
21
+ parent::preDispatch();
22
+
23
+ $allowGuest = Mage::helper('review')->getIsGuestAllowToWrite();
24
+ if (!$this->getRequest()->isDispatched()) {
25
+ return;
26
+ }
27
+
28
+ $action = $this->getRequest()->getActionName();
29
+ if (!$allowGuest && $action == 'post' && $this->getRequest()->isPost()) {
30
+ if (!Mage::getSingleton('customer/session')->isLoggedIn()) {
31
+ $this->setFlag('', self::FLAG_NO_DISPATCH, true);
32
+ Mage::getSingleton('customer/session')->setBeforeAuthUrl(Mage::getUrl('*/*/*', array('_current' => true)));
33
+ Mage::getSingleton('review/session')->setFormData($this->getRequest()->getPost())
34
+ ->setRedirectUrl($this->_getRefererUrl());
35
+ $this->_redirectUrl(Mage::helper('customer')->getLoginUrl());
36
+ }
37
+ }
38
+
39
+ return $this;
40
+ }
41
+ /**
42
+ * Initialize and check product
43
+ *
44
+ * @return Mage_Catalog_Model_Product
45
+ */
46
+ protected function _initProduct()
47
+ {
48
+ Mage::dispatchEvent('review_controller_product_init_before', array('controller_action'=>$this));
49
+ $categoryId = (int) $this->getRequest()->getParam('category', false);
50
+ $productId = (int) $this->getRequest()->getParam('id');
51
+
52
+ $product = $this->_loadProduct($productId);
53
+ if (!$product) {
54
+ return false;
55
+ }
56
+
57
+ if ($categoryId) {
58
+ $category = Mage::getModel('catalog/category')->load($categoryId);
59
+ Mage::register('current_category', $category);
60
+ }
61
+
62
+ try {
63
+ Mage::dispatchEvent('review_controller_product_init', array('product'=>$product));
64
+ Mage::dispatchEvent('review_controller_product_init_after', array(
65
+ 'product' => $product,
66
+ 'controller_action' => $this
67
+ ));
68
+ } catch (Mage_Core_Exception $e) {
69
+ Mage::logException($e);
70
+ return false;
71
+ }
72
+
73
+ return $product;
74
+ }
75
+
76
+ /**
77
+ * Load product model with data by passed id.
78
+ * Return false if product was not loaded or has incorrect status.
79
+ *
80
+ * @param int $productId
81
+ * @return bool|Mage_Catalog_Model_Product
82
+ */
83
+ protected function _loadProduct($productId)
84
+ {
85
+ if (!$productId) {
86
+ return false;
87
+ }
88
+
89
+ $product = Mage::getModel('catalog/product')
90
+ ->setStoreId(Mage::app()->getStore()->getId())
91
+ ->load($productId);
92
+ /* @var $product Mage_Catalog_Model_Product */
93
+ if (!$product->getId() || !$product->isVisibleInCatalog() || !$product->isVisibleInSiteVisibility()) {
94
+ return false;
95
+ }
96
+
97
+ Mage::register('current_product', $product);
98
+ Mage::register('product', $product);
99
+
100
+ return $product;
101
+ }
102
+
103
+ /**
104
+ * Load review model with data by passed id.
105
+ * Return false if review was not loaded or review is not approved.
106
+ *
107
+ * @param int $productId
108
+ * @return bool|Mage_Review_Model_Review
109
+ */
110
+ protected function _loadReview($reviewId)
111
+ {
112
+ if (!$reviewId) {
113
+ return false;
114
+ }
115
+
116
+ $review = Mage::getModel('review/review')->load($reviewId);
117
+ /* @var $review Mage_Review_Model_Review */
118
+ if (!$review->getId() || !$review->isApproved() || !$review->isAvailableOnStore(Mage::app()->getStore())) {
119
+ return false;
120
+ }
121
+
122
+ Mage::register('current_review', $review);
123
+
124
+ return $review;
125
+ }
126
+
127
+ /**
128
+ * Submit new review action
129
+ *
130
+ */
131
+ public function postAction()
132
+ {
133
+ if ($data = Mage::getSingleton('review/session')->getFormData(true)) {
134
+ $rating = array();
135
+ if (isset($data['ratings']) && is_array($data['ratings'])) {
136
+ $rating = $data['ratings'];
137
+ }
138
+ } else {
139
+ $data = $this->getRequest()->getPost();
140
+ $rating = $this->getRequest()->getParam('ratings', array());
141
+ }
142
+
143
+ if (($product = $this->_initProduct()) && !empty($data)) {
144
+ $session = Mage::getSingleton('core/session');
145
+ /* @var $session Mage_Core_Model_Session */
146
+ $review = Mage::getModel('review/review')->setData($data);
147
+ /* @var $review Mage_Review_Model_Review */
148
+
149
+ $validate = $review->validate();
150
+ if ($validate === true) {
151
+ try {
152
+ $review->setEntityId($review->getEntityIdByCode(Mage_Review_Model_Review::ENTITY_PRODUCT_CODE))
153
+ ->setEntityPkValue($product->getId())
154
+ ->setStatusId(Mage_Review_Model_Review::STATUS_PENDING)
155
+ ->setCustomerId(Mage::getSingleton('customer/session')->getCustomerId())
156
+ ->setStoreId(Mage::app()->getStore()->getId())
157
+ ->setStores(array(Mage::app()->getStore()->getId()))
158
+ ->save();
159
+
160
+ foreach ($rating as $ratingId => $optionId) {
161
+ Mage::getModel('rating/rating')
162
+ ->setRatingId($ratingId)
163
+ ->setReviewId($review->getId())
164
+ ->setCustomerId(Mage::getSingleton('customer/session')->getCustomerId())
165
+ ->addOptionVote($optionId, $product->getId());
166
+ }
167
+
168
+ $review->aggregate();
169
+ $session->addSuccess($this->__('Your review has been accepted for moderation.'));
170
+ }
171
+ catch (Exception $e) {
172
+ $session->setFormData($data);
173
+ $session->addError($this->__('Unable to post the review.'));
174
+ }
175
+ }
176
+ else {
177
+ $session->setFormData($data);
178
+ if (is_array($validate)) {
179
+ foreach ($validate as $errorMessage) {
180
+ $session->addError($errorMessage);
181
+ }
182
+ }
183
+ else {
184
+ $session->addError($this->__('Unable to post the review.'));
185
+ }
186
+ }
187
+ }
188
+
189
+ if ($redirectUrl = Mage::getSingleton('review/session')->getRedirectUrl(true)) {
190
+ $this->_redirectUrl($redirectUrl);
191
+ return;
192
+ }
193
+ $this->_redirectReferer();
194
+ }
195
+
196
+ /**
197
+ * Show list of product's reviews
198
+ *
199
+ */
200
+ public function listAction()
201
+ {
202
+ if ($product = $this->_initProduct()) {
203
+ Mage::register('productId', $product->getId());
204
+
205
+ $design = Mage::getSingleton('catalog/design');
206
+ $settings = $design->getDesignSettings($product);
207
+ if ($settings->getCustomDesign()) {
208
+ $design->applyCustomDesign($settings->getCustomDesign());
209
+ }
210
+ $this->_initProductLayout($product);
211
+
212
+ // update breadcrumbs
213
+ if ($breadcrumbsBlock = $this->getLayout()->getBlock('breadcrumbs')) {
214
+ $breadcrumbsBlock->addCrumb('product', array(
215
+ 'label' => $product->getName(),
216
+ 'link' => $product->getProductUrl(),
217
+ 'readonly' => true,
218
+ ));
219
+ $breadcrumbsBlock->addCrumb('reviews', array('label' => Mage::helper('review')->__('Product Reviews')));
220
+ }
221
+
222
+ $this->renderLayout();
223
+ } elseif (!$this->getResponse()->isRedirect()) {
224
+ $this->_forward('noRoute');
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Show details of one review
230
+ *
231
+ */
232
+ public function viewAction()
233
+ {
234
+ $review = $this->_loadReview((int) $this->getRequest()->getParam('id'));
235
+ if (!$review) {
236
+ $this->_forward('noroute');
237
+ return;
238
+ }
239
+
240
+ $product = $this->_loadProduct($review->getEntityPkValue());
241
+ if (!$product) {
242
+ $this->_forward('noroute');
243
+ return;
244
+ }
245
+
246
+ $this->loadLayout();
247
+ $this->_initLayoutMessages('review/session');
248
+ $this->_initLayoutMessages('catalog/session');
249
+ $this->renderLayout();
250
+ }
251
+
252
+ /**
253
+ * Load specific layout handles by product type id
254
+ *
255
+ */
256
+ protected function _initProductLayout($product)
257
+ {
258
+ $update = $this->getLayout()->getUpdate();
259
+
260
+ $update->addHandle('default');
261
+ $this->addActionLayoutHandles();
262
+
263
+
264
+ $update->addHandle('PRODUCT_TYPE_'.$product->getTypeId());
265
+
266
+ if ($product->getPageLayout()) {
267
+ $this->getLayout()->helper('page/layout')
268
+ ->applyHandle($product->getPageLayout());
269
+ }
270
+
271
+ $this->loadLayoutUpdates();
272
+ if ($product->getPageLayout()) {
273
+ $this->getLayout()->helper('page/layout')
274
+ ->applyTemplate($product->getPageLayout());
275
+ }
276
+ $update->addUpdate($product->getCustomLayoutUpdate());
277
+ $this->generateLayoutXml()->generateLayoutBlocks();
278
+ }
279
+ }
app/code/local/Richdynamix/SimilarProducts/etc/adminhtml.xml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <admin>
6
+ <children>
7
+ <system>
8
+ <children>
9
+ <config>
10
+ <children>
11
+ <similarproducts translate="title" module="similarproducts">
12
+ <title>Similar Products Section</title>
13
+ <sort_order>0</sort_order>
14
+ </similarproducts>
15
+ </children>
16
+ </config>
17
+ </children>
18
+ </system>
19
+ </children>
20
+ </admin>
21
+ </resources>
22
+ </acl>
23
+ </config>
app/code/local/Richdynamix/SimilarProducts/etc/config.xml ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Richdynamix_SimilarProducts>
5
+ <version>0.1.0</version>
6
+ </Richdynamix_SimilarProducts>
7
+ </modules>
8
+ <!-- <frontend>
9
+ <routers>
10
+ <review>
11
+ <args>
12
+ <modules>
13
+ <Richdynamix_SimilarProducts before="Mage_Review_ProductController">
14
+ Richdynamix_SimilarProducts_Frontend_Review_ProductController
15
+ </Richdynamix_SimilarProducts>
16
+ </modules>
17
+ </args>
18
+ </review>
19
+ </routers>
20
+ </frontend> -->
21
+ <global>
22
+ <helpers>
23
+ <similarproducts>
24
+ <class>Richdynamix_SimilarProducts_Helper</class>
25
+ </similarproducts>
26
+ </helpers>
27
+ <blocks>
28
+ <similarproducts>
29
+ <class>Richdynamix_SimilarProducts_Block</class>
30
+ </similarproducts>
31
+ <catalog>
32
+ <rewrite>
33
+ <product_list_upsell>Richdynamix_SimilarProducts_Block_Catalog_Product_List_Upsell</product_list_upsell>
34
+ </rewrite>
35
+ </catalog>
36
+ </blocks>
37
+ <models>
38
+ <similarproducts>
39
+ <class>Richdynamix_SimilarProducts_Model</class>
40
+ <resourceModel>similarproducts_mysql4</resourceModel>
41
+ </similarproducts>
42
+ </models>
43
+ <events>
44
+
45
+ <!-- Add Customer once they Login and process any guest actions in the session -->
46
+ <customer_login>
47
+ <observers>
48
+ <customer_login_handler>
49
+ <type>model</type>
50
+ <class>similarproducts/observer</class>
51
+ <method>addCustomer</method>
52
+ </customer_login_handler>
53
+ </observers>
54
+ </customer_login>
55
+
56
+ <!-- Log the product view to customer -->
57
+ <catalog_controller_product_view>
58
+ <observers>
59
+ <catalog_controller_product_view_handler>
60
+ <type>model</type>
61
+ <class>similarproducts/observer</class>
62
+ <method>productView</method>
63
+ </catalog_controller_product_view_handler>
64
+ </observers>
65
+ </catalog_controller_product_view>
66
+
67
+ <!-- Log product review and rating with PredictionIO -->
68
+ <review_save_after>
69
+ <observers>
70
+ <review_save_after_handler>
71
+ <type>model</type>
72
+ <class>similarproducts/observer</class>
73
+ <method>productRate</method>
74
+ </review_save_after_handler>
75
+ </observers>
76
+ </review_save_after>
77
+
78
+ <!-- Log product conversion for each product in order -->
79
+ <checkout_type_onepage_save_order_after>
80
+ <observers>
81
+ <similarproducts_save_order_observer>
82
+ <class>similarproducts/observer</class>
83
+ <method>productSale</method>
84
+ </similarproducts_save_order_observer>
85
+ </observers>
86
+ </checkout_type_onepage_save_order_after>
87
+
88
+ </events>
89
+ </global>
90
+ </config>
app/code/local/Richdynamix/SimilarProducts/etc/system.xml ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <tabs>
4
+ <richdynamix translate="label" module="similarproducts">
5
+ <label>RichDynamix</label>
6
+ <sort_order>0</sort_order>
7
+ </richdynamix>
8
+ </tabs>
9
+ <sections>
10
+ <similarproducts translate="label" module="similarproducts">
11
+ <label>Similar Products</label>
12
+ <tab>richdynamix</tab>
13
+ <frontend_type>text</frontend_type>
14
+ <sort_order>0</sort_order>
15
+ <show_in_default>1</show_in_default>
16
+ <show_in_website>1</show_in_website>
17
+ <show_in_store>1</show_in_store>
18
+ <groups>
19
+ <settings translate="label">
20
+ <label>Settings</label>
21
+ <frontend_type>text</frontend_type>
22
+ <sort_order>0</sort_order>
23
+ <show_in_default>1</show_in_default>
24
+ <show_in_website>1</show_in_website>
25
+ <show_in_store>1</show_in_store>
26
+ <fields>
27
+ <enabled translate="label">
28
+ <label>Is Enabled</label>
29
+ <frontend_type>select</frontend_type>
30
+ <source_model>adminhtml/system_config_source_yesno</source_model>
31
+ <sort_order>1</sort_order>
32
+ <show_in_default>1</show_in_default>
33
+ <show_in_website>1</show_in_website>
34
+ <show_in_store>1</show_in_store>
35
+ <comment>If disabled data will not be added to PredictionIO</comment>
36
+ </enabled>
37
+ <predict_host translate="label">
38
+ <label>PredictionIO Host</label>
39
+ <frontend_type>text</frontend_type>
40
+ <sort_order>2</sort_order>
41
+ <show_in_default>1</show_in_default>
42
+ <show_in_website>1</show_in_website>
43
+ <show_in_store>1</show_in_store>
44
+ </predict_host>
45
+ <predict_port translate="label">
46
+ <label>PredictionIO Port</label>
47
+ <frontend_type>text</frontend_type>
48
+ <sort_order>3</sort_order>
49
+ <show_in_default>1</show_in_default>
50
+ <show_in_website>1</show_in_website>
51
+ <show_in_store>1</show_in_store>
52
+ <comment>Default 8000</comment>
53
+ </predict_port>
54
+ <predict_key translate="label">
55
+ <label>Similarity Engine Key</label>
56
+ <frontend_type>text</frontend_type>
57
+ <sort_order>4</sort_order>
58
+ <show_in_default>1</show_in_default>
59
+ <show_in_website>1</show_in_website>
60
+ <show_in_store>1</show_in_store>
61
+ </predict_key>
62
+ <engine_name translate="label">
63
+ <label>Similarity Engine Name</label>
64
+ <frontend_type>text</frontend_type>
65
+ <sort_order>5</sort_order>
66
+ <show_in_default>1</show_in_default>
67
+ <show_in_website>1</show_in_website>
68
+ <show_in_store>1</show_in_store>
69
+ </engine_name>
70
+ <product_count translate="label">
71
+ <label>Similarity Product Count</label>
72
+ <comment>Number of products to return from PredictionIO</comment>
73
+ <frontend_type>text</frontend_type>
74
+ <sort_order>6</sort_order>
75
+ <show_in_default>1</show_in_default>
76
+ <show_in_website>1</show_in_website>
77
+ <show_in_store>1</show_in_store>
78
+ </product_count>
79
+ <category_results translate="label">
80
+ <label>Same Category Results</label>
81
+ <comment>Selecting this will limit the returned products to be from the same category</comment>
82
+ <frontend_type>select</frontend_type>
83
+ <source_model>adminhtml/system_config_source_yesno</source_model>
84
+ <sort_order>7</sort_order>
85
+ <show_in_default>1</show_in_default>
86
+ <show_in_website>1</show_in_website>
87
+ <show_in_store>1</show_in_store>
88
+ </category_results>
89
+ </fields>
90
+ </settings>
91
+ </groups>
92
+ </similarproducts>
93
+ </sections>
94
+ </config>
app/etc/modules/Richdynamix_SimilarProducts.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Richdynamix_SimilarProducts>
5
+ <active>true</active>
6
+ <codePool>local</codePool>
7
+ <version>0.1.0</version>
8
+ </Richdynamix_SimilarProducts>
9
+ </modules>
10
+ </config>
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Richdynamix_SimilarProducts</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license>Open Software License (OSL)</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Better Magento Up-Sell Products with PredictionIO Integration</summary>
10
+ <description>Similar Products is a extension that utilizes the Similarity engine of PredictionIO to create a more personalized suggestion of up-sell products on the Magento product page. Tracking customers product views and purchases the PredictionIO engine provides fresh and relevant up-sell suggestions for the current customer.</description>
11
+ <notes>Latest release 3 month after Gihub release</notes>
12
+ <authors><author><name>Steven Richardson</name><user>gizmo84</user><email>steven@richdynamix.com</email></author></authors>
13
+ <date>2014-04-05</date>
14
+ <time>21:46:29</time>
15
+ <contents><target name="magelocal"><dir name="Richdynamix"><dir name="SimilarProducts"><dir name="Block"><dir name="Catalog"><dir name="Product"><dir name="List"><file name="Upsell.php" hash="171bd7fdbf1be826b84cd4785095f3d1"/></dir></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="7e9173e8c0f0c5575c8383859aa7c292"/></dir><dir name="Model"><file name="Observer.php" hash="43b3bf6b06e1e4de0d6925f65a6d3609"/></dir><dir name="controllers"><dir name="Frontend"><dir name="Review"><file name="ProductController.php" hash="4a20c66de03b1945ef3701e91aa05f4e"/></dir></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="34cb156b2e297792940786e3985cd132"/><file name="config.xml" hash="9b1f9d208392ea03139474610bb7c112"/><file name="system.xml" hash="5b81bbd2f36d9f0d10a613c42390f86d"/></dir><file name=".DS_Store" hash="5f8a321c73305c7c761f1c678facbd5c"/></dir><file name=".DS_Store" hash="db53355314e93675729c6a119b1595bf"/></dir></target><target name="mage"><dir name="shell"><file name="similarity.php" hash="1e1df061d38597f35dc1c544ec686c8d"/></dir></target><target name="mageetc"><dir name="modules"><file name="Richdynamix_SimilarProducts.xml" hash="c8dcc088872cbfad79af54f67df016ac"/></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.3.0</min><max>5.5.0</max></php><extension><name>curl</name><min>7.30</min><max/></extension></required></dependencies>
18
+ </package>
shell/similarity.php ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Simple script to import customers, products and actions
4
+ * into prediction engine for all past orders.
5
+ *
6
+ * This will only add a coversion action type as we cannot determine
7
+ * the previous actions of the customers
8
+ *
9
+ * @category Richdynamix
10
+ * @package Richdynamix_SimilarProducts
11
+ * @author Steven Richardson (steven@richdynamix.com) @troongizmo
12
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
13
+ */
14
+ require_once 'abstract.php';
15
+ class Richdynamix_Shell_Similarity extends Mage_Shell_Abstract
16
+ {
17
+
18
+ /**
19
+ * Define the a list of stores to run
20
+ * @var array
21
+ */
22
+ protected $_stores = array();
23
+
24
+ /**
25
+ * Store count for reporting
26
+ * @var int
27
+ */
28
+ protected $_sCount = 0;
29
+
30
+ /**
31
+ * Define the helper object
32
+ * @var NULL
33
+ */
34
+ protected $_helper;
35
+
36
+ /**
37
+ * API Endpoint for users
38
+ * @var string
39
+ */
40
+ protected $_userUrl = 'users.json';
41
+
42
+ /**
43
+ * API Endpoint for items
44
+ * @var string
45
+ */
46
+ protected $_itemsUrl = 'items.json';
47
+
48
+ /**
49
+ * API Endpoint for users-to-item actions
50
+ * @var string
51
+ */
52
+ protected $_actionsUrl = 'actions/u2i.json';
53
+
54
+ /**
55
+ * Setup the run command with the right data to process
56
+ */
57
+ public function __construct() {
58
+ parent::__construct();
59
+
60
+ set_time_limit(0);
61
+
62
+ $this->_helper = Mage::helper('similarproducts');
63
+
64
+
65
+ if($this->getArg('stores')) {
66
+ $this->_stores = array_merge(
67
+ $this->_stores,
68
+ array_map(
69
+ 'trim',
70
+ explode(',', $this->getArg('stores'))
71
+ )
72
+ );
73
+ }
74
+
75
+ }
76
+
77
+ // Shell script point of entry
78
+ public function run() {
79
+
80
+ try {
81
+
82
+ if(!empty($this->_stores)) {
83
+ $selectedStores = '"'.implode('", "', $this->_stores).'"';
84
+ } else {
85
+ $selectedStores = 'All';
86
+ }
87
+
88
+ printf(
89
+ 'Selected stores: %s'."\n",
90
+ $selectedStores
91
+ );
92
+
93
+ echo "\n";
94
+
95
+ $stores = Mage::app()->getStores();
96
+ foreach ($stores as $store) {
97
+ $storeName = $store->getName();
98
+ if(!empty($this->_stores) && !in_array($storeName, $this->_stores)) {
99
+ continue;
100
+ }
101
+ $this->_processStore($store);
102
+ }
103
+
104
+ printf(
105
+ 'Done processing.'."\n"
106
+ .'Total processed stores: %d'."\n",
107
+ $this->_sCount, $this->_iCount
108
+ );
109
+
110
+ } catch (Exception $e) {
111
+ echo $e->getMessage().'@'.time();
112
+ }
113
+
114
+ }
115
+
116
+ // Usage instructions
117
+ public function usageHelp()
118
+ {
119
+ return <<<USAGE
120
+ Usage: php -f prelaunch.php -- [options]
121
+
122
+ --stores <names> Process only these stores (comma-separated)
123
+
124
+ help This help
125
+
126
+ USAGE;
127
+ }
128
+
129
+ /**
130
+ * Lets process each store sales
131
+ * @param string $store Pass in the store to process
132
+ */
133
+ protected function _processStore($store) {
134
+ $storeName = $store->getName();
135
+
136
+ printf('Processing "%s" store'."\n", $storeName);
137
+
138
+ $this->_sCount++;
139
+
140
+ Mage::app()->setCurrentStore($store->getId());
141
+
142
+ echo "\n";
143
+
144
+ $salesModel = Mage::getModel("sales/order");
145
+ $salesCollection = $salesModel->getCollection();
146
+ foreach($salesCollection as $order)
147
+ {
148
+ if ($order->getCustomerId()) {
149
+ $_order[$order->getIncrementId()]['customer'][$order->getCustomerId()] = array();
150
+ foreach ($order->getAllItems() as $item) {
151
+ $_order[$order->getIncrementId()]['customer'][$order->getCustomerId()]['items'][] = $item->getProductId();
152
+ }
153
+ }
154
+ }
155
+ // print_r($_order);
156
+ $this->preparePost($_order);
157
+
158
+ echo "\n";
159
+ }
160
+
161
+ /**
162
+ * Setup customers, products and actions
163
+ * @param string $orders the order for given store
164
+ */
165
+ private function preparePost($orders) {
166
+
167
+ foreach ($orders as $order) {
168
+
169
+ foreach ($order['customer'] as $key => $items) {
170
+ $customerId = $key;
171
+ $products = $items['items'];
172
+ }
173
+
174
+ $this->_addCustomer($customerId);
175
+ $this->_addItems($products, $customerId);
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Sets up cURL request paramaters for adding a customer
181
+ * @param int $customerId Customer ID of loggedin customer
182
+ */
183
+ private function _addCustomer($customerId) {
184
+
185
+ $fields_string = 'pio_appkey='.$this->_helper->getEngineKey().'&';
186
+ $fields_string .= 'pio_uid='.$customerId;
187
+ $this->postCurl($this->_helper->getApiHost().':'.$this->_helper->getApiPort().'/'.$this->_userUrl, $fields_string);
188
+ }
189
+
190
+ /**
191
+ * Sets up cURL request paramaters for adding a parent
192
+ * item of ordered product (Since Upsells can only be shown on parents)
193
+ * @param int $productid Product ID of purchased item
194
+ * @param int $customerId Customer ID of loggedin customer
195
+ */
196
+ private function _addItems($products, $customerId) {
197
+
198
+ foreach ($products as $key => $productid) {
199
+ $product = Mage::getModel('catalog/product')->load($productid);
200
+ if($product->getTypeId() == "simple"){
201
+ $parentIds = Mage::getModel('catalog/product_type_grouped')->getParentIdsByChild($product->getId());
202
+ if(!$parentIds)
203
+ $parentIds = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($product->getId());
204
+ if(isset($parentIds[0])){
205
+ $_productId = $parentIds[0];
206
+ } else {
207
+ $_productId = $product->getId();
208
+ }
209
+ }
210
+ }
211
+
212
+ $fields_string = 'pio_appkey='.$this->_helper->getEngineKey().'&';
213
+ $fields_string .= 'pio_iid='.$_productId.'&';
214
+ $fields_string .= 'pio_itypes=1';
215
+ $this->postCurl($this->_helper->getApiHost().':'.$this->_helper->getApiPort().'/'.$this->_itemsUrl, $fields_string);
216
+
217
+ $this->_addAction($_productId, $customerId);
218
+
219
+ }
220
+
221
+ /**
222
+ * Sets up cURL request paramaters for adding a user-to-item action
223
+ * @param int $productid Product ID of item to action
224
+ * @param int $customerId Customer ID of loggedin customer
225
+ */
226
+ private function _addAction($_productId, $customerId) {
227
+
228
+ $fields_string = 'pio_appkey='.$this->_helper->getEngineKey().'&';
229
+ $fields_string .= 'pio_uid='.$customerId.'&';
230
+ $fields_string .= 'pio_iid='.$_productId.'&';
231
+ $fields_string .= 'pio_action=conversion';
232
+ $this->postCurl($this->_helper->getApiHost().':'.$this->_helper->getApiPort().'/'.$this->_actionsUrl, $fields_string);
233
+
234
+ }
235
+
236
+ /**
237
+ * Perform the cURL POST Request
238
+ * @param string $url URL of PredictionIO API
239
+ * @param string $fields_string Query params for POST data
240
+ */
241
+ private function postCurl($url, $fields_string) {
242
+ //open connection
243
+ $ch = curl_init();
244
+
245
+ //set the url, number of POST vars, POST data
246
+ curl_setopt($ch,CURLOPT_URL, $url);
247
+ curl_setopt($ch,CURLOPT_POST, 1);
248
+ curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
249
+ curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
250
+ curl_setopt($ch,CURLOPT_VERBOSE, 1);
251
+
252
+ //execute post
253
+ $result = curl_exec($ch);
254
+
255
+ // var_dump($result);
256
+
257
+ //close connection
258
+ curl_close($ch);
259
+ }
260
+
261
+ }
262
+ // Instantiate
263
+ $shell = new Richdynamix_Shell_Similarity();
264
+ // Initiate script
265
+ $shell->run();