Implements targets and campaigns for conference
This commit is contained in:
parent
678fe38a25
commit
857ac43e74
40 changed files with 1086 additions and 44 deletions
4
Gemfile
4
Gemfile
|
|
@ -97,6 +97,10 @@ gem 'rdoc-generator-fivefish'
|
||||||
# We use factory_girl for seeds
|
# We use factory_girl for seeds
|
||||||
gem 'factory_girl_rails'
|
gem 'factory_girl_rails'
|
||||||
|
|
||||||
|
# We use ahoy for visitor tracking
|
||||||
|
gem 'ahoy_matey'
|
||||||
|
gem 'activeuuid'
|
||||||
|
|
||||||
# Use guard and spring for testing in development
|
# Use guard and spring for testing in development
|
||||||
group :development do
|
group :development do
|
||||||
# rspec Guard rules
|
# rspec Guard rules
|
||||||
|
|
|
||||||
19
Gemfile.lock
19
Gemfile.lock
|
|
@ -29,11 +29,22 @@ GEM
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
|
activeuuid (0.5.0)
|
||||||
|
activerecord (>= 3.1)
|
||||||
|
uuidtools
|
||||||
acts_as_commentable_with_threading (1.2.0)
|
acts_as_commentable_with_threading (1.2.0)
|
||||||
activerecord (>= 3.0)
|
activerecord (>= 3.0)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
awesome_nested_set (>= 2.0)
|
awesome_nested_set (>= 2.0)
|
||||||
addressable (2.3.6)
|
addressable (2.3.6)
|
||||||
|
ahoy_matey (1.0.0)
|
||||||
|
addressable
|
||||||
|
browser (>= 0.4.0)
|
||||||
|
geocoder
|
||||||
|
referer-parser
|
||||||
|
request_store
|
||||||
|
user_agent_parser
|
||||||
|
uuidtools
|
||||||
arel (5.0.1.20140414130214)
|
arel (5.0.1.20140414130214)
|
||||||
ast (2.0.0)
|
ast (2.0.0)
|
||||||
awesome_nested_set (3.0.0.rc.5)
|
awesome_nested_set (3.0.0.rc.5)
|
||||||
|
|
@ -48,6 +59,7 @@ GEM
|
||||||
bcrypt (3.1.7)
|
bcrypt (3.1.7)
|
||||||
bootstrap-sass (3.1.1.1)
|
bootstrap-sass (3.1.1.1)
|
||||||
sass (~> 3.2)
|
sass (~> 3.2)
|
||||||
|
browser (0.6.0)
|
||||||
builder (3.2.2)
|
builder (3.2.2)
|
||||||
cancan (1.6.10)
|
cancan (1.6.10)
|
||||||
capybara (2.2.1)
|
capybara (2.2.1)
|
||||||
|
|
@ -111,6 +123,7 @@ GEM
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
formtastic-bootstrap (3.0.0)
|
formtastic-bootstrap (3.0.0)
|
||||||
formtastic (>= 2.2)
|
formtastic (>= 2.2)
|
||||||
|
geocoder (1.2.2)
|
||||||
gravtastic (3.2.6)
|
gravtastic (3.2.6)
|
||||||
guard (2.6.0)
|
guard (2.6.0)
|
||||||
formatador (>= 0.2.4)
|
formatador (>= 0.2.4)
|
||||||
|
|
@ -258,6 +271,8 @@ GEM
|
||||||
loggability (~> 0.6)
|
loggability (~> 0.6)
|
||||||
rdoc (~> 4.0)
|
rdoc (~> 4.0)
|
||||||
yajl-ruby (~> 1.1)
|
yajl-ruby (~> 1.1)
|
||||||
|
referer-parser (0.2.1)
|
||||||
|
request_store (1.0.6)
|
||||||
rest-client (1.6.7)
|
rest-client (1.6.7)
|
||||||
mime-types (>= 1.16)
|
mime-types (>= 1.16)
|
||||||
rspec (3.0.0.beta2)
|
rspec (3.0.0.beta2)
|
||||||
|
|
@ -343,6 +358,8 @@ GEM
|
||||||
uglifier (2.5.0)
|
uglifier (2.5.0)
|
||||||
execjs (>= 0.3.0)
|
execjs (>= 0.3.0)
|
||||||
json (>= 1.8.0)
|
json (>= 1.8.0)
|
||||||
|
user_agent_parser (2.1.5)
|
||||||
|
uuidtools (2.1.4)
|
||||||
warden (1.2.3)
|
warden (1.2.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
xpath (2.0.0)
|
xpath (2.0.0)
|
||||||
|
|
@ -354,7 +371,9 @@ PLATFORMS
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
active_model_serializers
|
active_model_serializers
|
||||||
|
activeuuid
|
||||||
acts_as_commentable_with_threading
|
acts_as_commentable_with_threading
|
||||||
|
ahoy_matey
|
||||||
awesome_nested_set (~> 3.0.0.rc.5)
|
awesome_nested_set (~> 3.0.0.rc.5)
|
||||||
axlsx_rails
|
axlsx_rails
|
||||||
bootstrap-sass
|
bootstrap-sass
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
//= require d3
|
//= require d3
|
||||||
//= require osem
|
//= require osem
|
||||||
//= require dashboard
|
//= require dashboard
|
||||||
|
//= require ahoy
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('a[disabled=disabled]').click(function(event){
|
$('a[disabled=disabled]').click(function(event){
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,41 @@
|
||||||
$(function() {
|
$(function() {
|
||||||
|
/**
|
||||||
|
* Opens a prompt with the URL to copy to clipboard.
|
||||||
|
* Used in the campaign index view.
|
||||||
|
*/
|
||||||
|
$('.copyLink').on('click', function(){
|
||||||
|
var url = $(this).data('url');
|
||||||
|
copyToClipboard(url);
|
||||||
|
})
|
||||||
|
function copyToClipboard(text) {
|
||||||
|
window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the targets on the conference site with a more / less link.
|
||||||
|
*/
|
||||||
|
$('.show_targets').click(function () {
|
||||||
|
if($(this).text().trim() == 'more'){
|
||||||
|
$(this).text("less");
|
||||||
|
}else{
|
||||||
|
$(this).text("more");
|
||||||
|
}
|
||||||
|
$('#' + $(this).data('name')).toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the datetimepicker to new injected nested target fields.
|
||||||
|
*/
|
||||||
|
$('a:contains("Add target")').click(function () {
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.target-due-date-datepicker').not('.hasDatepicker').datepicker({
|
||||||
|
dateFormat: 'yy/mm/dd',
|
||||||
|
numberOfMonths: 1
|
||||||
|
});
|
||||||
|
},
|
||||||
|
5)
|
||||||
|
});
|
||||||
|
|
||||||
$("#event_media_type").change(function () {
|
$("#event_media_type").change(function () {
|
||||||
$(".media-type").hide();
|
$(".media-type").hide();
|
||||||
$('#' + $(this).val().toLowerCase() + '-help').show();
|
$('#' + $(this).val().toLowerCase() + '-help').show();
|
||||||
|
|
@ -27,6 +64,11 @@ $(function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".target-due-date-datepicker").datepicker({
|
||||||
|
dateFormat: 'yy/mm/dd',
|
||||||
|
numberOfMonths: 1
|
||||||
|
});
|
||||||
|
|
||||||
$("#cfp-hard-datepicker").datepicker({
|
$("#cfp-hard-datepicker").datepicker({
|
||||||
dateFormat: 'yy/mm/dd',
|
dateFormat: 'yy/mm/dd',
|
||||||
numberOfMonths: 2,
|
numberOfMonths: 2,
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ body {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav > li.nav-header > a {
|
.nav-header-bigger {
|
||||||
font-size: 12px;
|
font-size: 15px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
65
app/controllers/admin/campaigns_controller.rb
Normal file
65
app/controllers/admin/campaigns_controller.rb
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
module Admin
|
||||||
|
class CampaignsController < ApplicationController
|
||||||
|
before_filter :verify_organizer
|
||||||
|
|
||||||
|
def index
|
||||||
|
@conference = Conference.find_by(short_title: params[:conference_id])
|
||||||
|
@campaigns = @conference.campaigns
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@conference = Conference.find_by(short_title: params[:conference_id])
|
||||||
|
@campaign = @conference.campaigns.new(params[:campaign])
|
||||||
|
@campaign.conference_id = @conference.id
|
||||||
|
|
||||||
|
if @conference.save
|
||||||
|
redirect_to(admin_conference_campaigns_path(conference_id: @conference.short_title),
|
||||||
|
notice: 'Campaign successfully created.')
|
||||||
|
else
|
||||||
|
redirect_to(new_admin_conference_campaign_path(conference_id: @conference.short_title),
|
||||||
|
alert: "Creating of Campaign for #{@conference.short_title} failed." \
|
||||||
|
"#{@campaign.errors.full_messages.join('. ')}.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@conference = Conference.find_by(short_title: params[:conference_id])
|
||||||
|
@campaign = @conference.campaigns.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@conference = Conference.find_by(short_title: params[:conference_id])
|
||||||
|
@campaign = Campaign.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@conference = Conference.find_by(short_title: params[:conference_id])
|
||||||
|
@campaign = Campaign.find(params[:id])
|
||||||
|
|
||||||
|
if @campaign.update_attributes(params[:campaign])
|
||||||
|
redirect_to(admin_conference_campaigns_path(
|
||||||
|
conference_id: @conference.short_title),
|
||||||
|
notice: "Campaign '#{@campaign.name}' successfully updated.")
|
||||||
|
else
|
||||||
|
redirect_to(edit_admin_conference_campaign_path(
|
||||||
|
conference_id: @conference.short_title,
|
||||||
|
id: @campaign.id),
|
||||||
|
alert: "Update of Campaign for #{@conference.short_title} failed." \
|
||||||
|
"#{@campaign.errors.full_messages.join('. ')}.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@conference = Conference.find_by(short_title: params[:conference_id])
|
||||||
|
@campaign = Campaign.find(params[:id])
|
||||||
|
if @campaign.destroy
|
||||||
|
redirect_to(admin_conference_campaigns_path(conference_id: @conference.short_title),
|
||||||
|
notice: "Campaign '#{@campaign.name}' successfully deleted.")
|
||||||
|
else
|
||||||
|
redirect_to(admin_conference_campaigns_path(conference_id: @conference.short_title),
|
||||||
|
alert: "Delete of Campaign for #{@conference.short_title} failed." \
|
||||||
|
"#{@campaign.errors.full_messages.join('. ')}.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -144,6 +144,14 @@ class Admin::ConferenceController < ApplicationController
|
||||||
|
|
||||||
@top_submitter = @conference.get_top_submitter
|
@top_submitter = @conference.get_top_submitter
|
||||||
|
|
||||||
|
# get targets
|
||||||
|
@registration_targets = @conference.get_targets(Target.units[:registrations])
|
||||||
|
@submission_targets = @conference.get_targets(Target.units[:submissions])
|
||||||
|
@program_minutes_targets = @conference.get_targets(Target.units[:program_minutes])
|
||||||
|
|
||||||
|
# get campaigns
|
||||||
|
@campaigns = @conference.get_campaigns
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
format.html
|
||||||
format.json { render json: @conference.to_json }
|
format.json { render json: @conference.to_json }
|
||||||
|
|
|
||||||
21
app/controllers/admin/targets_controller.rb
Normal file
21
app/controllers/admin/targets_controller.rb
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
module Admin
|
||||||
|
class TargetsController < ApplicationController
|
||||||
|
before_filter :verify_organizer
|
||||||
|
|
||||||
|
def index
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
if @conference.update_attributes(params[:conference])
|
||||||
|
redirect_to(admin_conference_targets_path(
|
||||||
|
conference_id: @conference.short_title),
|
||||||
|
notice: 'Targets were successfully updated.')
|
||||||
|
else
|
||||||
|
redirect_to(admin_conference_targets_path(
|
||||||
|
conference_id: @conference.short_title),
|
||||||
|
alert: 'Targets update failed: ' \
|
||||||
|
"#{@conference.errors.full_messages.join('. ')}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -75,6 +75,8 @@ class ConferenceRegistrationController < ApplicationController
|
||||||
if update_registration
|
if update_registration
|
||||||
redirect_message = "Registration updated."
|
redirect_message = "Registration updated."
|
||||||
else
|
else
|
||||||
|
# Track ahoy event
|
||||||
|
ahoy.track 'Registered', title: 'New registration'
|
||||||
if conference.email_settings.send_on_registration?
|
if conference.email_settings.send_on_registration?
|
||||||
Mailbot.registration_mail(conference, current_user.person).deliver
|
Mailbot.registration_mail(conference, current_user.person).deliver
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,7 @@ class ProposalController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
registration = person.registrations.where(:conference_id => @conference.id).first
|
registration = person.registrations.where(:conference_id => @conference.id).first
|
||||||
|
ahoy.track 'Event submission', title: 'New submission'
|
||||||
if registration.nil?
|
if registration.nil?
|
||||||
redirect_to(register_conference_path(@conference.short_title), :notice => 'Event was successfully submitted. You probably want to register for the conference now!')
|
redirect_to(register_conference_path(@conference.short_title), :notice => 'Event was successfully submitted. You probably want to register for the conference now!')
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,28 @@
|
||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
|
def target_progress_color(progress)
|
||||||
|
progress = progress.to_i
|
||||||
|
if progress > 90
|
||||||
|
result = 'green'
|
||||||
|
elsif progress < 90 && progress > 80
|
||||||
|
result = 'orange'
|
||||||
|
else
|
||||||
|
result = 'red'
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def days_left_color(days_left)
|
||||||
|
days_left = days_left.to_i
|
||||||
|
if days_left > 30
|
||||||
|
result = 'green'
|
||||||
|
elsif days_left < 30 && days_left > 10
|
||||||
|
result = 'orange'
|
||||||
|
else
|
||||||
|
result = 'red'
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
def bootstrap_class_for(flash_type)
|
def bootstrap_class_for(flash_type)
|
||||||
logger.debug "flash_type is #{flash_type}"
|
logger.debug "flash_type is #{flash_type}"
|
||||||
case flash_type
|
case flash_type
|
||||||
|
|
|
||||||
10
app/models/ahoy/event.rb
Normal file
10
app/models/ahoy/event.rb
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
module Ahoy
|
||||||
|
class Event < ActiveRecord::Base
|
||||||
|
self.table_name = "ahoy_events"
|
||||||
|
|
||||||
|
belongs_to :visit
|
||||||
|
belongs_to :user
|
||||||
|
|
||||||
|
serialize :properties, JSON
|
||||||
|
end
|
||||||
|
end
|
||||||
77
app/models/campaign.rb
Normal file
77
app/models/campaign.rb
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
class Campaign < ActiveRecord::Base
|
||||||
|
attr_accessible :name, :utm_source, :utm_medium, :utm_term,
|
||||||
|
:utm_content, :utm_campaign, :target_ids
|
||||||
|
|
||||||
|
validates :name, :utm_campaign, presence: true
|
||||||
|
|
||||||
|
has_many :targets
|
||||||
|
belongs_to :conference
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the utm parameters formatted as url.
|
||||||
|
#
|
||||||
|
# ====Returns
|
||||||
|
# * +String+ -> url parameters e.g. ?utm_source=facebook
|
||||||
|
def url_parameters
|
||||||
|
kv = []
|
||||||
|
get_parameters.each_pair do |k, v|
|
||||||
|
kv += ["#{k}=#{v}"]
|
||||||
|
end
|
||||||
|
'?' + kv.join('&') unless kv.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the counted visits generated by this campaign.
|
||||||
|
#
|
||||||
|
# ====Returns
|
||||||
|
# * +Fixnum+ -> visits
|
||||||
|
def visits_count
|
||||||
|
Visit.where(get_parameters).where('started_at > ?', created_at).count
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the counted registrations generated by this campaign.
|
||||||
|
#
|
||||||
|
# ====Returns
|
||||||
|
# * +Fixnum+ -> visits
|
||||||
|
def registrations_count
|
||||||
|
events_by_name('Registered')
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the counted event submissions generated by this campaign.
|
||||||
|
#
|
||||||
|
# ====Returns
|
||||||
|
# * +Fixnum+ -> visits
|
||||||
|
def submissions_count
|
||||||
|
events_by_name('Event submission')
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
##
|
||||||
|
# Helper method for submissions and registrations.
|
||||||
|
#
|
||||||
|
# ====Returns
|
||||||
|
# * +Fixnum+ -> registrations / submissions
|
||||||
|
def events_by_name(event_name)
|
||||||
|
parameters = get_parameters
|
||||||
|
parameters['ahoy_events.name'] = event_name
|
||||||
|
Visit.joins(:ahoy_events).where(parameters).where('started_at > ?', created_at).count
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Helper method to get the parameters for queries.
|
||||||
|
#
|
||||||
|
# ====Returns
|
||||||
|
# * +Hash+ -> parameter => value
|
||||||
|
def get_parameters
|
||||||
|
conditions = {}
|
||||||
|
conditions[:utm_source] = self[:utm_source] unless self[:utm_source].blank?
|
||||||
|
conditions[:utm_medium] = self[:utm_medium] unless self[:utm_medium].blank?
|
||||||
|
conditions[:utm_term] = self[:utm_term] unless self[:utm_term].blank?
|
||||||
|
conditions[:utm_content] = self[:utm_content] unless self[:utm_content].blank?
|
||||||
|
conditions[:utm_campaign] = self[:utm_campaign] unless self[:utm_campaign].blank?
|
||||||
|
conditions
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -21,7 +21,8 @@ class Conference < ActiveRecord::Base
|
||||||
:include_tickets_in_splash, :include_social_media_in_splash,
|
:include_tickets_in_splash, :include_social_media_in_splash,
|
||||||
:include_program_in_splash, :make_conference_public,
|
:include_program_in_splash, :make_conference_public,
|
||||||
:photos_attributes, :banner_photo,
|
:photos_attributes, :banner_photo,
|
||||||
:include_banner_in_splash
|
:include_banner_in_splash,
|
||||||
|
:targets, :targets_attributes, :campaigns, :campaigns_attributes
|
||||||
|
|
||||||
has_paper_trail
|
has_paper_trail
|
||||||
|
|
||||||
|
|
@ -45,6 +46,8 @@ class Conference < ActiveRecord::Base
|
||||||
has_many :sponsorship_levels, dependent: :destroy
|
has_many :sponsorship_levels, dependent: :destroy
|
||||||
has_many :sponsors, dependent: :destroy
|
has_many :sponsors, dependent: :destroy
|
||||||
has_many :photos, dependent: :destroy
|
has_many :photos, dependent: :destroy
|
||||||
|
has_many :targets, dependent: :destroy
|
||||||
|
has_many :campaigns, dependent: :destroy
|
||||||
belongs_to :venue
|
belongs_to :venue
|
||||||
|
|
||||||
accepts_nested_attributes_for :rooms, :reject_if => proc {|r| r["name"].blank?}, :allow_destroy => true
|
accepts_nested_attributes_for :rooms, :reject_if => proc {|r| r["name"].blank?}, :allow_destroy => true
|
||||||
|
|
@ -62,6 +65,8 @@ class Conference < ActiveRecord::Base
|
||||||
accepts_nested_attributes_for :vdays, :allow_destroy => true
|
accepts_nested_attributes_for :vdays, :allow_destroy => true
|
||||||
accepts_nested_attributes_for :vpositions, :allow_destroy => true
|
accepts_nested_attributes_for :vpositions, :allow_destroy => true
|
||||||
accepts_nested_attributes_for :photos, allow_destroy: true
|
accepts_nested_attributes_for :photos, allow_destroy: true
|
||||||
|
accepts_nested_attributes_for :targets, allow_destroy: true
|
||||||
|
accepts_nested_attributes_for :campaigns, allow_destroy: true
|
||||||
|
|
||||||
has_attached_file :logo,
|
has_attached_file :logo,
|
||||||
styles: { thumb: '100x100>', large: '300x300>' }
|
styles: { thumb: '100x100>', large: '300x300>' }
|
||||||
|
|
@ -457,6 +462,35 @@ class Conference < ActiveRecord::Base
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# A map with all conference targets with progress in percent of a certain unit.
|
||||||
|
#
|
||||||
|
# ====Returns
|
||||||
|
# * +Map+ -> target => progress
|
||||||
|
def get_targets(target_unit)
|
||||||
|
conference_target = targets.where('unit = ?', target_unit)
|
||||||
|
result = {}
|
||||||
|
conference_target.each do |target|
|
||||||
|
result[target.to_s] = target.get_progress
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# A map with all conference campaigns associated with targets.
|
||||||
|
#
|
||||||
|
# ====Returns
|
||||||
|
# * +Map+ -> campaign => {actual, target, progress}
|
||||||
|
def get_campaigns
|
||||||
|
result = {}
|
||||||
|
campaigns.each do |campaign|
|
||||||
|
campaign.targets.each do |target|
|
||||||
|
result["#{target} from #{campaign.name}"] = target.get_campaign
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
@ -655,12 +689,12 @@ class Conference < ActiveRecord::Base
|
||||||
#
|
#
|
||||||
# ====Returns
|
# ====Returns
|
||||||
# * +hash+ -> person: submissions
|
# * +hash+ -> person: submissions
|
||||||
def self.calculate_person_submission_hash(submitter, counter)
|
def self.calculate_person_submission_hash(submitters, counter)
|
||||||
result = ActiveSupport::OrderedHash.new
|
result = ActiveSupport::OrderedHash.new
|
||||||
counter.each do |key, value|
|
counter.each do |key, value|
|
||||||
submitter = submitter.find_by_id(key)
|
submitter = submitters.where(person_id: key).first
|
||||||
if submitter
|
if submitter
|
||||||
result[submitter] = value
|
result[submitter.person] = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
result
|
result
|
||||||
|
|
|
||||||
79
app/models/target.rb
Normal file
79
app/models/target.rb
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
class Target < ActiveRecord::Base
|
||||||
|
include ActionView::Helpers::TextHelper
|
||||||
|
|
||||||
|
attr_accessible :due_date, :target_count, :unit
|
||||||
|
|
||||||
|
default_scope { order('due_date ASC') }
|
||||||
|
|
||||||
|
def self.units
|
||||||
|
{
|
||||||
|
registrations: 'Registration',
|
||||||
|
submissions: 'Submission',
|
||||||
|
program_minutes: 'Program minute'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
validates :due_date, :target_count, :unit, presence: true
|
||||||
|
validates :target_count,
|
||||||
|
allow_nil: false,
|
||||||
|
numericality: { only_integer: true, greater_than: 0 }
|
||||||
|
validates :unit, allow_nil: false, inclusion: { in: Target.units.values }
|
||||||
|
|
||||||
|
belongs_to :conference
|
||||||
|
belongs_to :campaign
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the actual progress of the target in percent.
|
||||||
|
#
|
||||||
|
# ====Returns
|
||||||
|
# * +String+ -> progress in percent
|
||||||
|
def get_progress
|
||||||
|
numerator = 0
|
||||||
|
if unit == Target.units[:submissions]
|
||||||
|
numerator = conference.events.where('created_at < ?', due_date).count
|
||||||
|
elsif unit == Target.units[:registrations]
|
||||||
|
numerator = conference.registrations.where('created_at < ?', due_date).count
|
||||||
|
elsif unit == Target.units[:program_minutes]
|
||||||
|
numerator = conference.current_program_hours
|
||||||
|
end
|
||||||
|
(numerator / target_count.to_f * 100).round(0).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns a hash with values of the corresponding campaign.
|
||||||
|
#
|
||||||
|
# ====Returns
|
||||||
|
# * +Hash+ -> target_name, campaign_name, value, unit, created_at, progress, days_left
|
||||||
|
def get_campaign
|
||||||
|
numerator = 0
|
||||||
|
if unit == Target.units[:submissions]
|
||||||
|
numerator = campaign.submissions_count
|
||||||
|
elsif unit == Target.units[:registrations]
|
||||||
|
numerator = campaign.registrations_count
|
||||||
|
elsif unit == Target.units[:program_minutes]
|
||||||
|
numerator = campaign.current_program_hours
|
||||||
|
end
|
||||||
|
|
||||||
|
progress = (numerator / target_count.to_f * 100).round(0).to_s
|
||||||
|
result = {
|
||||||
|
'target_name' => to_s,
|
||||||
|
'campaign_name' => campaign.name,
|
||||||
|
'value' => numerator,
|
||||||
|
'unit' => unit,
|
||||||
|
'created_at' => created_at,
|
||||||
|
'progress' => progress,
|
||||||
|
'days_left' => days_left,
|
||||||
|
}
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{pluralize(target_count, unit)} by #{due_date}"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def days_left
|
||||||
|
(due_date - Date.today).to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
4
app/models/visit.rb
Normal file
4
app/models/visit.rb
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
class Visit < ActiveRecord::Base
|
||||||
|
has_many :ahoy_events, class_name: "Ahoy::Event"
|
||||||
|
belongs_to :user
|
||||||
|
end
|
||||||
12
app/views/admin/campaigns/_form.html.haml
Normal file
12
app/views/admin/campaigns/_form.html.haml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
= f.inputs do
|
||||||
|
= f.input :name
|
||||||
|
= f.inputs name: 'UTM Parameters' do
|
||||||
|
= f.input :utm_campaign, label: 'Campaign', hint: 'Groups all of the content form one campaign. E.g. 20percentpromocode'
|
||||||
|
= f.input :utm_source, label: 'Source', hint: 'Which website is sending you traffic. E.g. Facebook, google+, blog'
|
||||||
|
= f.input :utm_medium, label: 'Medium', hint: 'The type of marketing medium that the link is featured in. E.g. Facebook wallpost or facebook advertisement'
|
||||||
|
= f.input :utm_term, label: 'Term', hint: 'Campaign keywords. E.g. marketing+conference+opensource'
|
||||||
|
= f.input :utm_content, label: 'Content', hint: 'Used to track the different types of content that point to the same URL (A/B Test).'
|
||||||
|
= f.inputs name: 'Targets' do
|
||||||
|
= f.input :targets
|
||||||
|
= f.actions do
|
||||||
|
= f.action :submit, button_html: { class: 'btn btn-primary' }
|
||||||
2
app/views/admin/campaigns/edit.html.haml
Normal file
2
app/views/admin/campaigns/edit.html.haml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
= semantic_form_for(@campaign, url: admin_conference_campaign_path(@conference.short_title, @campaign)) do |f|
|
||||||
|
= render partial: 'form', locals: {f: f}
|
||||||
41
app/views/admin/campaigns/index.html.haml
Normal file
41
app/views/admin/campaigns/index.html.haml
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
%h1
|
||||||
|
Campaigns
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
.table-responsive
|
||||||
|
%table.table
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th #
|
||||||
|
%th Name
|
||||||
|
%th Visits
|
||||||
|
%th Registrations
|
||||||
|
%th Submissions
|
||||||
|
%th Link
|
||||||
|
%th Edit
|
||||||
|
%th Delete
|
||||||
|
%tbody
|
||||||
|
- @campaigns.each_with_index do |campaign, index|
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
= index + 1
|
||||||
|
%td{'id'=> "name_#{index}"}
|
||||||
|
= campaign.name
|
||||||
|
%td{'id'=> "visits_#{index}"}
|
||||||
|
= campaign.visits_count
|
||||||
|
%td{'id'=> "registrations_#{index}"}
|
||||||
|
= campaign.registrations_count
|
||||||
|
%td{'id'=> "submissions_#{ + index}"}
|
||||||
|
= campaign.submissions_count
|
||||||
|
%td
|
||||||
|
%a.copyLink{'href'=> '#', 'data-url'=>root_path + campaign.url_parameters}
|
||||||
|
Copy link
|
||||||
|
%td
|
||||||
|
= link_to 'Edit',
|
||||||
|
edit_admin_conference_campaign_path(@conference.short_title, campaign.id)
|
||||||
|
%td
|
||||||
|
= link_to 'Delete',
|
||||||
|
admin_conference_campaign_path(@conference.short_title, campaign.id), method: :delete
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
%b= link_to 'New Campaign', new_admin_conference_campaign_path, class: 'btn btn-success'
|
||||||
2
app/views/admin/campaigns/new.html.haml
Normal file
2
app/views/admin/campaigns/new.html.haml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
= semantic_form_for(@campaign, url: admin_conference_campaigns_path(@conference.short_title)) do |f|
|
||||||
|
= render partial: 'form', locals: {f: f}
|
||||||
40
app/views/admin/conference/_campaigns.html.haml
Normal file
40
app/views/admin/conference/_campaigns.html.haml
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
- if campaigns && !campaigns.empty?
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
%p
|
||||||
|
Your target "#{campaigns.values[0]['target_name']}" from campaign "#{campaigns.values[0]['campaign_name']}" has generated
|
||||||
|
%strong
|
||||||
|
#{pluralize(campaigns.values[0]['value'], campaigns.values[0]['unit'])}
|
||||||
|
since #{campaigns.values[0]['created_at']}.
|
||||||
|
%p
|
||||||
|
That is
|
||||||
|
%strong{'style'=>"color: #{target_progress_color(campaigns.values[0]['progress'])};"}
|
||||||
|
#{campaigns.values[0]['progress']} %
|
||||||
|
of your target, there are
|
||||||
|
%strong{'style'=>"color: #{days_left_color(campaigns.values[0]['days_left'])};"}
|
||||||
|
#{pluralize(campaigns.values[0]['days_left'], 'day')} left.
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
%div{'style'=>'display: none;', 'id'=>"#{name}"}
|
||||||
|
- campaigns.drop(1).each do |(key, value)|
|
||||||
|
%div
|
||||||
|
%p
|
||||||
|
Your target "#{value['target_name']}" from campaign "#{value['campaign_name']}" has generated
|
||||||
|
%strong
|
||||||
|
#{pluralize(value['value'], value['unit'])}
|
||||||
|
since #{value['created_at']}.
|
||||||
|
%p
|
||||||
|
That is
|
||||||
|
%strong{'style'=>"color: #{target_progress_color(value['progress'])};"}
|
||||||
|
#{value['progress']} %
|
||||||
|
of your target, there are
|
||||||
|
%strong{'style'=>"color: #{days_left_color(value['days_left'])};"}
|
||||||
|
#{pluralize(value['days_left'], 'day')} left.
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
- if campaigns.length > 1
|
||||||
|
%a.show_targets{'href'=>'#', 'data-name'=>"#{name}"}
|
||||||
|
more
|
||||||
|
- else
|
||||||
|
%h5.text-warning.text-center
|
||||||
|
No Campaigns!
|
||||||
32
app/views/admin/conference/_targets.html.haml
Normal file
32
app/views/admin/conference/_targets.html.haml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
- if targets && !targets.empty?
|
||||||
|
.row
|
||||||
|
.col-md-8.col-md-offset-2
|
||||||
|
.row
|
||||||
|
.col-md-10
|
||||||
|
%h6.text-muted.pull-left
|
||||||
|
Target 1
|
||||||
|
.row
|
||||||
|
.col-md-10
|
||||||
|
.progress{'title'=>"#{targets.keys[0]}"}
|
||||||
|
.progress-bar{ 'role'=>'progressbar', 'aria-valuenow'=>"#{targets.values[0]}", 'aria-valuemin'=>'0',
|
||||||
|
'aria-valuemax'=>'100', 'style'=>"width: #{targets.values[0]}%;"}
|
||||||
|
= "#{targets.values[0]} %"
|
||||||
|
.row
|
||||||
|
.col-md-8.col-md-offset-2
|
||||||
|
%div{'style'=>'display: none;', 'id'=>"#{name}"}
|
||||||
|
- targets.drop(1).each_with_index do |(key, value), index|
|
||||||
|
.row
|
||||||
|
.col-md-10
|
||||||
|
%h6.text-muted.pull-left
|
||||||
|
= "Target #{index + 2}"
|
||||||
|
.row
|
||||||
|
.col-md-10
|
||||||
|
.progress{'title'=>"#{key}"}
|
||||||
|
.progress-bar{ 'role'=>'progressbar', 'aria-valuenow'=>"#{value}", 'aria-valuemin'=>'0',
|
||||||
|
'aria-valuemax'=>'100', 'style'=>"width: #{value}%;" }
|
||||||
|
= "#{value} %"
|
||||||
|
.row
|
||||||
|
.col-md-2.col-md-offset-2
|
||||||
|
- if targets.length > 1
|
||||||
|
%a.show_targets{'href'=>'#', 'data-name'=>"#{name}"}
|
||||||
|
more
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
.text
|
.text
|
||||||
%label.text-muted total registrations: #{@total_reg}
|
%label.text-muted total registrations: #{@total_reg}
|
||||||
%label.text-muted new registrations: #{@new_reg}
|
%label.text-muted new registrations: #{@new_reg}
|
||||||
|
.row
|
||||||
|
= render partial: 'targets', locals: { targets: @registration_targets, name: 'registrations' }
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
.dashbox.text-center
|
.dashbox.text-center
|
||||||
.icon
|
.icon
|
||||||
|
|
@ -17,13 +19,17 @@
|
||||||
.text
|
.text
|
||||||
%label.text-muted total submissions: #{@total_submissions}
|
%label.text-muted total submissions: #{@total_submissions}
|
||||||
%label.text-muted new submissions: #{@new_submissions}
|
%label.text-muted new submissions: #{@new_submissions}
|
||||||
|
.row
|
||||||
|
= render partial: 'targets', locals: { targets: @submission_targets, name: 'submissions' }
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
.dashbox.text-center
|
.dashbox.text-center
|
||||||
.icon
|
.icon
|
||||||
%i.glyphicon.glyphicon-user
|
%i.glyphicon.glyphicon-user
|
||||||
.text
|
.text
|
||||||
%label.text-muted programm hours: #{@program_length} h
|
%label.text-muted programm: #{@program_length} min
|
||||||
%label.text-muted new programm hours: #{@new_program_length} h
|
%label.text-muted new programm: #{@new_program_length} min
|
||||||
|
.row
|
||||||
|
= render partial: 'targets', locals: { targets: @program_minutes_targets, name: 'program_hours' }
|
||||||
.row
|
.row
|
||||||
.col-md-12
|
.col-md-12
|
||||||
.row
|
.row
|
||||||
|
|
@ -97,6 +103,13 @@
|
||||||
= render partial: 'recent_submissions', locals: {recent_events: @recent_events}
|
= render partial: 'recent_submissions', locals: {recent_events: @recent_events}
|
||||||
.col-md-4
|
.col-md-4
|
||||||
= render partial: 'top_submitter', locals: {top_submitter: @top_submitter}
|
= render partial: 'top_submitter', locals: {top_submitter: @top_submitter}
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
%h3
|
||||||
|
%span
|
||||||
|
%i.glyphicon.glyphicon-flag
|
||||||
|
Campaigns
|
||||||
|
= render partial: 'campaigns', locals: {campaigns: @campaigns, name: 'campaigns'}
|
||||||
|
|
||||||
:javascript
|
:javascript
|
||||||
$('#recentTable a').click(function (e) {
|
$('#recentTable a').click(function (e) {
|
||||||
|
|
|
||||||
6
app/views/admin/targets/_target_fields.html.haml
Normal file
6
app/views/admin/targets/_target_fields.html.haml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
.nested-fields
|
||||||
|
= f.inputs do
|
||||||
|
= f.input :due_date, as: :string, input_html: { class: 'target-due-date-datepicker', readonly: 'readonly' }
|
||||||
|
= f.input :target_count
|
||||||
|
= f.input :unit, as: :select, label: 'Unit', class: 'form-control', collection: Target.units.values, include_blank: false
|
||||||
|
= remove_association_link :target, f
|
||||||
5
app/views/admin/targets/index.html.haml
Normal file
5
app/views/admin/targets/index.html.haml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
.row
|
||||||
|
.col-md-8
|
||||||
|
= semantic_form_for(@conference, url: admin_conference_target_path(@conference.short_title, @conference.targets)) do |f|
|
||||||
|
= dynamic_association :targets, 'Targets', f
|
||||||
|
= f.action :submit, as: :button, button_html: { class: 'btn btn-primary' }
|
||||||
|
|
@ -20,26 +20,38 @@
|
||||||
%span.glyphicon.glyphicon-plus
|
%span.glyphicon.glyphicon-plus
|
||||||
New Conference
|
New Conference
|
||||||
%hr
|
%hr
|
||||||
%li{:class=> active_nav_li(admin_conference_registrations_path(@conference.short_title))}
|
%li{:class=> "#{active_nav_li(admin_conference_path(@conference.short_title))} nav-header nav-header-bigger"}
|
||||||
= link_to(admin_conference_registrations_path(@conference.short_title)) do
|
= link_to(admin_conference_path(@conference.short_title)) do
|
||||||
%span.glyphicon.glyphicon-user
|
%span.glyphicon.glyphicon-dashboard
|
||||||
Registrations
|
Manage
|
||||||
%li{:class=> active_nav_li(admin_conference_events_path(@conference.short_title))}
|
%li{:class=> active_nav_li(admin_conference_events_path(@conference.short_title))}
|
||||||
= link_to(admin_conference_events_path(@conference.short_title)) do
|
= link_to(admin_conference_events_path(@conference.short_title)) do
|
||||||
%span.glyphicon.glyphicon-comment
|
%span.glyphicon.glyphicon-comment
|
||||||
Events
|
Events
|
||||||
|
%li{:class=> active_nav_li(admin_conference_registrations_path(@conference.short_title))}
|
||||||
|
= link_to(admin_conference_registrations_path(@conference.short_title)) do
|
||||||
|
%span.glyphicon.glyphicon-user
|
||||||
|
Registrations
|
||||||
%li{class: active_nav_li(admin_conference_schedule_path(@conference.short_title))}
|
%li{class: active_nav_li(admin_conference_schedule_path(@conference.short_title))}
|
||||||
= link_to(admin_conference_schedule_path(@conference.short_title), target: '_blank') do
|
= link_to(admin_conference_schedule_path(@conference.short_title), target: '_blank') do
|
||||||
%span.glyphicon.glyphicon-calendar
|
%span.glyphicon.glyphicon-calendar
|
||||||
Schedule
|
Schedule
|
||||||
|
%li{class: active_nav_li(admin_conference_campaigns_path(@conference.short_title))}
|
||||||
|
= link_to(admin_conference_campaigns_path(@conference.short_title)) do
|
||||||
|
%span.glyphicon.glyphicon-bullhorn
|
||||||
|
Campaigns
|
||||||
%hr
|
%hr
|
||||||
%li{:class=> "#{active_nav_li(edit_admin_conference_path(@conference.short_title))} nav-header"}
|
%li{:class=> "#{active_nav_li(edit_admin_conference_path(@conference.short_title))} nav-header nav-header-bigger"}
|
||||||
= link_to(edit_admin_conference_path(@conference.short_title)) do
|
= link_to(edit_admin_conference_path(@conference.short_title)) do
|
||||||
%span.glyphicon.glyphicon-cog
|
%span.glyphicon.glyphicon-cog
|
||||||
Settings
|
Settings
|
||||||
|
%li{:class=> "#{active_nav_li(admin_conference_targets_path(@conference.short_title))}"}
|
||||||
|
= link_to(admin_conference_targets_path(@conference.short_title)) do
|
||||||
|
%span.glyphicon.glyphicon-flag
|
||||||
|
Targets
|
||||||
%li{:class=> "#{active_nav_li(admin_conference_venue_info_path(@conference.short_title))} myAccordion"}
|
%li{:class=> "#{active_nav_li(admin_conference_venue_info_path(@conference.short_title))} myAccordion"}
|
||||||
= link_to(admin_conference_venue_info_path(@conference.short_title)) do
|
= link_to(admin_conference_venue_info_path(@conference.short_title)) do
|
||||||
%span.glyphicon.glyphicon-calendar
|
%span.glyphicon.glyphicon-road
|
||||||
Venue
|
Venue
|
||||||
%span.small.glyphicon.glyphicon-chevron-right
|
%span.small.glyphicon.glyphicon-chevron-right
|
||||||
%ul.nav.nav-stacked.nav-pills.small.collapse.subNav
|
%ul.nav.nav-stacked.nav-pills.small.collapse.subNav
|
||||||
|
|
|
||||||
3
config/initializers/ahoy.rb
Normal file
3
config/initializers/ahoy.rb
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
class Ahoy::Store < Ahoy::Stores::ActiveRecordStore
|
||||||
|
# customize here
|
||||||
|
end
|
||||||
|
|
@ -35,6 +35,10 @@ Osem::Application.routes.draw do
|
||||||
|
|
||||||
resources :lodgings, only: [:show, :update, :index]
|
resources :lodgings, only: [:show, :update, :index]
|
||||||
|
|
||||||
|
resources :targets, only: [:update, :index]
|
||||||
|
|
||||||
|
resources :campaigns
|
||||||
|
|
||||||
resources :eventtypes, only: [:show, :index] do
|
resources :eventtypes, only: [:show, :index] do
|
||||||
collection do
|
collection do
|
||||||
patch :update
|
patch :update
|
||||||
|
|
|
||||||
12
db/migrate/20140616132153_create_targets.rb
Normal file
12
db/migrate/20140616132153_create_targets.rb
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
class CreateTargets < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :targets do |t|
|
||||||
|
t.integer :conference_id
|
||||||
|
t.integer :campaign_id
|
||||||
|
t.date :due_date
|
||||||
|
t.integer :target_count
|
||||||
|
t.string :unit
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
14
db/migrate/20140616132456_create_campaigns.rb
Normal file
14
db/migrate/20140616132456_create_campaigns.rb
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
class CreateCampaigns < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :campaigns do |t|
|
||||||
|
t.integer :conference_id
|
||||||
|
t.string :name
|
||||||
|
t.string :utm_source
|
||||||
|
t.string :utm_medium
|
||||||
|
t.string :utm_term
|
||||||
|
t.string :utm_content
|
||||||
|
t.string :utm_campaign
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
51
db/migrate/20140623100942_create_visits.rb
Normal file
51
db/migrate/20140623100942_create_visits.rb
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
class CreateVisits < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :visits, id: false do |t|
|
||||||
|
t.uuid :id, primary_key: true
|
||||||
|
t.uuid :visitor_id
|
||||||
|
|
||||||
|
# the rest are recommended but optional
|
||||||
|
# simply remove the columns you don't want
|
||||||
|
|
||||||
|
# standard
|
||||||
|
t.string :ip
|
||||||
|
t.text :user_agent
|
||||||
|
t.text :referrer
|
||||||
|
t.text :landing_page
|
||||||
|
|
||||||
|
# user
|
||||||
|
t.integer :user_id
|
||||||
|
# add t.string :user_type if polymorphic
|
||||||
|
|
||||||
|
# traffic source
|
||||||
|
t.string :referring_domain
|
||||||
|
t.string :search_keyword
|
||||||
|
|
||||||
|
# technology
|
||||||
|
t.string :browser
|
||||||
|
t.string :os
|
||||||
|
t.string :device_type
|
||||||
|
|
||||||
|
# location
|
||||||
|
t.string :country
|
||||||
|
t.string :region
|
||||||
|
t.string :city
|
||||||
|
|
||||||
|
# utm parameters
|
||||||
|
t.string :utm_source
|
||||||
|
t.string :utm_medium
|
||||||
|
t.string :utm_term
|
||||||
|
t.string :utm_content
|
||||||
|
t.string :utm_campaign
|
||||||
|
|
||||||
|
# native apps
|
||||||
|
# t.string :platform
|
||||||
|
# t.string :app_version
|
||||||
|
# t.string :os_version
|
||||||
|
|
||||||
|
t.timestamp :started_at
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :visits, [:user_id]
|
||||||
|
end
|
||||||
|
end
|
||||||
20
db/migrate/20140623101032_create_ahoy_events.rb
Normal file
20
db/migrate/20140623101032_create_ahoy_events.rb
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
class CreateAhoyEvents < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :ahoy_events, id: false do |t|
|
||||||
|
t.uuid :id, primary_key: true
|
||||||
|
t.uuid :visit_id
|
||||||
|
|
||||||
|
# user
|
||||||
|
t.integer :user_id
|
||||||
|
# add t.string :user_type if polymorphic
|
||||||
|
|
||||||
|
t.string :name
|
||||||
|
t.text :properties
|
||||||
|
t.timestamp :time
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :ahoy_events, [:visit_id]
|
||||||
|
add_index :ahoy_events, [:user_id]
|
||||||
|
add_index :ahoy_events, [:time]
|
||||||
|
end
|
||||||
|
end
|
||||||
63
db/schema.rb
63
db/schema.rb
|
|
@ -11,7 +11,20 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20140620134535) do
|
ActiveRecord::Schema.define(version: 20140623101032) do
|
||||||
|
|
||||||
|
create_table "ahoy_events", force: true do |t|
|
||||||
|
t.uuid "visit_id"
|
||||||
|
t.integer "user_id"
|
||||||
|
t.string "name"
|
||||||
|
t.text "properties"
|
||||||
|
t.datetime "time"
|
||||||
|
end
|
||||||
|
|
||||||
|
#add_index "ahoy_events", ["id"], name: "sqlite_autoindex_ahoy_events_1", unique: true
|
||||||
|
add_index "ahoy_events", ["time"], name: "index_ahoy_events_on_time"
|
||||||
|
add_index "ahoy_events", ["user_id"], name: "index_ahoy_events_on_user_id"
|
||||||
|
add_index "ahoy_events", ["visit_id"], name: "index_ahoy_events_on_visit_id"
|
||||||
|
|
||||||
create_table "answers", force: true do |t|
|
create_table "answers", force: true do |t|
|
||||||
t.string "title"
|
t.string "title"
|
||||||
|
|
@ -32,6 +45,18 @@ ActiveRecord::Schema.define(version: 20140620134535) do
|
||||||
t.boolean "include_cfp_in_splash", default: false
|
t.boolean "include_cfp_in_splash", default: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "campaigns", force: true do |t|
|
||||||
|
t.integer "conference_id"
|
||||||
|
t.string "name"
|
||||||
|
t.string "utm_source"
|
||||||
|
t.string "utm_medium"
|
||||||
|
t.string "utm_term"
|
||||||
|
t.string "utm_content"
|
||||||
|
t.string "utm_campaign"
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "comments", force: true do |t|
|
create_table "comments", force: true do |t|
|
||||||
t.string "title", limit: 50, default: ""
|
t.string "title", limit: 50, default: ""
|
||||||
t.text "body"
|
t.text "body"
|
||||||
|
|
@ -381,6 +406,16 @@ ActiveRecord::Schema.define(version: 20140620134535) do
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "targets", force: true do |t|
|
||||||
|
t.integer "conference_id"
|
||||||
|
t.integer "campaign_id"
|
||||||
|
t.date "due_date"
|
||||||
|
t.integer "target_count"
|
||||||
|
t.string "unit"
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "tracks", force: true do |t|
|
create_table "tracks", force: true do |t|
|
||||||
t.string "guid", null: false
|
t.string "guid", null: false
|
||||||
t.integer "conference_id"
|
t.integer "conference_id"
|
||||||
|
|
@ -457,6 +492,32 @@ ActiveRecord::Schema.define(version: 20140620134535) do
|
||||||
|
|
||||||
add_index "versions", ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id"
|
add_index "versions", ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id"
|
||||||
|
|
||||||
|
create_table "visits", force: true do |t|
|
||||||
|
t.uuid "visitor_id"
|
||||||
|
t.string "ip"
|
||||||
|
t.text "user_agent"
|
||||||
|
t.text "referrer"
|
||||||
|
t.text "landing_page"
|
||||||
|
t.integer "user_id"
|
||||||
|
t.string "referring_domain"
|
||||||
|
t.string "search_keyword"
|
||||||
|
t.string "browser"
|
||||||
|
t.string "os"
|
||||||
|
t.string "device_type"
|
||||||
|
t.string "country"
|
||||||
|
t.string "region"
|
||||||
|
t.string "city"
|
||||||
|
t.string "utm_source"
|
||||||
|
t.string "utm_medium"
|
||||||
|
t.string "utm_term"
|
||||||
|
t.string "utm_content"
|
||||||
|
t.string "utm_campaign"
|
||||||
|
t.datetime "started_at"
|
||||||
|
end
|
||||||
|
|
||||||
|
#add_index "visits", ["id"], name: "sqlite_autoindex_visits_1", unique: true
|
||||||
|
add_index "visits", ["user_id"], name: "index_visits_on_user_id"
|
||||||
|
|
||||||
create_table "votes", force: true do |t|
|
create_table "votes", force: true do |t|
|
||||||
t.integer "person_id"
|
t.integer "person_id"
|
||||||
t.integer "event_id"
|
t.integer "event_id"
|
||||||
|
|
|
||||||
8
spec/factories/campaigns.rb
Normal file
8
spec/factories/campaigns.rb
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Read about factories at https://github.com/thoughtbot/factory_girl
|
||||||
|
|
||||||
|
FactoryGirl.define do
|
||||||
|
factory :campaign do
|
||||||
|
name 'Test Campaign'
|
||||||
|
utm_campaign 'testcampaign'
|
||||||
|
end
|
||||||
|
end
|
||||||
9
spec/factories/targets.rb
Normal file
9
spec/factories/targets.rb
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Read about factories at https://github.com/thoughtbot/factory_girl
|
||||||
|
|
||||||
|
FactoryGirl.define do
|
||||||
|
factory :target do
|
||||||
|
due_date Date.today + 14
|
||||||
|
target_count 100
|
||||||
|
unit Target.units[:submissions]
|
||||||
|
end
|
||||||
|
end
|
||||||
6
spec/factories/visits.rb
Normal file
6
spec/factories/visits.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Read about factories at https://github.com/thoughtbot/factory_girl
|
||||||
|
|
||||||
|
FactoryGirl.define do
|
||||||
|
factory :visit do
|
||||||
|
end
|
||||||
|
end
|
||||||
59
spec/features/campaign_spec.rb
Normal file
59
spec/features/campaign_spec.rb
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
feature Campaign do
|
||||||
|
|
||||||
|
# It is necessary to use bang version of let to build roles before user
|
||||||
|
let!(:organizer_role) { create(:organizer_role) }
|
||||||
|
let!(:participant_role) { create(:participant_role) }
|
||||||
|
let!(:admin_role) { create(:admin_role) }
|
||||||
|
|
||||||
|
shared_examples 'add and update campaign' do |user|
|
||||||
|
scenario 'adds and update a campaign', feature: true, js: true do
|
||||||
|
expected_count = Campaign.count + 1
|
||||||
|
conference = create(:conference, short_title: 'osc14')
|
||||||
|
sign_in create(user)
|
||||||
|
|
||||||
|
visit admin_conference_campaigns_path(conference.short_title)
|
||||||
|
|
||||||
|
click_link 'New Campaign'
|
||||||
|
|
||||||
|
click_button 'Create Campaign'
|
||||||
|
|
||||||
|
expect(flash).
|
||||||
|
to eq("Creating of Campaign for osc14 failed.Name can't be blank. Utm campaign can't be blank.")
|
||||||
|
|
||||||
|
fill_in 'campaign_name', with: 'Test Campaign'
|
||||||
|
fill_in 'campaign_utm_campaign', with: 'campaign'
|
||||||
|
fill_in 'campaign_utm_source', with: 'source'
|
||||||
|
fill_in 'campaign_utm_medium', with: 'medium'
|
||||||
|
fill_in 'campaign_utm_term', with: 'term'
|
||||||
|
fill_in 'campaign_utm_content', with: 'content'
|
||||||
|
|
||||||
|
click_button 'Create Campaign'
|
||||||
|
|
||||||
|
# Validations
|
||||||
|
expect(flash).
|
||||||
|
to eq('Campaign successfully created.')
|
||||||
|
|
||||||
|
expect(find('#name_0').text).to eq('Test Campaign')
|
||||||
|
expect(find('#visits_0').text).to eq('0')
|
||||||
|
expect(find('#registrations_0').text).to eq('0')
|
||||||
|
expect(find('#submissions_0').text).to eq('0')
|
||||||
|
|
||||||
|
expect(Campaign.count).to eq(expected_count)
|
||||||
|
|
||||||
|
campaign = Campaign.where('name'=> 'Test Campaign').first
|
||||||
|
visit edit_admin_conference_campaign_path(conference.short_title, campaign.id)
|
||||||
|
|
||||||
|
fill_in 'campaign_name', with: 'Test Campaign 42'
|
||||||
|
click_button 'Update Campaign'
|
||||||
|
expect(flash).
|
||||||
|
to eq("Campaign 'Test Campaign 42' successfully updated.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'admin' do
|
||||||
|
it_behaves_like 'add and update campaign', :admin
|
||||||
|
it_behaves_like 'add and update campaign', :organizer
|
||||||
|
end
|
||||||
|
end
|
||||||
71
spec/models/campaign_spec.rb
Normal file
71
spec/models/campaign_spec.rb
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'ahoy'
|
||||||
|
|
||||||
|
describe Campaign do
|
||||||
|
describe 'validations' do
|
||||||
|
it 'has a valid factory' do
|
||||||
|
expect(build(:campaign)).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not valid without a name' do
|
||||||
|
should validate_presence_of(:name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#url_parameters' do
|
||||||
|
it 'returns the parameters in the correct format' do
|
||||||
|
campaign = create(:campaign, utm_source: 'google+', utm_medium: 'advertisement',
|
||||||
|
utm_term: 'opensource', utm_content: 'content', utm_campaign: '20percent')
|
||||||
|
campaign.conference = create(:conference)
|
||||||
|
|
||||||
|
result = '?utm_source=google+&utm_medium=advertisement&utm_term=opensource&utm_content=content&utm_campaign=20percent'
|
||||||
|
expect(campaign.url_parameters).to eq(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns only utm_campaign parameter if there are no parameters' do
|
||||||
|
campaign = create(:campaign)
|
||||||
|
campaign.conference = create(:conference)
|
||||||
|
expect(campaign.url_parameters).to eq('?utm_campaign=testcampaign')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#visits' do
|
||||||
|
it 'returns one if there is one visit' do
|
||||||
|
campaign = create(:campaign, utm_source: 'google+', utm_medium: 'advertisement',
|
||||||
|
utm_term: 'opensource', utm_content: 'content', utm_campaign: '20percent')
|
||||||
|
campaign.conference = build(:conference)
|
||||||
|
|
||||||
|
create(:visit, utm_source: 'google+', utm_medium: 'advertisement',
|
||||||
|
utm_term: 'opensource', utm_content: 'content', utm_campaign: '20percent', started_at: Time.now)
|
||||||
|
expect(campaign.visits_count).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns zero if there are no visits' do
|
||||||
|
campaign = create(:campaign, utm_source: 'google+', utm_medium: 'advertisement',
|
||||||
|
utm_term: 'opensource', utm_content: 'content', utm_campaign: '20percent')
|
||||||
|
campaign.conference = create(:conference)
|
||||||
|
|
||||||
|
expect(campaign.visits_count).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#registrations' do
|
||||||
|
it 'returns zero if there are no registration' do
|
||||||
|
campaign = build(:campaign, utm_source: 'google+', utm_medium: 'advertisement',
|
||||||
|
utm_term: 'opensource', utm_content: 'content', utm_campaign: '20percent')
|
||||||
|
campaign.conference = build(:conference)
|
||||||
|
|
||||||
|
expect(campaign.registrations_count).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#submissions' do
|
||||||
|
it 'returns zero if there are no submissions' do
|
||||||
|
campaign = build(:campaign, utm_source: 'google+', utm_medium: 'advertisement',
|
||||||
|
utm_term: 'opensource', utm_content: 'content', utm_campaign: '20percent')
|
||||||
|
campaign.conference = build(:conference)
|
||||||
|
|
||||||
|
expect(campaign.submissions_count).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -6,35 +6,98 @@ describe Conference do
|
||||||
|
|
||||||
let(:subject) { create(:conference) }
|
let(:subject) { create(:conference) }
|
||||||
|
|
||||||
# describe '#get_top_submitter' do
|
describe '#get_top_submitter' do
|
||||||
# # It is necessary to use bang version of let to build roles before user
|
# It is necessary to use bang version of let to build roles before user
|
||||||
# let!(:organizer_role) { create(:organizer_role) }
|
let!(:organizer_role) { create(:organizer_role) }
|
||||||
# let!(:participant_role) { create(:participant_role) }
|
let!(:participant_role) { create(:participant_role) }
|
||||||
# let!(:admin_role) { create(:admin_role) }
|
let!(:admin_role) { create(:admin_role) }
|
||||||
#
|
|
||||||
# it 'calculates correct hash with top submitters' do
|
it 'calculates correct hash with top submitters' do
|
||||||
# event = create(:event, conference: subject)
|
event = create(:event, conference: subject)
|
||||||
# result = {
|
result = {
|
||||||
# event.submitter => 1
|
event.submitter => 1
|
||||||
# }
|
}
|
||||||
# expect(subject.get_top_submitter).to eq(result)
|
expect(subject.get_top_submitter).to eq(result)
|
||||||
# end
|
end
|
||||||
#
|
|
||||||
# it 'returns the submitter ordered by submissions' do
|
it 'returns the submitter ordered by submissions' do
|
||||||
# e1 = create(:event, conference: subject)
|
e1 = create(:event, conference: subject)
|
||||||
#
|
|
||||||
# e2 = create(:event, conference: subject)
|
e2 = create(:event, conference: subject)
|
||||||
# e3 = create(:event, conference: subject)
|
e3 = create(:event, conference: subject)
|
||||||
# e4 = create(:event, conference: subject)
|
e4 = create(:event, conference: subject)
|
||||||
#
|
|
||||||
# e3.event_people = [create(:event_person, person: e2.submitter, event_role: 'submitter')]
|
e3.event_people = [create(:event_person, person: e2.submitter, event_role: 'submitter')]
|
||||||
# e4.event_people = [create(:event_person, person: e2.submitter, event_role: 'submitter')]
|
e4.event_people = [create(:event_person, person: e2.submitter, event_role: 'submitter')]
|
||||||
#
|
|
||||||
# expect(subject.get_top_submitter.values).to eq([3, 1])
|
expect(subject.get_top_submitter.values).to eq([3, 1])
|
||||||
# expect(subject.get_top_submitter.keys).to eq([e2.submitter, e1.submitter])
|
expect(subject.get_top_submitter.keys).to eq([e2.submitter, e1.submitter])
|
||||||
# end
|
end
|
||||||
#
|
|
||||||
# end
|
end
|
||||||
|
|
||||||
|
describe '#get_targets' do
|
||||||
|
it 'returns 0 if there is no registration' do
|
||||||
|
target = build(:target, target_count: 10, unit: Target.units[:registrations])
|
||||||
|
subject.targets = [target]
|
||||||
|
result = {
|
||||||
|
"10 Registrations by #{target.due_date}" => '0'
|
||||||
|
}
|
||||||
|
expect(subject.get_targets(Target.units[:registrations])).to eq(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 10 if there is 1 registration of 10' do
|
||||||
|
target = build(:target, target_count: 10, unit: Target.units[:registrations])
|
||||||
|
subject.targets = [target]
|
||||||
|
subject.registrations = [create(:registration)]
|
||||||
|
result = {
|
||||||
|
"10 Registrations by #{target.due_date}" => '10'
|
||||||
|
}
|
||||||
|
expect(subject.get_targets(Target.units[:registrations])).to eq(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty hash if there is no target' do
|
||||||
|
expect(subject.get_targets(Target.units[:registrations])).to eq({})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 0 if there is no submission' do
|
||||||
|
target = build(:target, target_count: 10, unit: Target.units[:submissions])
|
||||||
|
subject.targets = [target]
|
||||||
|
result = {
|
||||||
|
"10 Submissions by #{target.due_date}" => '0'
|
||||||
|
}
|
||||||
|
expect(subject.get_targets(Target.units[:submissions])).to eq(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 10 if there is 1 submissions of 10' do
|
||||||
|
target = build(:target, target_count: 10, unit: Target.units[:submissions])
|
||||||
|
subject.targets = [target]
|
||||||
|
subject.events = [create(:event)]
|
||||||
|
result = {
|
||||||
|
"10 Submissions by #{target.due_date}" => '10'
|
||||||
|
}
|
||||||
|
expect(subject.get_targets(Target.units[:submissions])).to eq(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 0 if there is no program minute' do
|
||||||
|
target = build(:target, target_count: 300, unit: Target.units[:program_minutes])
|
||||||
|
subject.targets = [target]
|
||||||
|
result = {
|
||||||
|
"300 Program minutes by #{target.due_date}" => '0'
|
||||||
|
}
|
||||||
|
expect(subject.get_targets(Target.units[:program_minutes])).to eq(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 10 if there is 30 program minutes of 300' do
|
||||||
|
target = build(:target, target_count: 300, unit: Target.units[:program_minutes])
|
||||||
|
subject.targets = [target]
|
||||||
|
subject.events = [create(:event)]
|
||||||
|
result = {
|
||||||
|
"300 Program minutes by #{target.due_date}" => '10'
|
||||||
|
}
|
||||||
|
expect(subject.get_targets(Target.units[:program_minutes])).to eq(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'program hours' do
|
describe 'program hours' do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
|
|
|
||||||
105
spec/models/target_spec.rb
Normal file
105
spec/models/target_spec.rb
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Target do
|
||||||
|
|
||||||
|
describe 'validations' do
|
||||||
|
it 'has a valid factory' do
|
||||||
|
expect(build(:target)).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not valid without a due date' do
|
||||||
|
should validate_presence_of(:due_date)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not valid without a target_count' do
|
||||||
|
should validate_presence_of(:target_count)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not valid without a unit' do
|
||||||
|
should validate_presence_of(:unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is valid with a target_count greater than zero' do
|
||||||
|
should allow_value(10).for(:target_count)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not valid with a target_count equals zero' do
|
||||||
|
should_not allow_value(0).for(:target_count)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not valid with a target_count smaller than zero' do
|
||||||
|
should_not allow_value(-10).for(:target_count)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#get_progress' do
|
||||||
|
it 'returns zero if there are no registrations' do
|
||||||
|
conference = build(:conference)
|
||||||
|
target = build(:target, target_count: 10, unit: Target.units[:registrations])
|
||||||
|
conference.targets = [target]
|
||||||
|
|
||||||
|
expect(target.get_progress).to eq('0')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 10 if there one registrations of 10' do
|
||||||
|
conference = create(:conference)
|
||||||
|
target = create(:target, target_count: 10, unit: Target.units[:registrations])
|
||||||
|
registration = create(:registration)
|
||||||
|
|
||||||
|
conference.targets = [target]
|
||||||
|
conference.registrations = [registration]
|
||||||
|
|
||||||
|
expect(target.get_progress).to eq('10')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns zero if there are no submissions' do
|
||||||
|
conference = build(:conference)
|
||||||
|
target = build(:target, target_count: 10, unit: Target.units[:submissions])
|
||||||
|
conference.targets = [target]
|
||||||
|
|
||||||
|
expect(target.get_progress).to eq('0')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 10 if there one submissions of 10' do
|
||||||
|
conference = create(:conference)
|
||||||
|
target = create(:target, target_count: 10, unit: Target.units[:submissions])
|
||||||
|
event = create(:event)
|
||||||
|
|
||||||
|
conference.targets = [target]
|
||||||
|
conference.events = [event]
|
||||||
|
|
||||||
|
expect(target.get_progress).to eq('10')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns zero if there are no program minutes' do
|
||||||
|
conference = build(:conference)
|
||||||
|
target = build(:target, target_count: 10, unit: Target.units[:program_minutes])
|
||||||
|
conference.targets = [target]
|
||||||
|
|
||||||
|
expect(target.get_progress).to eq('0')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 10 if there are 30 program minutes of 300' do
|
||||||
|
conference = create(:conference)
|
||||||
|
target = create(:target, target_count: 300, unit: Target.units[:program_minutes])
|
||||||
|
event = create(:event)
|
||||||
|
|
||||||
|
conference.targets = [target]
|
||||||
|
conference.events = [event]
|
||||||
|
|
||||||
|
expect(target.get_progress).to eq('10')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_s' do
|
||||||
|
it 'returns a string in the correct format' do
|
||||||
|
conference = build(:conference)
|
||||||
|
target = build(:target, target_count: 10, unit: Target.units[:registrations])
|
||||||
|
conference.targets = [target]
|
||||||
|
|
||||||
|
result = "10 Registrations by #{Date.today + 14}"
|
||||||
|
|
||||||
|
expect(target.to_s).to eq(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue