chart-candy 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/Gemfile +4 -0
  2. data/LICENSE +7 -0
  3. data/README.md +109 -0
  4. data/Rakefile +11 -0
  5. data/app/assets/javascripts/chart_candy/base.coffee +16 -0
  6. data/app/assets/javascripts/chart_candy/counter.coffee +50 -0
  7. data/app/assets/javascripts/chart_candy/donut.coffee +162 -0
  8. data/app/assets/javascripts/chart_candy/index.js +4 -0
  9. data/app/assets/javascripts/chart_candy/line.coffee +338 -0
  10. data/app/assets/stylesheets/chart_candy/base.css.scss +66 -0
  11. data/app/assets/stylesheets/chart_candy/counter.css.scss +11 -0
  12. data/app/assets/stylesheets/chart_candy/donut.css.scss +34 -0
  13. data/app/assets/stylesheets/chart_candy/index.css +6 -0
  14. data/app/assets/stylesheets/chart_candy/line.css.scss +107 -0
  15. data/app/controllers/candy_charts_controller.rb +60 -0
  16. data/config/locales/fr.yml +36 -0
  17. data/config/routes.rb +3 -0
  18. data/lib/chart-candy.rb +23 -0
  19. data/lib/chart-candy/authentication.rb +45 -0
  20. data/lib/chart-candy/base_chart.rb +11 -0
  21. data/lib/chart-candy/builder.rb +46 -0
  22. data/lib/chart-candy/builder/base.rb +70 -0
  23. data/lib/chart-candy/builder/counter.rb +12 -0
  24. data/lib/chart-candy/builder/donut.rb +73 -0
  25. data/lib/chart-candy/builder/line.rb +114 -0
  26. data/lib/chart-candy/builder/xls_builder.rb +159 -0
  27. data/lib/chart-candy/engine.rb +5 -0
  28. data/lib/chart-candy/helpers.rb +169 -0
  29. data/lib/chart-candy/implants.rb +10 -0
  30. data/lib/chart-candy/implants/railtie.rb +17 -0
  31. data/spec/chart-candy_spec.rb +11 -0
  32. data/spec/spec_helper.rb +12 -0
  33. data/vendor/assets/javascripts/d3.js +4 -0
  34. metadata +118 -0
@@ -0,0 +1,66 @@
1
+ div.wrapper-chart{font-family:"Lucida Grande", Arial;float:left;margin:0px 0px 40px 0px;width:100%;
2
+ h2{clear:both;float:left;font-size:24px;margin:0px 0px 20px 0px;width:100%;}
3
+ h2 span.subtitle{clear:both;color:#777;display:block;font-size:16px;}
4
+
5
+ div.tools{border:1px solid #dadada;border-bottom:1px solid #ccc;border-top-left-radius:8px;border-top-right-radius:8px;clear:both;float:left;height:50px;margin:0px 0px -1px 0px;padding:0px;width:100%;
6
+ background: #ececec;
7
+ background: linear-gradient(top, #F2F2F2, #ececec);
8
+ background: -moz-linear-gradient(top, #F2F2F2, #ececec);
9
+ background: -webkit-linear-gradient(top, #F2F2F2, #ececec);
10
+
11
+ select{margin:5px 0px 0px 0px;}
12
+
13
+ div.tool{float:left;margin:10px 30px 0px 0px;}
14
+
15
+ div.holder-template{margin-left:30px;
16
+ ul.items{margin:0px;
17
+ li{padding:0px;}
18
+ li span.text{background-image:image-url("application/icon-charts.png");display:block;text-indent:-9999px;height:15px;margin:0px 15px;padding-left:0px;padding-right:0px;width:20px;}
19
+ li.table span.text{background-position:-100px 5px;}
20
+ li.selected.table span.text{background-position:-100px -45px;}
21
+ }
22
+ }
23
+
24
+ div.holder-export-xls{float:right;
25
+ a{background:#fafafa;border:1px solid #ddd;border-radius:4px;color:#555;display:block;font-size:11px;margin:0px 0px 0px 0px;padding:7px 15px;text-decoration:none;text-indent:0px;}
26
+ a:hover{background:#fff;color:#222;}
27
+ }
28
+ }
29
+
30
+ div.templates{width:100%;}
31
+
32
+ div.table{clear:both;display:none;float:left;margin:0px 0px 0px 0px;overflow:hidden;width:100%;
33
+ table{border-collapse:collapse;border-spacing:0;border:none;border-left:1px solid #ddd;border-right:1px solid #ddd;clear:both;float:left;margin:0px;padding:0px;width:100%;
34
+ thead{
35
+ tr{
36
+ th{border:none;border-left:1px solid #dddddd;border-bottom:1px solid #d1d1d1;border-top:1px solid #e1e1e1;box-shadow:inset 0 1px 0 0 #fff;color:#000;cursor:default;font-family:'Lucida Grande', Helvetica, Arial, sans-serif;font-weight:bold;font-size:13px;height:auto;padding:10px 20px 10px 20px;
37
+ background: #ececec;
38
+ background: linear-gradient(top, #F2F2F2, #ececec);
39
+ background: -moz-linear-gradient(top, #F2F2F2, #ececec);
40
+ background: -webkit-linear-gradient(top, #F2F2F2, #ececec);
41
+ }
42
+ th:first-child{border-left:none;padding-left:30px;}
43
+ th:last-child{padding-right:30px;}
44
+ }
45
+
46
+ }
47
+ tbody{
48
+ tr{
49
+ td{border-bottom:1px solid #ddd;border-left:none;border-right:none;color:#555;font-size:12px;margin:0px;padding:10px 20px 10px 20px;}
50
+ td.number{text-align:right;}
51
+ td:first-child{padding-left:30px;}
52
+ td:last-child{padding-right:30px;}
53
+ }
54
+ tr:hover td{background-color:#fafafa;}
55
+ }
56
+ tfoot{
57
+ tr{
58
+ td{background-color:#F6F6F6;border-bottom:1px solid #ddd;color:#000;font-size:13px;font-weight:bold;padding:10px 20px;}
59
+ td.number{text-align:right;}
60
+ td:first-child{padding-left:30px;}
61
+ td:last-child{padding-right:30px;}
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,11 @@
1
+ div.chart-counter{border:1px solid #ddd;border-radius:8px;padding:10px 0px;width:150px;
2
+ background: #fff;
3
+ background: linear-gradient(top, #fff, #f5f5f5);
4
+ background: -moz-linear-gradient(top, #fff, #f5f5f5);
5
+ background: -webkit-linear-gradient(top, #fff, #f5f5f5);
6
+
7
+ h2{display:none;}
8
+ span{display:block;text-align:center;width:100%;}
9
+ span.label.primary{color:#999;font-size:11px;margin:0px 0px 3px 0px;}
10
+ span.value.primary{color:#000;font-family:Arial;font-size:36px;font-weight:bold;}
11
+ }
@@ -0,0 +1,34 @@
1
+ div.chart-donut{clear:none;float:left;margin-left:30px;width:450px;
2
+ div.tools{background:none;border:none;margin:0px;
3
+ div.holder-template {margin-left:0px;
4
+ ul.items{
5
+ li.chart span.text{background-position:0px 5px;}
6
+ li.selected.chart span.text{background-position:0px -45px;}
7
+ }
8
+ }
9
+ div.holder-export-xls{margin-right:0px;}
10
+ }
11
+
12
+ div.templates{float:left;
13
+ div.chart{display:block;height:340px;padding:20px 80px 0px 80px;width:450px;
14
+ svg{
15
+ path.slice-1{fill:#10c7df;}
16
+ path.slice-2{fill:#ffcc00;}
17
+ path.slice-3{fill:#c1e443;}
18
+ path.slice-4{fill:#fd41cd;}
19
+ path.slice-5{fill:#11aec6;}
20
+ path.slice-6{fill:#e09200;}
21
+ path.slice-7{fill:#84d200;}
22
+ path.slice-8{fill:#f2a1e7;}
23
+
24
+ text{cursor:default;}
25
+ .hole{
26
+ .label{fill:#000;font-size:14px;font-weight:bold;}
27
+ .text0{fill:#999;font-size:12px;text-transform:uppercase;}
28
+ }
29
+ }
30
+
31
+ div.tooltip{background-color:#fff;border:1px solid #ddd;border-radius:8;box-shadow:0px 5px 6px -6px #333;color:#000;display:none;height:auto;padding:10px 20px;position:absolute;visibility:visible;width:auto;z-index:100;}
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,6 @@
1
+ /*
2
+ *= require ./base
3
+ *= require ./counter
4
+ *= require ./donut
5
+ *= require ./line
6
+ */
@@ -0,0 +1,107 @@
1
+ div.chart-line{clear:both;float:left;width:100%;
2
+
3
+ div.tools{margin-left:60px;width:979px;
4
+ div.holder-step{float:right;margin-right:30px;
5
+ div.select-field div.current span.text{width:120px;}
6
+ }
7
+
8
+ div.holder-template ul.items{
9
+ li.chart span.text{background-position:-50px 5px;}
10
+ li.selected.chart span.text{background-position:-50px -45px;}
11
+ }
12
+ }
13
+
14
+ div.templates{
15
+ div.table{margin-left:60px;width:981px;}
16
+
17
+ div.chart{clear:both;height:350px;margin-top:-5px;padding:0px 60px 40px 60px;width:1100px;
18
+ svg{overflow:visible;height:100%;width:100%;
19
+ rect.bg{ fill:#fff; }
20
+
21
+ g.tooltip{
22
+ rect{border-radius:1em;fill:#fff;stroke:#ccc;}
23
+ text{cursor:default;fill:#555;font-family:'Arial';
24
+ tspan.x{font-size:1em;fill:#777;}
25
+ tspan.y{font-size:1em;fill:#333;}
26
+ }
27
+ }
28
+
29
+ path{fill:none;}
30
+
31
+ g.axis {shape-rendering:crispEdges;
32
+ line {stroke:#f1f1f1;}
33
+ .minor {stroke-opacity:.3;}
34
+ path {stroke:#e1e1e1;}
35
+ }
36
+
37
+ g.pointer{opacity:0;
38
+ circle{fill:#fff;stroke-width:2;}
39
+ }
40
+ g.pointer-1 circle{stroke:#10c7df;}
41
+ g.pointer-2 circle{stroke:#ffcc00;}
42
+ g.pointer-3 circle{stroke:#c1e443;}
43
+ g.pointer-4 circle{stroke:#fd41cd;}
44
+ g.pointer-5 circle{stroke:#11aec6;}
45
+ g.pointer-6 circle{stroke:#e09200;}
46
+ g.pointer-7 circle{stroke:#84d200;}
47
+ g.pointer-8 circle{stroke:#f2a1e7;}
48
+ g.pointer.disabled{opacity:0;}
49
+
50
+
51
+ g.line{display:block;width:100%;height:100%;
52
+ path.stroke{stroke-opacity:1;stroke-width:2;}
53
+ path.area{fill:none;fill-opacity:.3;}
54
+ }
55
+
56
+ g.line-1{
57
+ path.stroke{stroke:#10c7df;}
58
+ path.area{fill:#c7ffff;}
59
+ }
60
+ g.line-2 path.stroke{stroke:#ffcc00;}
61
+ g.line-3 path.stroke{stroke:#c1e443;}
62
+ g.line-4 path.stroke{stroke:#fd41cd;}
63
+ g.line-5 path.stroke{stroke:#11aec6;}
64
+ g.line-6 path.stroke{stroke:#e09200;}
65
+ g.line-7 path.stroke{stroke:#84d200;}
66
+ g.line-8 path.stroke{stroke:#f2a1e7;}
67
+
68
+ g.line.selected{
69
+ path.stroke{stroke-width:3;}
70
+ path.area{fill-opacity:.5;}
71
+ }
72
+
73
+ g.line.hidden{
74
+ path.stroke{stroke:#f00;}
75
+ }
76
+
77
+
78
+ g.legend{
79
+ rect{fill:#fafafa;fill-opacity:.6;}
80
+
81
+ g.item{cursor:pointer;
82
+ text{fill:#555;}
83
+ line{stroke-width:3;}
84
+ }
85
+ g.item:hover{
86
+ text{fill:#000;}
87
+ line{stroke-width:4;}
88
+ }
89
+ g.item.item-1 line{stroke:#10c7df;}
90
+ g.item.item-2 line{stroke:#ffcc00;}
91
+ g.item.item-3 line{stroke:#c1e443;}
92
+ g.item.item-4 line{stroke:#fd41cd;}
93
+ g.item.item-5 line{stroke:#11aec6;}
94
+ g.item.item-6 line{stroke:#e09200;}
95
+ g.item.item-7 line{stroke:#84d200;}
96
+ g.item.item-8 line{stroke:#f2a1e7;}
97
+
98
+ g.item.disabled{
99
+ text{fill:#999;}
100
+ line{stroke-opacity:0.1;}
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ }
@@ -0,0 +1,60 @@
1
+ #*************************************************************************************
2
+ # TOCOMMENT
3
+ #*************************************************************************************
4
+ class CandyChartsController < ApplicationController
5
+ before_filter :authenticate
6
+
7
+ def show
8
+ if @granted
9
+ set_default_to if params[:from] and not params[:to]
10
+
11
+ name = (params[:id].gsub('-', '_').camelize + 'Chart')
12
+
13
+ begin
14
+ chart = name.constantize.new(params)
15
+ rescue
16
+ raise "Chart Candy: You must defined #{name}"
17
+ end
18
+
19
+ builder = "ChartCandy::Builder::#{params[:nature].camelize}".constantize.new(params[:id], params)
20
+
21
+ chart.build builder
22
+
23
+ respond_to do |format|
24
+ format.json { render json: builder.to_json }
25
+ format.xls { render_xls builder }
26
+ end
27
+ else
28
+ respond_to do |format|
29
+ format.json { render json: { 'state' => 'access_refused' } }
30
+ format.xls { render text: 'access_refused' }
31
+ end
32
+ end
33
+ end
34
+
35
+ def render_xls(builder)
36
+ spreadsheet = StringIO.new
37
+ builder.to_xls.write spreadsheet
38
+
39
+ send_data spreadsheet.string, filename: builder.filename, type: "application/vnd.ms-excel"
40
+ end
41
+
42
+ def authenticate
43
+ auth = ChartCandy::Authentication.new(request.url, params)
44
+
45
+ @granted = (auth.valid_token? and not auth.expired?)
46
+ end
47
+
48
+ def set_default_to
49
+ if params[:nature] == 'line'
50
+ params[:to] = case params[:step]
51
+ when 'day' then (Time.now.utc - 1.day).end_of_day.iso8601
52
+ when 'week' then (Time.now.utc - 1.week).end_of_week.iso8601
53
+ when 'month' then (Time.now.utc - 1.month).end_of_month.iso8601
54
+ else Time.now.utc.iso8601
55
+ end
56
+ else
57
+ params[:to] = Time.now.utc.iso8601
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,36 @@
1
+ fr:
2
+ date:
3
+ abbr_day_names: [lun, mar, mer, jeu, ven, sam, dim]
4
+ day_names: [dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi]
5
+ abbr_month_names: [~, jan, fév, mar, avr, mai, jun, jul, aoû, sep, oct, nov, déc]
6
+ month_names: [~, janvier, février, mars, avril, mai, juin, juillet, août, septembre, octobre, novembre, décembre]
7
+
8
+ chart_candy:
9
+ base:
10
+ steps:
11
+ day: Quotidien
12
+ week: Hebdomadaire
13
+ month: Mensuel
14
+
15
+ template:
16
+ table: Tableau
17
+ chart: Diagramme
18
+
19
+ xls_export: "Exporter en Excel"
20
+
21
+ date:
22
+ period:
23
+ label: Période du
24
+ label_month: Période de
25
+ to: au
26
+ to_month: à
27
+ week: "Semaine du"
28
+
29
+ time:
30
+ formats:
31
+ chart_candy_date_without_day: "%B %Y"
32
+ chart_candy_date_long: "%e %B %Y"
33
+ chart_candy_date_long_without_year: "%e %B"
34
+ chart_candy_day: "%e"
35
+ chart_candy_long: "%e %B %Y à %H:%M"
36
+ chart_candy_year: "%Y"
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ resources :candy_charts, only: [:index, :show], controller: "candy_charts"
3
+ end
@@ -0,0 +1,23 @@
1
+ module ChartCandy
2
+ def self.localize(path, options={})
3
+ options.reverse_merge! format: :date_long
4
+
5
+ options[:format] = "chart_candy_#{options[:format]}".to_sym
6
+
7
+ return I18n.localize(path, options)
8
+ end
9
+
10
+ def self.translate(path, options={})
11
+ I18n.translate("chart_candy.#{path}", options)
12
+ end
13
+
14
+ def self.t(path, options={})
15
+ self.translate path, options
16
+ end
17
+ end
18
+
19
+ require "chart-candy/authentication"
20
+ require "chart-candy/base_chart"
21
+ require "chart-candy/builder"
22
+ require "chart-candy/engine"
23
+ require 'chart-candy/implants'
@@ -0,0 +1,45 @@
1
+ module ChartCandy
2
+ class Authentication
3
+ def self.compact_params(original_params)
4
+ compacted_params = ''
5
+
6
+ original_params.each { |k,v| compacted_params << (k.to_s + v.to_s) if not self.reserved_params.include?(k.to_s) }
7
+
8
+ return compacted_params
9
+ end
10
+
11
+ def self.reserved_params
12
+ ['action', 'class', 'controller', 'format', 'from', 'nature', 'step', 'to', 'token', 'tools', 'update_every', 'version']
13
+ end
14
+
15
+ def self.tokenize(str)
16
+ Digest::HMAC.hexdigest(str.chars.sort.join.gsub('/', ''), Rails.configuration.secret_token, Digest::SHA1)
17
+ #HMAC::SHA1.hexdigest(Rails.configuration.secret_token, str.chars.sort.join.gsub('/', ''))
18
+ end
19
+
20
+ def initialize(request_url, params={})
21
+ @request_url = request_url
22
+ @params = params
23
+ end
24
+
25
+ def expired?
26
+ @params[:timestamp] and Time.parse(@params[:timestamp]) + 12.hours < Time.now
27
+ end
28
+
29
+ def valid_token?
30
+ @params[:token] == tokenize(filter_url)
31
+ end
32
+
33
+ private
34
+
35
+ def filter_url
36
+ filtered_url = @request_url.split('?').first.rpartition('/').first
37
+
38
+ return filtered_url + ChartCandy::Authentication.compact_params(@params)
39
+ end
40
+
41
+ def tokenize(str)
42
+ ChartCandy::Authentication.tokenize(str)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,11 @@
1
+ module ChartCandy
2
+ class BaseChart
3
+ def initialize(params)
4
+ @params = params
5
+
6
+ @from = params[:from] ? Time.parse(params[:from]) : nil
7
+ @to = params[:to] ? Time.parse(params[:to]) : Time.now
8
+ @step = params[:step] || 'month'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,46 @@
1
+ module ChartCandy::Builder
2
+ def self.period(start_at, end_at, options={})
3
+ options.reverse_merge!(step: 'day')
4
+
5
+ start_at_format = case
6
+ when options[:step] == 'month' then :date_without_day
7
+ when options[:step] == 'year' then :year
8
+ when start_at.year != end_at.year then :date_long
9
+ when start_at.month != end_at.month then :date_long_without_year
10
+ else :day
11
+ end
12
+
13
+ end_at_format = case options[:step]
14
+ when 'month' then :date_without_day
15
+ when 'year' then :year
16
+ else :date_long
17
+ end
18
+
19
+ content = []
20
+ content << ChartCandy.localize(start_at, format: start_at_format)
21
+ content << (['month', 'year'].include?(options[:step]) ? ChartCandy.t('date.period.to_month') : ChartCandy.t('date.period.to'))
22
+ content << ChartCandy.localize(end_at, format: end_at_format)
23
+
24
+ content[0].capitalize!
25
+
26
+ return content.join(' ')
27
+ end
28
+
29
+ def self.get_step_from_interval(interval)
30
+ days = (interval / (3600 * 24)).to_i.abs
31
+
32
+ return case days
33
+ when 0..5 then 'day'
34
+ when 6..27 then 'week'
35
+ when 28..88 then 'month'
36
+ when 89..363 then 'quarter'
37
+ else 'year'
38
+ end
39
+ end
40
+ end
41
+
42
+ require 'chart-candy/builder/base.rb'
43
+ require 'chart-candy/builder/counter.rb'
44
+ require 'chart-candy/builder/donut.rb'
45
+ require 'chart-candy/builder/line.rb'
46
+ require 'chart-candy/builder/xls_builder.rb'