app/controllers/spectrums_controller.rb
# frozen_string_literal: true
require 'will_paginate/array'
class SpectrumsController < ApplicationController
respond_to :html, :xml, :js, :csv, :json
# expand this:
protect_from_forgery only: %i(clone_calibration extract calibrate save)
# http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ClassMethods.html
before_action :require_login, only: %i(new edit upload save update destroy calibrate extract clone_calibration fork setsamplerow find_brightest_row rotate reverse choose)
# switch to -- except: [ :index, :stats, :show, :show2, :anonymous, :embed, :embed2, :search, :recent, :all, :rss, :plots_rss, :match, :clone_search, :compare_search, :set_search
before_action :no_cache, only: %i(show latest latest_snapshot_id embed embed2)
def stats; end
# GET /spectrums
# GET /spectrums.xml
def index
if logged_in?
redirect_to '/dashboard'
else
@spectrums = Spectrum.select('title, created_at, id, user_id, author, photo_file_name, like_count, photo_content_type')
.order('created_at DESC')
.where('user_id != 0')
.paginate(page: params[:page], per_page: 24)
@sets = SpectraSet.limit(4)
.order('created_at DESC')
@comments = Comment.limit(12)
.order('id DESC')
respond_with(@spectrums) do |format|
format.html do
render template: 'spectrums/index'
end
format.xml { render xml: @spectrums }
end
end
end
# Returns a list of spectrums by tag, name, or id
# in a partial for use in macros and tools.
# ..can we merge this with search?
def choose
params[:id] = params[:id].to_s
comparison = if params[:id] == 'calibration' # special case; include linearCalibration too
"LIKE 'linearCalibration' OR tags.name LIKE"
else
# add wildcards
'LIKE'
end
params[:id] += '%'
# user's own spectra
params[:author] = current_user.login if logged_in? && params[:own] == 'true'
# must re-craft this query to search spectra with no tags:
@spectrums = Spectrum.order('spectrums.id DESC')
.select('DISTINCT(spectrums.id), spectrums.title, spectrums.created_at, spectrums.user_id, spectrums.author, spectrums.calibrated')
.joins('LEFT OUTER JOIN tags ON tags.spectrum_id = spectrums.id')
.joins('JOIN users ON users.id = spectrums.user_id')
.paginate(page: params[:page], per_page: 6)
# exclude self:
@spectrums = @spectrums.where('spectrums.id != ?', params[:not]) if params[:not]
if params[:id] != 'all' && !params[:id].nil?
@spectrums = @spectrums.where("tags.name #{comparison} (?) OR spectrums.title #{comparison} (?) OR spectrums.id = ? OR users.login = ?",
params[:id], params[:id], params[:id].to_i, params[:id])
@spectrums = @spectrums.where(user_id: User.find_by_login(params[:author]).id) if params[:author]
end
respond_with(@spectrums) do |format|
format.html { render partial: 'macros/spectra', locals: { spectrums: @spectrums } }
format.xml { render xml: @spectrums }
format.json { render json: @spectrums }
end
end
def show2
redirect_to spectrum_path(params[:id]), status: 301
end
# eventually start selecting everything but spectrum.data, as show2
# doesn't use this to fetch data, but makes a 2nd call.
# However, format.json does use it!
def show
@spectrum = Spectrum.find(params[:id])
respond_with(@spectrum) do |format|
format.html do
# temporary routing until we deprecate 1.0 paths to /legacy
if params[:v] != '1'
render template: 'spectrums/show2'
else
@spectra = if logged_in?
Spectrum.where('id != ? AND author = ?', @spectrum.id, current_user.login).order(created_at: :desc).limit(12)
else
Spectrum.where('id != ?', @spectrum.id).order(created_at: :desc).limit(12)
end
@sets = @spectrum.sets
@user_sets = SpectraSet.where(author: current_user.login).limit(20).order("created_at DESC") if logged_in?
@macros = Macro.where(macro_type: "analyze")
@calibrations = current_user.calibrations.reject { |s| s.id = @spectrum.id } if logged_in?
@comment = Comment.new
end
end
format.xml do
render xml: @spectrum.data
end
format.csv do
render html: SpectrumsHelper.show_csv(@spectrum)
end
format.json do
render json: @spectrum.json
end
end
end
# used when referencing a snapshot
def latest_snapshot_id
spectrum = Spectrum.select(:id).find(params[:id])
if spectrum.snapshots.count.positive?
render html: spectrum.latest_snapshot.id
else
render html: false
end
end
def latest
spectrum = Spectrum.find(params[:id])
if spectrum.snapshots.count.positive?
@snapshot = spectrum.latest_snapshot
is_snapshot = true
else
@snapshot = spectrum
is_snapshot = false
end
respond_with(@snapshot) do |format|
format.xml do
render xml: @snapshot.data
end
format.csv do
render html: SpectrumsHelper.show_csv_snapshot(@snapshot) if is_snapshot
render html: SpectrumsHelper.show_csv(@snapshot) unless is_snapshot
end
format.json do
render json: @snapshot.json
end
end
end
def anonymous
@spectrums = Spectrum.where(author: 'anonymous').order(created_at: :desc).paginate(page: params[:page])
render template: 'spectrums/search'
end
def embed
@spectrum = Spectrum.find(params[:id])
@width = (params[:width] || 500).to_i
@height = (params[:height] || 300).to_i
render layout: false
end
def embed2
@spectrum = Spectrum.find(params[:id])
render template: 'embed/spectrum', layout: 'embed'
end
def search
params[:id] = params[:q].to_s if params[:id].nil?
@spectrums = Spectrum.where('title LIKE ? OR author LIKE ?', "%#{params[:id]}%", "%#{params[:id]}%").order('id DESC').paginate(page: params[:page], per_page: 24)
if params[:capture]
render partial: 'capture/results', layout: false
else
@sets = SpectraSet.where('title LIKE ? OR author LIKE ?', "%#{params[:id]}%", "%#{params[:id]}%").order('id DESC').paginate(page: params[:set_page])
end
end
def recent
@spectrums = Spectrum.all.order(id: :desc).limit(10)
render partial: 'capture/results', layout: false if params[:capture]
end
# GET /spectrums/new
# GET /spectrums/new.xml
def new
@spectrum = Spectrum.new
respond_with(@spectrum) do |format|
format.html {}
format.xml { render xml: @spectrum }
end
end
# GET /spectrums/1/edit
def edit
@spectrum = Spectrum.find(params[:id])
require_ownership(@spectrum)
end
# POST /spectrums
# POST /spectrums.xml
# POST /spectrums.json
# ?spectrum[title]=TITLE&spectrum[author]=anonymous&startWavelength=STARTW&endWavelength=ENDW;
# replacing this with capture/save soon
def create # rubocop:disable Metrics/AbcSize
if logged_in? || params[:token] && User.find_by_token(params[:token])
user = current_user || User.find_by_token(params[:token])
params[:spectrum][:json] = params[:spectrum][:data] if params[:spectrum] && params[:spectrum][:data]
@spectrum = Spectrum.new(
title: params[:spectrum][:title],
author: user.login,
user_id: user.id,
notes: params[:spectrum][:notes]
)
if params[:dataurl] # mediastream webclient
@spectrum.image_from_dataurl(params[:dataurl])
# upload json; CSV is converted to JSON before upload in upload.js
elsif params[:spectrum][:data_type] == 'csv' || params[:spectrum][:data_type] == 'json' || params[:format] == 'json'
lines = params[:spectrum][:json]
@spectrum.data = '{ "lines": ' + lines.to_s + ' }'
elsif params[:spectrum][:data_type] == 'image' # upload form at /upload
@spectrum.photo = params[:spectrum][:photo]
end
if @spectrum.save
respond_with(@spectrum) do |format|
if APP_CONFIG['local'] || logged_in? || User.find_by_token(params[:token])
if mobile? || ios?
@spectrum.save
@spectrum = Spectrum.find @spectrum.id
@spectrum.sample_row = @spectrum.find_brightest_row
# @spectrum.tag("mobile", user.id)
end
@spectrum.rotate if params[:spectrum][:json] == '' && params[:vertical]
@spectrum.tag('iOS', user.id) if ios?
@spectrum.tag('json', user.id) if params[:spectrum][:data_type] == 'json'
@spectrum.tag('csv', user.id) if params[:spectrum][:data_type] == 'csv'
params[:tags].to_s.split(',').each do |tag|
@spectrum.tag(tag, user.id)
end
@spectrum.tag('upload', user.id) if params[:upload]
@spectrum.tag(params[:device], user.id) if params[:device] && params[:device] != 'none'
@spectrum.tag("video_row:#{params[:video_row]}", user.id) if params[:video_row]
# @spectrum.tag("sample_row:#{params[:video_row]}", user.id) if params[:video_row]
@spectrum.extract_data if !params[:spectrum][:json] || params[:spectrum][:json].empty?
calibration_param = if params[:spectrum][:calibration_id] && !params[:is_calibration] && params[:spectrum][:calibration_id] != 'calibration' && params[:spectrum][:calibration_id] != 'undefined'
# @spectrum.clone_calibration(params[:spectrum][:calibration_id])
# instead, append params[:spectrum][:calibration_id] to "#addTag=calibrate:#{params[:spectrum][:calibration_id].to_i}"
"#addTag=calibrate:#{params[:spectrum][:calibration_id].to_i}"
else
''
end
if params[:geotag]
@spectrum.lat = params[:lat]
@spectrum.lon = params[:lon]
end
@spectrum.reversed = true if params[:spectrum][:reversed] == 'true'
if @spectrum.save!
flash[:notice] = 'Spectrum was successfully created.'
format.html { redirect_to spectrum_path(@spectrum) + calibration_param }
format.xml { render xml: @spectrum, status: :created, location: @spectrum }
format.json { render html: spectrum_path(@spectrum), status: :created, location: @spectrum }
else
render 'spectrums/new'
end
else
format.html { render action: 'new' }
format.xml { render xml: @spectrum.errors, status: :unprocessable_entity }
format.json { render json: @spectrum.errors, status: :unprocessable_entity }
end
end
else
respond_to do |format|
format.html { render action: 'new' }
format.xml { render xml: @spectrum.errors, status: :unprocessable_entity }
format.json { render json: @spectrum.errors, status: :unprocessable_entity }
end
end
else
respond_to do |format|
if request.xhr?
format.json do
render html: 'Token required for unauthenticated API usage.'
end
else
format.html do
flash[:error] = 'You must be logged in to upload data.'
redirect_to '/'
end
end
end
end
end
# used to upload numerical spectrum data as a new spectrum (untested, no image??)
def upload
@spectrum = Spectrum.new(title: params[:spectrum][:title],
author: author,
user_id: user_id,
notes: params[:spectrum][:notes],
data: params[:data],
photo: params[:photo])
@spectrum.save!
params[:tags].to_s.split(',').each do |tag|
@spectrum.tag(tag, current_user.id)
end
redirect_to spectrum_path(@spectrum)
end
# only ajax/POST accessible for now:
def save
@spectrum = Spectrum.find(params[:id])
require_ownership(@spectrum)
@spectrum.data = params[:data]
params[:tags].to_s.split(',').each do |tag|
@spectrum.tag(tag, current_user.id)
end
render html: @spectrum.save
end
# PUT /spectrums/1
# PUT /spectrums/1.xml
def update
@spectrum = Spectrum.find(params[:id])
require_ownership(@spectrum)
@spectrum.title = params[:spectrum][:title] unless params[:spectrum].nil? || params[:spectrum][:title].nil?
@spectrum.notes = params[:spectrum][:notes] unless params[:spectrum].nil? || params[:spectrum][:notes].nil?
@spectrum.data = params[:spectrum][:data] unless params[:spectrum].nil? || params[:spectrum][:data].nil?
# clean this up
respond_to do |format|
if @spectrum.save
if request.xhr?
format.json { render json: @spectrum }
else
flash[:notice] = 'Spectrum was successfully updated.'
format.html { redirect_to(@spectrum) }
format.xml { head :ok }
end
else
format.html { render action: 'edit' }
format.xml { render xml: @spectrum.errors, status: :unprocessable_entity }
end
end
end
# DELETE /spectrums/1
# DELETE /spectrums/1.xml
def destroy
@spectrum = Spectrum.find(params[:id])
if require_ownership(@spectrum)
if @spectrum.destroy
flash[:notice] = 'Spectrum deleted.'
respond_with(@spectrum) do |format|
format.html { redirect_to('/') }
format.xml { head :ok }
end
else
flash[:error] = 'Spectrum could not be deleted.' # this is not displaying, not sure why
redirect_to spectrum_path(@spectrum)
end
end
end
# Start doing this client side!
def calibrate
@spectrum = Spectrum.find(params[:id])
require_ownership(@spectrum)
@spectrum.calibrate(params[:x1], params[:w1], params[:x2], params[:w2])
@spectrum.save
@spectrum.tag('calibration', current_user.id)
flash[:notice] = "Great, calibrated! <b>Next steps:</b> sign up on <a href='//publiclab.org/wiki/spectrometer'>the mailing list</a>, or browse/contribute to <a href='//publiclab.org'>Public Lab website</a>"
redirect_to spectrum_path(@spectrum)
end
# Start doing this client side!
def extract
@spectrum = Spectrum.find(params[:id])
require_ownership(@spectrum)
@spectrum.extract_data
@spectrum.save
flash[:warning] = "Now, recalibrate, since you've <a href='//publiclab.org/wiki/spectral-workbench-calibration#Cross+section'>set a new cross-section</a>."
redirect_to spectrum_path(@spectrum)
end
def fork
@spectrum = Spectrum.find(params[:id])
@new = @spectrum.fork(current_user)
flash[:notice] = "You successfully forked <a href='#{spectrum_path(@spectrum)}'>Spectrum ##{@spectrum.id}</a>"
redirect_to spectrum_path(@new)
end
# Copy calibration from an existing calibrated spectrum.
# Start doing this client side!
def clone_calibration
@spectrum = Spectrum.find(params[:id])
@calibration_clone_source = Spectrum.find(params[:clone_id])
require_ownership(@spectrum)
@spectrum.clone_calibration(@calibration_clone_source.id)
@spectrum.save
@spectrum.remove_powertags('calibration')
@spectrum.tag("calibrate:#{@calibration_clone_source.id}", current_user.id)
respond_with(@spectrums) do |format|
format.html do
flash[:notice] = 'Spectrum was successfully calibrated.'
redirect_to spectrum_path(@spectrum)
end
format.json { render json: @spectrum }
end
end
def all
@spectrums = Spectrum.all.paginate(page: params[:page])
respond_with(@spectrums) do |format|
format.xml { render xml: @spectrums }
format.json { render json: @spectrums }
end
end
def rss
if params[:author]
Spectrum.where(author: params[:author])
@spectrums = Spectrum.where(author: params[:author]).paginate(page: params[:page])
else
@spectrums = Spectrum.find(:all, order: 'created_at DESC', limit: 12).paginate(page: params[:page])
end
respond_to do |format|
format.xml
end
end
def plots_rss
@spectrums = Spectrum.find(:all, order: 'created_at DESC', limit: 12, conditions: ['author != ?', 'anonymous']).paginate(page: params[:page])
render layout: false
response.headers['Content-Type'] = 'application/xml; charset=utf-8'
end
def match
@spectrum = Spectrum.find params[:id]
render html: @spectrum.find_match_in_set(params[:set]).to_json
end
# Start doing this client side!
def setsamplerow
require 'rubygems'
require 'rmagick'
@spectrum = Spectrum.find params[:id]
require_ownership(@spectrum)
image = Magick::ImageList.new('public' + (@spectrum.photo.url.split('?')[0]).gsub('%20', ' '))
@spectrum.sample_row = (params[:row].to_f * image.rows)
@spectrum.extract_data
@spectrum.save
flash[:warning] = "If this spectrum image is not perfectly vertical, you may need to recalibrate after <a href='//publiclab.org/wiki/spectral-workbench-calibration#Cross+section'>setting a new cross-section</a>."
redirect_to spectrum_path(@spectrum)
end
# Start doing this client side!
def find_brightest_row
@spectrum = Spectrum.find params[:id]
require_ownership(@spectrum)
@spectrum.sample_row = @spectrum.find_brightest_row
@spectrum.extract_data
@spectrum.clone_calibration(@spectrum.id) # recover calibration
@spectrum.save
flash[:warning] = "If this spectrum image is not perfectly vertical, you may need to recalibrate after <a href='//publiclab.org/wiki/spectral-workbench-calibration#Cross+section'>setting a new cross-section</a>."
redirect_to spectrum_path(@spectrum)
end
# rotates the image and re-extracts it
def rotate
@spectrum = Spectrum.find params[:id]
require_ownership(@spectrum)
@spectrum.rotate
@spectrum.extract_data
@spectrum.clone_calibration(@spectrum.id)
@spectrum.save
redirect_to spectrum_path(@spectrum)
end
# Just reverses the image, not the data.
def reverse
@spectrum = Spectrum.find params[:id]
require_ownership(@spectrum)
@spectrum.reversed = !@spectrum.reversed
@spectrum.toggle_tag('reversed', current_user.id)
@spectrum.reverse
@spectrum.save
redirect_to spectrum_path(@spectrum)
end
# search for calibrations to clone from
def clone_search
@spectrum = Spectrum.find(params[:id])
@calibrations = Spectrum.where(calibrated: true)
.where('id != ?', @spectrum.id)
.where('title LIKE ? OR notes LIKE ? OR author LIKE ?', "%#{params[:q]}%", "%#{params[:q]}%", "%#{params[:q]}%")
.limit(20)
.order('created_at DESC')
render partial: 'spectrums/show/clone_results', layout: false
end
def compare_search
@spectrum = Spectrum.find(params[:id])
@spectra = Spectrum.where(calibrated: true)
.where('id != ?', @spectrum.id)
.where('title LIKE ? OR notes LIKE ? OR author LIKE ?', "%#{params[:q]}%", "%#{params[:q]}%", "%#{params[:q]}%")
.limit(20)
.order('created_at DESC')
render partial: 'spectrums/show/compare_search', layout: false
end
def set_search
@spectrum = Spectrum.find(params[:id])
@user_sets = SpectraSet.where('author = ? AND (title LIKE ? OR notes LIKE ?)', current_user.login, "%#{params[:q]}%", "%#{params[:q]}%")
.limit(20)
.order('created_at DESC')
@user_sets = current_user.sets if logged_in?
render partial: 'spectrums/show/set_results', layout: false
end
end