mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-25 17:31:18 -04:00
Compare commits
20 Commits
datepicker
...
fix-naming
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1511de8a81 | ||
|
|
8000a51e8b | ||
|
|
b3ba05d834 | ||
|
|
110b18cc9e | ||
|
|
396af468fa | ||
|
|
ac2d998711 | ||
|
|
4564d0afe0 | ||
|
|
a325ada964 | ||
|
|
2b818e9f50 | ||
|
|
9b9de06140 | ||
|
|
2f290efc5b | ||
|
|
23ef0f9cac | ||
|
|
7106b141d9 | ||
|
|
ada567dcab | ||
|
|
d620dc3bfc | ||
|
|
c189e3b01a | ||
|
|
70e6c44d82 | ||
|
|
df15383dd0 | ||
|
|
bcaeaf3688 | ||
|
|
2fc5b63c0e |
@@ -17,7 +17,9 @@ encourage participation from people of all backgrounds and skill levels.
|
||||
## Want to contribute?
|
||||
|
||||
Don't ask to ask, the best way to get started is to fork the project, start a codespace and get hacking.
|
||||
Dive on in and submit your PRs.
|
||||
Dive on in and submit your PRs!
|
||||
|
||||
Vibe Coding is more than okay, just make sure you indicate if you have done so and ensure there are tests.
|
||||
|
||||
## Important links
|
||||
|
||||
@@ -35,6 +37,10 @@ frontend features. We welcome contributions -- see
|
||||
* To set up your development environment, see [Getting started](https://github.com/Growstuff/growstuff/wiki/New-contributor-guide).
|
||||
* You may also be interested in our [API](https://github.com/Growstuff/growstuff/wiki/API).
|
||||
|
||||
### For Home Automation enthusiasts
|
||||
|
||||
https://github.com/Growstuff/homeassistant-growstuff/
|
||||
|
||||
## For designers, writers, researchers, data wranglers, and other contributors
|
||||
|
||||
There are heaps of ways to get involved and contribute no matter what
|
||||
|
||||
@@ -133,7 +133,7 @@ class PlantingsController < DataController
|
||||
:crop_id, :description, :garden_id, :planted_at,
|
||||
:parent_seed_id,
|
||||
:quantity, :sunniness, :planted_from, :finished,
|
||||
:finished_at, :failed
|
||||
:finished_at, :failed, :overall_rating
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ class Harvest < ApplicationRecord
|
||||
|
||||
delegate :name, :slug, to: :crop, prefix: true
|
||||
delegate :login_name, :slug, to: :owner, prefix: true
|
||||
delegate :name, to: :plant_part, prefix: true
|
||||
|
||||
##
|
||||
## Validations
|
||||
@@ -109,7 +108,7 @@ class Harvest < ApplicationRecord
|
||||
def to_s
|
||||
# 50 individual apples, weighing 3lb
|
||||
# 2 buckets of apricots, weighing 10kg
|
||||
"#{quantity_to_human} #{unit_to_human} #{crop_name_to_human} #{weight_to_human}".strip
|
||||
"#{quantity_to_human} #{unit_to_human} #{plant_part_name_to_human} of #{crop_name} #{weight_to_human}".strip
|
||||
end
|
||||
|
||||
def quantity_to_human
|
||||
@@ -132,13 +131,13 @@ class Harvest < ApplicationRecord
|
||||
"weighing #{number_to_human(weight_quantity, strip_insignificant_zeros: true)} #{weight_unit}"
|
||||
end
|
||||
|
||||
def crop_name_to_human
|
||||
def plant_part_name_to_human
|
||||
if unit != 'individual' # buckets of apricot*s*
|
||||
crop.name.pluralize
|
||||
plant_part.name.pluralize
|
||||
elsif quantity == 1
|
||||
crop.name
|
||||
plant_part.name
|
||||
else
|
||||
crop.name.pluralize
|
||||
plant_part.name.pluralize
|
||||
end.to_s
|
||||
end
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ class Photo < ApplicationRecord
|
||||
flickr = owner.flickr
|
||||
info = flickr.photos.getInfo(photo_id: source_id)
|
||||
licenses = flickr.photos.licenses.getInfo
|
||||
license = licenses.find { |l| l.id == info.license }
|
||||
license = licenses.find { |l| l.id.to_i == info.license.to_i }
|
||||
Rails.logger.error("Cannot find license: " + [info.license, licenses].inspect) unless license
|
||||
{
|
||||
title: calculate_title(info),
|
||||
license_name: license.name,
|
||||
|
||||
@@ -11,6 +11,10 @@ class PlantPart < ApplicationRecord
|
||||
|
||||
scope :joins_members, -> { joins("INNER JOIN members ON members.id = harvests.owner_id") }
|
||||
|
||||
def whole_plant?
|
||||
name == 'whole plant'
|
||||
end
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
@@ -83,6 +83,9 @@ class Planting < ApplicationRecord
|
||||
validates :planted_from, allow_blank: true, inclusion: {
|
||||
in: PLANTED_FROM_VALUES, message: "%<value>s is not a valid planting method"
|
||||
}
|
||||
validates :overall_rating, allow_blank: true, numericality: {
|
||||
only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5
|
||||
}
|
||||
|
||||
def planting_slug
|
||||
[
|
||||
|
||||
@@ -5,3 +5,8 @@
|
||||
- @matching_plantings.each do |planting|
|
||||
= f.radio_button :planting_id, planting.id, label: planting
|
||||
= f.submit "save", class: 'btn btn-sm'
|
||||
|
||||
- if @harvest.planting.present? && @harvest.planting.overall_rating.blank?
|
||||
.alert.alert-info{role: "alert"}
|
||||
This harvest is from a planting that hasn't been rated yet.
|
||||
= link_to "Rate this planting", edit_planting_path(@harvest.planting), class: 'alert-link'
|
||||
|
||||
@@ -89,3 +89,9 @@
|
||||
- if planting.finished_at.present?
|
||||
%span.plantingfact--finish
|
||||
= planting.finished_at.year
|
||||
- if planting.overall_rating.present?
|
||||
.card.fact-card
|
||||
.card-body
|
||||
%h3 Overall Rating
|
||||
%p.card-text
|
||||
%strong= "#{planting.overall_rating}/5"
|
||||
@@ -43,6 +43,15 @@
|
||||
= f.select(:sunniness, Planting::SUNNINESS_VALUES, { include_blank: '', label: 'Sun or shade?' } )
|
||||
.col-md-4
|
||||
= f.number_field :quantity, label: 'How many?', min: 1
|
||||
.col-md-12
|
||||
= f.range_field :overall_rating, min: 1, max: 5, include_blank: 'Leave blank', label: 'Overall Rating', list: "rating-list", title: "How well is the planting going?"
|
||||
%datalist{"id": "rating-list"}
|
||||
%option{"value": "1"} Poor
|
||||
%option{"value": "2"}
|
||||
%option{"value": "3"}
|
||||
%option{"value": "4"}
|
||||
%option{"value": "5"} Great
|
||||
|
||||
= f.text_area :description, rows: 6, label: 'Tell us more about it'
|
||||
|
||||
.row
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
= tag("meta", property: "og:type", content: "website")
|
||||
= tag("meta", property: "og:url", content: request.original_url)
|
||||
= tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME'])
|
||||
- if @planting.overall_rating.present?
|
||||
%script{type: "application/ld+json"}
|
||||
:plain
|
||||
{
|
||||
"@context": "http://schema.org",
|
||||
"@type": "Rating",
|
||||
"ratingValue": "#{@planting.overall_rating}",
|
||||
"bestRating": "5"
|
||||
}
|
||||
|
||||
- content_for :breadcrumbs do
|
||||
%li.breadcrumb-item= link_to 'Plantings', plantings_path
|
||||
|
||||
@@ -4,7 +4,6 @@ class AddIndexesCrops < ActiveRecord::Migration[7.2]
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddOverallRatingPlantings < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
add_column :plantings, :overall_rating, :integer
|
||||
end
|
||||
end
|
||||
45
db/schema.rb
45
db/schema.rb
@@ -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_09_01_110545) do
|
||||
ActiveRecord::Schema[7.2].define(version: 2025_09_01_130830) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
||||
@@ -555,16 +555,6 @@ ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) do
|
||||
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|
|
||||
t.integer "garden_id", null: false
|
||||
t.integer "crop_id", null: false
|
||||
@@ -586,6 +576,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) do
|
||||
t.integer "harvests_count", default: 0
|
||||
t.integer "likes_count", default: 0
|
||||
t.boolean "failed", default: false, null: false
|
||||
t.integer "overall_rating"
|
||||
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"
|
||||
@@ -608,32 +599,6 @@ ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) do
|
||||
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"
|
||||
@@ -692,12 +657,6 @@ ActiveRecord::Schema[7.2].define(version: 2025_09_01_110545) 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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe "footer", :js do
|
||||
describe "footer" do
|
||||
before { visit root_path }
|
||||
|
||||
it "footer is on home page" do
|
||||
|
||||
@@ -198,6 +198,7 @@ describe "Planting a crop", :js, :search do
|
||||
within "form#new_planting" do
|
||||
fill_in "When?", with: "2014-07-01"
|
||||
check "Mark as finished"
|
||||
find_by_id('planting_overall_rating').set 4
|
||||
fill_in "Finished date", with: "2014-08-30"
|
||||
uncheck 'Mark as finished'
|
||||
end
|
||||
@@ -220,6 +221,7 @@ describe "Planting a crop", :js, :search do
|
||||
expect(page).to have_content "planting was successfully created"
|
||||
expect(page).to have_content "Finished"
|
||||
expect(page).to have_content "Aug 2014"
|
||||
expect(page).to have_content "4/5"
|
||||
|
||||
# ensure we've indexed in elastic search
|
||||
planting.reindex(refresh: true)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe "signin", :js do
|
||||
describe "signin" do
|
||||
let(:member) { FactoryBot.create(:member) }
|
||||
let(:recipient) { FactoryBot.create(:member) }
|
||||
let(:wrangler) { FactoryBot.create(:crop_wrangling_member) }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe "signup", :js do
|
||||
describe "signup" do
|
||||
it "sign up for new account from top menubar" do
|
||||
visit crops_path # something other than front page, which has multiple signup links
|
||||
click_link 'Sign up'
|
||||
|
||||
@@ -149,78 +149,94 @@ describe Harvest do
|
||||
end
|
||||
|
||||
context "stringification" do
|
||||
let(:crop) { FactoryBot.create(:crop, name: "apricot") }
|
||||
let(:whole_plant) { FactoryBot.create(:plant_part, name: "whole plant") }
|
||||
let(:leaf) { FactoryBot.create(:plant_part, name: "leaf") }
|
||||
let(:fruit) { FactoryBot.create(:plant_part, name: "fruit") }
|
||||
let(:other) { FactoryBot.create(:plant_part, name: "other") }
|
||||
|
||||
it "apricots" do
|
||||
@h = FactoryBot.create(:harvest, crop:,
|
||||
quantity: nil,
|
||||
unit: nil,
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "apricots"
|
||||
end
|
||||
let(:apricot) { FactoryBot.create(:crop, name: "apricot") }
|
||||
let(:lettuce) { FactoryBot.create(:crop, name: "lettuce") }
|
||||
|
||||
it "1 individual apricot" do
|
||||
@h = FactoryBot.create(:harvest, crop:,
|
||||
quantity: 1,
|
||||
unit: 'individual',
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "1 individual apricot"
|
||||
end
|
||||
context "apricots" do
|
||||
it "apricots" do
|
||||
@h = FactoryBot.create(:harvest, crop: apricot,
|
||||
plant_part: fruit,
|
||||
quantity: nil,
|
||||
unit: nil,
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "fruits of apricot"
|
||||
end
|
||||
|
||||
it "10 individual apricots" do
|
||||
@h = FactoryBot.create(:harvest, crop:,
|
||||
quantity: 10,
|
||||
unit: 'individual',
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "10 individual apricots"
|
||||
end
|
||||
it "1 individual apricot" do
|
||||
@h = FactoryBot.create(:harvest, crop: apricot,
|
||||
plant_part: fruit,
|
||||
quantity: 1,
|
||||
unit: 'individual',
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "1 individual fruit of apricot"
|
||||
end
|
||||
|
||||
it "1 bushel of apricots" do
|
||||
@h = FactoryBot.create(:harvest, crop:,
|
||||
quantity: 1,
|
||||
unit: 'bushel',
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "1 bushel of apricots"
|
||||
end
|
||||
it "10 individual apricots" do
|
||||
@h = FactoryBot.create(:harvest, crop: apricot,
|
||||
plant_part: fruit,
|
||||
quantity: 10,
|
||||
unit: 'individual',
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "10 individual fruits of apricot"
|
||||
end
|
||||
|
||||
it "1.5 bushels of apricots" do
|
||||
@h = FactoryBot.create(:harvest, crop:,
|
||||
quantity: 1.5,
|
||||
unit: 'bushel',
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "1.5 bushels of apricots"
|
||||
end
|
||||
it "1 bushel of apricots" do
|
||||
@h = FactoryBot.create(:harvest, crop: apricot,
|
||||
plant_part: fruit,
|
||||
quantity: 1,
|
||||
unit: 'bushel',
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "1 bushel of fruits of apricot"
|
||||
end
|
||||
|
||||
it "10 bushels of apricots" do
|
||||
@h = FactoryBot.create(:harvest, crop:,
|
||||
quantity: 10,
|
||||
unit: 'bushel',
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "10 bushels of apricots"
|
||||
end
|
||||
it "1.5 bushels of apricots" do
|
||||
@h = FactoryBot.create(:harvest, crop: apricot,
|
||||
plant_part: fruit,
|
||||
quantity: 1.5,
|
||||
unit: 'bushel',
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "1.5 bushels of fruits of apricot"
|
||||
end
|
||||
|
||||
it "apricots weighing 1.2 kg" do
|
||||
@h = FactoryBot.create(:harvest, crop:,
|
||||
quantity: nil,
|
||||
unit: nil,
|
||||
weight_quantity: 1.2,
|
||||
weight_unit: 'kg')
|
||||
expect(@h.to_s).to eq "apricots weighing 1.2 kg"
|
||||
end
|
||||
it "10 bushels of apricots" do
|
||||
@h = FactoryBot.create(:harvest, crop: apricot,
|
||||
plant_part: fruit,
|
||||
quantity: 10,
|
||||
unit: 'bushel',
|
||||
weight_quantity: nil,
|
||||
weight_unit: nil)
|
||||
expect(@h.to_s).to eq "10 bushels of fruits of apricot"
|
||||
end
|
||||
|
||||
it "10 bushels of apricots weighing 100 kg" do
|
||||
@h = FactoryBot.create(:harvest, crop:,
|
||||
quantity: 10,
|
||||
unit: 'bushel',
|
||||
weight_quantity: 100,
|
||||
weight_unit: 'kg')
|
||||
expect(@h.to_s).to eq "10 bushels of apricots weighing 100 kg"
|
||||
it "apricots weighing 1.2 kg" do
|
||||
@h = FactoryBot.create(:harvest, crop: apricot,
|
||||
plant_part: fruit,
|
||||
quantity: nil,
|
||||
unit: nil,
|
||||
weight_quantity: 1.2,
|
||||
weight_unit: 'kg')
|
||||
expect(@h.to_s).to eq "fruits of apricot weighing 1.2 kg"
|
||||
end
|
||||
|
||||
it "10 bushels of apricots weighing 100 kg" do
|
||||
@h = FactoryBot.create(:harvest, crop: apricot,
|
||||
plant_part: fruit,
|
||||
quantity: 10,
|
||||
unit: 'bushel',
|
||||
weight_quantity: 100,
|
||||
weight_unit: 'kg')
|
||||
expect(@h.to_s).to eq "10 bushels of fruits of apricot weighing 100 kg"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user