caseflow 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +35 -0
- data/Rakefile +6 -0
- data/app/assets/fonts/merriweather.woff +0 -0
- data/app/assets/fonts/merriweather.woff2 +0 -0
- data/app/assets/fonts/source-sans-pro.woff +0 -0
- data/app/assets/fonts/source-sans-pro.woff2 +0 -0
- data/app/assets/images/icons/icon-check.svg +1 -0
- data/app/assets/javascripts/caseflow_stats.js +23 -0
- data/app/assets/javascripts/stats/dashboard.js +172 -0
- data/app/assets/javascripts/stats/panel_toggle.js +19 -0
- data/app/assets/stylesheets/_fonts.scss +19 -0
- data/app/assets/stylesheets/_stats.scss +54 -0
- data/app/assets/stylesheets/_tabs.scss +79 -0
- data/app/assets/stylesheets/caseflow_main.scss +1135 -0
- data/app/models/caseflow/stats.rb +124 -0
- data/app/services/caseflow/fakes/s3_service.rb +22 -0
- data/app/services/caseflow/s3_service.rb +54 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/caseflow.gemspec +34 -0
- data/lib/caseflow.rb +6 -0
- data/lib/caseflow/engine.rb +5 -0
- data/lib/caseflow/version.rb +3 -0
- metadata +210 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 46d897006bb989bb7eb63c21d4edb1fb24430b81
|
4
|
+
data.tar.gz: 22a7df4b8f00ccb4192e31881c36c459d80d2d4e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1576cc07f39c82e763801ac325a1c7167c1c9c7a01591c99295430d966cadf95042a64dfb7c889627ecbff62943f163f408649e8867fcb8007785fc513644b07
|
7
|
+
data.tar.gz: a45324f9e9735db247cd304b744d1c0b9223cbac4e71430adfbad58ba7dd4053084c0d647db0503a19e029a4af0722079cf1068b85055d6a604bdcc003655fc9
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Caseflow
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/caseflow`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'caseflow'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install caseflow
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/department-of-veterans-affairs/caseflow-commons.
|
data/Rakefile
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg width="60" height="50" class="cf-icon-found" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 50" aria-labelledby="title"><title>found</title><path fill="#2e8540" d="M57 13.3L29.9 41.7 24.8 47c-.7.7-1.6 1.1-2.5 1.1-.9 0-1.9-.4-2.5-1.1l-5.1-5.3L1 27.5c-.7-.7-1-1.7-1-2.7s.4-2 1-2.7l5.1-5.3c.7-.7 1.6-1.1 2.5-1.1.9 0 1.9.4 2.5 1.1l11 11.6L46.8 2.7c.7-.7 1.6-1.1 2.5-1.1.9 0 1.9.4 2.5 1.1L57 8c.7.7 1 1.7 1 2.7 0 1-.4 1.9-1 2.6z"/></svg>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into stats.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require d3
|
14
|
+
//= require moment
|
15
|
+
//= require moment-timezone
|
16
|
+
//= require_tree ./stats
|
17
|
+
|
18
|
+
// Offset data for the Eastern time zone
|
19
|
+
moment.tz.add("America/New_York|EST EDT EWT EPT|50 40 40 40|01010101010101010101010101010101010101010101010102301010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-261t0 1nX0 11B0 1nX0 11B0 1qL0 1a10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 RB0 8x40 iv0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1qN0 WL0 1qN0 11z0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1cN0 1cL0 1cN0 1cL0 s10 1Vz0 LB0 1BX0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 1cN0 1fz0 1a10 1fz0 1cN0 1cL0 1cN0 1cL0 1cN0 1cL0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|21e6")
|
20
|
+
|
21
|
+
$(function() {
|
22
|
+
window.StatsPanelToggle.bind();
|
23
|
+
});
|
@@ -0,0 +1,172 @@
|
|
1
|
+
window.Dashboard = (function(d3, moment) {
|
2
|
+
// private
|
3
|
+
var data, interval, index, maxIndex, dateStrings, charts, values, rates, times, dates, tables, x, y, bars
|
4
|
+
|
5
|
+
function init(opts) {
|
6
|
+
data = opts.data
|
7
|
+
interval = opts.interval
|
8
|
+
|
9
|
+
index = 0
|
10
|
+
maxIndex = data.length - 1
|
11
|
+
dateStrings = data.map(function (d, i) {
|
12
|
+
var date = moment.unix(d.key).tz('America/New_York')
|
13
|
+
var str = ''
|
14
|
+
switch (interval) {
|
15
|
+
case 'hourly': str += date.format('HH:mm') + '–' + date.add(59, 'm').format('HH:mm z'); break
|
16
|
+
case 'daily': str += date.format('MMMM D'); break
|
17
|
+
case 'weekly': str += 'the week of ' + date.format('MMMM D'); break
|
18
|
+
case 'monthly': str += date.format(i < 12 ? 'MMMM' : 'MMMM YYYY'); break
|
19
|
+
default: str += date.format('X')
|
20
|
+
}
|
21
|
+
|
22
|
+
if (i === 0) { str += ' (so far)'}
|
23
|
+
|
24
|
+
return str
|
25
|
+
})
|
26
|
+
|
27
|
+
charts = d3.selectAll('.data-chart')
|
28
|
+
.call(setDataByKey)
|
29
|
+
.append('svg')
|
30
|
+
values = d3.selectAll('.data-value')
|
31
|
+
.call(setDataByKey)
|
32
|
+
rates = d3.selectAll('.data-rate')
|
33
|
+
.call(setDataByKey)
|
34
|
+
times = d3.selectAll('.data-time')
|
35
|
+
.call(setDataByKey)
|
36
|
+
tables = d3.select('.data-table')
|
37
|
+
.call(setDataByKey)
|
38
|
+
dates = d3.selectAll('.data-date')
|
39
|
+
|
40
|
+
x = d3.scaleBand()
|
41
|
+
.domain(d3.range(0, data.length))
|
42
|
+
.paddingInner(0.1)
|
43
|
+
|
44
|
+
y = d3.local()
|
45
|
+
charts.property(y, function (d) {
|
46
|
+
var max
|
47
|
+
|
48
|
+
switch (d.key) {
|
49
|
+
default: max = d3.max(d.values)
|
50
|
+
}
|
51
|
+
|
52
|
+
max = max > 1 ? max : 1
|
53
|
+
|
54
|
+
return d3.scaleLinear()
|
55
|
+
.domain([0, max])
|
56
|
+
})
|
57
|
+
|
58
|
+
charts.append('g').append('title').text('Historical Data')
|
59
|
+
|
60
|
+
bars = charts.selectAll('.bar')
|
61
|
+
.data(function (d) { return d.values })
|
62
|
+
.enter().append('rect')
|
63
|
+
.attr('class', 'bar')
|
64
|
+
.attr('title', function (d, i) { return dateStrings[i] + ': ' + d })
|
65
|
+
|
66
|
+
resize()
|
67
|
+
|
68
|
+
d3.select(window)
|
69
|
+
.on('resize', resize)
|
70
|
+
.on('keydown', function () {
|
71
|
+
switch (d3.event.keyCode || d3.event.detail.keyCode) {
|
72
|
+
case 37: index += 1; d3.event.preventDefault(); break // left
|
73
|
+
case 39: index -= 1; d3.event.preventDefault(); break // right
|
74
|
+
}
|
75
|
+
|
76
|
+
if (index > maxIndex) { index = 0 }
|
77
|
+
else if (index < 0) { index = maxIndex }
|
78
|
+
|
79
|
+
update()
|
80
|
+
})
|
81
|
+
|
82
|
+
charts.on('mousemove', function () {
|
83
|
+
index = maxIndex - Math.max(Math.floor((d3.event.offsetX) / x.step()), 0)
|
84
|
+
update()
|
85
|
+
})
|
86
|
+
}
|
87
|
+
|
88
|
+
function resize() {
|
89
|
+
var testNode = d3.select('.data-chart').node()
|
90
|
+
var width = testNode.offsetWidth - 2
|
91
|
+
var height = testNode.offsetHeight - 2
|
92
|
+
|
93
|
+
x.range([width, 0])
|
94
|
+
|
95
|
+
charts
|
96
|
+
.attr('width', width)
|
97
|
+
.attr('height', height)
|
98
|
+
.each(function () { y.get(this).range([2, height / 2]) })
|
99
|
+
|
100
|
+
bars
|
101
|
+
.attr('x', function (d, i) { return x(i) })
|
102
|
+
.attr('y', function (d) { return height - y.get(this)(d || 0) })
|
103
|
+
.attr('width', x.bandwidth())
|
104
|
+
.attr('height', function (d) { return y.get(this)(d || 0) })
|
105
|
+
|
106
|
+
update()
|
107
|
+
}
|
108
|
+
|
109
|
+
function update() {
|
110
|
+
values.text(function (d) { return d.values[index] })
|
111
|
+
rates.html(function (d) { return rateFormat(d.values[index]) })
|
112
|
+
times.html(function (d) { return timeFormat(d.values[index]) })
|
113
|
+
dates.text('for ' + dateStrings[index])
|
114
|
+
bars.attr('opacity', function (d, i) { return i === index ? 0.67 : 0.33 })
|
115
|
+
|
116
|
+
rows = tables.selectAll('tr')
|
117
|
+
.data(function (d) { return d.values[index] })
|
118
|
+
|
119
|
+
var enterRows = rows.enter().append('tr')
|
120
|
+
enterRows.append('td').attr('class', 'id')
|
121
|
+
enterRows.append('td').attr('class', 'count')
|
122
|
+
|
123
|
+
rows.exit().remove()
|
124
|
+
|
125
|
+
rows = rows.merge(enterRows)
|
126
|
+
|
127
|
+
rows.each(function (d) {
|
128
|
+
var row = d3.select(this);
|
129
|
+
|
130
|
+
row.select('.id').text(d.id)
|
131
|
+
row.select('.count').text(d.count + (d.count === 1 ? ' Download' : ' Downloads'))
|
132
|
+
})
|
133
|
+
}
|
134
|
+
|
135
|
+
function setDataByKey(sel) {
|
136
|
+
return sel.datum(function () {
|
137
|
+
var mapper,
|
138
|
+
key = d3.select(this).attr('data-key'),
|
139
|
+
keys = key.split('/'),
|
140
|
+
isRate = false
|
141
|
+
|
142
|
+
if (keys.length === 2) {
|
143
|
+
isRate = true
|
144
|
+
mapper = function (d) { return d.value[keys[1]] === 0 ? null : d.value[keys[0]] / d.value[keys[1]] }
|
145
|
+
} else {
|
146
|
+
mapper = function (d) { return d.value[key] }
|
147
|
+
}
|
148
|
+
|
149
|
+
return {
|
150
|
+
key: key,
|
151
|
+
isRate: isRate,
|
152
|
+
values: data.map(mapper)
|
153
|
+
}
|
154
|
+
})
|
155
|
+
}
|
156
|
+
|
157
|
+
var format = d3.format('.2f')
|
158
|
+
function rateFormat(n) {
|
159
|
+
return (n === null ? '??' : Math.round(n * 100)) +
|
160
|
+
' <span class="cf-stat-unit">%</span>'
|
161
|
+
}
|
162
|
+
function timeFormat(seconds) {
|
163
|
+
return !seconds ? '?? <span class="cf-stat-unit">sec</span>' :
|
164
|
+
seconds > 60 ? format(seconds / 60) + ' <span class="cf-stat-unit">min</span>' :
|
165
|
+
format(seconds) + ' <span class="cf-stat-unit">sec</span>'
|
166
|
+
}
|
167
|
+
|
168
|
+
// public
|
169
|
+
return {
|
170
|
+
init: init
|
171
|
+
}
|
172
|
+
})(d3, moment)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
window.StatsPanelToggle = (function($) {
|
2
|
+
// private
|
3
|
+
|
4
|
+
function switchActive(e) {
|
5
|
+
var current = $(e.currentTarget).children('.cf-stat-option.active').first();
|
6
|
+
var next = current.next()
|
7
|
+
if (next.length === 0) { next = current.siblings().first(); }
|
8
|
+
|
9
|
+
current.removeClass('active');
|
10
|
+
next.addClass('active');
|
11
|
+
}
|
12
|
+
|
13
|
+
// public
|
14
|
+
return {
|
15
|
+
bind: function() {
|
16
|
+
$('.cf-stat-panel.-toggle').on('click', switchActive);
|
17
|
+
}
|
18
|
+
};
|
19
|
+
})($);
|
@@ -0,0 +1,19 @@
|
|
1
|
+
@font-face {
|
2
|
+
font-family: 'Source Sans Pro';
|
3
|
+
font-style: normal;
|
4
|
+
font-weight: 400;
|
5
|
+
src: local('Source Sans Pro'),
|
6
|
+
local('SourceSansPro-Regular'),
|
7
|
+
asset-url('source-sans-pro.woff2') format('woff2'),
|
8
|
+
asset-url('source-sans-pro.woff') format('woff');
|
9
|
+
}
|
10
|
+
|
11
|
+
@font-face {
|
12
|
+
font-family: 'Merriweather';
|
13
|
+
font-style: normal;
|
14
|
+
font-weight: 400;
|
15
|
+
src: local('Merriweather'),
|
16
|
+
local('Merriweather-Regular'),
|
17
|
+
asset-url('merriweather.woff2') format('woff2'),
|
18
|
+
asset-url('merriweather.woff') format('woff');
|
19
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
.cf-stats {
|
2
|
+
margin-top: 20px;
|
3
|
+
}
|
4
|
+
|
5
|
+
.cf-stats-section {
|
6
|
+
@include outer-container;
|
7
|
+
margin-bottom: 1em;
|
8
|
+
}
|
9
|
+
|
10
|
+
.cf-stats-table {
|
11
|
+
margin-top: 0;
|
12
|
+
}
|
13
|
+
|
14
|
+
.cf-stat-panel {
|
15
|
+
@include span-columns(4);
|
16
|
+
position: relative;
|
17
|
+
border: 1px solid $color-gray-lighter;
|
18
|
+
padding: 1em;
|
19
|
+
padding-bottom: 6em;
|
20
|
+
overflow: hidden;
|
21
|
+
|
22
|
+
&.-toggle .cf-stat-option {
|
23
|
+
display: none;
|
24
|
+
&.active { display: block; }
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
.cf-stat-title {
|
29
|
+
color: $color-gray;
|
30
|
+
font-weight: 400;
|
31
|
+
margin: 0;
|
32
|
+
}
|
33
|
+
|
34
|
+
.cf-stat-figure {
|
35
|
+
font-size: 6rem;
|
36
|
+
font-weight: bold;
|
37
|
+
color: $color-gray;
|
38
|
+
line-height: .9;
|
39
|
+
}
|
40
|
+
|
41
|
+
.cf-stat-unit {
|
42
|
+
font-size: 1.6rem;
|
43
|
+
font-weight: normal;
|
44
|
+
color: $color-gray;
|
45
|
+
margin-left: -0.5em;
|
46
|
+
}
|
47
|
+
|
48
|
+
.data-chart svg {
|
49
|
+
position: absolute;
|
50
|
+
bottom: 0;
|
51
|
+
left: 0;
|
52
|
+
|
53
|
+
* { pointer-events: none; }
|
54
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
.cf-tab-navigation {
|
2
|
+
@include unstyled-list;
|
3
|
+
|
4
|
+
margin-top: 2em;
|
5
|
+
position: relative;
|
6
|
+
width: 100%;
|
7
|
+
border-bottom: 1px solid $color-primary;
|
8
|
+
|
9
|
+
li {
|
10
|
+
display: inline-block;
|
11
|
+
margin-right: -1px;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
.cf-tab {
|
16
|
+
@extend .usa-button-unstyled;
|
17
|
+
|
18
|
+
display: inline-block;
|
19
|
+
margin: 0 -3.5px 0 0;
|
20
|
+
padding: 15px 1.5em;
|
21
|
+
color: $color-gray;
|
22
|
+
background-color: $color-white;
|
23
|
+
border: 1px solid $color-gray-light;
|
24
|
+
border-bottom: 0;
|
25
|
+
border-left-width: 0;
|
26
|
+
font-weight: normal;
|
27
|
+
white-space: nowrap;
|
28
|
+
|
29
|
+
&::before { content: ''; }
|
30
|
+
&::after { margin: 0; }
|
31
|
+
&:first-child { border-left-width: 1px; }
|
32
|
+
|
33
|
+
&:hover {
|
34
|
+
color: $color-gray-dark;
|
35
|
+
background-color: $color-white;
|
36
|
+
text-decoration: none;
|
37
|
+
}
|
38
|
+
|
39
|
+
&:disabled:hover {
|
40
|
+
color: $color-gray;
|
41
|
+
}
|
42
|
+
|
43
|
+
&:focus {
|
44
|
+
outline: -webkit-focus-ring-color auto 5px;
|
45
|
+
|
46
|
+
svg path {
|
47
|
+
fill: $color-primary;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
&.cf-active {
|
52
|
+
position: relative;
|
53
|
+
color: $color-primary;
|
54
|
+
border-color: $color-primary;
|
55
|
+
border-top-width: 4px;
|
56
|
+
border-left-width: 1px;
|
57
|
+
padding-top: 13px;
|
58
|
+
right: 1px;
|
59
|
+
top: 1px;
|
60
|
+
margin-right: -4.5px;
|
61
|
+
|
62
|
+
svg path { fill: $color-primary; }
|
63
|
+
}
|
64
|
+
|
65
|
+
svg {
|
66
|
+
margin-right: 0.25em;
|
67
|
+
margin-left: -0.4em;
|
68
|
+
path { fill: $color-gray; }
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
.cf-tab-content {
|
73
|
+
border: 1px solid $color-gray-lighter;
|
74
|
+
border-top: 0;
|
75
|
+
padding-top: 1px;
|
76
|
+
max-height: 600px;
|
77
|
+
overflow-y: scroll;
|
78
|
+
}
|
79
|
+
|