mail_spy 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -9,4 +9,66 @@
9
9
  * compiled file, but it's generally better to create a new file per style scope.
10
10
  *
11
11
  *= require_self
12
+ *= require_tree
12
13
  */
14
+
15
+ /*************************************** Reset Styles */
16
+
17
+ html, body, div, span, applet, object, iframe,
18
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
19
+ a, abbr, acronym, address, big, cite, code,
20
+ del, dfn, em, img, ins, kbd, q, s, samp,
21
+ small, strike, strong, sub, sup, tt, var,
22
+ b, u, i, center,
23
+ dl, dt, dd, ol, ul, li,
24
+ fieldset, form, label, legend,
25
+ table, caption, tbody, tfoot, thead, tr, th, td,
26
+ article, aside, canvas, details, embed,
27
+ figure, figcaption, footer, header, hgroup,
28
+ menu, nav, output, ruby, section, summary,
29
+ time, mark, audio, video {
30
+ margin: 0;
31
+ padding: 0;
32
+ border: 0;
33
+ font-size: 100%;
34
+ font: inherit;
35
+ vertical-align: baseline;
36
+ }
37
+ /* HTML5 display-role reset for older browsers */
38
+ article, aside, details, figcaption, figure,
39
+ footer, header, hgroup, menu, nav, section {
40
+ display: block;
41
+ }
42
+ body {
43
+ line-height: 1;
44
+ }
45
+ ol, ul {
46
+ list-style: none;
47
+ }
48
+ blockquote, q {
49
+ quotes: none;
50
+ }
51
+ blockquote:before, blockquote:after,
52
+ q:before, q:after {
53
+ content: '';
54
+ content: none;
55
+ }
56
+ table {
57
+ border-collapse: collapse;
58
+ border-spacing: 0;
59
+ }
60
+
61
+ /*************************************** End Reset */
62
+ /*************************************** New Global Styles */
63
+
64
+ body{
65
+ font-family: 'Spinnaker', sans-serif;
66
+ color:#fff;
67
+ background-image: url(background_tile.gif);
68
+ }
69
+ h1{
70
+ font-size:24px;
71
+ }
72
+ .Deemph{
73
+ font-size:.72em;
74
+ }
@@ -0,0 +1,128 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
5
+ $colorHighlight:#80ff7a;
6
+ $colorMedlight:#214d30;
7
+ $colorSemiLowlight:#c3c3c3;
8
+ $colorLowlight:#3e4041;
9
+ $colorHighlightBad:#62222a;
10
+ $colorMedlightBad:#663137;
11
+
12
+
13
+
14
+ ._active{
15
+ color:$colorHighlight;
16
+ }
17
+
18
+ /**************************** Sprites for mailspy_sprite.png */
19
+ .MsSprite{
20
+ background-image:url(mailspy_sprite.png);
21
+ background-repeat:no-repeat;
22
+ display:inline-block;
23
+ *display:inline; zoom:1;
24
+ vertical-align:middle;
25
+ text-indent:-9999px;
26
+
27
+ &.Logo{
28
+ background-position:0 0;
29
+ width:180px;
30
+ height:50px;
31
+ }
32
+
33
+ &.Nav{
34
+ width: 27px;
35
+ height: 34px;
36
+ background-position-x:-50px;
37
+
38
+ &.Campaign{
39
+ background-position-y:-100px;
40
+ }
41
+ &.History{
42
+ background-position-y:-150px;
43
+ }
44
+ &.Template{
45
+ background-position-y:-200px;
46
+ }
47
+ &.Menu{
48
+ background-position-y:-250px;
49
+ }
50
+
51
+ &._active{
52
+ background-position-x:0;
53
+ }
54
+ &:hover{
55
+ background-position-x:-100px;
56
+ }
57
+ }
58
+
59
+ &.People{
60
+ background-position: -200px 0px;
61
+ height:60px;
62
+ width:80px;
63
+ }
64
+ &.Mail{
65
+ background-position: -200px -100px;
66
+ height:60px;
67
+ width:80px;
68
+ }
69
+ &.Pointer{
70
+ background-position: -200px -200px;
71
+ height:60px;
72
+ width:80px;
73
+ }
74
+ &.Money{
75
+ background-position: -200px -300px;
76
+ height:60px;
77
+ width:80px;
78
+ }
79
+ }
80
+
81
+ /**************************** End mailspy_sprite.png*/
82
+ /**************************** Report Components */
83
+ .KpiContainer{
84
+ margin:1em;
85
+ }
86
+ .Kpi{
87
+ display:inline-block;
88
+ *display:inline; zoom:1;
89
+ width:200px;
90
+ text-align:center;
91
+
92
+ .Icon{
93
+ display:block;
94
+ margin:8px auto;
95
+ }
96
+ kpi{
97
+ color:$colorMedlight;
98
+ font-size:1.2em;
99
+ margin:.2em;
100
+ }
101
+ figure{
102
+ color:$colorHighlight;
103
+ font-size:1.8em;
104
+ margin:.2em;
105
+ }
106
+ }
107
+
108
+ .CampaignContainer{
109
+ background-color:rgba(0,0,0,.4);
110
+ padding:.2em 1em 1em 1em;
111
+ margin:2em 0;
112
+ }
113
+
114
+ .FunnelContainer{
115
+ padding:.2em 1em 1em 1em;
116
+ margin:2em 0;
117
+ thead{
118
+ td{
119
+ color:$colorLowlight;
120
+ text-align:left;
121
+ }
122
+ }
123
+ td{
124
+ padding:.5em 2em;
125
+ color:$colorHighlight;
126
+ text-align:left;
127
+ }
128
+ }
@@ -0,0 +1,29 @@
1
+ module MailSpy
2
+ class ReportsController < ApplicationController
3
+
4
+ def campaigns
5
+
6
+ end
7
+
8
+ def emails
9
+
10
+ end
11
+
12
+ def email
13
+ @email = MailSpy.create_email({
14
+ :campaign => "2012-valentines",
15
+ :stream => "a-yikes",
16
+ :component => "a",
17
+ :schedule_at => Date.today()+1, # Should never be used
18
+ :subject => "Just a test",
19
+ :template_values => {},
20
+ :from => "EMAIL PREVIEW",
21
+ :reply_to => "NOREPLY",
22
+ :to => "nowhere"
23
+ })
24
+ @email[:cache_bust] = true
25
+ render :text=>@email.parsed_html_content, :html_safe => true
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,4 @@
1
+ module MailSpy
2
+ module ReportsHelper
3
+ end
4
+ end
@@ -68,13 +68,35 @@ module MailSpy
68
68
  end
69
69
 
70
70
  def html_erb
71
- load_template("html.erb")
71
+ load_template("html.erb")
72
72
  end
73
73
 
74
74
  def text_erb
75
75
  load_template("text.erb")
76
76
  end
77
77
 
78
+ def self.deliver_batch(current_time, start_id, offset, total, step=200, num_threads=100)
79
+ wq = WorkQueue.new(num_threads, step*2)
80
+ processed = 0
81
+ while true
82
+ emails = self.where(
83
+ :schedule_at.lte => current_time,
84
+ :sent => false,
85
+ :failed => false,
86
+ :_id.gt => start_id
87
+ ).limit(step).offset(offset)
88
+
89
+ break if total >= processed || emails.blank?
90
+ emails.each do |email|
91
+ wq.enqueue_b do
92
+ email.deliver
93
+ end
94
+ end
95
+ processed += emails.count
96
+ end
97
+ wq.join
98
+ processed
99
+ end
78
100
 
79
101
  def deliver
80
102
  begin
@@ -97,7 +119,7 @@ module MailSpy
97
119
 
98
120
  # Check the cache using our convention
99
121
  path = "#{self.campaign}/#{self.stream}/#{self.component}.#{suffix}"
100
- return @@template_cache[path] if @@template_cache[path].present?
122
+ return @@template_cache[path] if @@template_cache[path].present? && !self[:cache_bust]
101
123
 
102
124
  # Load the object from s3
103
125
  s3 = AWS::S3.new(:access_key_id => MailSpy.aws_access_key_id,
@@ -2,8 +2,11 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>MailSpy</title>
5
+ <link rel="icon" href="/assets/mail_spy/spy.ico" type="image/x-icon">
6
+ <link rel="shortcut icon" href="/assets/mail_spy/spy.ico" type="image/x-icon">
5
7
  <%= stylesheet_link_tag "mail_spy/application", :media => "all" %>
6
8
  <%= javascript_include_tag "mail_spy/application" %>
9
+ <link href='http://fonts.googleapis.com/css?family=Spinnaker' rel='stylesheet' type='text/css'>
7
10
  <%= csrf_meta_tags %>
8
11
  </head>
9
12
  <body>
@@ -1,48 +1,138 @@
1
1
  <% @campaigns = [{
2
- :name=>"Valentine's Day",
3
- :last_sent => Date.today(),
4
- :sent=>5320
2
+ :name=>"Valentine's Day",
3
+ :first_sent => Date.today()-14,
4
+ :last_sent => Date.today(),
5
+ :kpis=>{
6
+ "Population"=> {
7
+ :icon=>"People",
8
+ :figure=>1000000
9
+ },
10
+ "Emails Sent"=>{
11
+ :icon=>"Mail",
12
+ :figure=>66478
13
+ },
14
+ "Click-Throughs"=>{
15
+ :icon=>"Pointer",
16
+ :figure=>1549
17
+ },
18
+ "Total Purchases"=>{
19
+ :icon=>"Money",
20
+ :figure=>1214
21
+ }
22
+ },
23
+ :purchase_funnel=>{
24
+ "delivered"=>66478,
25
+ "opened"=>7643,
26
+ "clicked"=>1549,
27
+ "purchased"=>5
28
+ }
29
+
5
30
  },{
6
- :name=>"Mother's Day"
31
+ :name=>"Mother's Day",
32
+ :last_sent => Date.today(),
33
+ :sent=>1221,
34
+ :kpis=>{},
35
+ :purchase_funnel=>{}
7
36
  }] %>
8
- <%= debug @campaigns %>
9
- <%# ------------------------------------- Backbone Templates %>
37
+
10
38
  <script type="text/javascript">
39
+ $(function(){
40
+
41
+ getValue = function(_o){
42
+ try{
43
+ if(typeof(_o) === 'number' ||
44
+ typeof(parseInt(_o,10) === 'number')){
45
+ return _o;
46
+ } else if (_o.r && _o.c){
11
47
 
12
- Campaign = Backbone.Model.extend({
13
- initialize: function(){
14
- alert("Welcome to this world");
48
+ }
49
+ } catch(e){
50
+ console.log("Can't evaluate:");
51
+ console.log(_o);
52
+ console.log(e);
53
+ }
54
+ };
55
+
56
+ $.each($("[data-operator]"),function(i,e){
57
+ var lhs, rhs,
58
+ result = "NA",
59
+ op = $(this).attr("data-operator"),
60
+ ops = $(this).attr("data-operands");
61
+
62
+ try{
63
+ ops = ops.split("|");
64
+ lhs = $.parseJSON(ops[0]);
65
+ rhs = $.parseJSON(ops[1]);
66
+ console.log(lhs + " : " + rhs);
67
+
68
+ //Switch operators
69
+ if(op == "+"){
70
+ result = getValue(lhs) + getValue(rhs);
71
+ } else if(op == "%"){
72
+ result = (lhs + "/" + rhs);
15
73
  }
16
- });
17
- CampaignView = Backbone.View.extend({
18
- initialize: function(){
19
- this.render();
20
- },
21
- render: function(){
22
- // Compile the template using underscore
23
- var template = _.template( $("#campaign_template").html(), {} );
24
- // Load the compiled HTML into the Backbone "el"
25
- this.el.html( template );
26
- }
27
- });
28
-
29
- var search_view = new SearchView({ el: $("#search_container") });
30
- </script>
31
- <%# ------------------------------------- Backbone Templates %>
32
- <script type="text/template" id="campaign_template">
33
- <div class="Campaign">
34
- <h2><%= c[:name] %></h2>
35
- </div>
74
+ } catch(e) {
75
+ console.log(e);
76
+ }
77
+
78
+ $(this).html(result);
79
+ });
80
+
81
+
82
+ $.each($("[data-graph]"),function(i,e){
83
+ $(this).html($(this).attr("data-value"));
84
+ });
85
+
86
+ });
36
87
  </script>
88
+
37
89
  <%# ------------------------------------- Start actual view %>
38
90
 
39
- <h1>All Campaigns</h1>
91
+ <div class="MsSprite Logo">Mail Spy</div>
92
+
93
+ <h1 class="_active">
94
+ <div class="MsSprite Nav Campaign _active"></div> All Campaigns
95
+ </h1>
40
96
 
41
97
  <div class="CampaignReport">
42
98
  <%# For each campaign %>
43
99
  <% @campaigns.each do |c| %>
44
- <div class="CampaignSummary">
45
- <h2><%= c[:name] %></h2>
46
- </div>
100
+ <div class="CampaignContainer">
101
+ <h1 ><%= c[:name] %></h1>
102
+
103
+ <div class="KpiContainer">
104
+ <% c[:kpis].each do |kpi, kpi_data|%>
105
+ <div class="Kpi">
106
+ <div class="MsSprite <%= kpi_data[:icon] %> Icon"></div>
107
+ <kpi><%= kpi %></kpi>
108
+ <figure><%= kpi_data[:figure] %></figure>
109
+ </div>
110
+ <% end %>
111
+ </div>
112
+ <div class="FunnelContainer">
113
+ <h2>
114
+ Purchase Funnel
115
+ </h2>
116
+ <table>
117
+ <thead>
118
+ <tr>
119
+ <td>Action</td>
120
+ <td>Raw</td>
121
+ <td>%</td>
122
+ <td></td>
123
+ </tr>
124
+ </thead>
125
+ <% c[:purchase_funnel].each do |step, step_data|%>
126
+ <tr>
127
+ <td><%= step %></td>
128
+ <td><%=step_data%></funnelCell>
129
+ <td data-operator="%" data-operands='{"r":-0,"c":-1}|{"r":-1,"c":-1}'></td>
130
+ <td data-operator="+" data-operands='6|9'></td>
131
+ <td data-graph="bar" data-value="<%=step_data%>"></td>
132
+ </tr>
133
+ <% end %>
134
+ </table>
135
+ </div>
136
+ </div>
47
137
  <% end %>
48
138
  </div>