TracksApp/tracks

View on GitHub
app/controllers/data_controller.rb

Summary

Maintainability
A
0 mins
Test Coverage
class DataController < ApplicationController
  require 'csv'

  def index
    @page_title = t('data.export.page_title')
  end

  def import; end

  def csv_map
    if params[:file].blank?
      flash[:notice] = t "data.import.errors.file_blank"
      redirect_back fallback_location: root_path
    else
      @import_to = params[:import_to]

      begin
        # Get column headers and format as [['name', column_number]...]
        i = -1
        @headers = import_headers(params[:file].path).collect { |v| [v, i += 1] }
        @headers.unshift ['', i]
      rescue Exception => e
        flash[:error] = t "data.import.errors.invalid_csv", e: e
        redirect_back fallback_location: root_path
        return
      end

      # Save file for later
      begin
        uploaded_file = params[:file]
        @filename = sanitize_filename(uploaded_file.original_filename)
        path_and_file = Rails.root.join('public', 'uploads', 'csv', @filename)
        File.open(path_and_file, "wb") { |f| f.write(uploaded_file.read) }
      rescue Exception => e
        flash[:error] = t "data.import.errors.save_error", path_and_file: path_and_file, e: e
        redirect_back fallback_location: root_path
        return
      end

      case @import_to
      when 'projects'
        @labels = [:name, :description]
      when 'todos'
        @labels = [:description, :context, :project, :notes, :created_at, :due, :completed_at]
      else
        flash[:error] = t "data.import.errors.invalid_destination"
        redirect_back fallback_location: root_path
      end
      respond_to do |format|
        format.html
      end
    end
  end

  def csv_import
    begin
      filename = sanitize_filename(params[:file])
      path_and_file = Rails.root.join('public', 'uploads', 'csv', filename)
      case params[:import_to]
      when 'projects'
        count = Project.import path_and_file, params, current_user
        flash[:notice] = t 'data.import.projects_count', count: count
      when 'todos'
        count = Todo.import path_and_file, params, current_user
        if !count
          flash[:error] = t('data.import.errors.no_context')
        else
          flash[:notice] = t 'data.import.todos_count', count: count
        end
      else
        flash[:error] = t('data.import.errors.invalid_destination')
      end
    rescue Exception => e
      flash[:error] = t 'data.import.errors.invalid_destination', e: e
    end
    File.delete(path_and_file)
    redirect_to import_data_path
  end

  def import_headers(file)
    CSV.foreach(file, headers: false) do |row|
      return row
    end
  end

  def export
    # Show list of formats for export
  end

  # Thanks to a tip by Gleb Arshinov
  # <http://lists.rubyonrails.org/pipermail/rails/2004-November/000199.html>
  def yaml_export
    all_tables = {}

    all_tables['todos'] = current_user.todos.includes(:tags).load
    all_tables['contexts'] = current_user.contexts.load
    all_tables['projects'] = current_user.projects.load

    todo_tag_ids = Tag.find_by_sql([
        "SELECT DISTINCT tags.id
         FROM tags, taggings, todos
         WHERE todos.user_id = ?
         AND tags.id = taggings.tag_id
         AND taggings.taggable_id = todos.id", current_user.id])
    rec_todo_tag_ids = Tag.find_by_sql([
        "SELECT DISTINCT tags.id
         FROM tags, taggings, recurring_todos
         WHERE recurring_todos.user_id = ?
         AND tags.id = taggings.tag_id
         AND taggings.taggable_id = recurring_todos.id", current_user.id])
    tags = Tag.where("id IN (?) OR id IN (?)", todo_tag_ids, rec_todo_tag_ids)
    taggings = Tagging.where("tag_id IN (?) OR tag_id IN(?)", todo_tag_ids, rec_todo_tag_ids)

    all_tables['tags'] = tags.load
    all_tables['taggings'] = taggings.load
    all_tables['notes'] = current_user.notes.load
    all_tables['recurring_todos'] = current_user.recurring_todos.load

    result = all_tables.to_yaml
    # TODO: general functionality for line endings
    result.gsub!(/\n/, "\r\n")
    send_data(result, :filename => "tracks_backup.yml", :type => 'text/plain')
  end

  # export all actions as csv
  def csv_actions
    content_type = 'text/csv'
    CSV.generate(result = "") do |csv|
      csv << ["id", "Context", "Project", "Description", "Notes", "Tags",
        "Created at", "Due", "Completed at", "User ID", "Show from",
        "state"]
      current_user.todos.includes(:context, :project, :taggings, :tags).each do |todo|
        csv << [todo.id, todo.context.name,
          todo.project_id.nil? ? "" : todo.project.name,
          todo.description,
          todo.notes, todo.tags.collect { |t| t.name }.join(', '),
          todo.created_at.to_formatted_s(:db),
          todo.due? ? todo.due.to_formatted_s(:db) : "",
          todo.completed_at? ? todo.completed_at.to_formatted_s(:db) : "",
          todo.user_id,
          todo.show_from? ? todo.show_from.to_formatted_s(:db) : "",
          todo.state]
      end
    end
    send_data(result, :filename => "todos.csv", :type => content_type)
  end

  # export all notes as csv
  def csv_notes
    content_type = 'text/csv'
    CSV.generate(result = "") do |csv|
      csv << ["id", "User ID", "Project", "Note", "Created at", "Updated at"]
      # had to remove project include because it's association order is leaking
      # through and causing an ambiguous column ref even with_exclusive_scope
      # didn't seem to help -JamesKebinger
      current_user.notes.reorder("notes.created_at").each do |note|
        # Format dates in ISO format for easy sorting in spreadsheet Print
        # context and project names for easy viewing
        csv << [note.id, note.user_id,
          note.project_id = note.project_id.nil? ? "" : note.project.name,
          note.body, note.created_at.to_formatted_s(:db),
          note.updated_at.to_formatted_s(:db)]
      end
    end
    send_data(result, :filename => "notes.csv", :type => content_type)
  end

  def xml_export
    todo_tag_ids = Tag.find_by_sql([
        "SELECT DISTINCT tags.id
         FROM tags, taggings, todos
         WHERE todos.user_id = ?
         AND tags.id = taggings.tag_id
         AND taggings.taggable_id = todos.id", current_user.id])
    rec_todo_tag_ids = Tag.find_by_sql([
        "SELECT DISTINCT tags.id
         FROM tags, taggings, recurring_todos
         WHERE recurring_todos.user_id = ?
         AND tags.id = taggings.tag_id
         AND taggings.taggable_id = recurring_todos.id", current_user.id])
    tags = Tag.where("id IN (?) OR id IN (?)", todo_tag_ids, rec_todo_tag_ids)
    taggings = Tagging.where("tag_id IN (?) OR tag_id IN(?)", todo_tag_ids, rec_todo_tag_ids)

    result = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><tracks_data>"
    result << current_user.todos.to_xml(:skip_instruct => true)
    result << current_user.contexts.to_xml(:skip_instruct => true)
    result << current_user.projects.to_xml(:skip_instruct => true)
    result << tags.to_xml(:skip_instruct => true)
    result << taggings.to_xml(:skip_instruct => true)
    result << current_user.notes.to_xml(:skip_instruct => true)
    result << current_user.recurring_todos.to_xml(:skip_instruct => true)
    result << "</tracks_data>"
    send_data(result, :filename => "tracks_data.xml", :type => 'text/xml')
  end

  def yaml_form
    # Draw the form to input the YAML text data
  end

  # adjusts time to utc
  def adjust_time(timestring)
    if (timestring == '') || (timestring == nil)
      return nil
    else
      return Time.parse(timestring + 'UTC')
    end
  end

  def yaml_import
    raise t "data.import.yaml_disabled"
  end

  private

  def sanitize_filename(filename)
    filename.gsub(/[^0-9A-z.\-]/, '_')
  end
end