Compare commits

...

52 Commits

Author SHA1 Message Date
Daniel O'Connor
75807ba2af Merge branch 'dev' of https://github.com/Growstuff/growstuff into datepicker 2025-09-01 13:22:47 +00:00
Daniel O'Connor
b69d1bd14b Merge pull request #4189 from Growstuff/remove-openfarm-service
Remove openfarm service
2025-09-01 22:34:52 +09:30
Daniel O'Connor
468e34a551 Remove openfarm service 2025-09-01 12:56:22 +00:00
Daniel O'Connor
8385beb406 Merge pull request #4188 from Growstuff/remove-dead-gems
Remove haml-lint-extractor
2025-09-01 21:55:26 +09:30
google-labs-jules[bot]
0f4803392d Add seed source to Seed model (#4186)
* Add seed source to Seed model

* Update _form.html.haml

* Add to schema

* Default option

* Default option

* Fix test

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: Daniel O'Connor <daniel.oconnor@gmail.com>
2025-09-01 21:47:31 +09:30
Daniel O'Connor
d185ce495f Remove haml-lint-extractor 2025-09-01 12:13:08 +00:00
Daniel O'Connor
90bd70603b Add a lot of indexes (#4187) 2025-09-01 21:06:58 +09:30
Daniel O'Connor
a4db05c0f6 Add a lot of indexes 2025-09-01 11:25:02 +00:00
google-labs-jules[bot]
0079513b35 Merge pull request #4183 from Growstuff/feature/timeline-likes
Feature: Display likes on timeline
2025-09-01 19:51:24 +09:30
Daniel O'Connor
62406a6573 Merge branch 'dev' into datepicker 2025-03-29 14:24:22 +10:30
Daniel O'Connor
53e90f57dd Merge branch 'dev' into datepicker 2024-10-14 00:41:45 +10:30
Daniel O'Connor
54acc2c108 Merge branch 'dev' into datepicker 2024-10-14 00:10:34 +10:30
Daniel O'Connor
0639cd5ab8 Merge branch 'dev' into datepicker 2024-10-13 23:54:46 +10:30
Daniel O'Connor
41c4ae1448 Merge branch 'dev' into datepicker 2024-10-13 23:51:09 +10:30
Daniel O'Connor
99221d7cf3 Merge branch 'dev' into datepicker 2024-10-13 23:40:14 +10:30
Daniel O'Connor
eeb8c84c31 Merge branch 'dev' into datepicker 2024-10-13 23:30:36 +10:30
Daniel O'Connor
e65dd4ef85 Merge branch 'dev' into datepicker 2024-10-13 23:05:10 +10:30
Daniel O'Connor
40ed3c8007 Merge branch 'dev' into datepicker 2024-10-13 22:54:03 +10:30
Daniel O'Connor
035decb132 Update spec/features/plantings/planting_a_crop_spec.rb 2024-10-13 22:53:17 +10:30
Daniel O'Connor
7102552bfe Update spec/features/seeds/adding_seeds_spec.rb 2024-10-13 22:52:39 +10:30
Daniel O'Connor
e1270512dc Update app/views/seeds/_form.html.haml 2024-10-13 22:52:05 +10:30
Daniel O'Connor
8d40355355 Update app/views/harvests/_form.html.haml 2024-10-13 22:51:04 +10:30
Daniel O'Connor
919c3dbf37 Merge branch 'dev' into datepicker 2024-10-13 22:47:40 +10:30
Daniel O'Connor
3e7a58fcfc Merge branch 'dev' into datepicker 2024-10-13 22:38:28 +10:30
Daniel O'Connor
fc301558e1 Merge branch 'dev' into datepicker 2024-10-13 22:21:39 +10:30
Daniel O'Connor
2523bea154 Merge branch 'dev' into datepicker 2024-10-13 22:11:42 +10:30
Daniel O'Connor
f89d64ac3a Mark spec pending 2024-10-13 10:28:29 +00:00
Daniel O'Connor
65a0540e3d Mark spec pending 2024-10-13 10:19:51 +00:00
Daniel O'Connor
654aa318c4 Fix specs 2024-10-13 10:05:52 +00:00
Daniel O'Connor
5128c8be0e Fix specs 2024-10-13 09:55:20 +00:00
Daniel O'Connor
0e1578aeef Fix specs 2024-10-13 09:45:10 +00:00
Daniel O'Connor
c6b5cc61da Fix specs 2024-10-13 09:30:36 +00:00
Daniel O'Connor
dddc85c338 Fix specs 2024-10-13 09:19:10 +00:00
Daniel O'Connor
69e764dd63 Fix specs 2024-10-13 09:01:29 +00:00
Daniel O'Connor
8aa0f98196 Fix specs 2024-10-13 09:00:00 +00:00
Daniel O'Connor
4c8c54eadd Fix specs 2024-10-13 08:48:42 +00:00
Daniel O'Connor
9c6797e850 Swap to html5 control 2024-10-13 08:17:40 +00:00
Daniel O'Connor
a5f774f043 Spec no longer possible 2024-10-13 03:59:12 +00:00
Daniel O'Connor
fe0d4295be Spec no longer possible 2024-10-13 03:57:53 +00:00
Daniel O'Connor
c5bdac4a5c Merge branch 'dev' of https://github.com/Growstuff/growstuff into datepicker 2024-10-13 03:56:30 +00:00
Daniel O'Connor
195602288c Fix test 2024-10-13 03:29:29 +00:00
Daniel O'Connor
c78a347002 Mark required 2024-10-13 02:59:41 +00:00
Daniel O'Connor
5178e1257e Merge branch 'dev' into datepicker 2024-10-13 13:16:22 +10:30
Daniel O'Connor
03f8acdd1d Add placehikder 2024-10-13 02:41:22 +00:00
Daniel O'Connor
426cb3ed37 Remove redundant UI element 2024-10-13 02:36:16 +00:00
Daniel O'Connor
4716b33d82 Mark required 2024-10-13 02:33:35 +00:00
Daniel O'Connor
329c3ddddd Mark required 2024-10-13 02:31:59 +00:00
Daniel O'Connor
3405d224b4 Add input validations 2024-10-13 02:29:17 +00:00
Daniel O'Connor
bd858a0b23 Mark required 2024-10-13 02:26:27 +00:00
Daniel O'Connor
defc3def4f Allow autosuggests to be required 2024-10-13 02:26:16 +00:00
Daniel O'Connor
4b0e228525 Swap to native datepicker 2024-10-13 02:24:20 +00:00
Daniel O'Connor
0f9e151c15 Swap to native datepicker 2024-10-13 02:22:49 +00:00
26 changed files with 204 additions and 152 deletions

View File

@@ -178,7 +178,6 @@ group :development, :test do
gem 'dotenv-rails'
# cli utils
gem 'haml-i18n-extractor', require: false
gem 'haml_lint', '>= 0.25.1', require: false # Checks haml files for goodness
gem 'i18n-tasks', require: false # adds tests for finding missing and unused translations
gem 'rspectre', require: false # finds unused code in specs

View File

@@ -294,12 +294,6 @@ GEM
temple (>= 0.8.2)
thor
tilt
haml-i18n-extractor (0.5.9)
activesupport
haml
highline
tilt
trollop (= 1.16.2)
haml-rails (2.1.0)
actionpack (>= 5.1)
activesupport (>= 5.1)
@@ -701,7 +695,6 @@ GEM
tilt (2.6.1)
timecop (0.9.10)
timeout (0.4.3)
trollop (1.16.2)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (3.1.5)
@@ -788,7 +781,6 @@ DEPENDENCIES
gibbon (~> 1.2.0)
gravatar-ultimate
haml
haml-i18n-extractor
haml-rails
haml_lint (>= 0.25.1)
hashie (>= 3.5.3)

View File

@@ -43,6 +43,7 @@ class SeedsController < DataController
def new
@seed = Seed.new
@seed.source = 'my own seed saving'
if params[:planting_slug]
@planting = Planting.find_by(slug: params[:planting_slug])
@@ -56,6 +57,7 @@ class SeedsController < DataController
def create
@seed = Seed.new(seed_params)
@seed.source ||= 'my own seed saving'
@seed.finished ||= false
@seed.owner = current_member
@seed.crop = @seed.parent_planting.crop if @seed.parent_planting
@@ -84,7 +86,7 @@ class SeedsController < DataController
:crop_id, :description, :quantity, :plant_before,
:parent_planting_id, :saved_at,
:days_until_maturity_min, :days_until_maturity_max,
:organic, :gmo,
:organic, :gmo, :source,
:heirloom, :tradable_to, :slug,
:finished, :finished_at
)

View File

@@ -7,6 +7,8 @@ module EventHelper
def event_description(event)
render "#{event.event_type.pluralize}/description", event_model: resolve_model(event)
rescue ActionView::MissingTemplate
"#{event.event_type.humanize.downcase}d"
end
def resolve_model(event)

View File

@@ -12,6 +12,8 @@ class Seed < ApplicationRecord
ORGANIC_VALUES = ['certified organic', 'non-certified organic', 'conventional/non-organic', 'unknown'].freeze
GMO_VALUES = ['certified GMO-free', 'non-certified GMO-free', 'GMO', 'unknown'].freeze
HEIRLOOM_VALUES = %w(heirloom hybrid unknown).freeze
SOURCE_VALUES = ['seed catalogue', 'retail outlet', 'seed bank or similar institution',
'traded from another person', 'my own seed saving', 'other/unknown'].freeze
#
# Relationships
@@ -44,6 +46,9 @@ class Seed < ApplicationRecord
validates :heirloom, allow_blank: false,
inclusion: { in: HEIRLOOM_VALUES, message: "You must say whether the seeds" \
"are heirloom, hybrid, or unknown" }
validates :source, allow_blank: true,
inclusion: { in: SOURCE_VALUES, message: "You must say where the seeds are from," \
"or that you don't know" }
#
# Delegations

View File

@@ -1,108 +0,0 @@
# frozen_string_literal: true
BASE = 'https://openfarm.cc/api/v1/'
# BASE = 'http://127.0.0.1:3000/api/v1/'
class OpenfarmService
def initialize
@cropbot = Member.find_by(login_name: 'cropbot')
end
def import!
Crop.all.order(updated_at: :desc).each do |crop|
Rails.logger.debug { "#{crop.id}, #{crop.name}" }
update_crop(crop) if crop.valid?
end
end
def update_crop(crop)
openfarm_record = fetch(crop.name)
if openfarm_record.present? && openfarm_record.is_a?(String)
Rails.logger.info(openfarm_record)
elsif openfarm_record.present? && openfarm_record.fetch('data', false)
crop.update! openfarm_data: openfarm_record.fetch('data', false)
save_companions(crop, openfarm_record)
save_photos(crop)
else
Rails.logger.debug "\tcrop not found on Open Farm"
crop.update!(openfarm_data: false)
end
end
def save_companions(crop, openfarm_record)
companions = openfarm_record.fetch('data').fetch('relationships').fetch('companions').fetch('data')
crops = openfarm_record.fetch('included', []).select { |rec| rec["type"] == 'crops' }
CropCompanion.transaction do
companions.each do |com|
companion_crop_hash = crops.detect { |c| c.fetch('id') == com.fetch('id') }
companion_crop_name = companion_crop_hash.fetch('attributes').fetch('name').downcase
companion_crop = Crop.where('lower(name) = ?', companion_crop_name).first
companion_crop = Crop.create!(name: companion_crop_name, requester: @cropbot, approval_status: "pending") if companion_crop.nil?
crop.companions << companion_crop unless crop.companions.where(id: companion_crop.id).any?
end
end
end
def save_photos(crop)
pictures = fetch_pictures(crop.name)
pictures.each do |picture|
data = picture.fetch('attributes')
Rails.logger.debug(data)
next unless data.fetch('image_url').start_with? 'http'
next if Photo.find_by(source_id: picture.fetch('id'), source: 'openfarm')
photo = Photo.new(
source_id: picture.fetch('id'),
source: 'openfarm',
owner: @cropbot,
thumbnail_url: data.fetch('thumbnail_url'),
fullsize_url: data.fetch('image_url'),
title: 'Open Farm photo',
license_name: 'No rights reserved',
link_url: "https://openfarm.cc/en/crops/#{name_to_slug(crop.name)}"
)
if photo.valid?
Photo.transaction do
photo.save
PhotoAssociation.find_or_create_by! photo:, photographable: crop
end
Rails.logger.debug { "\t saved photo #{photo.id} #{photo.source_id}" }
else
Rails.logger.warn "Photo not valid"
end
end
end
def fetch(name)
conn.get("crops/#{name_to_slug(name)}.json").body
rescue NoMethodError
Rails.logger.debug "error fetching crop"
Rails.logger.debug "BODY: "
Rails.logger.debug body
end
def name_to_slug(name)
CGI.escape(name.gsub(' ', '-').downcase)
end
def fetch_all(page)
conn.get("crops.json?page=#{page}").body.fetch('data', {})
end
def fetch_pictures(name)
body = conn.get("crops/#{name_to_slug(name)}/pictures.json").body
body.fetch('data', false)
rescue StandardError
Rails.logger.debug "Error fetching photos"
Rails.logger.debug []
end
private
def conn
Faraday.new BASE do |conn|
conn.response :json, content_type: /\bjson$/
conn.adapter Faraday.default_adapter
end
end
end

View File

@@ -18,10 +18,20 @@ class TimelineService
.union_all(photos_query)
.union_all(seeds_query)
.union_all(activities_query)
.union_all(likes_query)
.where.not(event_at: nil)
.order(event_at: :desc)
end
def self.likes_query
Like
.select("likes.id",
"'like' as event_type",
"likes.created_at as event_at",
"likes.member_id as owner_id",
"null as crop_id")
end
def self.activities_query
Activity.select(
:id,

View File

@@ -39,9 +39,7 @@
= link_to "Add a planting.", new_planting_path
.col-md-4
= f.date_field :due_date,
value: @activity.due_date ? @activity.due_date.to_fs(:ymd) : '',
label: 'When?'
= f.date_field :due_date, value: @activity.due_date ? @activity.due_date.to_fs(:ymd) : '', label: 'When?'
%hr

View File

@@ -30,7 +30,7 @@
= link_to "Request new crops.", new_crop_path
.col-md-4
= f.date_field :harvested_at, value: @harvest.harvested_at ? @harvest.harvested_at.to_fs(:ymd) : '', label: 'When?'
= f.date_field :harvested_at, value: @harvest.harvested_at ? @harvest.harvested_at.to_fs(:ymd) : '', label: 'When?', required: true
.col-12
= f.form_group :plant_part_id, label: { text: "Harvested Plant Part" } do
.row

View File

@@ -0,0 +1 @@
#{link_to event_model.member, event_model.member} liked #{link_to event_model.likeable.class.name.downcase, event_model.likeable}

View File

@@ -32,7 +32,7 @@
label: 'Where did you plant it?')
= link_to "Add a garden.", new_garden_path
.col-md-4
= f.text_field :planted_at,
= f.date_field :planted_at,
value: @planting.planted_at ? @planting.planted_at.to_fs(:ymd) : '',
class: 'add-datepicker', label: 'When?', title: "Plan out your future plantings by forward dating, and subscribe to your iCalendar feed for reminders to plant"
@@ -50,9 +50,8 @@
= f.check_box :finished, label: t('buttons.mark_as_finished'), title: t('.finish_helper')
.col-md-6
= f.text_field :finished_at,
= f.date_field :finished_at,
value: @planting.finished_at ? @planting.finished_at.to_fs(:ymd) : '',
class: 'add-datepicker',
label: 'Finished date',
placeholder: 'optional'
.row

View File

@@ -28,38 +28,40 @@
= link_to "Request new crops.", new_crop_path
.row
.col-12.col-md-4
= f.text_field :saved_at,
= f.date_field :saved_at,
value: @seed.saved_at ? @seed.saved_at.to_fs(:ymd) : '',
class: 'add-datepicker', label: 'When were the seeds harvested/saved?'
label: 'When were the seeds harvested/saved?'
.col-12.col-md-4= f.number_field :quantity, label: 'Quantity', min: 1
.col-12.col-md-4
= f.text_field :plant_before, class: 'add-datepicker',
value: @seed.plant_before ? @seed.plant_before.to_fs(:ymd) : ''
= f.date_field :plant_before, value: @seed.plant_before ? @seed.plant_before.to_fs(:ymd) : ''
.row
.col-12.col-md-4
= f.check_box :finished, label: t('buttons.mark_as_finished')
.col-12.col-md-4
= f.text_field :finished_at, class: 'add-datepicker', value: @seed.finished_at ? @seed.finished_at.to_fs(:ymd) : ''
= f.date_field :finished_at, value: @seed.finished_at ? @seed.finished_at.to_fs(:ymd) : ''
.col-12.col-md-4
%span.help-inline= t('.finish_helper')
.row
-# TODO: Range control?
.col-md-6= f.number_field :days_until_maturity_min, label_as_placeholder: true, label: 'min', prepend: 'Days until maturity', min: 1
.col-md-6= f.number_field :days_until_maturity_max, label_as_placeholder: true, label: 'max', prepend: 'to', append: "days", min: 1
.row
.col-md-4
= f.select(:organic, Seed::ORGANIC_VALUES, {label: 'Organic?', wrapper: { class: 'required'}, required: true}, default: 'unknown')
.col-md-4
= f.select(:gmo, Seed::GMO_VALUES, {label: 'GMO?', wrapper: { class: 'required'}, required: true}, default: 'unknown')
.col-md-4
= f.select(:heirloom, Seed::HEIRLOOM_VALUES, {label: 'Heirloom?', wrapper: { class: 'required'}, required: true}, default: 'unknown')
.col-md-3
= f.select(:organic, Seed::ORGANIC_VALUES, { label: 'Organic?', wrapper: { class: 'required' }, required: true }, default: 'unknown')
.col-md-3
= f.select(:gmo, Seed::GMO_VALUES, { label: 'GMO?', wrapper: { class: 'required' }, required: true }, default: 'unknown')
.col-md-3
= f.select(:heirloom, Seed::HEIRLOOM_VALUES, { label: 'Heirloom?', wrapper: { class: 'required' }, required: true }, default: 'unknown')
.col-md-3
= f.select(:source, Seed::SOURCE_VALUES, { label: 'Source?', wrapper: { class: 'required' }, required: true }, default: 'unknown')
= f.text_area :description, rows: 6
%hr/
= t('.trade_help', site_name: ENV['GROWSTUFF_SITE_NAME'])
= f.select(:tradable_to, Seed::TRADABLE_TO_VALUES, {label: 'Will trade', wrapper: { class: 'required'}, required: true})
= f.select(:tradable_to, Seed::TRADABLE_TO_VALUES, { label: 'Will trade', wrapper: { class: 'required' }, required: true })
%span.help_inline
- if current_member.location.blank?
Don't forget to

View File

@@ -5,6 +5,5 @@
= edit_icon
.hide{id: "date--#{model.id}-#{field.to_s}"}
= bootstrap_form_for(model) do |f|
= f.date_field field,
value: model.send(field) ? model.send(field).to_fs(:ymd) : '', label: 'When?'
= f.date_field field, value: model.send(field) ? model.send(field).to_fs(:ymd) : '', label: 'When?'
= f.submit :save

View File

@@ -12,6 +12,5 @@
- elsif field_type == :select
= f.select field, collection
- elsif field_type == :date
= f.date_field field,
value: model.send(field) ? model.send(field).to_fs(:ymd) : '', label: 'When?'
= f.date_field field, value: model.send(field) ? model.send(field).to_fs(:ymd) : '', label: 'When?'
= f.submit :save

View File

@@ -0,0 +1,2 @@
- likeable = like.likeable
= render "timeline/likeables/#{likeable.class.name.downcase}", likeable: likeable

View File

@@ -14,6 +14,7 @@
= link_to owner, owner
= event_description(event)
= render 'timeline/photos', photo: resolve_model(event) if event.event_type == 'photo'
= render 'timeline/like', like: resolve_model(event) if event.event_type == 'like'
%small
- if event.event_at.present?
- if event.event_at.kind_of?(Date)

View File

@@ -0,0 +1 @@
= render 'timeline/photos', photo: likeable

View File

@@ -0,0 +1,6 @@
.card.my-2
.card-body
%blockquote.blockquote.mb-0
%p= truncate(likeable.body, length: 140)
%footer.blockquote-footer
= link_to "view post", likeable

View File

@@ -0,0 +1,6 @@
class AddSourceToSeeds < ActiveRecord::Migration[7.2]
def change
add_column :seeds, :source, :string
add_index :seeds, :source
end
end

View File

@@ -0,0 +1,53 @@
class AddIndexesCrops < ActiveRecord::Migration[7.2]
def change
add_index :alternate_names, :crop_id
add_index :alternate_names, :creator_id
add_index :alternate_names, :language
add_index :comments, %i(commentable_type commentable_id)
add_index :comments, :author_id
add_index :crop_companions, %i(crop_a_id crop_b_id)
add_index :crops, :creator_id
add_index :crops, :parent_id
add_index :follows, %i(follower_id followed_id)
add_index :forums, :owner_id
add_index :harvests, :crop_id
add_index :harvests, :owner_id
add_index :harvests, :plant_part_id
add_index :members_roles, %i(member_id role_id)
add_index :notifications, :sender_id
add_index :notifications, :recipient_id
add_index :orders_products, %i(order_id product_id)
add_index :photo_associations, :crop_id # TODO: Is this still in use?
add_index :photos, :owner_id
add_index :photos, :source_id
add_index :photos_plantings, %i(photo_id planting_id)
add_index :plant_parts, :slug, unique: true
add_index :plantings, :crop_id
add_index :plantings, :garden_id
add_index :plantings, :owner_id
add_index :plantings, :parent_seed_id
add_index :posts, :forum_id
add_index :scientific_names, :crop_id
add_index :scientific_names, :creator_id
add_index :seeds, :owner_id
add_index :seeds, :crop_id
add_index :seeds, :parent_planting_id
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -68,6 +68,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "created_at", precision: nil
t.datetime "updated_at", precision: nil
t.string "language"
t.index ["creator_id"], name: "index_alternate_names_on_creator_id"
t.index ["crop_id"], name: "index_alternate_names_on_crop_id"
t.index ["language"], name: "index_alternate_names_on_language"
end
create_table "authentications", id: :serial, force: :cascade do |t|
@@ -209,6 +212,8 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "created_at", precision: nil
t.datetime "updated_at", precision: nil
t.string "commentable_type"
t.index ["author_id"], name: "index_comments_on_author_id"
t.index ["commentable_type", "commentable_id"], name: "index_comments_on_commentable_type_and_commentable_id"
end
create_table "crop_companions", force: :cascade do |t|
@@ -216,6 +221,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.integer "crop_b_id", null: false
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.index ["crop_a_id", "crop_b_id"], name: "index_crop_companions_on_crop_a_id_and_crop_b_id"
end
create_table "crop_posts", id: false, force: :cascade do |t|
@@ -246,7 +252,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.jsonb "openfarm_data"
t.integer "harvests_count", default: 0
t.integer "photo_associations_count", default: 0
t.index ["creator_id"], name: "index_crops_on_creator_id"
t.index ["name"], name: "index_crops_on_name"
t.index ["parent_id"], name: "index_crops_on_parent_id"
t.index ["requester_id"], name: "index_crops_on_requester_id"
t.index ["slug"], name: "index_crops_on_slug", unique: true
end
@@ -256,6 +264,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.integer "followed_id"
t.datetime "created_at", precision: nil
t.datetime "updated_at", precision: nil
t.index ["follower_id", "followed_id"], name: "index_follows_on_follower_id_and_followed_id"
end
create_table "forums", id: :serial, force: :cascade do |t|
@@ -265,6 +274,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "created_at", precision: nil
t.datetime "updated_at", precision: nil
t.string "slug"
t.index ["owner_id"], name: "index_forums_on_owner_id"
t.index ["slug"], name: "index_forums_on_slug", unique: true
end
@@ -328,6 +338,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.float "si_weight"
t.integer "planting_id"
t.integer "likes_count", default: 0
t.index ["crop_id"], name: "index_harvests_on_crop_id"
t.index ["owner_id"], name: "index_harvests_on_owner_id"
t.index ["plant_part_id"], name: "index_harvests_on_plant_part_id"
t.index ["planting_id"], name: "index_harvests_on_planting_id"
end
@@ -464,6 +477,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
create_table "members_roles", id: false, force: :cascade do |t|
t.integer "member_id"
t.integer "role_id"
t.index ["member_id", "role_id"], name: "index_members_roles_on_member_id_and_role_id"
end
create_table "notifications", id: :serial, force: :cascade do |t|
@@ -477,11 +491,14 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "updated_at", precision: nil
t.string "notifiable_type"
t.index ["notifiable_type", "notifiable_id"], name: "index_notifications_on_notifiable_type_and_notifiable_id"
t.index ["recipient_id"], name: "index_notifications_on_recipient_id"
t.index ["sender_id"], name: "index_notifications_on_sender_id"
end
create_table "orders_products", id: false, force: :cascade do |t|
t.integer "order_id"
t.integer "product_id"
t.index ["order_id", "product_id"], name: "index_orders_products_on_order_id_and_product_id"
end
create_table "photo_associations", id: :serial, force: :cascade do |t|
@@ -491,6 +508,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.integer "crop_id"
t.index ["crop_id"], name: "index_photo_associations_on_crop_id"
t.index ["photographable_id", "photographable_type", "photo_id"], name: "items_to_photos_idx", unique: true
t.index ["photographable_id", "photographable_type"], name: "photographable_idx"
end
@@ -511,12 +529,15 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.string "source"
t.integer "comments_count", default: 0
t.index ["fullsize_url"], name: "index_photos_on_fullsize_url", unique: true
t.index ["owner_id"], name: "index_photos_on_owner_id"
t.index ["source_id"], name: "index_photos_on_source_id"
t.index ["thumbnail_url"], name: "index_photos_on_thumbnail_url", unique: true
end
create_table "photos_plantings", id: false, force: :cascade do |t|
t.integer "photo_id"
t.integer "planting_id"
t.index ["photo_id", "planting_id"], name: "index_photos_plantings_on_photo_id_and_planting_id"
end
create_table "photos_seeds", id: false, force: :cascade do |t|
@@ -531,6 +552,17 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "updated_at", precision: nil
t.string "slug"
t.integer "harvests_count", default: 0
t.index ["slug"], name: "index_plant_parts_on_slug", unique: true
end
create_table "planting_problems", force: :cascade do |t|
t.bigint "planting_id"
t.bigint "problem_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["planting_id", "problem_id"], name: "index_planting_problems_on_planting_id_and_problem_id", unique: true
t.index ["planting_id"], name: "index_planting_problems_on_planting_id"
t.index ["problem_id"], name: "index_planting_problems_on_problem_id"
end
create_table "plantings", id: :serial, force: :cascade do |t|
@@ -554,6 +586,10 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.integer "harvests_count", default: 0
t.integer "likes_count", default: 0
t.boolean "failed", default: false, null: false
t.index ["crop_id"], name: "index_plantings_on_crop_id"
t.index ["garden_id"], name: "index_plantings_on_garden_id"
t.index ["owner_id"], name: "index_plantings_on_owner_id"
t.index ["parent_seed_id"], name: "index_plantings_on_parent_seed_id"
t.index ["slug"], name: "index_plantings_on_slug", unique: true
end
@@ -568,9 +604,36 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.integer "likes_count", default: 0
t.integer "comments_count", default: 0
t.index ["created_at", "author_id"], name: "index_posts_on_created_at_and_author_id"
t.index ["forum_id"], name: "index_posts_on_forum_id"
t.index ["slug"], name: "index_posts_on_slug", unique: true
end
create_table "problem_posts", force: :cascade do |t|
t.bigint "problem_id"
t.bigint "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["post_id"], name: "index_problem_posts_on_post_id"
t.index ["problem_id", "post_id"], name: "index_problem_posts_on_problem_id_and_post_id", unique: true
t.index ["problem_id"], name: "index_problem_posts_on_problem_id"
end
create_table "problems", force: :cascade do |t|
t.string "name"
t.string "reason_for_rejection"
t.string "rejection_notes"
t.string "approval_status", default: "pending", null: false
t.bigint "requester_id"
t.bigint "creator_id"
t.string "slug"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["creator_id"], name: "index_problems_on_creator_id"
t.index ["name"], name: "index_problems_on_name"
t.index ["requester_id"], name: "index_problems_on_requester_id"
t.index ["slug"], name: "index_problems_on_slug"
end
create_table "roles", id: :serial, force: :cascade do |t|
t.string "name", null: false
t.text "description"
@@ -590,6 +653,8 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.string "gbif_rank"
t.string "gbif_status"
t.string "wikidata_id"
t.index ["creator_id"], name: "index_scientific_names_on_creator_id"
t.index ["crop_id"], name: "index_scientific_names_on_crop_id"
end
create_table "seeds", id: :serial, force: :cascade do |t|
@@ -611,7 +676,12 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.date "finished_at"
t.integer "parent_planting_id"
t.date "saved_at"
t.string "source"
t.index ["crop_id"], name: "index_seeds_on_crop_id"
t.index ["owner_id"], name: "index_seeds_on_owner_id"
t.index ["parent_planting_id"], name: "index_seeds_on_parent_planting_id"
t.index ["slug"], name: "index_seeds_on_slug", unique: true
t.index ["source"], name: "index_seeds_on_source"
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
@@ -622,6 +692,12 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
add_foreign_key "mailboxer_receipts", "mailboxer_notifications", column: "notification_id", name: "receipts_on_notification_id"
add_foreign_key "photo_associations", "crops"
add_foreign_key "photo_associations", "photos"
add_foreign_key "planting_problems", "plantings"
add_foreign_key "planting_problems", "problems"
add_foreign_key "plantings", "seeds", column: "parent_seed_id", name: "parent_seed", on_delete: :nullify
add_foreign_key "problem_posts", "posts"
add_foreign_key "problem_posts", "problems"
add_foreign_key "problems", "members", column: "creator_id"
add_foreign_key "problems", "members", column: "requester_id"
add_foreign_key "seeds", "plantings", column: "parent_planting_id", name: "parent_planting", on_delete: :nullify
end

View File

@@ -62,9 +62,9 @@ describe "Planting a crop", :js, :search do
before do
visit new_planting_path
@a_past_date = 15.days.ago.strftime("%Y-%m-%d")
@right_now = Time.zone.today.strftime("%Y-%m-%d")
@a_future_date = 1.year.from_now.strftime("%Y-%m-%d")
@a_past_date = 15.days.ago
@right_now = Time.zone.today
@a_future_date = 1.year.from_now
end
it "shows that it is not planted yet" do
@@ -111,7 +111,7 @@ describe "Planting a crop", :js, :search do
fill_in "How many?", with: 42
select "cutting", from: "Planted from"
select "semi-shade", from: "Sun or shade?"
fill_in "When?", with: '2013-03-10'
fill_in "When?", with: Time.new(2013, 3, 10)
fill_in "Tell us more about it", with: "It's rad."
fill_in "Finished date", with: @a_future_date
click_button "Save"
@@ -185,7 +185,7 @@ describe "Planting a crop", :js, :search do
click_link 'Actions'
click_link "Edit"
check "finished"
fill_in "Finished date", with: "2015-06-25"
fill_in "Finished date", with: Time.new(2015, 06, 25)
click_button "Save"
expect(page).to have_content "planting was successfully updated"
expect(page).to have_content "Finished"
@@ -196,9 +196,9 @@ describe "Planting a crop", :js, :search do
select_from_autocomplete "maize"
choose(member.gardens.first.name)
within "form#new_planting" do
fill_in "When?", with: "2014-07-01"
fill_in "When?", with: Time.new(2014, 7, 1)
check "Mark as finished"
fill_in "Finished date", with: "2014-08-30"
fill_in "Finished date", with: Time.new(2014, 8, 30)
uncheck 'Mark as finished'
end
@@ -276,7 +276,7 @@ describe "Planting a crop", :js, :search do
fill_autocomplete "crop", with: "mai"
select_from_autocomplete "maize"
within "form#new_planting" do
fill_in "When", with: "2015-10-15"
fill_in "When", with: Time.new(2015, 10, 15)
fill_in "How many?", with: 42
select "cutting", from: "Planted from"
select "sun", from: "Sun or shade?"

View File

@@ -33,7 +33,7 @@ describe "Seeds", :js, :search do
select_from_autocomplete "maize"
within "form#new_seed" do
fill_in "Quantity", with: 42
fill_in "Plant before", with: "2014-06-15"
fill_in "Plant before", with: TIme.new(2014, 6, 15)
fill_in "min", with: 999
fill_in "max", with: 1999
select "certified organic", from: "Organic?"

View File

@@ -49,6 +49,7 @@ describe "seeds", :js do
click_link 'Edit'
expect(page).to have_current_path edit_seed_path(seed), ignore_query: true
fill_in 'Quantity', with: seed.quantity * 2
select 'traded from another person', from: 'Source'
click_button 'Save'
expect(page).to have_current_path seed_path(seed), ignore_query: true
end

View File

@@ -17,6 +17,10 @@ describe "timeline", :js do
let!(:friend_harvest) { FactoryBot.create(:planting, owner: friend2, planted_at: 3.years.ago) }
let!(:finished_planting) { FactoryBot.create(:finished_planting, owner: friend1) }
let!(:no_planted_at_planting) { FactoryBot.create(:planting, owner: friend2, planted_at: nil) }
let!(:friend_photo) { FactoryBot.create(:photo, owner: friend1) }
let!(:friend_post) { FactoryBot.create(:post, author: friend2) }
let!(:liked_post) { FactoryBot.create(:like, likeable: friend_photo, member: friend2) }
let!(:liked_photo) { FactoryBot.create(:like, likeable: friend_post, member: friend1) }
before do
login_as(member)
@@ -28,6 +32,8 @@ describe "timeline", :js do
it { expect(page).to have_link href: planting_path(friend_harvest) }
it { expect(page).to have_link href: planting_path(finished_planting) }
it { expect(page).to have_no_link href: planting_path(no_planted_at_planting) }
it { expect(page).to have_link href: photo_path(friend_photo) }
it { expect(page).to have_link href: post_path(friend_post) }
end
describe 'shows the friends you follow' do

View File

@@ -22,6 +22,6 @@ describe "plantings/_form" do
end
it "has a free-form text field containing the planting date in ISO format" do
assert_select "input#planting_planted_at[type='text'][value='2013-03-01']"
assert_select "input#planting_planted_at[type='date'][value='2013-03-01']"
end
end