mail_spy 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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>