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 +56 -0
- app/code/local/Reckless/Prediction/Block/Adminhtml/Grid.php +8 -0
- app/code/local/Reckless/Prediction/Block/Adminhtml/Predictions.php +12 -0
- app/code/local/Reckless/Prediction/Block/Adminhtml/Predictions/Grid.php +91 -0
- app/code/local/Reckless/Prediction/Block/Adminhtml/Redemptions.php +12 -0
- app/code/local/Reckless/Prediction/Block/Adminhtml/Redemptions/Grid.php +151 -0
- app/code/local/Reckless/Prediction/Helper/Data.php +485 -0
- app/code/local/Reckless/Prediction/Model/Promotion/Observer.php +47 -0
- app/code/local/Reckless/Prediction/Model/Promotions.php +376 -0
- app/code/local/Reckless/Prediction/Model/Resource/Promotions.php +16 -0
- app/code/local/Reckless/Prediction/Model/Resource/Promotions/Collection.php +16 -0
- app/code/local/Reckless/Prediction/Model/Resource/Setup.php +12 -0
- app/code/local/Reckless/Prediction/controllers/Adminhtml/PredictionController.php +44 -0
- app/code/local/Reckless/Prediction/data/reckless_prediction_setup/data-install-0.1.0.php +32 -0
- app/code/local/Reckless/Prediction/etc/adminhtml.xml +62 -0
- app/code/local/Reckless/Prediction/etc/config.xml +117 -0
- app/code/local/Reckless/Prediction/etc/system.xml +174 -0
- app/code/local/Reckless/Prediction/sql/reckless_prediction_setup/mysql4-install-0.1.0.php +61 -0
- app/etc/modules/Reckless_Prediction.xml +9 -0
- package.xml +24 -0
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.
|
11 |
+

|
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.
|
13 |
+
</description>
|
14 |
+
<notes>- First version
|
15 |
+
- Works for both logged in(registered) and guest customers
|
16 |
+
- Promotion Budget control with Coupon validity Days and maximum limit
|
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>
|