mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-03-09 10:29:11 -04:00
* feat: Add data improvement page to crops controller This commit introduces a new data improvement page to the crops controller. The page displays tabbed lists of crops with missing data, allowing users to easily identify areas for data quality improvement. The following data quality categories are included: - Crops without photos - Crops without descriptions - Crops without a youtube video - Crops without alternate names - Crops without a scientific name with a wikidata id - Crops without row spacing - Crops without sun requirements - Crops without height All lists are sorted by planting count in descending order. * refactor: Optimize data improvement page to load tab data on demand This commit refactors the data improvement page to load data for each tab on demand, rather than loading all queries at once. This improves the performance of the page by only executing the query for the currently active tab. The controller action now uses a `case` statement based on a `tab` URL parameter to execute the appropriate query. The view has been updated to pass this parameter when a tab is clicked. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
256 lines
7.3 KiB
Ruby
256 lines
7.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'will_paginate/array'
|
|
|
|
class CropsController < ApplicationController
|
|
before_action :authenticate_member!, except: %i(index hierarchy search show)
|
|
load_and_authorize_resource id_param: :slug
|
|
skip_authorize_resource only: %i(hierarchy search)
|
|
respond_to :html, :json, :rss, :csv, :svg
|
|
responders :flash
|
|
|
|
def index
|
|
@crops = Crop.search('*', boost_by: %i(plantings_count harvests_count),
|
|
limit: 100,
|
|
page: params[:page],
|
|
load: false)
|
|
@num_requested_crops = requested_crops.size if current_member
|
|
@filename = filename
|
|
respond_with @crops
|
|
end
|
|
|
|
def requested
|
|
@requested = requested_crops.paginate(page: params[:page])
|
|
respond_with @requested
|
|
end
|
|
|
|
def wrangle
|
|
@approval_status = params[:approval_status]
|
|
@crops = case @approval_status
|
|
when "pending"
|
|
Crop.pending_approval
|
|
when "rejected"
|
|
Crop.rejected
|
|
else
|
|
Crop.recent
|
|
end.paginate(page: params[:page])
|
|
|
|
@crop_wranglers = Role.crop_wranglers
|
|
respond_with @crops
|
|
end
|
|
|
|
def gbif
|
|
@crop = Crop.find(params[:crop_slug])
|
|
@crop.update_gbif_data!
|
|
respond_with @crop, location: @crop
|
|
end
|
|
|
|
def hierarchy
|
|
@crops = Crop.toplevel.order(:name)
|
|
respond_with @crops
|
|
end
|
|
|
|
def search
|
|
@term = params[:term]
|
|
|
|
@crops = CropSearchService.search(@term,
|
|
page: params[:page],
|
|
per_page: Crop.per_page,
|
|
current_member:)
|
|
|
|
respond_to do |format|
|
|
format.html do
|
|
render
|
|
end
|
|
format.json do
|
|
render json: @crops.to_a
|
|
end
|
|
end
|
|
end
|
|
|
|
def show
|
|
respond_to do |format|
|
|
format.html do
|
|
@posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page])
|
|
@companions = @crop.companions.approved
|
|
member_ids = @crop.versions.map(&:whodunnit).compact.map(&:to_i)
|
|
@version_members = Member.where(id: member_ids).index_by(&:id)
|
|
end
|
|
format.svg do
|
|
icon_data = @crop.svg_icon.presence || File.read(Rails.root.join("app/assets/images/icons/sprout.svg"))
|
|
send_data(icon_data, type: "image/svg+xml", disposition: "inline")
|
|
end
|
|
format.json do
|
|
render json: @crop.to_json(crop_json_fields)
|
|
end
|
|
end
|
|
end
|
|
|
|
def new
|
|
@crop = Crop.new
|
|
@crop.alternate_names.build
|
|
@crop.scientific_names.build
|
|
|
|
respond_with @crop
|
|
end
|
|
|
|
def edit
|
|
@crop.alternate_names.build if @crop.alternate_names.blank?
|
|
@crop.scientific_names.build if @crop.scientific_names.blank?
|
|
end
|
|
|
|
def create
|
|
@crop = Crop.new(crop_params)
|
|
|
|
if current_member.role? :crop_wrangler
|
|
@crop.creator = current_member
|
|
else
|
|
@crop.requester = current_member
|
|
@crop.approval_status = "pending"
|
|
end
|
|
|
|
if Crop.transaction { @crop.save && save_crop_names }
|
|
notify_wranglers
|
|
else
|
|
@crop.alternate_names.build
|
|
@crop.scientific_names.build
|
|
end
|
|
|
|
respond_with @crop
|
|
end
|
|
|
|
def update
|
|
if can?(:wrangle, @crop)
|
|
@crop.approval_status = 'rejected' if params.fetch("reject", false)
|
|
@crop.approval_status = 'approved' if params.fetch("approve", false)
|
|
end
|
|
|
|
@crop.creator = current_member if @crop.approval_status == "pending"
|
|
|
|
if @crop.update(crop_params)
|
|
recreate_names('alt_name', 'alternate')
|
|
recreate_names('sci_name', 'scientific')
|
|
|
|
if @crop.approval_status_changed?(from: "pending", to: "approved")
|
|
notifier.deliver_now!
|
|
@crop.update_gbif_data!
|
|
end
|
|
else
|
|
@crop.approval_status = @crop.approval_status_was
|
|
end
|
|
|
|
respond_with @crop
|
|
end
|
|
|
|
def destroy
|
|
@crop = Crop.find_by!(slug: params[:slug])
|
|
authorize! :destroy, @crop
|
|
@crop.destroy
|
|
respond_with @crop
|
|
end
|
|
|
|
def data_improvement
|
|
@active_tab = params[:tab] || 'photos'
|
|
|
|
@crops = case @active_tab
|
|
when 'photos'
|
|
Crop.approved.where(photo_associations_count: 0).order(plantings_count: :desc)
|
|
when 'descriptions'
|
|
Crop.approved.where(description: [nil, '']).order(plantings_count: :desc)
|
|
when 'youtube'
|
|
Crop.approved.where(en_youtube_url: [nil, '']).order(plantings_count: :desc)
|
|
when 'alternate_names'
|
|
Crop.approved.left_joins(:alternate_names).where(alternate_names: { id: nil }).order(plantings_count: :desc)
|
|
when 'wikidata'
|
|
crops_with_wikidata = Crop.joins(:scientific_names).where.not(scientific_names: { wikidata_id: nil }).distinct
|
|
Crop.approved.where.not(id: crops_with_wikidata).order(plantings_count: :desc)
|
|
when 'row_spacing'
|
|
Crop.approved.where(row_spacing: nil).order(plantings_count: :desc)
|
|
when 'sun_requirements'
|
|
Crop.approved.where(sun_requirements: [nil, '']).order(plantings_count: :desc)
|
|
when 'height'
|
|
Crop.approved.where(height: nil).order(plantings_count: :desc)
|
|
else
|
|
Crop.none
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def notifier
|
|
case @crop.approval_status
|
|
when "approved"
|
|
NotifierMailer.crop_request_approved(@crop.requester, @crop)
|
|
when "rejected"
|
|
NotifierMailer.crop_request_rejected(@crop.requester, @crop)
|
|
end
|
|
end
|
|
|
|
def save_crop_names
|
|
AlternateName.create!(names_params(:alt_name).map { |n| { name: n, creator_id: current_member.id, crop_id: @crop.id, language: "EN" } })
|
|
ScientificName.create!(names_params(:sci_name).map { |n| { name: n, creator_id: current_member.id, crop_id: @crop.id } })
|
|
end
|
|
|
|
def notify_wranglers
|
|
return if current_member.role? :crop_wrangler
|
|
|
|
Role.crop_wranglers&.each do |w|
|
|
NotifierMailer.new_crop_request(w, @crop).deliver_now!
|
|
end
|
|
end
|
|
|
|
def recreate_names(param_name, name_type)
|
|
return if params[param_name].blank?
|
|
|
|
@crop.send("#{name_type}_names").each(&:destroy)
|
|
params[param_name].each_value do |value|
|
|
next if value.empty?
|
|
|
|
if name_type == 'alternate'
|
|
@crop.send("#{name_type}_names").create!(name: value, creator_id: current_member.id, language: "EN")
|
|
else
|
|
@crop.send("#{name_type}_names").create!(name: value, creator_id: current_member.id)
|
|
end
|
|
end
|
|
end
|
|
|
|
def crop_params
|
|
params.require(:crop).permit(
|
|
:name, :en_wikipedia_url, :en_youtube_url,
|
|
:parent_id, :perennial,
|
|
:request_notes, :reason_for_rejection,
|
|
:rejection_notes,
|
|
:description,
|
|
:public_food_key,
|
|
:row_spacing, :spread, :height,
|
|
:sowing_method, :sun_requirements, :growing_degree_days,
|
|
scientific_names_attributes: %i(scientific_name _destroy id)
|
|
)
|
|
end
|
|
|
|
def names_params(name_type)
|
|
params.require(name_type).values&.reject { |n| n.empty? }
|
|
end
|
|
|
|
def filename
|
|
"Growstuff-Crops-#{Time.zone.now.to_fs(:number)}.csv"
|
|
end
|
|
|
|
def crop_json_fields
|
|
{
|
|
include: {
|
|
plantings: {
|
|
include: {
|
|
owner: { only: %i(id login_name location latitude longitude) }
|
|
}
|
|
},
|
|
scientific_names: { only: [:name] }, alternate_names: { only: %i(name language) }
|
|
}
|
|
}
|
|
end
|
|
|
|
def requested_crops
|
|
current_member.requested_crops.pending_approval
|
|
end
|
|
end
|