mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-10-25 00:08:25 -04:00
iCalendar feed of plantings (#3588)
* Register mime type * Add rough first pass * Index predictions, and add links * Trailing space * Fix implementation * Fix names, make public * Fix names, make public * Fix * Fix tyyos * Add date * If there are no predictions, keep growing * Add a todo * Fix factories * Specs
This commit is contained in:
1 parent
254fd156f0
commit
ad4a6e17e2
10 files changed
+99
-7
No files matched your search
@@ -127,6 +127,8 @@ gem 'faraday'
|
||||
|
||||
gem 'rack-cors'
|
||||
|
||||
gem 'icalendar'
|
||||
|
||||
# for signups as requested by email service
|
||||
gem 'recaptcha'
|
||||
|
||||
|
||||
@@ -315,6 +315,9 @@ GEM
|
||||
rails-i18n
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
terminal-table (>= 1.5.1)
|
||||
icalendar (2.10.1)
|
||||
ice_cube (~> 0.16)
|
||||
ice_cube (0.16.4)
|
||||
ice_nine (0.11.2)
|
||||
image_processing (1.12.2)
|
||||
mini_magick (>= 4.9.5, < 5)
|
||||
@@ -702,6 +705,7 @@ DEPENDENCIES
|
||||
haml_lint (>= 0.25.1)
|
||||
hashie (>= 3.5.3)
|
||||
i18n-tasks
|
||||
icalendar
|
||||
jquery-rails
|
||||
jquery-ui-rails!
|
||||
jsonapi-resources
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
class PlantingsController < DataController
|
||||
after_action :update_crop_medians, only: %i(create update destroy)
|
||||
after_action :update_planting_medians, only: :update
|
||||
respond_to :ics, only: [:index] # TODO: This can be shifted up when all relevant controllers respond to ical
|
||||
|
||||
def index
|
||||
@show_all = params[:all] == '1'
|
||||
@@ -29,7 +30,6 @@ class PlantingsController < DataController
|
||||
)
|
||||
|
||||
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_fs(:number)}.csv"
|
||||
|
||||
respond_with(@plantings)
|
||||
end
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
= link_to (@owner ? member_plantings_path(@owner, format: format) : plantings_path(format: format)) do
|
||||
= icon 'fas', format.to_s
|
||||
= format.upcase
|
||||
- if @owner
|
||||
.badge.badge-info= link_to "iCal", member_plantings_path(@owner, format: 'ics', protocol: 'webcal', only_path: false)
|
||||
.badge.badge-success= link_to 'API Methods', '/api-docs'
|
||||
|
||||
.col-md-10
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<%
|
||||
# TODO Refactor to a Planting <-> Ical view class?
|
||||
cal = Icalendar::Calendar.new
|
||||
cal.description = "Plantings by #{@owner.login_name}"
|
||||
@plantings.each do |planting|
|
||||
|
||||
event = Icalendar::Event.new
|
||||
|
||||
lines = []
|
||||
lines << "Quantity: #{planting['quantity'] ? planting['quantity'] : 'unknown' }"
|
||||
lines << "Planted on: #{planting['planted_at'] ? planting['planted_at'] : 'unknown' }"
|
||||
lines << "Sunniness: #{planting['sunniness'] ? planting['sunniness'] : 'unknown' }"
|
||||
lines << "Planted from: #{planting['planted_from'] ? planting['planted_from'] : 'unknown' }"
|
||||
lines << "First harvest from: #{planting['first_harvest_predicted_at'] ? planting['first_harvest_predicted_at'] : 'unknown' }"
|
||||
lines << "Last harvest from: #{planting['last_harvest_predicted_at'] ? planting['last_harvest_predicted_at'] : 'unknown' }"
|
||||
lines << "Finish predicted at: #{planting['finish_predicted_at'] ? planting['finish_predicted_at'] : 'unknown'}"
|
||||
lines << "Finished at: #{planting['finished_at'] ? planting['finished_at'] : 'unknown' }"
|
||||
|
||||
lines << planting.description
|
||||
finish_date = Date.parse(planting['finished_at'] || planting['finish_predicted_at'] || planting['last_harvest_predicted_at']) rescue nil
|
||||
|
||||
event.dtstart = Time.at(planting['created_at'])
|
||||
event.dtend = finish_date || 1.day.from_now
|
||||
event.summary = planting['crop_name']
|
||||
event.description = lines.join("\n")
|
||||
event.ip_class = "PUBLIC"
|
||||
event.url = planting_url(slug: planting['slug'])
|
||||
|
||||
cal.add_event(event)
|
||||
|
||||
if finish_date && finish_date > Date.today
|
||||
todo = Icalendar::Todo.new
|
||||
todo.dtstart = planting['first_harvest_predicted_at'] || finish_date || Date.today
|
||||
todo.due = finish_date
|
||||
todo.summary = "Harvest #{planting['crop_name']}"
|
||||
|
||||
cal.add_todo(todo)
|
||||
end
|
||||
end
|
||||
cal.publish
|
||||
%>
|
||||
<%= cal.to_ical %>
|
||||
@@ -3,3 +3,4 @@
|
||||
|
||||
# Add new mime types for use in respond_to blocks:
|
||||
# Mime::Type.register "text/richtext", :rtf
|
||||
Mime::Type.register "text/calendar", :ics
|
||||
@@ -49,7 +49,7 @@ FactoryBot.define do
|
||||
crop { FactoryBot.create(:perennial_crop) }
|
||||
end
|
||||
|
||||
factory :predicatable_planting do
|
||||
factory :predictable_planting do
|
||||
crop do
|
||||
crop = FactoryBot.create(:annual_crop)
|
||||
FactoryBot.create(:planting, crop:, planted_at: 10.days.ago)
|
||||
|
||||
@@ -30,8 +30,8 @@ describe "Planting reminder email", :js do
|
||||
context "when member has some plantings" do
|
||||
# Bangs are used on the following 2 let blocks in order to ensure that the plantings are present
|
||||
# in the database before the email is generated: otherwise, they won't be present in the email.
|
||||
let!(:p1) { FactoryBot.create(:predicatable_planting, planted_at: 10.days.ago, garden: member.gardens.first, owner: member) }
|
||||
let!(:p2) { FactoryBot.create(:predicatable_planting, planted_at: 30.days.ago, garden: member.gardens.first, owner: member) }
|
||||
let!(:p1) { FactoryBot.create(:predictable_planting, planted_at: 10.days.ago, garden: member.gardens.first, owner: member) }
|
||||
let!(:p2) { FactoryBot.create(:predictable_planting, planted_at: 30.days.ago, garden: member.gardens.first, owner: member) }
|
||||
|
||||
describe "lists plantings" do
|
||||
it { expect(mail).to have_content "Progress report" }
|
||||
@@ -50,8 +50,8 @@ describe "Planting reminder email", :js do
|
||||
context "when member has some harvests" do
|
||||
# Bangs are used on the following 2 let blocks in order to ensure that the plantings are present
|
||||
# in the database before the spec is run.
|
||||
let!(:p1) { FactoryBot.create(:predicatable_planting, garden: member.gardens.first, owner: member, planted_at: 20.days.ago) }
|
||||
let!(:p2) { FactoryBot.create(:predicatable_planting, garden: member.gardens.first, owner: member) }
|
||||
let!(:p1) { FactoryBot.create(:predictable_planting, garden: member.gardens.first, owner: member, planted_at: 20.days.ago) }
|
||||
let!(:p2) { FactoryBot.create(:predictable_planting, garden: member.gardens.first, owner: member) }
|
||||
let!(:h1) { FactoryBot.create(:harvest, owner: member, planting: p1, harvested_at: 1.day.ago) }
|
||||
let!(:h2) { FactoryBot.create(:harvest, owner: member, planting: p2, harvested_at: 3.days.ago) }
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ describe "Display a planting", :js do
|
||||
end
|
||||
|
||||
context 'Annual with predicted finish' do
|
||||
let(:planting) { FactoryBot.create(:predicatable_planting, planted_at: 2.weeks.ago) }
|
||||
let(:planting) { FactoryBot.create(:predictable_planting, planted_at: 2.weeks.ago) }
|
||||
|
||||
it { expect(page).to have_text '28%' }
|
||||
it { expect(page).to have_text '14/50 days' }
|
||||
|
||||
@@ -10,4 +10,45 @@ describe "Plantings" do
|
||||
response.status.should be(200)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a member" do
|
||||
before do
|
||||
@member = create(:interesting_member)
|
||||
@predictable_planting = create(:predictable_planting, owner: @member)
|
||||
@seedling_planting = create(:seedling_planting, owner: @member)
|
||||
@seed_planting = create(:seed_planting, owner: @member)
|
||||
@finished_planting = create(:finished_planting, owner: @member)
|
||||
@annual_planting = create(:annual_planting, owner: @member)
|
||||
@perennial_planting = create(:perennial_planting, owner: @member)
|
||||
|
||||
Planting.reindex
|
||||
end
|
||||
|
||||
describe "GET /members/x/plantings.ics" do
|
||||
it "works!" do
|
||||
get member_plantings_path(@member, format: "ics")
|
||||
|
||||
calendar = Icalendar::Parser.new(response.body, true).parse.first
|
||||
expect(calendar.description[0].to_s).to eq "Plantings by #{@member.login_name}"
|
||||
events = calendar.events
|
||||
expect(events.length).to eq 6 # There are 7, but finished plantings aren't included
|
||||
|
||||
# TODO: Better date comparison
|
||||
# Predicted finish should be used
|
||||
expect(events[1].summary.to_s).to include @predictable_planting.crop.name
|
||||
expect(events[1].dtstart.to_datetime.to_i).to be_within(1.second).of @predictable_planting.created_at.to_i
|
||||
expect(events[1].dtend.to_date).to eq @predictable_planting.finish_predicted_at
|
||||
|
||||
# Actual finish should be used
|
||||
# expect(events[4].dtend.to_date).to be_within(1.second).of @finised_planting.finished_at
|
||||
|
||||
# Otherwise, tomorrow should be used
|
||||
expect(events[2].dtend.to_date).to eq 1.day.from_now.to_date
|
||||
|
||||
# TBA: Perennial and annual crops predictions of 'next' harvest date don't really fit
|
||||
|
||||
response.status.should be(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user