mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-16 20:49:26 -04:00
Merge branch 'dev' into awesomecode-factorybot/dynamicattributedefinedstatically-7220
This commit is contained in:
3
Gemfile
3
Gemfile
@@ -78,8 +78,7 @@ gem 'omniauth-facebook'
|
||||
gem 'omniauth-flickr', '>= 0.0.15'
|
||||
gem 'omniauth-twitter'
|
||||
|
||||
# For charting data
|
||||
gem 'd3-rails', '~> 3.5' # 4.* produces Error: <spyOn> : could not find an object to spy upon for linear() - see https://travis-ci.org/Growstuff/growstuff/jobs/204461482
|
||||
gem "chartkick"
|
||||
|
||||
# client for Elasticsearch. Elasticsearch is a flexible
|
||||
# and powerful, distributed, real-time search and analytics engine.
|
||||
|
||||
@@ -99,6 +99,7 @@ GEM
|
||||
capybara-screenshot (1.0.18)
|
||||
capybara (>= 1.0, < 3)
|
||||
launchy
|
||||
chartkick (2.2.5)
|
||||
childprocess (0.8.0)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
climate_control (0.2.0)
|
||||
@@ -143,8 +144,6 @@ GEM
|
||||
crass (1.0.3)
|
||||
csv_shaper (1.3.0)
|
||||
activesupport (>= 3.0.0)
|
||||
d3-rails (3.5.17)
|
||||
railties (>= 3.1)
|
||||
dalli (2.7.6)
|
||||
database_cleaner (1.6.2)
|
||||
debug_inspector (0.0.3)
|
||||
@@ -569,12 +568,12 @@ DEPENDENCIES
|
||||
capybara
|
||||
capybara-email
|
||||
capybara-screenshot
|
||||
chartkick
|
||||
codeclimate-test-reporter
|
||||
coffee-rails
|
||||
comfortable_mexican_sofa
|
||||
coveralls
|
||||
csv_shaper
|
||||
d3-rails (~> 3.5)
|
||||
dalli
|
||||
database_cleaner
|
||||
devise
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
||||
// GO AFTER THE REQUIRES BELOW.
|
||||
//
|
||||
// = require Chart.bundle
|
||||
// = require chartkick
|
||||
// = require leaflet
|
||||
// = require leaflet.markercluster
|
||||
// = require js-routes
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
//= require graphs/horizontal_bar_graph
|
||||
|
||||
if (document.getElementById("cropmap") !== null) {
|
||||
mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>";
|
||||
mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token;
|
||||
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
|
||||
cropmap = L.map('cropmap').setView([0.0, -0.0], 2);
|
||||
showCropMap(cropmap);
|
||||
}
|
||||
|
||||
function showCropMap(cropmap) {
|
||||
|
||||
L.tileLayer(mapbox_base_url, {
|
||||
@@ -51,41 +38,20 @@ function showCropMap(cropmap) {
|
||||
cropmap.addLayer(markers);
|
||||
}
|
||||
|
||||
function plantingStats(crop) {
|
||||
var sunniness_counts = { 'empty': 0, 'sun': 0, 'semi-shade': 0, 'shade': 0 };
|
||||
$.each(crop.plantings, function(i, planting) {
|
||||
if (planting.sunniness) {
|
||||
sunniness_counts[planting.sunniness]++;
|
||||
} else {
|
||||
sunniness_counts['Empty']++;
|
||||
}
|
||||
$(document).ready(function() {
|
||||
|
||||
if (document.getElementById("cropmap") !== null) {
|
||||
mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>";
|
||||
mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token;
|
||||
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
|
||||
cropmap = L.map('cropmap').setView([0.0, -0.0], 2);
|
||||
showCropMap(cropmap);
|
||||
}
|
||||
|
||||
$('.btn.toggle.crop-hierarchy').click(function () {
|
||||
$('.toggle.crop-hierarchy').toggleClass('hide');
|
||||
});
|
||||
return [
|
||||
{name: 'Empty', value: sunniness_counts['empty']},
|
||||
{name: 'Sun', value: sunniness_counts['sun']},
|
||||
{name: 'Semi-shade', value: sunniness_counts['semi-shade']},
|
||||
{name: 'Shade', value: sunniness_counts['shade']}
|
||||
];
|
||||
}
|
||||
|
||||
if ($("#sunchart")[0] !== null) {
|
||||
var HorizontalBarGraph = growstuff.HorizontalBarGraph;
|
||||
$.getJSON(location.pathname + '.json', function (crop) {
|
||||
data = {
|
||||
bars: plantingStats(crop),
|
||||
bar_color: 'steelblue',
|
||||
width: {size: 300, scale: 'linear'},
|
||||
height: {size: 100, scale: 'ordinal'},
|
||||
//left is used to shift the bars over so that there is
|
||||
//room for the labels
|
||||
margin: {top: 0, right: 0, bottom: 0, left: 100}
|
||||
};
|
||||
|
||||
var graph = new HorizontalBarGraph(data);
|
||||
graph.render(d3.select($('#sunchart')[0]));
|
||||
});
|
||||
}
|
||||
|
||||
$('.btn.toggle.crop-hierarchy').click(function () {
|
||||
$('.toggle.crop-hierarchy').toggleClass('hide');
|
||||
});
|
||||
});
|
||||
@@ -1,53 +0,0 @@
|
||||
// =require d3
|
||||
// = require graphs/width_scale
|
||||
// = require graphs/height_scale
|
||||
|
||||
/*
|
||||
* This represents bars for a bar graph.
|
||||
* Currently these are used for HorizontalBarGraph.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
|
||||
var growstuff = (window.growstuff = window.growstuff || {});
|
||||
var WidthScale = growstuff.WidthScale;
|
||||
var HeightScale = growstuff.HeightScale;
|
||||
|
||||
/**
|
||||
* data object for bar group
|
||||
* @param {Object} data Graph configuration
|
||||
*/
|
||||
function BarGroup(data) {
|
||||
this._data = data;
|
||||
}
|
||||
BarGroup.prototype.render = function(root) {
|
||||
var data = this._data;
|
||||
var bars = this._data.bars;
|
||||
var widthScale = new WidthScale(data).render();
|
||||
var heightScale = new HeightScale(data).render();
|
||||
|
||||
return root.append('g')
|
||||
.attr('class', 'bar')
|
||||
.selectAll('rect')
|
||||
.data(bars.map(function(bar) {
|
||||
return bar.value;
|
||||
}))
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('y', function(d, i) {
|
||||
return heightScale(i);
|
||||
})
|
||||
.attr('height', heightScale.rangeBand())
|
||||
.attr('fill', data.bar_color)
|
||||
.attr('width', function(d) {
|
||||
return widthScale(d);
|
||||
})
|
||||
.append('title')
|
||||
.text(function(d) {
|
||||
return 'This value is ' + d + '.';
|
||||
});
|
||||
};
|
||||
|
||||
growstuff.BarGroup = BarGroup;
|
||||
}());
|
||||
@@ -1,45 +0,0 @@
|
||||
// =require d3
|
||||
/**
|
||||
* This file draws the labels to the left of each bar.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var growstuff = (window.growstuff = window.growstuff || {});
|
||||
/**
|
||||
* new bar label object
|
||||
* @param {Object} data Graph configuration
|
||||
*/
|
||||
function BarLabelGroup(data) {
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
BarLabelGroup.prototype.render = function(d3) {
|
||||
var bars = this._data.bars;
|
||||
// vvcopy pasta from spike vv -- this is a good candidate for refactor
|
||||
var barHeight = 40;
|
||||
|
||||
return d3.append('g')
|
||||
.attr('class', 'bar-label')
|
||||
.selectAll('text')
|
||||
.data(bars.map(function(bar) {
|
||||
return bar.name;
|
||||
}))
|
||||
.enter()
|
||||
.append('text')
|
||||
.attr('x', -80)
|
||||
.attr('y', function(d, i) {
|
||||
// shrink the margin between each label to give them an even spread with
|
||||
// bars
|
||||
var barLabelSpread = 2/3;
|
||||
// move them downward to line up with bars
|
||||
var barLabelTopEdge = 17;
|
||||
return i * barHeight * (barLabelSpread) + barLabelTopEdge;
|
||||
})
|
||||
.text(function(d) {
|
||||
return d;
|
||||
});
|
||||
};
|
||||
|
||||
growstuff.BarLabelGroup = BarLabelGroup;
|
||||
}());
|
||||
@@ -1,31 +0,0 @@
|
||||
// =require d3
|
||||
|
||||
/**
|
||||
* Height Scale is used to map the number of bars to the display size of
|
||||
* the svg
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var growstuff = (window.growstuff = window.growstuff || {});
|
||||
|
||||
/**
|
||||
* new height scale object
|
||||
* @param {Object} data Graph configuration
|
||||
*/
|
||||
function HeightScale(data) {
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
HeightScale.prototype.render = function() {
|
||||
var data = this._data;
|
||||
var scaleType = data.height.scale;
|
||||
|
||||
return d3.scale[scaleType]()
|
||||
.domain(d3.range(data.bars.length))
|
||||
.rangeRoundBands([0, data.height.size], 0.05, 0);
|
||||
};
|
||||
|
||||
growstuff.HeightScale = HeightScale;
|
||||
}());
|
||||
@@ -1,54 +0,0 @@
|
||||
// = require d3
|
||||
// = require graphs/bar_group
|
||||
// = require graphs/bar_label_group
|
||||
|
||||
/**
|
||||
* Horizontal Bar Graph represents sum total of the graph including all of the parts:
|
||||
* Bars
|
||||
* Bar Labels
|
||||
*
|
||||
* The main dimensions of the graph are rendered here.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var growstuff = (window.growstuff = window.growstuff || {});
|
||||
var BarGroup = growstuff.BarGroup;
|
||||
var BarLabelGroup = growstuff.BarLabelGroup;
|
||||
|
||||
/**
|
||||
* create a new graph object
|
||||
* @param {Object} data Graph configuration
|
||||
*/
|
||||
function HorizontalBarGraph(data) {
|
||||
this._data = data;
|
||||
this._d3 = d3;
|
||||
}
|
||||
|
||||
HorizontalBarGraph.prototype.render = function(root) {
|
||||
var width = this._data.width;
|
||||
var height = this._data.height;
|
||||
|
||||
var barLabelGroup = new BarLabelGroup(this._data);
|
||||
var margin = this._data.margin;
|
||||
|
||||
var barGroup = new BarGroup(this._data);
|
||||
|
||||
var svg = root
|
||||
.append('svg')
|
||||
.attr('width', width.size + margin.left + margin.right)
|
||||
.attr('height', height.size + margin.top + margin.bottom)
|
||||
.append('g')
|
||||
.attr('class', 'bar-graph')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
|
||||
barGroup.render(svg);
|
||||
barLabelGroup.render(svg);
|
||||
|
||||
return svg;
|
||||
};
|
||||
|
||||
growstuff.HorizontalBarGraph = HorizontalBarGraph;
|
||||
}());
|
||||
@@ -1,37 +0,0 @@
|
||||
// =require d3
|
||||
|
||||
/**
|
||||
* Width scale is used to map the value for the length of each bar
|
||||
* to the display size of the svg
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var growstuff = (window.growstuff = window.growstuff || {});
|
||||
|
||||
/**
|
||||
* Object for WidthScale
|
||||
* @param {Object} data Graph configuration
|
||||
*/
|
||||
function WidthScale(data) {
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
WidthScale.prototype.render = function() {
|
||||
var data = this._data;
|
||||
var scaleType = data.width.scale;
|
||||
var axisSize = data.width.size;
|
||||
|
||||
return d3.scale[scaleType]()
|
||||
.domain([0, this.getMaxValue()])
|
||||
.range([0, axisSize]);
|
||||
};
|
||||
|
||||
WidthScale.prototype.getMaxValue = function() {
|
||||
return d3.max(this._data.bars.map(function(bar) {
|
||||
return bar.value;
|
||||
}));
|
||||
};
|
||||
|
||||
growstuff.WidthScale = WidthScale;
|
||||
}());
|
||||
@@ -5,3 +5,4 @@
|
||||
@import 'custom_bootstrap/custom_bootstrap'
|
||||
@import 'overrides'
|
||||
@import 'graphs'
|
||||
@import 'predictions'
|
||||
|
||||
@@ -13,6 +13,7 @@ $blue: #2f4365
|
||||
$red: #8e4d43
|
||||
$orange: #ffa500
|
||||
$yellow: #b2935c
|
||||
$white: #ffffff
|
||||
|
||||
$body-bg: $beige
|
||||
$text-color: $brown
|
||||
|
||||
@@ -3,37 +3,18 @@
|
||||
@import "custom_bootstrap/variables"
|
||||
// this padding needs to be done before the responsive stuff is imported
|
||||
body
|
||||
// modifying this for our promotional banner. can be replaced after if
|
||||
// needed.
|
||||
// padding-top: $navbar-height + 15px
|
||||
padding-top: $navbar-height
|
||||
|
||||
|
||||
//@import "bootstrap/responsive"
|
||||
|
||||
// Font Awesome
|
||||
@import "font-awesome-sprockets"
|
||||
@import "font-awesome"
|
||||
|
||||
// Glyphicons
|
||||
//@import "bootstrap/glyphicons"
|
||||
|
||||
|
||||
.list-inline > li.first
|
||||
padding-left: 0px
|
||||
|
||||
h2
|
||||
font-size: 150%
|
||||
|
||||
|
||||
//#subtitle
|
||||
// color: lighten($brown, 30%)
|
||||
// font-style: italic
|
||||
// font-weight: normal
|
||||
// margin-top: 0px
|
||||
// padding-left: 1em
|
||||
// padding-top: 0px
|
||||
|
||||
h3
|
||||
font-size: 120%
|
||||
|
||||
|
||||
11
app/assets/stylesheets/predictions.sass
Normal file
11
app/assets/stylesheets/predictions.sass
Normal file
@@ -0,0 +1,11 @@
|
||||
.predictions
|
||||
.metric
|
||||
height: 180px
|
||||
border: 1px solid lighten($green, 20%)
|
||||
background: $white
|
||||
margin: 4px
|
||||
strong
|
||||
font-size: 250%
|
||||
font-align: center
|
||||
h3
|
||||
|
||||
@@ -51,11 +51,7 @@ class CropsController < ApplicationController
|
||||
def show
|
||||
@crop = Crop.includes(:scientific_names, plantings: :photos).find(params[:id])
|
||||
@posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json { render json: @crop.to_json(crop_json_fields) }
|
||||
end
|
||||
respond_with(@crop)
|
||||
end
|
||||
|
||||
def new
|
||||
@@ -106,8 +102,30 @@ class CropsController < ApplicationController
|
||||
respond_with @crop
|
||||
end
|
||||
|
||||
def sunniness
|
||||
pie_chart_query 'sunniness'
|
||||
end
|
||||
|
||||
def planted_from
|
||||
pie_chart_query 'planted_from'
|
||||
end
|
||||
|
||||
def harvested_for
|
||||
@crop = Crop.find(params[:crop_id])
|
||||
render json: Harvest.joins(:plant_part).where(crop: @crop)
|
||||
.group("plant_parts.name").count(:id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pie_chart_query(field)
|
||||
@crop = Crop.find(params[:crop_id])
|
||||
render json: Planting.where(crop: @crop)
|
||||
.where.not(field.to_sym => nil)
|
||||
.where.not(field.to_sym => '')
|
||||
.group(field.to_sym).count(:id)
|
||||
end
|
||||
|
||||
def notifier
|
||||
case @crop.approval_status
|
||||
when "approved"
|
||||
|
||||
@@ -64,7 +64,7 @@ class CsvImporter
|
||||
def cropbot
|
||||
@cropbot = Member.find_by!(login_name: 'cropbot') unless @cropbot
|
||||
@cropbot
|
||||
rescue
|
||||
rescue StandardError
|
||||
raise "cropbot account not found: run rake db:seed"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,29 +1,35 @@
|
||||
- unless crop.perennial.nil?
|
||||
%p
|
||||
#{crop.name} is
|
||||
- if crop.perennial == true
|
||||
= link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do
|
||||
a perennial crop
|
||||
(living more than two years)
|
||||
- elsif crop.perennial == false
|
||||
= link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do
|
||||
an annual crop
|
||||
(living and reproducing in a single year or less)
|
||||
.predictions
|
||||
- unless crop.perennial.nil?
|
||||
.row
|
||||
.col-md-12
|
||||
%p
|
||||
#{crop.name} is
|
||||
- if crop.perennial == true
|
||||
= link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do
|
||||
a perennial crop
|
||||
(living more than two years)
|
||||
- elsif crop.perennial == false
|
||||
= link_to 'https://en.wikipedia.org/wiki/Annual_vs._perennial_plant_evolution' do
|
||||
an annual crop
|
||||
(living and reproducing in a single year or less)
|
||||
|
||||
- if crop.annual? && crop.median_lifespan.present?
|
||||
%p
|
||||
Median lifespan of #{crop.name} plants is
|
||||
%strong= crop.median_lifespan
|
||||
days
|
||||
.row
|
||||
- if crop.annual? && crop.median_lifespan.present?
|
||||
.metric.col-md-3.col-xs-5
|
||||
%h3 Median lifespan
|
||||
%strong= crop.median_lifespan
|
||||
%span days
|
||||
|
||||
- if crop.median_days_to_first_harvest.present?
|
||||
%p
|
||||
First harvest expected
|
||||
%strong= crop.median_days_to_first_harvest
|
||||
days after planting
|
||||
- if crop.median_days_to_first_harvest.present?
|
||||
.metric.col-md-3.col-xs-5
|
||||
%h3 First harvest expected
|
||||
%strong= crop.median_days_to_first_harvest
|
||||
%span days
|
||||
after planting
|
||||
|
||||
- if crop.annual? && crop.median_days_to_last_harvest.present?
|
||||
%p
|
||||
Last harvest expected
|
||||
%strong= crop.median_days_to_last_harvest
|
||||
days after planting
|
||||
- if crop.annual? && crop.median_days_to_last_harvest.present?
|
||||
.metric.col-md-3.col-xs-5
|
||||
%h3 Last harvest expected
|
||||
%strong= crop.median_days_to_last_harvest
|
||||
%span days
|
||||
after planting
|
||||
|
||||
@@ -23,37 +23,51 @@
|
||||
|
||||
.row
|
||||
.col-md-9
|
||||
- if member_signed_in?
|
||||
= display_seed_availability(@current_member, @crop)
|
||||
= link_to "View your seeds", seeds_by_owner_path(owner: current_member.slug)
|
||||
.row
|
||||
.col-md-12
|
||||
- if member_signed_in?
|
||||
= display_seed_availability(@current_member, @crop)
|
||||
= link_to "View your seeds", seeds_by_owner_path(owner: current_member.slug)
|
||||
|
||||
%h2 Predictions
|
||||
= render 'predictions', crop: @crop
|
||||
%h2
|
||||
- if !@crop.plantings.empty?
|
||||
= @crop.name.titleize
|
||||
has been planted
|
||||
= pluralize(@crop.plantings.size, "time")
|
||||
by #{ENV['GROWSTUFF_SITE_NAME']} members.
|
||||
- else
|
||||
Nobody is growing this yet. You could be the first!
|
||||
|
||||
%p= render 'crops/photos', crop: @crop
|
||||
|
||||
%h2
|
||||
- if !@crop.plantings.empty?
|
||||
= @crop.name.titleize
|
||||
has been planted
|
||||
= pluralize(@crop.plantings.size, "time")
|
||||
by #{ENV['GROWSTUFF_SITE_NAME']} members.
|
||||
- else
|
||||
Nobody is growing this yet. You could be the first!
|
||||
.row
|
||||
.col-md-12
|
||||
%h2 Predictions
|
||||
= render 'predictions', crop: @crop
|
||||
|
||||
%h2
|
||||
Sunniness Chart
|
||||
.row
|
||||
.col-md-12
|
||||
%h2 Photos
|
||||
%p= render 'crops/photos', crop: @crop
|
||||
.row
|
||||
.col-md-3
|
||||
%h2 Sunniness
|
||||
= pie_chart crop_sunniness_path(@crop), legend: "bottom"
|
||||
|
||||
#sunchart
|
||||
.col-md-3
|
||||
%h2 Planted from
|
||||
= pie_chart crop_planted_from_path(@crop), legend: "bottom"
|
||||
.col-md-3
|
||||
%h2 Harvested for
|
||||
= pie_chart crop_harvested_for_path(@crop), legend: "bottom"
|
||||
|
||||
%h2
|
||||
Crop Map
|
||||
%p
|
||||
Only plantings by members who have set their locations are shown on this map.
|
||||
- if current_member && current_member.location.blank?
|
||||
= link_to "Set your location.", edit_member_registration_path
|
||||
|
||||
#cropmap
|
||||
.row
|
||||
.col-md-12
|
||||
%h2 Crop Map
|
||||
%p
|
||||
Only plantings by members who have set their locations are shown on this map.
|
||||
- if current_member && current_member.location.blank?
|
||||
= link_to "Set your location.", edit_member_registration_path
|
||||
#cropmap
|
||||
|
||||
%a{ name: 'posts' }
|
||||
%h2 What people are saying about #{@crop.name.pluralize}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
!!! 5
|
||||
%html{ lang: "en", prefix: "og: http://ogp.me/ns#" }
|
||||
= javascript_include_tag "application"
|
||||
= render partial: "layouts/meta"
|
||||
%body
|
||||
= render partial: "layouts/header"
|
||||
@@ -26,6 +27,4 @@
|
||||
Javascripts
|
||||
\==================================================
|
||||
/ Placed at the end of the document so the pages load faster
|
||||
= javascript_include_tag "application"
|
||||
|
||||
!= Growstuff::Application.config.analytics_code
|
||||
|
||||
14
config/initializers/assets.rb
Normal file
14
config/initializers/assets.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Version of your assets, change this if you want to expire all your assets.
|
||||
Rails.application.config.assets.version = '1.0'
|
||||
|
||||
# Add additional assets to the asset load path.
|
||||
# Rails.application.config.assets.paths << Emoji.images_path
|
||||
# Add Yarn node_modules folder to the asset load path.
|
||||
Rails.application.config.assets.paths << Rails.root.join('node_modules')
|
||||
|
||||
# Precompile additional assets.
|
||||
# application.js, application.css, and all non-JS/CSS in the app/assets
|
||||
# folder are already added.
|
||||
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
|
||||
@@ -50,6 +50,9 @@ Growstuff::Application.routes.draw do
|
||||
get 'crops/search' => 'crops#search', as: 'crops_search'
|
||||
resources :crops do
|
||||
get 'photos' => 'photos#index'
|
||||
get 'sunniness' => 'crops#sunniness'
|
||||
get 'planted_from' => 'crops#planted_from'
|
||||
get 'harvested_for' => 'crops#harvested_for'
|
||||
end
|
||||
|
||||
resources :comments
|
||||
|
||||
@@ -65,7 +65,7 @@ def load_test_users # rubocop:disable Metrics/AbcSize
|
||||
source_path = Rails.root.join('db', 'seeds')
|
||||
begin
|
||||
suburb_file = File.open("#{source_path}/suburbs.csv")
|
||||
rescue
|
||||
rescue StandardError
|
||||
puts "Warning: unable to open suburbs.csv"
|
||||
end
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ describe PhotoAssociationsController do
|
||||
expect do
|
||||
begin
|
||||
delete :destroy, valid_params
|
||||
rescue
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
end.not_to change(photo.harvests, :count)
|
||||
|
||||
@@ -181,6 +181,7 @@ feature "crop detail page", js: true do
|
||||
# 10 days to harvest
|
||||
FactoryBot.create(:harvest, harvested_at: 190.days.ago, crop: planting.crop,
|
||||
planting: FactoryBot.create(:planting, planted_at: 200.days.ago, crop: crop))
|
||||
planting.crop.update_medians
|
||||
end
|
||||
it "predicts harvest" do
|
||||
is_expected.to have_text("First harvest expected 20 days after planting")
|
||||
@@ -216,7 +217,8 @@ feature "crop detail page", js: true do
|
||||
end
|
||||
|
||||
it "predicts lifespan" do
|
||||
is_expected.to have_text "Median lifespan of #{crop.name} plants is 99 days"
|
||||
is_expected.to have_text "Median lifespan"
|
||||
is_expected.to have_text "99 days"
|
||||
end
|
||||
|
||||
it "describes annual crops" do
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
These tests are for the BarGroup object.
|
||||
*/
|
||||
|
||||
describe('when drawing the group of bars', function() {
|
||||
var BarGroup; var subject; var bars; var data;
|
||||
|
||||
beforeEach(function() {
|
||||
BarGroup = growstuff.BarGroup;
|
||||
|
||||
bars = [
|
||||
{name: 'Shade', value: 0.2},
|
||||
{name: 'Half Shade', value: 0.5},
|
||||
];
|
||||
|
||||
data = {
|
||||
bars: bars,
|
||||
bar_color: 'steelblue',
|
||||
width: {size: 300, scale: 'linear'},
|
||||
height: {size: 400, scale: 'ordinal'},
|
||||
};
|
||||
|
||||
subject = new BarGroup(data);
|
||||
subject.render(d3.select('#jasmine_content').append('svg'));
|
||||
});
|
||||
|
||||
it('draws a group', function() {
|
||||
expect($('g.bar')).toExist();
|
||||
});
|
||||
|
||||
it('draws 2 bars', function() {
|
||||
expect($('g.bar rect')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('fills the bars with color', function() {
|
||||
expect($('g.bar rect')).toHaveAttr('fill', 'steelblue');
|
||||
});
|
||||
|
||||
it('shows a tooltip on hover', function() {
|
||||
var i;
|
||||
|
||||
// get all of the title nodes for the bars
|
||||
var barNodes = $('g.bar rect title');
|
||||
|
||||
for (i = 0; i < bars.length; i++) {
|
||||
expect(barNodes[i].textContent)
|
||||
.toBe('This value is ' + bars[i].value + '' + '.');
|
||||
}
|
||||
});
|
||||
});
|
||||
}());
|
||||
@@ -1,38 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
This file contains tests for the labels that get rendered next to each bar
|
||||
*/
|
||||
|
||||
describe('BarLabelGroup', function() {
|
||||
var BarLabelGroup; var subject; var data;
|
||||
|
||||
beforeEach(function() {
|
||||
BarLabelGroup = growstuff.BarLabelGroup;
|
||||
var bars = [
|
||||
{name: 'Shade', value: 0.2},
|
||||
{name: 'Half Shade', value: 0.5},
|
||||
];
|
||||
data = {
|
||||
bars: bars,
|
||||
};
|
||||
subject = new BarLabelGroup(data);
|
||||
subject.render(d3.select('#jasmine_content').append('svg'));
|
||||
});
|
||||
|
||||
it('draws a group for labels', function() {
|
||||
expect($('g.bar-label')).toExist();
|
||||
});
|
||||
|
||||
it('draws 2 bar labels', function() {
|
||||
expect($('g.bar-label text')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('has text for 2 bar labels', function() {
|
||||
// jquery jasmine appends text from all text elements
|
||||
// into one string
|
||||
expect($('g.bar-label text')).toHaveText('ShadeHalf Shade');
|
||||
});
|
||||
});
|
||||
}());
|
||||
@@ -1,35 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
Tests for mapping the number of bars to the size of the svg
|
||||
*/
|
||||
|
||||
describe('HeightScale when specifying height', function() {
|
||||
var data; var bars; var HeightScale; var subject; var mockD3;
|
||||
|
||||
beforeEach(function() {
|
||||
HeightScale = growstuff.HeightScale;
|
||||
bars = [
|
||||
{name: 'Shade', value: 0.2},
|
||||
{name: 'Half Shade', value: 0.5},
|
||||
];
|
||||
data = {
|
||||
bars: bars,
|
||||
width: {size: 300, scale: 'linear'},
|
||||
height: {size: 400, scale: 'ordinal'},
|
||||
};
|
||||
|
||||
subject = new HeightScale(data);
|
||||
mockD3 = jasmine.createSpyObj('d3', ['domain', 'rangeRoundBands']);
|
||||
mockD3.domain.and.returnValue(mockD3);
|
||||
mockD3.rangeRoundBands.and.returnValue(mockD3);
|
||||
spyOn(d3.scale, 'ordinal').and.returnValue(mockD3);
|
||||
subject.render();
|
||||
});
|
||||
|
||||
it('calls the d3 range round bands function to draw the height', function() {
|
||||
expect(mockD3.rangeRoundBands).toHaveBeenCalledWith([0, 400], 0.05, 0);
|
||||
});
|
||||
});
|
||||
}());
|
||||
@@ -1,74 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
Tests in this file are for the pieces of HorizontalBarGraph or
|
||||
are more integration-y type tests that require the full graph.
|
||||
*/
|
||||
|
||||
describe('HorizontalBarGraph', function() {
|
||||
var BarLabelGroup; var BarGroup; var subject; var data;
|
||||
|
||||
beforeEach(function() {
|
||||
var HorizontalBarGraph = growstuff.HorizontalBarGraph;
|
||||
var bars = [
|
||||
{name: 'Shade', value: 0.2},
|
||||
{name: 'Half Shade', value: 0.5},
|
||||
];
|
||||
data = {
|
||||
bars: bars,
|
||||
bar_color: 'steelblue',
|
||||
width: {size: 300, scale: 'linear'},
|
||||
height: {size: 400, scale: 'ordinal'},
|
||||
// left is used to shift the bars over so that there is
|
||||
// room for the labels
|
||||
margin: {top: 0, right: 0, bottom: 0, left: 100},
|
||||
};
|
||||
|
||||
subject = new HorizontalBarGraph(data);
|
||||
BarGroup = growstuff.BarGroup;
|
||||
BarLabelGroup = growstuff.BarLabelGroup;
|
||||
expect(BarLabelGroup).toExist();
|
||||
spyOn(BarGroup.prototype, 'render').and.callThrough();
|
||||
spyOn(BarLabelGroup.prototype, 'render').and.callThrough();
|
||||
subject.render(d3.select($('#jasmine_content')[0]));
|
||||
});
|
||||
|
||||
it('draws a graph', function() {
|
||||
expect($('#jasmine_content svg')).toExist();
|
||||
});
|
||||
|
||||
it('draws a group for the whole graph', function() {
|
||||
expect($('g.bar-graph')).toExist();
|
||||
});
|
||||
|
||||
it('draws a bar group', function() {
|
||||
expect(BarGroup.prototype.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('draws a group of bar labels', function() {
|
||||
expect(BarLabelGroup.prototype.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('has the expected width and height', function() {
|
||||
var $svg = $('svg');
|
||||
var margin = data.margin;
|
||||
expect($svg).toHaveAttr('width', (data.width.size + margin.left + margin.right) + '');
|
||||
expect($svg).toHaveAttr('height', (data.height.size + margin.top + margin.bottom) + '');
|
||||
});
|
||||
|
||||
it('draws the graph shifted to the right to accommodate for labels', function() {
|
||||
expect('g.bar-graph').toHaveAttr('transform', 'translate(100,0)');
|
||||
});
|
||||
|
||||
it('on the x axis, draws at least one bar at max width less margin width', function() {
|
||||
// because of domain and range mapping
|
||||
expect('g.bar rect:eq(1)').toHaveAttr('width', '300' );
|
||||
});
|
||||
|
||||
it('on the y axis, all bars are the same height', function() {
|
||||
expect('g.bar rect:eq(0)').toHaveAttr('height', '195');
|
||||
expect('g.bar rect:eq(1)').toHaveAttr('height', '195');
|
||||
});
|
||||
});
|
||||
}());
|
||||
@@ -1,40 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
This file contains tests for the mapping the data values to
|
||||
the length of a bar so that it is the correct size for the screen
|
||||
*/
|
||||
|
||||
describe('GraphScale, when specifying width', function() {
|
||||
var data; var WidthScale; var subject; var mockD3;
|
||||
|
||||
beforeEach(function() {
|
||||
WidthScale = growstuff.WidthScale;
|
||||
var bars = [
|
||||
{name: 'Shade', value: 0.2},
|
||||
{name: 'Half Shade', value: 0.5},
|
||||
];
|
||||
data = {
|
||||
bars: bars,
|
||||
width: {size: 300, scale: 'linear'},
|
||||
height: {size: 400, scale: 'ordinal'},
|
||||
};
|
||||
|
||||
subject = new WidthScale(data, 'width');
|
||||
mockD3 = jasmine.createSpyObj('d3', ['domain', 'range', 'max']);
|
||||
mockD3.domain.and.returnValue(mockD3);
|
||||
mockD3.range.and.returnValue(mockD3);
|
||||
spyOn(d3.scale, 'linear').and.returnValue(mockD3);
|
||||
subject.render();
|
||||
});
|
||||
|
||||
it('gets the value of the longest bar', function() {
|
||||
expect(subject.getMaxValue()).toEqual(0.5);
|
||||
});
|
||||
|
||||
it('calls the d3 range function to draw the width', function() {
|
||||
expect(mockD3.range).toHaveBeenCalledWith([0, 300]);
|
||||
});
|
||||
});
|
||||
}());
|
||||
Reference in New Issue
Block a user