class PipelineController < ApplicationController
include PipelineBackground
include SortHelper
# TODO: move some stuff into the private area so it can't be called by URL-hackers
#CONSTANTS
AUTOUPLOADLABEL = "Submit"
helper :sort
before_filter :login_required
before_filter :check_user_is_owner, :except =>
[ :new, :create, :list, :show_active, :show_user, :show,
:valid_status, :load_status, :unload_status, :upload_status,
:show_tools, :mass_tools, :mass_ftp, :mass_url, :delete_archive ]
layout 'main'
def list
sort_init 'updated_at', {:default_order => 'desc'}
sort_update
#@autoRefresh = true
@sort_key = params[:sort_key]
if @sort_key == "pi" || @sort_key == "login"
@projects = Project.find(:all, :include => :user, :order => sort_clause)
elsif @sort_key
@projects = Project.find(:all, :order => sort_clause)
else
@projects = Project.find(:all, :order => 'updated_at DESC')
end
end
def show_active
sort_init 'updated_at', {:default_order => 'desc'}
sort_update
#@autoRefresh = true
active_conditions = [
"status != 'reviewing' and " +
"status != 'displayed' and " +
"status != 'approved' and " +
"status != 'revoked' and " +
"status != 'released'"
]
@sort_key = params[:sort_key]
if @sort_key == "pi" || @sort_key == "login"
@projects = Project.find(:all, :include => :user, :order => sort_clause, :conditions => active_conditions)
elsif @sort_key
@projects = Project.find(:all, :order => sort_clause, :conditions => active_conditions)
else
@projects = Project.find(:all, :order => 'updated_at DESC', :conditions => active_conditions)
end
render :action => 'list'
end
def show_user
sort_init 'updated_at', {:default_order => 'desc'}
sort_update
@sort_key = params[:sort_key]
@user = params[:submitter] ? User.find(:first, :conditions => {:login => params[:submitter]}) : User.find(current_user.id)
if @sort_key
@projects = Project.find(:all, :conditions => {:user_id => @user.id}, :order => sort_clause)
else
@projects = Project.find(:all, :conditions => {:user_id => @user.id}, :order => 'updated_at DESC')
end
@projects.each do
|p|
if p.run_stat
@autoRefresh = true
end
end
render :action => 'list'
end
def show
@project = Project.find(params[:id])
if @project.run_stat
@autoRefresh = true
end
@errText = ""
case @project.status
when "upload failed"
@errText = getUploadErrText(@project)
when "expand failed"
@errText = getExpandErrText(@project)
when "validate failed"
@errText = getValidateErrText(@project)
when "load failed"
@errText = getLoadErrText(@project)
when "unload failed"
@errText = getUnloadErrText(@project)
when "uploading"
# old wget method
#upText = getUploadErrText(@project)
#unless upText.blank?
# upText = upText.split("\n")
# if upText.last
# upText = upText.last.split(" ").first
# if upText.last(1) == "K"
# @uploadText = upText
# end
# end
#end
# new paraFetch method
upName,upText = getNewestFileByExtensionIgnoringCase(@project.id, "paraFetchStatus")
unless upText.blank?
upText = upText.split("\n")
unless upText.length < 4
url = upText.shift
size = upText.shift.to_i
fdate = upText.shift
downloaded = 0
while not upText.empty?
l = upText.shift
downloaded += l.split(" ").last.to_i
end
@uploadText = "#{goGreek(downloaded)} of #{goGreek(size)} #{url}"
end
end
end
@dafName,@dafText = getDafText(@project)
@ddfName,@ddfText = getDdfText(@project)
if @project.run_stat and @project.run_stat == "waiting"
job = QueuedJob.find(:first, :conditions => ["project_id = ?", @project.id])
if job
@aheadOfYou = QueuedJob.count_by_sql "select count(*) from queued_jobs where id < #{job.id} and source_code <> 'done'"
else
@aheadOfYou = nil
end
end
@deadline = get_deadline
rescue
redirect_to :action => 'show_user'
return
end
def valid_status
@project = Project.find(params[:id])
@errText = getValidateErrText(@project)
end
def load_status
@project = Project.find(params[:id])
@errText = getLoadErrText(@project)
end
def unload_status
@project = Project.find(params[:id])
@errText = getUnloadErrText(@project)
end
def upload_status
@project = Project.find(params[:id])
@errText = getUploadErrText(@project)
end
def expand_status
@project = Project.find(params[:id])
@errText = getExpandErrText(@project)
end
def show_daf
@project = Project.find(params[:id])
@dafName,@dafText = getDafText(@project)
end
def download_daf
@project = Project.find(params[:id])
@dafName,@dafText = getDafText(@project)
headers.merge!('Content-Disposition' => "attachment; filename=\"#{@dafName}\"")
render :text => @dafText, :content_type => 'text/plain'
end
def show_ddf
@project = Project.find(params[:id])
@ddfName,@ddfText = getDdfText(@project)
end
def download_ddf
@project = Project.find(params[:id])
@ddfName,@ddfText = getDdfText(@project)
headers.merge!('Content-Disposition' => "attachment; filename=\"#{@ddfName}\"")
render :text => @ddfText, :content_type => 'text/plain'
end
def db_load
@project = Project.find(params[:id])
if @project.run_stat
flash[:error] = "Please wait, a background job is still running."
redirect_to :action => 'show', :id => @project.id
return
end
if @project.status == "validated"
new_status @project, "load requested"
unless queue_job @project.id, "load_background(#{@project.id})"
flash[:error] = "System error - queued_jobs save failed."
return
end
end
redirect_to :action => 'show', :id => @project.id
end
def validate
@project = Project.find(params[:id])
allowReloads = ""
if @project.run_stat
flash[:error] = "Please wait, a background job is still running."
redirect_to :action => 'show', :id => @project.id
return
end
if (@project.status == "expanded") or (@project.status == "validate failed")
new_status @project, "validate requested"
unless queue_job @project.id, "validate_background(#{@project.id}, \"#{allowReloads}\")"
flash[:error] = "System error - queued_jobs save failed."
return
end
end
redirect_to :action => 'show', :id => @project.id
end
def new
@project = Project.new
@projectTypes = getProjectTypes
@deadline = get_deadline
#@projectTypesArray = @projectTypes.to_a.sort_by { |x| x[1]["displayOrder"] }
end
def create
if params[:commit] == "Cancel"
redirect_to :action => 'show_user'
return
end
@project = Project.new(params[:project])
@project.user_id = @current_user.id
@project.status = 'new'
@project.archives_active = ""
if @project.save
redirect_to :action => 'upload', :id => @project.id
log_project_status @project
else
@projectTypes = getProjectTypes
render :action => 'new'
end
end
def edit
@project = Project.find(params[:id])
@projectTypes = getProjectTypes
#@projectTypesArray = @projectTypes.to_a.sort_by { |x| x[1]["displayOrder"] }
end
def update
@project = Project.find(params[:id])
old_project_type_id = @project.project_type_id
if @project.update_attributes(params[:project])
if old_project_type_id != @project.project_type_id
if @project.project_archives.length == 0
@project.status = "new"
else
@project.status = "expanded"
end
new_status @project, @project.status
end
redirect_to :action => 'show', :id => @project
else
render :action => 'edit'
end
end
def unload
@project = Project.find(params[:id])
if @project.run_stat
flash[:error] = "Please wait, a background job is still running."
redirect_to :action => 'show', :id => @project.id
return
end
projectDir= path_to_project_dir(@project.id)
msg = ""
msg += "Submission unload requested."
new_status @project, "unload requested"
unless queue_job @project.id, "unload_background(#{@project.id})"
flash[:error] = "System error - queued_jobs save failed."
return
end
flash[:notice] = msg
redirect_to :action => 'show', :id => @project
end
def delete
@project = Project.find(params[:id])
if @project.run_stat
flash[:error] = "Please wait, a background job is still running."
redirect_to :action => 'show', :id => @project.id
return
end
projectDir= path_to_project_dir(@project.id)
msg = ""
msg += "Submission delete requested."
new_status @project, "delete requested"
unless queue_job @project.id, "delete_background(#{@project.id})"
flash[:error] = "System error - queued_jobs save failed."
return
end
flash[:notice] = msg
redirect_to :action => 'show_user'
end
def mass_ftp
@user = current_user
get_ftp_list
return unless request.post?
if params[:commit] == "Cancel"
redirect_to :action => 'show_user'
return
end
# create submissions
@ftpList.each do |file|
#take project name from archive filename
project_name = file
#chop off archive file extension
@extensions.each do |ext|
if project_name.ends_with?("." + ext)
project_name = project_name[0..(project_name.length - 1 - (ext.length + 1))]
end
end
#if a submission with that name exists already, tweak the name with (number) until unique.
base_name = project_name
suffix = ""
retryCount = 0
unique = false
while not unique
project_name = base_name + suffix
@existing = Project.find(:first, :conditions => {:name => project_name})
if @existing
retryCount += 1
suffix = " (" + retryCount.to_s + ")"
else
unique = true
end
end
@project = Project.new()
@project.name = project_name
@project.project_type_id = 5
@project.user_id = @current_user.id
@project.status = 'new'
@project.archives_active = ""
if @project.save
# queue it up to be uploaded etc.
@upurl = ""
@upload = ""
@upftp = file
@filename = sanitize_filename(@upftp)
upload_one
else
msg = ("Error creating submission for "+file+" as "+project_name+"
")
if flash[:error]
flash[:error] += msg
else
flash[:error] = msg
end
end
end
redirect_to :action => 'show_user'
end
def mass_url
@user = current_user
return unless request.post?
if params[:commit] == "Cancel"
redirect_to :action => 'show_user'
return
end
uplist = params[:url_list].split("\n")
@extensions = ["zip", "ZIP", "tar.gz", "TAR.GZ", "tar.bz2", "TAR.BZ2", "tgz", "TGZ"]
# check urls have correct extension
uplist.each do |url|
# get trim lead/trail whitespace
url.strip!
if url.blank?
next
end
@filename = sanitize_filename(url)
# todo may need to move this up earlier?
unless @extensions.any? {|ext| @filename.ends_with?("." + ext) }
flash[:error] = "File name #{@filename} is invalid. " +
"Only a compressed archive file (tar.gz, tar.bz2, zip) is allowed."
return
end
end
# create submissions
uplist.each do |url|
# get trim lead/trail whitespace
url.strip!
if url.blank?
next
end
#take project name from archive filename
project_name = url
#chop off archive file extension
@extensions.each do |ext|
if project_name.ends_with?("." + ext)
project_name = project_name[0..(project_name.length - 1 - (ext.length + 1))]
end
end
#if a submission with that name exists already, tweak the name with (number) until unique.
base_name = project_name
suffix = ""
retryCount = 0
unique = false
while not unique
project_name = base_name + suffix
@existing = Project.find(:first, :conditions => {:name => project_name})
if @existing
retryCount += 1
suffix = " (" + retryCount.to_s + ")"
else
unique = true
end
end
@project = Project.new()
@project.name = project_name
@project.project_type_id = 5
@project.user_id = @current_user.id
@project.status = 'new'
@project.archives_active = ""
if @project.save
# queue it up to be uploaded etc.
@upurl = url
@upload = ""
@upftp = ""
@filename = sanitize_filename(@upurl)
upload_one
else
msg = ("Error creating submission for "+url+" as "+project_name+"
")
if flash[:error]
flash[:error] += msg
else
flash[:error] = msg
end
end
end
redirect_to :action => 'show_user'
end
def upload
@project = Project.find(params[:id])
if @project.run_stat
flash[:error] = "Please wait, a background job is still running."
redirect_to :action => 'show', :id => @project.id
return
end
@user = current_user
@autoUploadLabel = AUTOUPLOADLABEL
get_ftp_list
return unless request.post?
if params[:commit] == "Cancel"
redirect_to :action => 'show', :id => @project
return
end
@upurl = params[:upload_url]
@upload = params[:upload_file]
@upftp = params[:ftp]
if @upurl == "http://"
@upurl = ""
end
unless @upftp
@upftp = ""
end
return if @upload.blank? && @upurl.blank? && @upftp.blank?
unless @upurl.blank?
@protos = ["ftp", "http", "https"]
unless @protos.any? {|proto| @upurl.starts_with?(proto+"://") }
flash[:error] = "only these protocols are allowed in the url:"
@protos.each do
|proto|
flash[:error] += (" "+proto)
end
return
end
@filename = sanitize_filename(@upurl)
#TODO add a HEAD would check it exists before starting?
else
unless @upftp.blank?
@filename = sanitize_filename(@upftp)
else
@filename = sanitize_filename(@upload.original_filename)
extensionsByMIME = {
"application/zip" => ["zip", "ZIP"],
"application/x-compressed-tar" => ["tar.gz", "TAR.GZ", "tar.bz2", "TAR.BZ2", "tgz", "TGZ"],
"application/x-tar" => ["tar.gz", "TAR.GZ", "tar.bz2", "TAR.BZ2", "tgz", "TGZ"],
"application/octet-stream" => ["tar.gz", "TAR.GZ", "tar.bz2", "TAR.BZ2", "tgz", "TGZ"],
"application/gzip" => ["tar.gz", "TAR.GZ", "tgz", "TGZ"],
"application/x-gzip" => ["tar.gz", "TAR.GZ", "tgz", "TGZ"]
}
@extensions = extensionsByMIME[@upload.content_type.chomp]
unless @extensions
flash[:error] = "Invalid content_type=#{@upload.content_type.chomp}."
return
end
end
end
unless @extensions.any? {|ext| @filename.ends_with?("." + ext) }
flash[:error] = "File name #{@filename} is invalid. " +
"Only a compressed archive file (tar.gz, tar.bz2, zip) is allowed."
return
end
upload_one
redirect_to :action => 'show', :id => @project
end
def delete_archive
# This is really a toggle on/off and the archives are only marked for exclusion
archive = ProjectArchive.find(params[:id])
params[:id] = archive.project_id
unless check_user_is_owner
return false
end
msg = ""
@project = Project.find(params[:id])
if @project.run_stat
flash[:error] = "Please wait, a background job is still running."
redirect_to :action => 'show', :id => @project.id
return
end
n = archive.archive_no-1
c = @project.archives_active[n..n]
if c == "1"
c = "0"
msg += "Archive deleted."
else
c = "1"
msg += "Archive undeleted."
end
@project.archives_active[n..n] = c
unless @project.save
flash[:error] = "System error - project record save failed."
redirect_to :action => 'show', :id => @project
return false
end
flash[:notice] = msg
new_status @project, "re-expand all requested"
unless queue_job @project.id, "reexpand_all_background(#{@project.id})"
flash[:error] = "System error - queued_jobs save failed."
return
end
redirect_to :action => 'show', :id => @project.id
end
def reexpand_all
@project = Project.find(params[:id])
if @project.run_stat
flash[:error] = "Please wait, a background job is still running."
redirect_to :action => 'show', :id => @project.id
return
end
if @project.status != "new"
new_status @project, "re-expand all requested"
unless queue_job @project.id, "reexpand_all_background(#{@project.id})"
flash[:error] = "System error - queued_jobs save failed."
return
end
flash[:notice] = "re-expanding all"
end
redirect_to :action => 'show', :id => @project.id
end
# --------- PRIVATE ---------
private
def check_user_is_owner
@project = Project.find(params[:id])
unless @project.user_id == @current_user.id or @current_user.role == "admin"
flash[:error] = "That project does not belong to you."
redirect_to :action => 'list'
return false
end
return true
end
def get_ftp_list
@ftpUrl = "ftp://#{@user.login}@#{ActiveRecord::Base.configurations[RAILS_ENV]['ftpServer']}"+
":#{ActiveRecord::Base.configurations[RAILS_ENV]['ftpPort']}"
@ftpList = []
@fullPath = ActiveRecord::Base.configurations[RAILS_ENV]['ftpMount']+'/'+@user.login
@extensions = ["zip", "ZIP", "tar.gz", "TAR.GZ", "tar.bz2", "TAR.BZ2", "tgz", "TGZ"]
if File.exists?(@fullPath)
Dir.entries(@fullPath).each do
|file|
fullName = File.join(@fullPath,file)
if File.ftype(fullName) == "file"
if @extensions.any? {|ext| file.ends_with?("." + ext) }
@ftpList << file
end
end
end
end
@ftpList.sort!
end
def upload_one
bg_local_path = ""
msg = ""
# make sure parent paths exist
projectDir = path_to_project_dir(@project.id)
Dir.mkdir(projectDir,0775) unless File.exists?(projectDir)
nextArchiveNo = @project.archive_count+1
plainName = @filename
@filename = "#{"%03d" % nextArchiveNo}_#{@filename}"
#debugging
#msg += "sanitized filename=#{@filename}
"
#msg += "RAILS_ROOT=#{RAILS_ROOT}
"
#msg += "upload path=#{ActiveRecord::Base.configurations[RAILS_ENV]['upload']}
"
#msg += "path_to_file=#{pf}
"
#msg += "nextArchiveNo=#{nextArchiveNo}
"
msg += "Uploading/expanding #{plainName}.
"
# local file upload by browser cannot be passed in bg job params
if @upurl.blank?
if @upftp.blank?
# should be local-file upload, Hmm... where does Mongrel put it during upload?
pf = path_to_file(@project.id, @filename)
if @upload.respond_to?(:local_path) and @upload.local_path and File.exists?(@upload.local_path)
logger.info "#DEBUG local_path=#{@upload.local_path} length=#{@upload.length} original_filename=#{@upload.original_filename}" #debug
bg_local_path = "#{@upload.local_path}_BG"
File.link(@upload.local_path, bg_local_path) # we want the file to be preserved beyond cgi call for bg to use
elsif @upload.respond_to?(:read)
logger.info "#DEBUG length=#{@upload.length} original_filename=#{@upload.original_filename}" #debug
File.open(pf, "wb") { |f| f.write(@upload.read); f.close }
else
raise ArgumentError.new("Do not know how to handle #{@upload.inspect}?")
end
@upload.close
# old way
#if defined? @upload.local_path
# FileUtils.copy(@upload.local_path, pf)
# else
# File.open(pf, "wb") { |f| f.write(@upload.read) }
# end
end
end
allowReloads = "";
if (defined? @params['allow_reloads']) and (@params['allow_reloads']['0'] == "1")
allowReloads = "-allowReloads"
end
# need to preserve the status for the archive NOW
if @project.project_archives.last
@project.project_archives.last.status = @project.status
@project.project_archives.last.archives_active = @project.archives_active
unless saver @project.project_archives.last
flash[:error] = "unable to save @project.project_archives.last"
end
end
new_status @project, "upload requested"
upload_name = @upload.blank? ? "" : @upload.original_filename
param_string = "#{@project.id},\"#{@upurl}\", \"#{@upftp}\", \"#{upload_name}\", \"#{bg_local_path}\", \"#{allowReloads}\""
unless queue_job @project.id, "upload_background(#{param_string})"
flash[:error] = "System error - queued_jobs save failed."
return
end
if flash[:notice]
flash[:notice] += msg
else
flash[:notice] = msg
end
end
end