Recklessio_Prediction - Version 1.0.1

Version Notes

- First version
- Works for both logged in(registered) and guest customers
- Promotion Budget control with Coupon validity Days and maximum limit
- Customizable messages, coupon code/ discount percent/ and other basic configurations

Download this release

Release Info

Developer Reckless
Extension Recklessio_Prediction
Version 1.0.1
Comparing to
See all releases


Version 1.0.1

app/code/local/Reckless/Prediction/Block/Adminhtml/Customer/Online/Grid.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Reckless_Prediction_Block_Adminhtml_Customer_Online_Grid
4
+ *
5
+ * @uses Mage_Adminhtml_Block_Customer_Online_Grid
6
+ * @package
7
+ * @version $id$
8
+ * @copyright 2014, Reckless.io
9
+ * @author Javier Carrascal <hello@reckless.io>
10
+ */
11
+ class Reckless_Prediction_Block_Adminhtml_Customer_Online_Grid extends Mage_Adminhtml_Block_Customer_Online_Grid
12
+ {
13
+ /**
14
+ * Initialize Grid block
15
+ *
16
+ */
17
+ public function __construct()
18
+ {
19
+ parent::__construct();
20
+ }
21
+
22
+ /**
23
+ * Add custom column to the grid
24
+ *
25
+ */
26
+ public function setCollection($collection)
27
+ {
28
+ $collection->getSelect()->joinLeft(
29
+ array('reckless_promo'=> 'reckless_promotions'),
30
+ 'reckless_promo.visitor_id = main_table.visitor_id',
31
+ array('reckless_promo.checkout_intent')
32
+ );
33
+
34
+ // Prevent collection exception when same visitor_id has more than one quote
35
+ $collection->getSelect()->group('main_table.visitor_id');
36
+ parent::setCollection($collection);
37
+ }
38
+
39
+ /**
40
+ * Prepare columns
41
+ *
42
+ * @return Reckless_Prediction_Block_Adminhtml_Customer_Online_Grid
43
+ */
44
+ protected function _prepareColumns()
45
+ {
46
+ parent::_prepareColumns();
47
+ $this->addColumn(
48
+ 'checkout_intent',
49
+ array(
50
+ 'header' => Mage::helper('customer')->__('Checkout Intent'),
51
+ 'default' => Mage::helper('customer')->__('n/a'),
52
+ 'index' => 'checkout_intent',
53
+ )
54
+ );
55
+ }
56
+ }
app/code/local/Reckless/Prediction/Block/Adminhtml/Grid.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Reckless_Prediction_Block_Adminhtml_Grid extends Mage_Adminhtml_Block_Widget_Grid_Container
3
+ {
4
+ public function __construct()
5
+ {
6
+ parent::__construct();
7
+ }
8
+ }
app/code/local/Reckless/Prediction/Block/Adminhtml/Predictions.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Reckless_Prediction_Block_Adminhtml_Predictions extends Mage_Adminhtml_Block_Widget_Grid_Container
3
+ {
4
+ public function __construct()
5
+ {
6
+ $this->_blockGroup = 'reckless_prediction';
7
+ $this->_controller = 'adminhtml_predictions';
8
+ $this->_headerText = 'Reckless Promotions';
9
+ parent::__construct();
10
+ $this->_removeButton('add');
11
+ }
12
+ }
app/code/local/Reckless/Prediction/Block/Adminhtml/Predictions/Grid.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Reckless_Prediction_Block_Adminhtml_Predictions_Grid extends Mage_Adminhtml_Block_Widget_Grid
3
+ {
4
+ public function __construct()
5
+ {
6
+ parent::__construct();
7
+ $this->setId('reckless_promotionGrid');
8
+ $this->setDefaultSort('entity_id');
9
+ $this->setDefaultDir('DESC');
10
+ $this->setSaveParametersInSession(true);
11
+ }
12
+ protected function _prepareCollection()
13
+ {
14
+ $collection = Mage::getModel('prediction/promotions')
15
+ ->getCollection();
16
+
17
+ $this->setCollection($collection);
18
+ return parent::_prepareCollection();
19
+ }
20
+
21
+ protected function _prepareColumns()
22
+ {
23
+ $this->addColumn(
24
+ 'entity_id',
25
+ array(
26
+ 'header' => 'ID',
27
+ 'align' =>'right',
28
+ 'width' => '50px',
29
+ 'index' => 'entity_id',
30
+ 'type' => 'number',
31
+ )
32
+ );
33
+ $this->addColumn(
34
+ 'customer_id',
35
+ array(
36
+ 'header' => 'CustomerID',
37
+ 'align' =>'right',
38
+ 'index' => 'customer_id',
39
+ 'type' => 'number',
40
+ )
41
+ );
42
+ $this->addColumn(
43
+ 'quote_id',
44
+ array(
45
+ 'header' => 'QuoteID',
46
+ 'align' =>'right',
47
+ 'index' => 'quote_id',
48
+ 'type' => 'number',
49
+ )
50
+ );
51
+
52
+ $this->addColumn(
53
+ 'checkout_intent',
54
+ array(
55
+ 'header' => 'Checkout',
56
+ 'align' =>'right',
57
+ 'index' => 'checkout_intent',
58
+ )
59
+ );
60
+ $this->addColumn(
61
+ 'coupon_code',
62
+ array(
63
+ 'header' => 'Coupon Code',
64
+ 'align' =>'left',
65
+ 'index' => 'coupon_code',
66
+ )
67
+ );
68
+ $this->addColumn(
69
+ 'discount_percent',
70
+ array(
71
+ 'header' => 'Discount',
72
+ 'align' =>'left',
73
+ 'index' => 'discount_percent',
74
+ )
75
+ );
76
+ $this->addColumn(
77
+ 'created_at',
78
+ array(
79
+ 'header' => 'Created At',
80
+ 'align' =>'left',
81
+ 'index' => 'created_at',
82
+ 'type' => 'datetime',
83
+
84
+ )
85
+ );
86
+
87
+ $this->addExportType('*/*/exportPredictionsCsv', 'CSV');
88
+ $this->addExportType('*/*/exportPredictionsExcel', 'Excel');
89
+ return parent::_prepareColumns();
90
+ }
91
+ }
app/code/local/Reckless/Prediction/Block/Adminhtml/Redemptions.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Reckless_Prediction_Block_Adminhtml_Redemptions extends Mage_Adminhtml_Block_Widget_Grid_Container
3
+ {
4
+ public function __construct()
5
+ {
6
+ $this->_blockGroup = 'reckless_prediction';
7
+ $this->_controller = 'adminhtml_redemptions';
8
+ $this->_headerText = 'Reckless Redemptions';
9
+ parent::__construct();
10
+ $this->_removeButton('add');
11
+ }
12
+ }
app/code/local/Reckless/Prediction/Block/Adminhtml/Redemptions/Grid.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Reckless_Prediction_Block_Adminhtml_Redemptions_Grid extends Mage_Adminhtml_Block_Widget_Grid
3
+ {
4
+ public function __construct()
5
+ {
6
+ parent::__construct();
7
+ $this->setId('reckless_redemptionsGrid');
8
+ $this->setDefaultSort('entity_id');
9
+ $this->setDefaultDir('DESC');
10
+ $this->setSaveParametersInSession(true);
11
+ }
12
+
13
+ /**
14
+ * Add custom column to the grid
15
+ *
16
+ */
17
+ //public function setCollection($collection)
18
+ //{
19
+ // $collection->getSelect()->joinLeft(array('coupon_agg'=> 'coupon_aggregated'), 'coupon_agg.coupon_code = main_table.code', array('coupon_agg.coupon_uses'));
20
+ // parent::setCollection($collection);
21
+ //}
22
+ public function setCollection($collection)
23
+ {
24
+ $collection->getSelect()->joinLeft(
25
+ array('orders'=> 'sales_flat_order'),
26
+ 'orders.coupon_code = main_table.coupon_code',
27
+ array('orders.base_grand_total', 'orders.increment_id', 'orders.created_at', 'orders.base_discount_amount', 'orders.customer_is_guest')
28
+ );
29
+ parent::setCollection($collection);
30
+ }
31
+
32
+ protected function _prepareCollection()
33
+ {
34
+ // $collection = Mage::getModel('salesrule/coupon')
35
+ // ->getCollection()
36
+ // ->addFieldToFilter('code',array('like'=>'CEO%'));
37
+ $collection = Mage::getModel('prediction/promotions')->getCollection();
38
+ $collection->addFieldToSelect(array('checkout_intent', 'coupon_code'));
39
+
40
+ //Prevent from collection failure if the same coupon has been used more than once
41
+ $collection->getSelect()->group('main_table.entity_id');
42
+ $this->setCollection($collection);
43
+ return parent::_prepareCollection();
44
+
45
+ }
46
+
47
+ protected function _prepareColumns()
48
+ {
49
+ $this->addColumn(
50
+ 'created_at',
51
+ array(
52
+ 'header' => 'Period',
53
+ 'align' =>'right',
54
+ 'index' => 'created_at',
55
+ 'type' => 'datetime',
56
+ 'filter_index' => 'orders.created_at',
57
+ )
58
+ );
59
+ $this->addColumn(
60
+ 'coupon_code',
61
+ array(
62
+ 'header' => 'Coupon Code',
63
+ 'align' =>'right',
64
+ 'index' => 'coupon_code',
65
+ 'filter_index' => 'main_table.coupon_code',
66
+ )
67
+ );
68
+ $this->addColumn(
69
+ 'checkout_intent',
70
+ array(
71
+ 'header' => 'Checkout Intent',
72
+ 'align' =>'right',
73
+ 'index' => 'checkout_intent',
74
+ )
75
+ );
76
+ $this->addColumn(
77
+ 'base_discount_amount',
78
+ array(
79
+ 'header' => 'Discount Amount',
80
+ 'align' =>'right',
81
+ 'index' => 'base_discount_amount',
82
+ )
83
+ );
84
+ $this->addColumn(
85
+ 'customer_is_guest',
86
+ array(
87
+ 'header' => 'Guest User',
88
+ 'align' =>'right',
89
+ 'index' => 'customer_is_guest',
90
+ )
91
+ );
92
+ $this->addColumn(
93
+ 'increment_id',
94
+ array(
95
+ 'header' => 'Order Id',
96
+ 'align' =>'right',
97
+ 'index' => 'increment_id',
98
+ 'type' => 'number',
99
+ )
100
+ );
101
+ $this->addColumn(
102
+ 'base_grand_total',
103
+ array(
104
+ 'header' => 'Grand Total',
105
+ 'align' =>'right',
106
+ 'index' => 'base_grand_total',
107
+ )
108
+ );
109
+ /*
110
+ $this->addColumn('coupon_uses',
111
+ array(
112
+ 'header' => 'Uses',
113
+ 'align' =>'right',
114
+ 'index' => 'coupon_uses',
115
+ ));
116
+ $this->addColumn('coupon_uses',
117
+ array(
118
+ 'header' => 'Uses',
119
+ 'align' =>'right',
120
+ 'index' => 'coupon_uses',
121
+ ));
122
+ $this->addColumn('coupon_uses',
123
+ array(
124
+ 'header' => 'Uses',
125
+ 'align' =>'right',
126
+ 'index' => 'coupon_uses',
127
+ ));
128
+
129
+ $this->addColumn('coupon_uses',
130
+ array(
131
+ 'header' => 'Uses',
132
+ 'align' =>'right',
133
+ 'index' => 'coupon_uses',
134
+ ));
135
+
136
+ $this->addColumn('coupon_uses',
137
+ array(
138
+ 'header' => 'Uses',
139
+ 'align' =>'right',
140
+ 'index' => 'coupon_uses',
141
+ ));
142
+ */
143
+ $this->addExportType('*/*/exportRedemptionsCsv', 'CSV');
144
+ $this->addExportType('*/*/exportRedemptionsExcel', 'Excel');
145
+ return parent::_prepareColumns();
146
+ }
147
+ // public function getRowUrl($row)
148
+ // {
149
+ // return $this->getUrl('*/*/edit', array('id' => $row->getId()));
150
+ // }
151
+ }
app/code/local/Reckless/Prediction/Helper/Data.php ADDED
@@ -0,0 +1,485 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Helper Class for Reckless Data Module
4
+ *
5
+ * @version $Id$
6
+ * @copyright Reckless Data, 11th June 2014
7
+ * @package Reckless_Prediction
8
+ * @author Dhruv Boruah <hello@reckless.io>
9
+ */
10
+ class Reckless_Prediction_Helper_Data extends Mage_Core_Helper_Abstract
11
+ {
12
+ /**
13
+ * Constants for different configurations in Admin Panel
14
+ */
15
+ const RECKLESS_CORE_ENABLED = 'reckless_prediction/reckless_prediction_section/reckless_prediction_enable';
16
+ const RECKLESS_CORE_API_KEY = 'reckless_prediction/reckless_prediction_section/reckless_prediction_api_key';
17
+ const RECKLESS_CORE_BASE_URL = 'reckless_prediction/reckless_prediction_section/reckless_prediction_base_url';
18
+ const RECKLESS_CORE_SYNC_CUST_EMAIL = 'reckless_prediction/reckless_prediction_section/reckless_prediction_sync_customer_email';
19
+ const RECKLESS_CORE_NOTIFY_EMAIL = 'reckless_prediction/reckless_prediction_section/reckless_prediction_notificationemail';
20
+ const RECKLESS_CORE_ENABLE_LOG = 'reckless_prediction/reckless_prediction_section/reckless_prediction_enablelog';
21
+ const RECKLESS_CORE_LOG_FILE = 'reckless_prediction/reckless_prediction_section/reckless_prediction_logfilename';
22
+
23
+ const RECKLESS_CORE_LAST_ORDER = 'reckless_prediction/reckless_prediction_section/reckless_prediction_lastorder';
24
+ const RECKLESS_CORE_LAST_QUOTE = 'reckless_prediction/reckless_prediction_section/reckless_prediction_lastquote';
25
+
26
+ const RECKLESS_PROMOTIONS_ENABLED = 'reckless_prediction/reckless_promotions_section/reckless_dynamic_promotion_enable';
27
+ const RECKLESS_PRMOTIONS_THRESHOLD = 'reckless_prediction/reckless_promotions_section/reckless_prediction_promotion_threshould';
28
+ const RECKLESS_PROMOTIONS_VAILDITY = 'reckless_prediction/reckless_promotions_section/reckless_prediction_promocodevalid_time';
29
+ const RECKLESS_PROMOTIONS_STOPRULESPROCESSING = 'reckless_prediction/reckless_promotions_section/reckless_prediction_promocode_stoprulesprocessing';
30
+ const RECKLESS_PROMOTIONS_COUPONTYPE = 'reckless_prediction/reckless_promotions_section/reckless_prediction_promocode_type';
31
+ const RECKLESS_PROMOTIONS_USAGEPERCUSTOMER = 'reckless_prediction/reckless_promotions_section/reckless_prediction_promocode_usagepercustomer';
32
+ const RECKLESS_PROMOTIONS_COUPONUSAGE = 'reckless_prediction/reckless_promotions_section/reckless_prediction_promocode_usage';
33
+ const RECKLESS_PROMOTIONS_COUPONPREFIX = 'reckless_prediction/reckless_promotions_section/reckless_prediction_promocode_prefix';
34
+ const RECKLESS_PROMOTIONS_COUPONDESC = 'reckless_prediction/reckless_promotions_section/reckless_prediction_promocode_desc';
35
+ const RECKLESS_PROMOTIONS_COUPONDISCPERCENT = 'reckless_prediction/reckless_promotions_section/reckless_prediction_promocode_percent';
36
+ const RECKLESS_PROMOTIONS_CUSTOMER_PROMO_MESSAGE = 'reckless_prediction/reckless_promotions_section/reckless_prediction_customer_promo_message';
37
+
38
+ /**
39
+ * Check if Reckless Module is enabled
40
+ *
41
+ * @return bool
42
+ * @author Dhruv Boruah <hello@reckless.io>
43
+ */
44
+ public function isRecklessModuleEnabled()
45
+ {
46
+ return (bool) Mage::getStoreConfig(self::RECKLESS_CORE_ENABLED);
47
+ }
48
+
49
+ /**
50
+ * Check if Log is Enabled
51
+ *
52
+ * @return bool
53
+ * @author Dhruv Boruah <hello@reckless.io>
54
+ */
55
+ public function isLogEnabled($store = null)
56
+ {
57
+ return (bool) Mage::getStoreConfig(self::RECKLESS_CORE_ENABLE_LOG);
58
+ }
59
+
60
+ /**
61
+ * Check if Reckless Customer Sync is enabled
62
+ *
63
+ * @return bool
64
+ * @author Dhruv Boruah <hello@reckless.io>
65
+ */
66
+ public function isCustomerSyncEnabled()
67
+ {
68
+ return (bool) Mage::getStoreConfig(self::RECKLESS_CORE_SYNC_CUST_EMAIL);
69
+ }
70
+
71
+ /**
72
+ * Get the Reckless API Key
73
+ *
74
+ * @return string - returns "SIGNUP FOR API KEY" if this is not set
75
+ * @author Dhruv Boruah <hello@reckless.io>
76
+ */
77
+ public function getRecklessAPIKey()
78
+ {
79
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_CORE_API_KEY)) == 0) {
80
+ throw new Exception("ERROR : API Key Missing. Please SIGNUP FOR API KEY");
81
+ } else {
82
+ return Mage::getStoreConfig(self::RECKLESS_CORE_API_KEY);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Set the Reckless API Key
88
+ *
89
+ * @author Dhruv Boruah <hello@reckless.io>
90
+ */
91
+ public function setRecklessAPIKey($apikey)
92
+ {
93
+ if (strlen($apikey) == 0) {
94
+ throw new Exception("ERROR : API Key Missing. Please SIGNUP FOR API KEY");
95
+ } else {
96
+ Mage::getModel('core/config')->saveConfig(self::RECKLESS_CORE_API_KEY, $apikey);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Get the Reckless Base Url
102
+ *
103
+ * @return string - returns "ENTER BASE URL" if this is not set
104
+ * @author Dhruv Boruah <hello@reckless.io>
105
+ */
106
+ public function getRecklessBaseUrl()
107
+ {
108
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_CORE_BASE_URL)) == 0) {
109
+ throw new Exception("ERROR : Missing Base Url for Reckless.io Verification");
110
+ } else {
111
+ return Mage::getStoreConfig(self::RECKLESS_CORE_BASE_URL);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Set the Reckless Base Url
117
+ *
118
+ * @author Dhruv Boruah <hello@reckless.io>
119
+ */
120
+ public function setRecklessBaseUrl($domain)
121
+ {
122
+ if (strlen($domain) == 0) {
123
+ throw new Exception("ERROR : Missing Base Url for Reckless.io Verification");
124
+ } else {
125
+ Mage::getModel('core/config')->saveConfig(self::RECKLESS_CORE_BASE_URL, $domain);
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Get the Reckless Nofify Email
131
+ *
132
+ * @return string
133
+ * @author Dhruv Boruah <hello@reckless.io>
134
+ */
135
+ public function getMonitorNotifyEmail()
136
+ {
137
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_CORE_NOTIFY_EMAIL)) == 0) {
138
+ throw new Exception("ERROR : Configured Notification Email is Empty");
139
+ } else {
140
+ return Mage::getStoreConfig(self::RECKLESS_CORE_NOTIFY_EMAIL);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Get the LogFileName
146
+ *
147
+ * @return string
148
+ * @author Dhruv Boruah <hello@reckless.io>
149
+ */
150
+ public function getLogFileName()
151
+ {
152
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_CORE_LOG_FILE)) == 0) {
153
+ return false;
154
+ } else {
155
+ return Mage::getStoreConfig(self::RECKLESS_CORE_LOG_FILE);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Check if Reckless Promotion is enabled
161
+ *
162
+ * @return bool
163
+ * @author Dhruv Boruah <hello@reckless.io>
164
+ */
165
+ public function isRecklessPromotionEnabled()
166
+ {
167
+ return (bool) Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_ENABLED);
168
+ }
169
+
170
+ /**
171
+ * Get Promotion Threshold
172
+ * Defaut: 10 GBP , Prediction for multiple stores and for the default value for different websites
173
+ *
174
+ * @return string
175
+ * @author Dhruv Boruah <hello@reckless.io>
176
+ */
177
+ public function getPromotionThreshold($store = null)
178
+ {
179
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_PRMOTIONS_THRESHOLD)) == 0) {
180
+ return 10;
181
+ } else {
182
+ return Mage::getStoreConfig(self::RECKLESS_PRMOTIONS_THRESHOLD);
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Get Promotion Validity
188
+ * Defaut: 86400 Minutes ( 60 days)
189
+ * @return string
190
+ * @author Dhruv Boruah <hello@reckless.io>
191
+ */
192
+ public function getPromotionValidity($store = null)
193
+ {
194
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_VAILDITY)) == 0) {
195
+ return 60;
196
+ } else {
197
+ return Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_VAILDITY);
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Method to send notification emails
203
+ * @param unknown_type $sendToName
204
+ * @param unknown_type $sendToEmail
205
+ * @param unknown_type $subject
206
+ * @param unknown_type $msg
207
+ */
208
+ public function notify($sendToName, $sendToEmail, $subject, $msg)
209
+ {
210
+ $mail = Mage::getModel('core/email');
211
+ $mail->setToName($sendToName);
212
+ $mail->setToEmail($sendToEmail);
213
+ $mail->setBody($msg);
214
+ $mail->setSubject('=?utf-8?B?' . base64_encode($subject) . '?=');
215
+ $mail->setFromEmail(Mage::getStoreConfig('trans_email/ident_general/email'));
216
+ $mail->setFromName(Mage::getStoreConfig('general/store_information/name'));
217
+ $mail->setType('html');
218
+
219
+ try {
220
+ $mail->send();
221
+ } catch (Exception $e) {
222
+ Mage::logException($e);
223
+
224
+ return false;
225
+ }
226
+
227
+ return true;
228
+ }
229
+
230
+ public function runCurl($curl_url)
231
+ {
232
+ $curl = curl_init();
233
+ curl_setopt($curl, CURLOPT_URL, $curl_url);
234
+ curl_setopt($curl, CURLOPT_HEADER, false);
235
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
236
+ curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
237
+ $response = curl_exec($curl);
238
+ //$status = curl_getinfo($curl);
239
+ curl_close($curl);
240
+
241
+ return $response;
242
+ }
243
+
244
+ public function checkVersion()
245
+ {
246
+ $val = 1;
247
+
248
+ if (Mage::getVersion() < 1.3) {
249
+ $val = 0;
250
+ } elseif (Mage::getVersion() > 1.3) {
251
+ if (Mage::getVersion() == '1.4.0.1') {
252
+ $val = 1;
253
+ } else {
254
+ $val = 2;
255
+ }
256
+ }
257
+
258
+ return $val;
259
+ }
260
+
261
+ public function getRuleModel()
262
+ {
263
+ if ($this->checkVersion() == 2) {
264
+ return Mage::getModel('salesrule/coupon');
265
+ } else {
266
+ return Mage::getModel('salesrule/rule');
267
+ }
268
+ }
269
+
270
+ public function getCouponPostfix($customer){
271
+ if ($customer->getCustomerId() == NULL)
272
+ return "G";
273
+ return $customer->getCustomerId();
274
+ }
275
+ /*
276
+ * Function to create
277
+ * a) Promotion Rule and
278
+ * b) coupon codes for Old and New Magento Versions
279
+ */
280
+
281
+ public function createCouponCode($discount_percent, $store, $customer)
282
+ {
283
+ $coupon= $this->getCouponPrefix() . Mage::helper('core')->getRandomString(4) . "-" . $this->getCouponPostfix($customer);
284
+ Mage::log("Creating Coupon " . $coupon);
285
+ $model = $this->getRuleModel();
286
+
287
+ $now = new DateTime('NOW');
288
+ $couponValidFromDate = new DateTime();
289
+ $couponValidFromDate = $now->format(DateTime::ISO8601);
290
+ $intervalToAdd = "P" . $this->getPromotionValidity() . "D";// Days
291
+ $couponValidToDate = $now->add(new DateInterval($intervalToAdd));
292
+
293
+ try {
294
+
295
+ if ($this->checkVersion() == 2) {
296
+
297
+ $rule = Mage::getModel('salesrule/rule')
298
+ ->setName($coupon)
299
+ ->setDescription($this->getCouponDescription() . $customer->getCustomerId())
300
+ ->setFromDate(date($couponValidFromDate))
301
+ ->setToDate($couponValidToDate->format('Y-m-d H:i:s'))
302
+ ->setCustomerGroupIds($this->getCustomerGroups())
303
+ ->setIsActive(1)
304
+ //->setStopRulesProcessing
305
+ //->setIsAdvanced
306
+ ->setSimpleAction('by_percent')
307
+ ->setDiscountAmount(min(100, $this->getCouponDiscountPercent()))
308
+ //->setDiscountQty
309
+ //->setDiscountStep
310
+ ->setStopRulesProcessing($this->getStopRulesProcessing())
311
+ ->setUseAutoGeneration(0)
312
+ ->setIsRss(1)
313
+ ->setUsesPerCoupon($this->getMaxCouponUsage())
314
+ ->setUsesPerCustomer($this->getUsagePerCustomer())
315
+ ->setExpirationDate($couponValidToDate)
316
+ ->setWebsiteIds($this->getAllWebsites())
317
+ ->setCouponType($this->getCouponType())
318
+ ->save();
319
+
320
+ $model->setRuleId($rule->getId())
321
+ ->setCode($coupon)
322
+ ->setIsPrimary(1)
323
+ ->save();
324
+
325
+ } else {
326
+ $model
327
+ ->setName($coupon)
328
+ ->setDescription($this->getCouponDescription() . $customer->getCustomerId())
329
+ ->setFromDate(date($couponValidFromDate))
330
+ ->setCouponCode($coupon)
331
+ ->setToDate($couponValidToDate->format('Y-m-d H:i:s'))
332
+ ->setCustomerGroupIds($this->getCustomerGroups())
333
+ ->setIsActive(1)
334
+ ->setSimpleAction('by_percent')
335
+ ->setUseAutoGeneration(0)
336
+ ->setWebsiteIds($this->getAllWebsites())
337
+ ->setDiscountAmount(min(100, $this->getCouponDiscountPercent()))
338
+ ->setStopRulesProcessing($this->getStopRulesProcessing())
339
+ ->setIsRss(1)
340
+ ->setUsesPerCoupon($this->getMaxCouponUsage())
341
+ ->setUsesPerCustomer($this->getUsagePerCustomer())
342
+ ->setExpirationDate($couponValidToDate)
343
+ ->setCouponType($this->getCouponType())
344
+ ->save();
345
+
346
+ }
347
+ } catch (exception $e) {
348
+ return $e->getMessage();
349
+ }
350
+
351
+ return $coupon;
352
+
353
+ }
354
+
355
+ /*
356
+ * Function to delete a Coupon Code and the Associated Rule
357
+ */
358
+ public function deleteCouponCode($couponCode)
359
+ {
360
+ $couponModel = Mage::getModel('salesrule/coupon')
361
+ ->getCollection()
362
+ ->addFieldToFilter('code', $couponCode)
363
+ ->getFirstItem();
364
+ $ruleId = $couponModel->getRuleId();
365
+
366
+ $ruleModel = Mage::getModel('salesrule/rule')
367
+ ->getCollection()
368
+ ->addFieldToFilter('rule_id', $ruleId)
369
+ ->getFirstItem();
370
+
371
+ $couponModel->delete();
372
+ $ruleModel->delete();
373
+ }
374
+
375
+ /*
376
+ * Function to return the website ids
377
+ */
378
+ protected function getAllWebsites()
379
+ {
380
+ $website_ids = array();
381
+ $websites = Mage::app()->getWebsites();
382
+ foreach ($websites as $website) {
383
+ $website_ids[] = $website->getId();
384
+ }
385
+
386
+ return $website_ids;
387
+ }
388
+
389
+ /*
390
+ * Function to return the customer groups
391
+ */
392
+ protected function getCustomerGroups()
393
+ {
394
+ $groupIds = array();
395
+ $collection = Mage::getModel('customer/group')->getCollection();
396
+ foreach ($collection as $customer) {
397
+ $groupIds[] = $customer->getId();
398
+ }
399
+
400
+ return $groupIds;
401
+ }
402
+
403
+ public function getStopRulesProcessing()
404
+ {
405
+ return (bool) Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_STOPRULESPROCESSING);
406
+ }
407
+
408
+ public function getCouponType()
409
+ {
410
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_COUPONTYPE)) == 0) {
411
+ return 2;
412
+ } else {
413
+ return Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_COUPONTYPE);
414
+ }
415
+ }
416
+
417
+ public function getUsagePerCustomer()
418
+ {
419
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_USAGEPERCUSTOMER)) == 0) {
420
+ return 1;
421
+ } else {
422
+ return Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_USAGEPERCUSTOMER);
423
+ }
424
+ }
425
+
426
+ public function getMaxCouponUsage()
427
+ {
428
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_COUPONUSAGE)) == 0) {
429
+ return 1;
430
+ } else {
431
+ return Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_COUPONUSAGE);
432
+ }
433
+ }
434
+
435
+ public function getCouponPrefix()
436
+ {
437
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_COUPONPREFIX)) == 0) {
438
+ return "PRSL";
439
+ } else {
440
+ return Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_COUPONPREFIX);
441
+ }
442
+ }
443
+
444
+ public function getCouponDescription()
445
+ {
446
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_COUPONDESC)) == 0) {
447
+ return "Personalised Promo Code for customer ";
448
+ } else {
449
+ return Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_COUPONDESC);
450
+ }
451
+ }
452
+
453
+ public function getCouponDiscountPercent()
454
+ {
455
+ if (strlen(Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_COUPONDISCPERCENT)) == 0) {
456
+ return 5;
457
+ } else {
458
+ return Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_COUPONDISCPERCENT);
459
+ }
460
+ }
461
+
462
+ public function getLastProcessedOrderId()
463
+ {
464
+ return Mage::getStoreConfig(self::RECKLESS_CORE_LAST_ORDER) ?: 0;
465
+ }
466
+
467
+ public function setLastProcessedOrderId($id)
468
+ {
469
+ Mage::getModel('core/config')->saveConfig(self::RECKLESS_CORE_LAST_ORDER, $id);
470
+ }
471
+
472
+ public function getLastProcessedQuoteId()
473
+ {
474
+ return Mage::getStoreConfig(self::RECKLESS_CORE_LAST_QUOTE) ?: 0;
475
+ }
476
+
477
+ public function setLastProcessedQuoteId($id)
478
+ {
479
+ Mage::getModel('core/config')->saveConfig(self::RECKLESS_CORE_LAST_QUOTE, $id);
480
+ }
481
+
482
+ public function getCustomerPromoMessage(){
483
+ return Mage::getStoreConfig(self::RECKLESS_PROMOTIONS_CUSTOMER_PROMO_MESSAGE);
484
+ }
485
+ }
app/code/local/Reckless/Prediction/Model/Promotion/Observer.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Reckless_Prediction_Model_Promotion_Observer
4
+ {
5
+ public static $errorLogFile = "";
6
+
7
+ /**
8
+ * Constructor
9
+ * Initialize the log file and the model
10
+ */
11
+ public function _construct()
12
+ {
13
+ self::$errorLogFile = Mage::helper('reckless_prediction')->getLogFileName();
14
+ }
15
+
16
+ public function show_predicted_coupon_code($observer)
17
+ {
18
+ if (!Mage::getSingleton('core/session')->getPromotionShowed()) {
19
+ $event = $observer->getEvent();
20
+ $collection = Mage::getModel('prediction/promotions')
21
+ ->getCollection()
22
+ ->addFieldToFilter('session_id', session_id());
23
+ foreach ($collection as $discount) {
24
+ Mage::getSingleton('core/session')->addNotice(
25
+ Mage::helper('reckless_prediction')->getCustomerPromoMessage() . ' ' . $discount->getCouponCode()
26
+ );
27
+ Mage::getSingleton('core/session')->setPromotionShowed(true);
28
+ }
29
+ $this->log("**********Observer Called for event : checkout_cart_add_product_complete");
30
+ }
31
+ return $this;
32
+ }
33
+
34
+ /**
35
+ * Generic function to log to a consistent log file
36
+ *
37
+ * @param unknown_type $message - Message to log
38
+ */
39
+ public function log($message)
40
+ {
41
+ if (Mage::helper('reckless_prediction')->isLogEnabled()) {
42
+ Mage::log($message, null, self::$errorLogFile);
43
+ } else {
44
+ return;
45
+ }
46
+ }
47
+ }
app/code/local/Reckless/Prediction/Model/Promotions.php ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *
4
+ *
5
+ * @version $Id$
6
+ * @copyright Reckless Data, 11th June 2014
7
+ * @package Reckless_Prediction
8
+ * @author Dhruv Boruah <hello@reckless.io>
9
+ * @author Javier Carrascal <hello@reckless.io>
10
+ */
11
+ class Reckless_Prediction_Model_Promotions extends Mage_Core_Model_Abstract
12
+ {
13
+ public static $errorLogFile = "";
14
+ public static $emailMessage = "";
15
+
16
+ const RECKLESS_SERVER_URL = 'http://javi-awesome-bi.appspot.com/machinelearning/%s/%s/%s/';
17
+ const TEMP_TRAINING_CSV_FILENAME = "reckless-training-set-v1.csv";
18
+ const MAX_NUM_ITEMS = 1000;
19
+
20
+ /**
21
+ * Constructor
22
+ * Initialize the log file and the model
23
+ */
24
+ public function _construct()
25
+ {
26
+ $this->_init('prediction/promotions');
27
+ self::$errorLogFile = Mage::helper('reckless_prediction')->getLogFileName();
28
+ }
29
+
30
+ /**
31
+ * Generic function to log to a consistent log file
32
+ *
33
+ * @param unknown_type $message - Message to log
34
+ */
35
+ public function log($message)
36
+ {
37
+ if (Mage::helper('reckless_prediction')->isLogEnabled()) {
38
+ Mage::log($message, null, self::$errorLogFile);
39
+ } else {
40
+ return;
41
+ }
42
+ }
43
+
44
+ private function saveToCSV($fileName, $csvData)
45
+ {
46
+ $file = Mage::getBaseDir('export') . '/' . $fileName;
47
+ $csv = new Varien_File_Csv();
48
+ $csv->saveData($file, $csvData);
49
+ }
50
+
51
+ private function getUploadUrl($apiKey)
52
+ {
53
+ return sprintf(self::RECKLESS_SERVER_URL, 'get_train_url', $apiKey, md5($apiKey));
54
+ }
55
+
56
+ private function uploadTrainingData($filename)
57
+ {
58
+ $this->log("--> UploadTrainingData");
59
+ //GET API Key and Domain
60
+ $apiKey = Mage::helper('reckless_prediction')->getRecklessAPIKey();
61
+
62
+ // Step 1: Get Upload Url
63
+ $requestUploadUrl = $this->getUploadUrl($apiKey);
64
+ $uploadUrl = Mage::helper('reckless_prediction')->runCurl($requestUploadUrl);
65
+ $this->log("Upload Url Request Reply " . $uploadUrl);
66
+
67
+ //Step 2: Upload File to Reckless Server and Train
68
+ $trainingCSVFilePath = Mage::getBaseDir('export') . '/' . $filename;
69
+ $post = array('file'=> '@' . $trainingCSVFilePath);
70
+
71
+ $ch = curl_init();
72
+ curl_setopt($ch, CURLOPT_URL, $uploadUrl);
73
+ curl_setopt($ch, CURLOPT_POST, 1);
74
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
75
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
76
+ $response = curl_exec($ch);
77
+ $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
78
+
79
+ curl_close($ch);
80
+ $this->log("UploadTrainingData --> Curl Staus: " . $httpcode);
81
+
82
+ }
83
+
84
+ private function getUnsuccessfulCheckouts($id = null, $field = 'entity_id')
85
+ {
86
+ $data = array();
87
+
88
+ $quote_collection = Mage::getModel('sales/quote')
89
+ ->getCollection()
90
+ ->addExpressionFieldToSelect('created', 'UNIX_TIMESTAMP(created_at)', null)
91
+ ->setOrder('entity_id', 'ASC')
92
+ // If quote is active, means that order hasn't been created (user didn't checkout)
93
+ ->addFieldToFilter('is_active', array('eq' => '1'));
94
+ if ($id) {
95
+ $quote_collection->addFieldToFilter($field, array('eq' => $id));
96
+ } else {
97
+ // Delta to filter the collection
98
+ $lastProcessed = Mage::helper('reckless_prediction')->getLastProcessedQuoteId();
99
+ $quote_collection->addFieldToFilter('entity_id', array('gt' => $lastProcessed));
100
+ // Avoid processing too
101
+ $quote_collection->addFieldToFilter('entity_id', array('gt' => $lastProcessed));
102
+ }
103
+
104
+ $quote_collection->getSelect()->limit(self::MAX_NUM_ITEMS);
105
+
106
+ foreach ($quote_collection as $quote) {
107
+ $data[] = array(
108
+ 'quote',
109
+ $quote->getCreated(),
110
+ $quote->getRemoteIp(),
111
+ $quote->getBaseSubtotal() - $quote->getBaseSubtotalWithDiscount(),
112
+ $quote->getBaseGrandTotal(),
113
+ $quote->getItemsQty(),
114
+ $quote->getCustomerIsGuest()
115
+ );
116
+ if ($id) {
117
+ $data[sizeof($data) - 1][] = $quote->getEntityId();
118
+ }
119
+ }
120
+ // Update delta only if it's the cronjob training (not customer_id specific data)
121
+ if (isset($lastProcessed) && isset($quote)) {
122
+ Mage::helper('reckless_prediction')->setLastProcessedQuoteId($quote->getEntityId());
123
+ }
124
+ return $data;
125
+ }
126
+
127
+ private function getSuccessfulCheckouts()
128
+ {
129
+ $lastProcessed = Mage::helper('reckless_prediction')->getLastProcessedOrderId();
130
+ $data = array();
131
+ $order_collection = Mage::getModel('sales/order')
132
+ ->getCollection()
133
+ ->addExpressionFieldToSelect('created', 'UNIX_TIMESTAMP(created_at)', null)
134
+ ->addAttributeToFilter('status', array('nin' => array('canceled')))
135
+ ->setOrder('entity_id', 'ASC');
136
+
137
+ // Delta processing data
138
+ $order_collection->addAttributeToFilter('entity_id', array('gt' => array($lastProcessed)));
139
+ $order_collection->getSelect()->limit(self::MAX_NUM_ITEMS);
140
+
141
+ foreach ($order_collection as $order) {
142
+ $data[] = array(
143
+ 'order',
144
+ $order->getCreated(),
145
+ $order->getRemoteIp(),
146
+ $order->getBaseDiscountInvoiced(),
147
+ $order->getBaseGrandTotal(),
148
+ $order->getTotalQtyOrdered(),
149
+ $order->getCustomerIsGuest()
150
+ );
151
+ }
152
+ // Update delta
153
+ if (isset($order)) {
154
+ Mage::helper('reckless_prediction')->setLastProcessedOrderId($order->getEntityId());
155
+ }
156
+ return $data;
157
+ }
158
+
159
+ private function getAndUploadTrainingData()
160
+ {
161
+ $this->log("--> getAndUploadTrainingData");
162
+
163
+ $unsuccessful = $this->getUnsuccessfulCheckouts();
164
+ $successful = $this->getSuccessfulCheckouts();
165
+ $trainData = array_merge($unsuccessful, $successful);
166
+
167
+ if (sizeOf($trainData)) {
168
+ // Re-init the config to flush the config cache with the delta values for the processed orders and quotes
169
+ Mage::getConfig()->reinit();
170
+
171
+ $this->saveToCSV(self::TEMP_TRAINING_CSV_FILENAME, $trainData);
172
+ $this->uploadTrainingData(self::TEMP_TRAINING_CSV_FILENAME);
173
+ } else {
174
+ $this->log("Nothing to train");
175
+ }
176
+
177
+ $this->log("getAndUploadTrainingData --> Done");
178
+ }
179
+
180
+ /**
181
+ * This method is triggered by the Cron job to train the prediction engine
182
+ */
183
+ public function train()
184
+ {
185
+ // Check if the Reckless Data Module is enabled
186
+ if (Mage::helper('reckless_prediction')->isRecklessModuleEnabled()) {
187
+ $this->log('Engine Training Started by Cron');
188
+ try {
189
+ // Start of Training Logic
190
+ $this->getAndUploadTrainingData();
191
+ } catch (Exception $e) {
192
+ $this->sendAlertEmail("Error: Reckless Data Training", $e->getTraceAsString());
193
+ }
194
+ } else {
195
+ $this->log("Reckless Data is DISABLED");
196
+ }
197
+
198
+ return $this;
199
+ }
200
+
201
+ /**
202
+ * This method is triggered by the Cron job to update the predictions
203
+ */
204
+ public function predict()
205
+ {
206
+ // Check if the Reckless Data Module is enabled
207
+ if (Mage::helper('reckless_prediction')->isRecklessModuleEnabled() &&
208
+ Mage::helper('reckless_prediction')->isRecklessPromotionEnabled()) {
209
+ $this->log('Prediction Started');
210
+ try {
211
+ $this->updatePredictions();
212
+ //$this->generatePromoCoupons();
213
+
214
+ } catch (Exception $e) {
215
+ $this->sendAlertEmail("Error: Reckless Data Predictions", $e->getTraceAsString());
216
+ }
217
+ } else {
218
+ $this->log("Reckless Data is Disabled");
219
+ }
220
+
221
+ return $this;
222
+ }
223
+
224
+ private function getSessionID($visitor_id)
225
+ {
226
+ $onlineCustomersCollection = Mage::getModel('log/visitor');
227
+ $sessionData = $onlineCustomersCollection->load($visitor_id)->getData();
228
+
229
+ return $sessionData['session_id'];
230
+ }
231
+
232
+ public function getOnlineCustomersCollection()
233
+ {
234
+ $onlineCustomersCollection = Mage::getModel('log/visitor_online')
235
+ ->prepare()
236
+ ->getCollection();
237
+ $onlineCustomersCollection->getSelect()->joinLeft(array('log_quote'=> 'log_quote'), 'log_quote.visitor_id = main_table.visitor_id', array('log_quote.quote_id'));
238
+ return $onlineCustomersCollection;
239
+ }
240
+
241
+ private function getPredictionUrl($apiKey)
242
+ {
243
+ return sprintf(self::RECKLESS_SERVER_URL, 'predict', $apiKey, md5($apiKey));
244
+ }
245
+
246
+ private function getCheckoutIntent($customerQuote)
247
+ {
248
+ $apiKey = Mage::helper('reckless_prediction')->getRecklessAPIKey();
249
+
250
+ $checkoutIntent = "X";
251
+
252
+ $checkOutIntentPredictionUrl = $this->getPredictionUrl($apiKey);
253
+ $fields_string = http_build_query(array_slice($customerQuote, 1));
254
+
255
+ $ch = curl_init();
256
+ curl_setopt($ch, CURLOPT_URL, $checkOutIntentPredictionUrl);
257
+ curl_setopt($ch, CURLOPT_POST, count($customerQuote) - 1);
258
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
259
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
260
+ $result = curl_exec($ch);
261
+ $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
262
+ curl_close($ch);
263
+
264
+ if (($httpcode) != '200') {
265
+ $this->log("Error! Curl Url" . $checkOutIntentPredictionUrl . ", POST DATA " . print_r($customerQuote, true));
266
+ $this->log("Response code: " . $httpcode);
267
+ throw new Exception('Error while fetching prediction results.');
268
+ }
269
+
270
+ return $result;
271
+
272
+ }
273
+
274
+ private function createCouponCode($discount_percent, $store, $customer)
275
+ {
276
+ $couponCode = Mage::helper('reckless_prediction')->createCouponCode($discount_percent, null, $customer);
277
+ return $couponCode;
278
+ }
279
+
280
+ private function updatePredictionModel($customer, $checkoutIntent, $quoteId, $predictedDiscountPercent)
281
+ {
282
+ //Check if a record exist for the customer
283
+ //If yes, Update the record
284
+ //If No, Add a new record
285
+ $collections = Mage::getModel('prediction/promotions')
286
+ ->getCollection()
287
+ ->addFieldToFilter('quote_id', $quoteId);
288
+
289
+ //If no record
290
+ if ($collections->count() < 1) {
291
+ $this->log("NEW RECORD : Visitor With ID: " . $customer->getVisitorId() . " will be created");
292
+ $pmodel = Mage::getModel('prediction/promotions');
293
+ $pmodel->setVisitorId($customer->getVisitorId())
294
+ ->setCustomerId($customer->getCustomerId())
295
+ ->setQuoteId($quoteId)
296
+ ->setSessionId($this->getSessionID($customer->getVisitorId()))
297
+ ->setCheckoutIntent($checkoutIntent)
298
+ ->setDiscountPercent($predictedDiscountPercent)
299
+ ->setCouponCode($this->createCouponCode($predictedDiscountPercent, null, $customer))
300
+ ->save();
301
+
302
+ } else {
303
+ $this->log("UPDATE RECORD : Visitor With ID: " . $customer->getVisitorId());
304
+ //Loop
305
+ foreach ($collections as $promo) {
306
+ $promo->setVisitorId($customer->getVisitorId());
307
+ $promo->setCheckoutIntent($checkoutIntent);
308
+ if ($predictedDiscountPercent != $promo->getDiscountPercent()) {
309
+ //delete old code
310
+ //create a new code
311
+ Mage::helper('reckless_prediction')->deleteCouponCode($promo->getCouponCode());
312
+ $promo->setCouponCode($this->createCouponCode($predictedDiscountPercent, null, $customer));
313
+ }
314
+
315
+ $promo->save();
316
+ }
317
+ }
318
+ }
319
+ private function predictAndProcess($customerCtr, $customer, $customerQuotes)
320
+ {
321
+ $sessionId = $this->getSessionID($customer->getVisitorId());
322
+
323
+ //Process all Quotes and predict a checkout intent
324
+ foreach ($customerQuotes as $customerQuote) {
325
+ try {
326
+ $quoteId = $customerQuote[sizeOf($customerQuote) -1];
327
+ unset($customerQuote[sizeOf($customerQuote) -1]);
328
+ //Step 2: Predict Y/N checkout
329
+ $checkoutIntent = $this->getCheckoutIntent($customerQuote);
330
+ //Step 3: Record prediction in DB
331
+ $this->updatePredictionModel($customer, $checkoutIntent, $quoteId, Mage::helper('reckless_prediction')->getCouponDiscountPercent());
332
+ $this->log("Customer: " . $customerCtr . ", Quote " . $quoteId . ", Visitor ID : " . $customer->getVisitorId() . ", CustomerId: " . $customer->getCustomerId() . ", Session ID: " . $sessionId . ", Checkout Intent: " . $checkoutIntent);
333
+
334
+ } catch (Exception $e) {
335
+ $this->sendAlertEmail("Error: Reckless Data Predicting", $e->getTraceAsString());
336
+ }
337
+ }
338
+ }
339
+
340
+ private function updatePredictions()
341
+ {
342
+ $this->log("--> updatePredictions");
343
+ $resource = Mage::getSingleton('core/resource');
344
+ $readConnection = $resource->getConnection('core_read');
345
+
346
+ $onlineCustomersCollection = $this->getOnlineCustomersCollection();
347
+ $this->log("Online Customers Size: " . $onlineCustomersCollection->count());
348
+ $customerCtr = 0;
349
+ foreach ($onlineCustomersCollection as $customer) {
350
+ $customerQuotes = null;
351
+ $customerCtr++;
352
+
353
+ //Step 1: Get Quote data if id is present
354
+ if ($customer->getQuoteId()) {
355
+ $this->log($customer->getQuoteId());
356
+ $customerQuotes = $this->getUnsuccessfulCheckouts($customer->getQuoteId());
357
+ } else if ($customer->getCustomerId()) {
358
+ // Attempt to fetch the quote for the logged customer
359
+ $customerQuotes = $this->getUnsuccessfulCheckouts($customer->getCustomerId(), 'customer_id');
360
+ }
361
+
362
+ if (($customer->getQuoteId() || $customer->getCustomerId()) && sizeOf($customerQuotes)) {
363
+ $this->log($customerQuotes);
364
+ $this->predictAndProcess($customerCtr, $customer, $customerQuotes);
365
+ } else {
366
+ $this->log("Customer: " . $customerCtr . ", Visitor Id: " . $customer->getVisitorId() . ". Error: NO Quotes for Customer " . $customer->getCustomerId());
367
+ }
368
+ }
369
+ $this->log("updatePredictions -->");
370
+ }
371
+ private function sendAlertEmail($subject, $body)
372
+ {
373
+ $helper = Mage::helper('reckless_prediction');
374
+ $helper->notify($helper->getMonitorNotifyEmail(), $helper->getMonitorNotifyEmail(), $subject, $body);
375
+ }
376
+ }
app/code/local/Reckless/Prediction/Model/Resource/Promotions.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *
4
+ *
5
+ * @version $Id$
6
+ * @copyright Reckless Data, 11th June 2014
7
+ * @package Reckless_Prediction
8
+ * @author Dhruv Boruah <hello@reckless.io>
9
+ */
10
+ class Reckless_Prediction_Model_Resource_Promotions extends Mage_Core_Model_Resource_Db_Abstract
11
+ {
12
+ public function _construct()
13
+ {
14
+ $this->_init('prediction/promotions', 'entity_id');
15
+ }
16
+ }
app/code/local/Reckless/Prediction/Model/Resource/Promotions/Collection.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *
4
+ *
5
+ * @version $Id$
6
+ * @copyright Reckless Data, 11th June 2014
7
+ * @package Reckless_Prediction
8
+ * @author Dhruv Boruah <hello@reckless.io>
9
+ */
10
+ class Reckless_Prediction_Model_Resource_Promotions_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
11
+ {
12
+ protected function _construct()
13
+ {
14
+ $this->_init('prediction/promotions');
15
+ }
16
+ }
app/code/local/Reckless/Prediction/Model/Resource/Setup.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *
4
+ *
5
+ * @version $Id$
6
+ * @copyright Reckless Data, 11th June 2014
7
+ * @package Reckless_Prediction
8
+ * @author Dhruv Boruah <hello@reckless.io>
9
+ */
10
+ class Reckless_Prediction_Model_Resource_Setup extends Mage_Core_Model_Resource_Setup
11
+ {
12
+ }
app/code/local/Reckless/Prediction/controllers/Adminhtml/PredictionController.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Reckless_Prediction_Adminhtml_PredictionController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+ public function indexAction()
5
+ {
6
+ $this->_title('Reckless Predictions');
7
+ $this->loadLayout();
8
+ $this->_setActiveMenu('customers/sales');
9
+ $this->_addContent($this->getLayout()->createBlock('reckless_prediction/adminhtml_predictions'));
10
+ $this->renderLayout();
11
+ }
12
+ public function exportPredictionsCsvAction()
13
+ {
14
+ $fileName = 'reckless_predictions.csv';
15
+ $grid = $this->getLayout()->createBlock('reckless_prediction/adminhtml_predictions_grid');
16
+ $this->_prepareDownloadResponse($fileName, $grid->getCsvFile());
17
+ }
18
+ public function exportPredictionsExcelAction()
19
+ {
20
+ $fileName = 'reckless_predictions.xls';
21
+ $grid = $this->getLayout()->createBlock('reckless_prediction/adminhtml_predictions_grid');
22
+ $this->_prepareDownloadResponse($fileName, $grid->getExcelFile($fileName));
23
+ }
24
+ public function redemptionsAction()
25
+ {
26
+ $this->_title('Reckless Redemptions');
27
+ $this->loadLayout();
28
+ $this->_setActiveMenu('reports/redemptions');
29
+ $this->_addContent($this->getLayout()->createBlock('reckless_prediction/adminhtml_redemptions'));
30
+ $this->renderLayout();
31
+ }
32
+ public function exportRedemptionsCsvAction()
33
+ {
34
+ $fileName = 'reckless_redemptions.csv';
35
+ $grid = $this->getLayout()->createBlock('reckless_prediction/adminhtml_redemptions_grid');
36
+ $this->_prepareDownloadResponse($fileName, $grid->getCsvFile());
37
+ }
38
+ public function exportRedemptionsExcelAction()
39
+ {
40
+ $fileName = 'reckless_redemptions.xls';
41
+ $grid = $this->getLayout()->createBlock('reckless_prediction/adminhtml_redemptions_grid');
42
+ $this->_prepareDownloadResponse($fileName, $grid->getExcelFile($fileName));
43
+ }
44
+ }
app/code/local/Reckless/Prediction/data/reckless_prediction_setup/data-install-0.1.0.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $this->startSetup();
3
+
4
+ $baseurl = Mage::getBaseUrl();
5
+ $domain = parse_url($baseurl, PHP_URL_HOST);
6
+ $apikey = md5($domain);
7
+
8
+ //Mage::log("\n BaseURL: " . $baseurl . ". Domain: " . $domain . " Hash: " . md5($domain));
9
+ $curl_url = "http://javi-awesome-bi.appspot.com/machinelearning/create/"
10
+ . $domain
11
+ . "/"
12
+ . $apikey
13
+ . "/";
14
+
15
+ Mage::log("calling " . $curl_url);
16
+
17
+ // Register the client app with Reckless Data Servers & Setup the default Values
18
+ $curl = curl_init();
19
+ curl_setopt($curl, CURLOPT_URL, $curl_url);
20
+ curl_setopt($curl, CURLOPT_HEADER, false);
21
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
22
+ curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
23
+ $response = curl_exec($curl);
24
+ //$status = curl_getinfo($curl);
25
+ curl_close($curl);
26
+
27
+ Mage::helper('reckless_prediction')->setRecklessBaseUrl($domain);
28
+ Mage::helper('reckless_prediction')->setRecklessAPIKey($response);
29
+
30
+ // Reinit the config to flush the config cache with the new settings saved
31
+ Mage::getConfig()->reinit();
32
+ $this->endSetup();
app/code/local/Reckless/Prediction/etc/adminhtml.xml ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <menu>
4
+ <customer>
5
+ <children>
6
+ <reckless_prediction translate="title" module="reckless_prediction">
7
+ <title>Reckless Predictions</title>
8
+ <sort_order>130</sort_order>
9
+ <action>reckless_prediction/adminhtml_prediction</action>
10
+ </reckless_prediction>
11
+ </children>
12
+ </customer>
13
+ <report>
14
+ <children>
15
+ <reckless_prediction translate="title" module="reckless_prediction">
16
+ <title>Reckless Redemptions</title>
17
+ <sort_order>130</sort_order>
18
+ <action>reckless_prediction/adminhtml_prediction/redemptions</action>
19
+ </reckless_prediction>
20
+ </children>
21
+ </report>
22
+ </menu>
23
+ <acl>
24
+ <resources>
25
+ <all>
26
+ <title>Allow Everything</title>
27
+ </all>
28
+ <admin translate="title" module="adminhtml">
29
+ <children>
30
+ <customer>
31
+ <children>
32
+ <reckless_prediction>
33
+ <title>Reckless Predictions</title>
34
+ <sort_order>200</sort_order>
35
+ </reckless_prediction>
36
+ </children>
37
+ </customer>
38
+ <report>
39
+ <children>
40
+ <reckless_prediction>
41
+ <title>Reckless Redemptions Report</title>
42
+ <sort_order>200</sort_order>
43
+ </reckless_prediction>
44
+ </children>
45
+ </report>
46
+ <system>
47
+ <children>
48
+ <config>
49
+ <children>
50
+ <reckless_prediction translate="title">
51
+ <title>Reckless Predictions</title>
52
+ <sort_order>200</sort_order>
53
+ </reckless_prediction>
54
+ </children>
55
+ </config>
56
+ </children>
57
+ </system>
58
+ </children>
59
+ </admin>
60
+ </resources>
61
+ </acl>
62
+ </config>
app/code/local/Reckless/Prediction/etc/config.xml ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Reckless_Prediction>
5
+ <version>0.1.0</version>
6
+ </Reckless_Prediction>
7
+ </modules>
8
+ <global>
9
+ <models>
10
+ <prediction>
11
+ <class>Reckless_Prediction_Model</class>
12
+ <resourceModel>prediction_resource</resourceModel>
13
+ </prediction>
14
+ <prediction_resource>
15
+ <class>Reckless_Prediction_Model_Resource</class>
16
+ <entities>
17
+ <promotions>
18
+ <table>reckless_promotions</table>
19
+ </promotions>
20
+ </entities>
21
+ </prediction_resource>
22
+ </models>
23
+ <blocks>
24
+ <adminhtml>
25
+ <rewrite>
26
+ <customer_online_grid>Reckless_Prediction_Block_Adminhtml_Customer_Online_Grid</customer_online_grid>
27
+ </rewrite>
28
+ </adminhtml>
29
+ <reckless_prediction>
30
+ <class>Reckless_Prediction_Block</class>
31
+ </reckless_prediction>
32
+ </blocks>
33
+ <helpers>
34
+ <reckless_prediction>
35
+ <class>Reckless_Prediction_Helper</class>
36
+ </reckless_prediction>
37
+ </helpers>
38
+ <resources>
39
+ <reckless_prediction_setup>
40
+ <setup>
41
+ <module>Reckless_Prediction</module>
42
+ <class>Reckless_Prediction_Model_Resource_Setup</class>
43
+ </setup>
44
+ </reckless_prediction_setup>
45
+ <reckless_prediction_write>
46
+ <connection>
47
+ <use>core_write</use>
48
+ </connection>
49
+ </reckless_prediction_write>
50
+ <reckless_prediction_read>
51
+ <connection>
52
+ <use>core_read</use>
53
+ </connection>
54
+ </reckless_prediction_read>
55
+ </resources>
56
+ <events>
57
+ <checkout_cart_add_product_complete>
58
+ <observers>
59
+ <reckless_prediction_promotion_observer>
60
+ <type>singleton</type>
61
+ <class>Reckless_Prediction_Model_Promotion_Observer</class>
62
+ <method>show_predicted_coupon_code</method>
63
+ </reckless_prediction_promotion_observer>
64
+ </observers>
65
+ </checkout_cart_add_product_complete>
66
+ </events>
67
+
68
+ </global>
69
+ <default>
70
+ <reckless_prediction>
71
+ <reckless_prediction_section>
72
+ <reckless_prediction_enable>0</reckless_prediction_enable>
73
+ <reckless_prediction_sync_customer_email>1</reckless_prediction_sync_customer_email>
74
+ <reckless_prediction_notificationemail>hello@reckless.io</reckless_prediction_notificationemail>
75
+ <reckless_prediction_enablelog>1</reckless_prediction_enablelog>
76
+ <reckless_prediction_logfilename>reckless_prediction.log</reckless_prediction_logfilename>
77
+ </reckless_prediction_section>
78
+ <reckless_promotions_section>
79
+ <reckless_dynamic_promotion_enable>0</reckless_dynamic_promotion_enable>
80
+ <reckless_prediction_promotion_threshould>10</reckless_prediction_promotion_threshould> <!-- Value in Base Currency Of the Store -->
81
+ <reckless_prediction_promocodevalid_time>86400</reckless_prediction_promocodevalid_time> <!-- In Minutes, Default: 60 Days -->
82
+ <reckless_prediction_promocode_stoprulesprocessing>0</reckless_prediction_promocode_stoprulesprocessing>
83
+ <reckless_prediction_promocode_type>2</reckless_prediction_promocode_type>
84
+ <reckless_prediction_promocode_usagepercustomer>1</reckless_prediction_promocode_usagepercustomer>
85
+ <reckless_prediction_promocode_usage>1</reckless_prediction_promocode_usage>
86
+ <reckless_prediction_promocode_prefix>PRSL</reckless_prediction_promocode_prefix>
87
+ <reckless_prediction_promocode_desc>Personalised Promo Code for customer </reckless_prediction_promocode_desc>
88
+ <reckless_prediction_promocode_percent>5</reckless_prediction_promocode_percent>
89
+ <reckless_prediction_customer_promo_message>Thank you for choosing us. As a token of appreciation, we would like to offer you 5% off next time. Please use code:</reckless_prediction_customer_promo_message>
90
+ </reckless_promotions_section>
91
+ </reckless_prediction>
92
+ </default>
93
+
94
+ <crontab>
95
+ <jobs>
96
+ <reckless_start_training>
97
+ <schedule><cron_expr>*/30 * * * *</cron_expr></schedule>
98
+ <run><model>prediction/promotions::train</model></run>
99
+ </reckless_start_training>
100
+ <reckless_prediction_heartbeat>
101
+ <schedule><cron_expr>*/5 * * * *</cron_expr></schedule>
102
+ <run><model>prediction/promotions::predict</model></run>
103
+ </reckless_prediction_heartbeat>
104
+ </jobs>
105
+ </crontab>
106
+ <admin>
107
+ <routers>
108
+ <reckless_prediction>
109
+ <use>admin</use>
110
+ <args>
111
+ <module>Reckless_Prediction</module>
112
+ <frontName>reckless_prediction</frontName>
113
+ </args>
114
+ </reckless_prediction>
115
+ </routers>
116
+ </admin>
117
+ </config>
app/code/local/Reckless/Prediction/etc/system.xml ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <tabs>
4
+ <reckless_tab translate="label">
5
+ <label>Reckless Data</label>
6
+ <sort_order>11</sort_order>
7
+ </reckless_tab>
8
+ </tabs>
9
+ <sections>
10
+ <reckless_prediction translate="label">
11
+ <label>Predictions</label>
12
+ <tab>reckless_tab</tab>
13
+ <frontend_type>text</frontend_type>
14
+ <sort_order>3</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
+ <reckless_prediction_section translate="label">
20
+ <label>Core Configuration</label>
21
+ <frontend_type>text</frontend_type>
22
+ <sort_order>1</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
+ <reckless_prediction_enable>
28
+ <label>Enable Reckless Data</label>
29
+ <frontend_type>select</frontend_type>
30
+ <source_model>adminhtml/system_config_source_yesno</source_model>
31
+ <comment><![CDATA[If enabled, Reckless Data module will be enabled which includes sync to Reckless Servers and predictions. Default: Disabled]]></comment>
32
+ <sort_order>10</sort_order>
33
+ <show_in_default>1</show_in_default>
34
+ </reckless_prediction_enable>
35
+ <reckless_prediction_api_key>
36
+ <label>API Key</label>
37
+ <frontend_type>label</frontend_type>
38
+ <sort_order>20</sort_order>
39
+ <comment><![CDATA[API key from Reckless.io. ***PLEASE DO NOT CHANGE.]]></comment>
40
+ <show_in_default>1</show_in_default>
41
+ </reckless_prediction_api_key>
42
+ <reckless_prediction_base_url>
43
+ <label>Registered Domain</label>
44
+ <frontend_type>label</frontend_type>
45
+ <sort_order>30</sort_order>
46
+ <comment><![CDATA[Set by Reckless.io. ***PLEASE DO NOT CHANGE.]]></comment>
47
+ <show_in_default>1</show_in_default>
48
+ </reckless_prediction_base_url>
49
+ <reckless_prediction_sync_customer_email>
50
+ <label>Enable Customer Email Sync</label>
51
+ <frontend_type>select</frontend_type>
52
+ <source_model>adminhtml/system_config_source_yesno</source_model>
53
+ <comment><![CDATA[If enabled, Customer emails will be used for analysis. If disabled, only customer id will be used. Default: Enabled]]></comment>
54
+ <sort_order>40</sort_order>
55
+ <show_in_default>1</show_in_default>
56
+ </reckless_prediction_sync_customer_email>
57
+ <reckless_prediction_notificationemail>
58
+ <label>Notification Email</label>
59
+ <frontend_type>text</frontend_type>
60
+ <sort_order>50</sort_order>
61
+ <comment><![CDATA[Email address to send error notifications to.]]></comment>
62
+ <show_in_default>1</show_in_default>
63
+ </reckless_prediction_notificationemail>
64
+ <reckless_prediction_enablelog>
65
+ <label>Enable Logs</label>
66
+ <frontend_type>select</frontend_type>
67
+ <source_model>adminhtml/system_config_source_yesno</source_model>
68
+ <sort_order>60</sort_order>
69
+ <comment><![CDATA[Enable or Disable cleanup logs. Default: Enabled]]></comment>
70
+ <show_in_default>1</show_in_default>
71
+ </reckless_prediction_enablelog>
72
+ <reckless_prediction_logfilename>
73
+ <label>Log file name</label>
74
+ <frontend_type>text</frontend_type>
75
+ <sort_order>70</sort_order>
76
+ <comment><![CDATA[All errors will be logged in this file]]></comment>
77
+ <show_in_default>1</show_in_default>
78
+ </reckless_prediction_logfilename>
79
+ </fields>
80
+ </reckless_prediction_section>
81
+ <reckless_promotions_section translate="label">
82
+ <label>Dynamic Promotions</label>
83
+ <frontend_type>text</frontend_type>
84
+ <sort_order>2</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
+ <fields>
89
+ <reckless_dynamic_promotion_enable>
90
+ <label>Enable Reckless Dynamic Promotion</label>
91
+ <frontend_type>select</frontend_type>
92
+ <source_model>adminhtml/system_config_source_yesno</source_model>
93
+ <comment><![CDATA[If enabled, Reckless Data will display dynamic promotins to customers on checkout. Default: Disabled]]></comment>
94
+ <sort_order>10</sort_order>
95
+ <show_in_default>1</show_in_default>
96
+ </reckless_dynamic_promotion_enable>
97
+ <reckless_prediction_promotion_threshould>
98
+ <label>Max promotion budget in 24hrs</label>
99
+ <frontend_type>text</frontend_type>
100
+ <sort_order>20</sort_order>
101
+ <comment><![CDATA[*** DO NOT USE. Feature not enabled in this version. Maximum promotions budget for Reckless to Giveaway in a 24 hour period. Default: 10 GBP]]></comment>
102
+ <show_in_default>1</show_in_default>
103
+ </reckless_prediction_promotion_threshould>
104
+ <reckless_prediction_promocodevalid_time>
105
+ <label>Promotion Code Validity (days)</label>
106
+ <frontend_type>text</frontend_type>
107
+ <sort_order>30</sort_order>
108
+ <comment><![CDATA[The time limit for the dynamically generated promocode from Created Date.Default: 60 Days]]></comment>
109
+ <show_in_default>1</show_in_default>
110
+ </reckless_prediction_promocodevalid_time>
111
+ <reckless_prediction_promocode_percent>
112
+ <label>Percentage Discount</label>
113
+ <frontend_type>text</frontend_type>
114
+ <sort_order>40</sort_order>
115
+ <comment><![CDATA[Discount Percentage for Promo Code.Default: 5%]]></comment>
116
+ <show_in_default>1</show_in_default>
117
+ </reckless_prediction_promocode_percent>
118
+ <reckless_prediction_promocode_desc>
119
+ <label>Promotion Code Description</label>
120
+ <frontend_type>text</frontend_type>
121
+ <sort_order>50</sort_order>
122
+ <comment><![CDATA[Description of Promo Code.Default: Personalised Promo Code for customer {CustomerID}]]></comment>
123
+ <show_in_default>1</show_in_default>
124
+ </reckless_prediction_promocode_desc>
125
+
126
+ <reckless_prediction_promocode_prefix>
127
+ <label>Promotion Code Prefix</label>
128
+ <frontend_type>text</frontend_type>
129
+ <sort_order>60</sort_order>
130
+ <comment><![CDATA[Will be prefixed before the promo code. Eg. {Prefix}{4RandomChars}-{CustomerID}.Default: PRSL]]></comment>
131
+ <show_in_default>1</show_in_default>
132
+ </reckless_prediction_promocode_prefix>
133
+ <reckless_prediction_promocode_usage>
134
+ <label>Promotion Code Max Usage</label>
135
+ <frontend_type>text</frontend_type>
136
+ <sort_order>70</sort_order>
137
+ <comment><![CDATA[How many times, this code can be used. Default: 1]]></comment>
138
+ <show_in_default>1</show_in_default>
139
+ </reckless_prediction_promocode_usage>
140
+ <reckless_prediction_promocode_usagepercustomer>
141
+ <label>Usage per customer</label>
142
+ <frontend_type>text</frontend_type>
143
+ <sort_order>80</sort_order>
144
+ <comment><![CDATA[How many times this code can be used by one customer.Default: 1]]></comment>
145
+ <show_in_default>1</show_in_default>
146
+ </reckless_prediction_promocode_usagepercustomer>
147
+ <reckless_prediction_promocode_type>
148
+ <label>Coupon Type</label>
149
+ <frontend_type>text</frontend_type>
150
+ <sort_order>90</sort_order>
151
+ <comment><![CDATA[Coupon Type.Default: 2]]></comment>
152
+ <show_in_default>1</show_in_default>
153
+ </reckless_prediction_promocode_type>
154
+ <reckless_prediction_promocode_stoprulesprocessing>
155
+ <label>Stop Rules Processing</label>
156
+ <frontend_type>select</frontend_type>
157
+ <source_model>adminhtml/system_config_source_yesno</source_model>
158
+ <sort_order>100</sort_order>
159
+ <comment><![CDATA[Stop Further Rules from Processing.Default: Disabled]]></comment>
160
+ <show_in_default>1</show_in_default>
161
+ </reckless_prediction_promocode_stoprulesprocessing>
162
+ <reckless_prediction_customer_promo_message>
163
+ <label>Message to show to customer</label>
164
+ <frontend_type>textarea</frontend_type>
165
+ <sort_order>80</sort_order>
166
+ <comment><![CDATA[Message that will be displayed to the customer in the view cart page. The coupon code will always be the last word in the sentence. Default: Thank you for choosing us. As a token of appreciation, we would like to offer you 5% off next time. Please use code: {Coupon Code}]]></comment>
167
+ <show_in_default>1</show_in_default>
168
+ </reckless_prediction_customer_promo_message>
169
+ </fields>
170
+ </reckless_promotions_section>
171
+ </groups>
172
+ </reckless_prediction>
173
+ </sections>
174
+ </config>
app/code/local/Reckless/Prediction/sql/reckless_prediction_setup/mysql4-install-0.1.0.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $installer = $this;
3
+
4
+ $installer->startSetup();
5
+
6
+
7
+ $table = $installer->getConnection()
8
+ ->newTable($installer->getTable('prediction/promotions'))
9
+ ->addColumn('entity_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
10
+ 'identity' => true,
11
+ 'unsigned' => true,
12
+ 'nullable' => false,
13
+ 'primary' => true,
14
+ ), 'Entity Id')
15
+ ->addColumn('visitor_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
16
+ 'unsigned' => true,
17
+ 'nullable' => false,
18
+ ), 'Visitor Id')
19
+ ->addColumn('customer_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
20
+ 'unsigned' => true,
21
+ 'nullable' => true,
22
+ 'default' => '0',
23
+ ), 'Customer Id')
24
+ ->addColumn('quote_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
25
+ 'unsigned' => true,
26
+ 'nullable' => false,
27
+ ), 'Quote Id')
28
+ ->addColumn('session_id', Varien_Db_Ddl_Table::TYPE_TEXT, 255, array(
29
+ ), 'Session Id')
30
+ ->addColumn('store_id', Varien_Db_Ddl_Table::TYPE_SMALLINT, null, array(
31
+ 'unsigned' => true,
32
+ 'nullable' => true,
33
+ 'default' => '0',
34
+ ), 'Group Id')
35
+ ->addColumn('checkout_intent', Varien_Db_Ddl_Table::TYPE_TEXT, 2, array(
36
+ ), 'Checkout Intent')
37
+ ->addColumn('coupon_code', Varien_Db_Ddl_Table::TYPE_TEXT, 255, array(
38
+ ), 'Coupon code')
39
+ ->addColumn('discount_percent', Varien_Db_Ddl_Table::TYPE_SMALLINT, null, array(
40
+ 'unsigned' => true,
41
+ 'nullable' => true,
42
+ 'default' => '0',
43
+ ), 'Discount Percent')
44
+ ->addColumn('created_at', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array(
45
+ 'nullable' => false,
46
+ ), 'Created At')
47
+ ->addColumn('updated_at', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array(
48
+ 'nullable' => false,
49
+ ), 'Updated At')
50
+ ->addIndex($installer->getIdxName('prediction/promotions', array('visitor_id')),
51
+ array('visitor_id'))
52
+ ->addIndex($installer->getIdxName('prediction/promotions', array('quote_id')),
53
+ array('quote_id'))
54
+ ->addIndex($installer->getIdxName('prediction/promotions', array('coupon_code')),
55
+ array('coupon_code'))
56
+ ->addForeignKey($installer->getFkName('prediction/promotions', 'store_id', 'core/store', 'store_id'),
57
+ 'store_id', $installer->getTable('core/store'), 'store_id',
58
+ Varien_Db_Ddl_Table::ACTION_SET_NULL, Varien_Db_Ddl_Table::ACTION_CASCADE)
59
+ ->setComment('Reckless Promotions Entity');
60
+ $installer->getConnection()->createTable($table);
61
+ $installer->endSetup();
app/etc/modules/Reckless_Prediction.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Reckless_Prediction>
5
+ <active>true</active>
6
+ <codePool>local</codePool>
7
+ </Reckless_Prediction>
8
+ </modules>
9
+ </config>
package.xml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Recklessio_Prediction</name>
4
+ <version>1.0.1</version>
5
+ <stability>stable</stability>
6
+ <license>GNU General Public License (GPL)</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Magento Prediction Machine Learning System</summary>
10
+ <description>This Magento extension learns from your historic Magento operational data and predicts whether a given online customer or guest will checkout or not. We use external variables like holidays, weather etc. in our models.&#xD;
11
+ &#xD;
12
+ Depending on the predicted checkout intent (Y/N), a promo code can be generated to either convert the customer and reduce another abandoned cart or to increase his average order value.&#xD;
13
+ </description>
14
+ <notes>- First version&#xD;
15
+ - Works for both logged in(registered) and guest customers&#xD;
16
+ - Promotion Budget control with Coupon validity Days and maximum limit&#xD;
17
+ - Customizable messages, coupon code/ discount percent/ and other basic configurations</notes>
18
+ <authors><author><name>Reckless</name><user>recklessio</user><email>hello@reckless.io</email></author></authors>
19
+ <date>2014-06-30</date>
20
+ <time>18:26:45</time>
21
+ <contents><target name="magelocal"><dir name="Reckless"><dir name="Prediction"><dir name="Block"><dir name="Adminhtml"><dir name="Customer"><dir name="Online"><file name="Grid.php" hash="6be15ebd43a6f04c56c77735429d618d"/></dir></dir><file name="Grid.php" hash="594b39a6b04566f12b520ea4188938d2"/><dir name="Predictions"><file name="Grid.php" hash="3aaee4a1b20597f77d4ed697913071f3"/></dir><file name="Predictions.php" hash="492539cb45eea7c1157dc86b253afa2a"/><dir name="Redemptions"><file name="Grid.php" hash="89fe19b650d14f595dd1f7a53484c4b1"/></dir><file name="Redemptions.php" hash="7e00d2e4b98b819b9662605009b0a23c"/></dir></dir><dir name="Helper"><file name="Data.php" hash="f7bc89cf9f9eaa4b336595292dbaba86"/></dir><dir name="Model"><dir name="Promotion"><file name="Observer.php" hash="622782d27dbcc6aa574b768f99714787"/></dir><file name="Promotions.php" hash="c4daffe562c0c275ce557ee151c09857"/><dir name="Resource"><dir name="Promotions"><file name="Collection.php" hash="25eb76955e8ebc45196143449394cd11"/></dir><file name="Promotions.php" hash="abb8647d9872b0db04687eac0acb8cc2"/><file name="Setup.php" hash="c711d1e3a93c01fe04dd668203a4fe0e"/></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="PredictionController.php" hash="fab9e8f6a6038cdb9b6afd67a1a87b4f"/></dir></dir><dir name="data"><dir name="reckless_prediction_setup"><file name="data-install-0.1.0.php" hash="358f68c28d18f02af84fee4b1b11691a"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="190aa60726698e82c99983eb533e119b"/><file name="config.xml" hash="33919d473722f72c26be0015a7c867e8"/><file name="system.xml" hash="9545d30dcfc88d7e194fc40f54d47ed6"/></dir><dir name="sql"><dir name="reckless_prediction_setup"><file name="mysql4-install-0.1.0.php" hash="f058cf3da3900069ee364d5ed211390d"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Reckless_Prediction.xml" hash="2b822e3b624ed0437395ee8ee9ab7190"/></dir></target></contents>
22
+ <compatible/>
23
+ <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php><package><name>Mage_Core_Modules</name><channel>community</channel><min>1.0.0.0</min><max>1.9</max></package></required></dependencies>
24
+ </package>