From 3450c22e971edd0ea9242a6bc0d8413a68ae9e64 Mon Sep 17 00:00:00 2001 From: Darren Cato Date: Sun, 21 Jul 2013 13:37:35 -0400 Subject: [PATCH] first pass at csv import functionality for tracks --- app/controllers/data_controller.rb | 58 ++++++++++++++++++++++++++ app/models/project.rb | 16 +++++++ app/models/todo.rb | 30 ++++++++++++- app/views/data/csv_import.html.erb | 0 app/views/data/csv_map.html.erb | 11 +++++ app/views/data/import.html.erb | 18 ++++++++ app/views/layouts/application.html.erb | 3 +- config/locales/en.yml | 4 +- config/routes.rb | 8 ++++ 9 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 app/views/data/csv_import.html.erb create mode 100644 app/views/data/csv_map.html.erb create mode 100644 app/views/data/import.html.erb diff --git a/app/controllers/data_controller.rb b/app/controllers/data_controller.rb index f9b879f7..1dcf4a58 100644 --- a/app/controllers/data_controller.rb +++ b/app/controllers/data_controller.rb @@ -7,6 +7,64 @@ class DataController < ApplicationController end def import + + end + + def csv_map + if params[:file].blank? + flash[:notice] = "File can't be blank" + redirect_to :back + else + @import_to = params[:import_to] + + #get column headers and formart as [['name', column_number]...] + i = -1 + @headers = import_headers(params[:file].path).collect { |v| [v, i+=1] } + @headers.unshift ['',i] + + #save file for later + directory = "public/uploads/csv" + @path = File.join(directory, params[:file].original_filename) + File.open(@path, "wb") { |f| f.write(params[:file].read) } + + case @import_to + when 'projects' + @labels = [:name, :description] + when 'todos' + @labels = [:description, :context, :project, :notes, :created_at, :due, :completed_at] + else + flash[:error] = "Ivalid import desitination" + redirect_to :back + end + respond_to do |format| + format.html + end + end + end + + def csv_import + begin + case params[:import_to] + when 'projects' + count = Project.import params, current_user + flash[:notice] = "#{count} Projects imported" + when 'todos' + count = Todo.import params, current_user + flash[:notice] = "#{count} Todos imported" + else + flash[:error] = "Ivalid import desitination" + end + rescue Exception => e + flash[:error] = "Error importing CSV: #{e}" + end + File.delete(params[:file]) + redirect_to import_data_path + end + + def import_headers file + CSV.foreach(file, headers: false) do |row| + return row + end end def export diff --git a/app/models/project.rb b/app/models/project.rb index d86785fe..014ad70a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -137,6 +137,22 @@ class Project < ActiveRecord::Base @age_in_days ||= (Date.today - created_at.to_date + 1).to_i end + def self.import params, user + count = 0 + CSV.foreach(params[:file], headers: true) do |row| + unless find_by_name_and_user_id row[params[:name].to_i], user.id + project = new + project.name = row[params[:name].to_i] + project.user = user + project.description = row[params[:description].to_i] if row[params[:description].to_i].present? + project.state = 'active' + project.save! + count += 1 + end + end + count + end + end class NullProject diff --git a/app/models/todo.rb b/app/models/todo.rb index 8eb054c8..c72eaca2 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -1,5 +1,8 @@ class Todo < ActiveRecord::Base + MAX_DESC_LENGTH = 300 + MAX_NOTES_LENGTH = 60000 + before_save :render_note after_save :save_predecessors @@ -106,8 +109,8 @@ class Todo < ActiveRecord::Base # Description field can't be empty, and must be < 100 bytes Notes must be < # 60,000 bytes (65,000 actually, but I'm being cautious) validates_presence_of :description - validates_length_of :description, :maximum => 100 - validates_length_of :notes, :maximum => 60000, :allow_nil => true + validates_length_of :description, :maximum => MAX_DESC_LENGTH + validates_length_of :notes, :maximum => MAX_NOTES_LENGTH, :allow_nil => true validates_presence_of :show_from, :if => :deferred? validates_presence_of :context validate :check_show_from_in_future @@ -431,4 +434,27 @@ class Todo < ActiveRecord::Base end end + def self.import params, user + default_context = Context.where(:user_id=>user.id).order('id').first + + count = 0 + CSV.foreach(params[:file], headers: true) do |row| + unless find_by_description_and_user_id row[params[:description].to_i], user.id + todo = new + todo.user = user + todo.description = row[params[:description].to_i].truncate MAX_DESC_LENGTH + todo.context = Context.find_by_name_and_user_id(row[params[:context].to_i], user.id) || default_context + todo.project = Project.find_by_name_and_user_id(row[params[:project].to_i], user.id) if row[params[:project].to_i].present? + todo.state = row[params[:completed_at].to_i].present? ? 'completed' : 'active' + todo.notes = row[params[:notes].to_i].truncate MAX_NOTES_LENGTH if row[params[:notes].to_i].present? + todo.created_at = row[params[:created_at].to_i] if row[params[:created_at].to_i].present? + todo.due = row[params[:due].to_i] + todo.completed_at = row[params[:completed_at].to_i] if row[params[:completed_at].to_i].present? + todo.save! + count += 1 + end + end + count + end + end diff --git a/app/views/data/csv_import.html.erb b/app/views/data/csv_import.html.erb new file mode 100644 index 00000000..e69de29b diff --git a/app/views/data/csv_map.html.erb b/app/views/data/csv_map.html.erb new file mode 100644 index 00000000..929faa1e --- /dev/null +++ b/app/views/data/csv_map.html.erb @@ -0,0 +1,11 @@ +

Map fields to be imported

+<%= form_tag csv_import_data_path do %> + <% @labels.each do |l| %> + <%= label_tag l %>: + <%= select_tag(l, options_for_select(@headers) ) %> +
+ <% end %> + <%= hidden_field_tag :file, @path %> + <%= hidden_field_tag :import_to, @import_to %> + <%= submit_tag "Import" %> +<% end %> \ No newline at end of file diff --git a/app/views/data/import.html.erb b/app/views/data/import.html.erb new file mode 100644 index 00000000..8984851a --- /dev/null +++ b/app/views/data/import.html.erb @@ -0,0 +1,18 @@ +
+
+
+

Importing data

+
+

Please upload your CSV file

+ <%= form_tag csv_map_data_path, :id => 'upload_form', multipart: true do %> + + <%= select_tag(:import_to, options_for_select([['Projects', 'projects'], ['Todos', 'todos']], 1) ) %> +

+ <%= file_field_tag :file %> + <%= submit_tag "Upload", :id => "upload_form_submit" %> + <% end %> +
+
+
+ +
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 783b3f82..2630f6c7 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -77,7 +77,8 @@
  • <%= t('layouts.navigation.admin') %>
    • <%= navigation_link( t('layouts.navigation.preferences'), preferences_path, {:accesskey => "u", :title => t('layouts.navigation.preferences_title')} ) %>
    • -
    • <%= navigation_link( t('layouts.navigation.export'), data_path, {:accesskey => "i", :title => t('layouts.navigation.export_title')} ) %>
    • +
    • <%= navigation_link( t('layouts.navigation.export'), data_path, {:accesskey => "e", :title => t('layouts.navigation.export_title')} ) %>
    • +
    • <%= navigation_link( t('layouts.navigation.import'), import_data_path, {:accesskey => "i", :title => t('layouts.navigation.import_title')} ) %>
    • <% if current_user.is_admin? -%>
    • <%= navigation_link(t('layouts.navigation.manage_users'), users_path, {:accesskey => "a", :title => t('layouts.navigation.manage_users_title')} ) %>
    • <% end -%> diff --git a/config/locales/en.yml b/config/locales/en.yml index fb4c3201..60ff0a1c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -386,7 +386,8 @@ en: stats: Statistics tickler_title: Tickler manage_users: Manage users - export_title: Import and export data + export_title: Export data + import_title: Import data preferences: Preferences integrations_: Integrate Tracks feeds_title: See a list of available feeds @@ -402,6 +403,7 @@ en: completed_tasks_title: Completed home: Home export: Export + import: Import contexts_title: Contexts calendar: Calendar projects_title: Projects diff --git a/config/routes.rb b/config/routes.rb index 234015b0..4c3fccb3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -139,4 +139,12 @@ Tracksapp::Application.routes.draw do resources :notes resources :preferences + resources :data do + collection do + get :import + post :csv_map + post :csv_import + end + end + end