mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-16 15:20:13 +01:00
first pass at csv import functionality for tracks
This commit is contained in:
parent
d093ba39bd
commit
3450c22e97
9 changed files with 144 additions and 4 deletions
|
|
@ -7,6 +7,64 @@ class DataController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def import
|
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
|
end
|
||||||
|
|
||||||
def export
|
def export
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,22 @@ class Project < ActiveRecord::Base
|
||||||
@age_in_days ||= (Date.today - created_at.to_date + 1).to_i
|
@age_in_days ||= (Date.today - created_at.to_date + 1).to_i
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
class NullProject
|
class NullProject
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
class Todo < ActiveRecord::Base
|
class Todo < ActiveRecord::Base
|
||||||
|
|
||||||
|
MAX_DESC_LENGTH = 300
|
||||||
|
MAX_NOTES_LENGTH = 60000
|
||||||
|
|
||||||
before_save :render_note
|
before_save :render_note
|
||||||
after_save :save_predecessors
|
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 <
|
# 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)
|
# 60,000 bytes (65,000 actually, but I'm being cautious)
|
||||||
validates_presence_of :description
|
validates_presence_of :description
|
||||||
validates_length_of :description, :maximum => 100
|
validates_length_of :description, :maximum => MAX_DESC_LENGTH
|
||||||
validates_length_of :notes, :maximum => 60000, :allow_nil => true
|
validates_length_of :notes, :maximum => MAX_NOTES_LENGTH, :allow_nil => true
|
||||||
validates_presence_of :show_from, :if => :deferred?
|
validates_presence_of :show_from, :if => :deferred?
|
||||||
validates_presence_of :context
|
validates_presence_of :context
|
||||||
validate :check_show_from_in_future
|
validate :check_show_from_in_future
|
||||||
|
|
@ -431,4 +434,27 @@ class Todo < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
||||||
0
app/views/data/csv_import.html.erb
Normal file
0
app/views/data/csv_import.html.erb
Normal file
11
app/views/data/csv_map.html.erb
Normal file
11
app/views/data/csv_map.html.erb
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<h3>Map fields to be imported<h3>
|
||||||
|
<%= form_tag csv_import_data_path do %>
|
||||||
|
<% @labels.each do |l| %>
|
||||||
|
<%= label_tag l %>:
|
||||||
|
<%= select_tag(l, options_for_select(@headers) ) %>
|
||||||
|
<br>
|
||||||
|
<% end %>
|
||||||
|
<%= hidden_field_tag :file, @path %>
|
||||||
|
<%= hidden_field_tag :import_to, @import_to %>
|
||||||
|
<%= submit_tag "Import" %>
|
||||||
|
<% end %>
|
||||||
18
app/views/data/import.html.erb
Normal file
18
app/views/data/import.html.erb
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<div id="display_box">
|
||||||
|
<div id="feeds">
|
||||||
|
<div id="feedlegend">
|
||||||
|
<h2>Importing data</h2>
|
||||||
|
<div>
|
||||||
|
<h3>Please upload your CSV file</h3>
|
||||||
|
<%= form_tag csv_map_data_path, :id => 'upload_form', multipart: true do %>
|
||||||
|
<label for="import_to">Import to:</label>
|
||||||
|
<%= select_tag(:import_to, options_for_select([['Projects', 'projects'], ['Todos', 'todos']], 1) ) %>
|
||||||
|
<br><br>
|
||||||
|
<%= file_field_tag :file %>
|
||||||
|
<%= submit_tag "Upload", :id => "upload_form_submit" %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
@ -77,7 +77,8 @@
|
||||||
<li><a href="#"><%= t('layouts.navigation.admin') %></a>
|
<li><a href="#"><%= t('layouts.navigation.admin') %></a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><%= navigation_link( t('layouts.navigation.preferences'), preferences_path, {:accesskey => "u", :title => t('layouts.navigation.preferences_title')} ) %></li>
|
<li><%= navigation_link( t('layouts.navigation.preferences'), preferences_path, {:accesskey => "u", :title => t('layouts.navigation.preferences_title')} ) %></li>
|
||||||
<li><%= navigation_link( t('layouts.navigation.export'), data_path, {:accesskey => "i", :title => t('layouts.navigation.export_title')} ) %></li>
|
<li><%= navigation_link( t('layouts.navigation.export'), data_path, {:accesskey => "e", :title => t('layouts.navigation.export_title')} ) %></li>
|
||||||
|
<li><%= navigation_link( t('layouts.navigation.import'), import_data_path, {:accesskey => "i", :title => t('layouts.navigation.import_title')} ) %></li>
|
||||||
<% if current_user.is_admin? -%>
|
<% if current_user.is_admin? -%>
|
||||||
<li><%= navigation_link(t('layouts.navigation.manage_users'), users_path, {:accesskey => "a", :title => t('layouts.navigation.manage_users_title')} ) %></li>
|
<li><%= navigation_link(t('layouts.navigation.manage_users'), users_path, {:accesskey => "a", :title => t('layouts.navigation.manage_users_title')} ) %></li>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
|
|
|
||||||
|
|
@ -386,7 +386,8 @@ en:
|
||||||
stats: Statistics
|
stats: Statistics
|
||||||
tickler_title: Tickler
|
tickler_title: Tickler
|
||||||
manage_users: Manage users
|
manage_users: Manage users
|
||||||
export_title: Import and export data
|
export_title: Export data
|
||||||
|
import_title: Import data
|
||||||
preferences: Preferences
|
preferences: Preferences
|
||||||
integrations_: Integrate Tracks
|
integrations_: Integrate Tracks
|
||||||
feeds_title: See a list of available feeds
|
feeds_title: See a list of available feeds
|
||||||
|
|
@ -402,6 +403,7 @@ en:
|
||||||
completed_tasks_title: Completed
|
completed_tasks_title: Completed
|
||||||
home: Home
|
home: Home
|
||||||
export: Export
|
export: Export
|
||||||
|
import: Import
|
||||||
contexts_title: Contexts
|
contexts_title: Contexts
|
||||||
calendar: Calendar
|
calendar: Calendar
|
||||||
projects_title: Projects
|
projects_title: Projects
|
||||||
|
|
|
||||||
|
|
@ -139,4 +139,12 @@ Tracksapp::Application.routes.draw do
|
||||||
resources :notes
|
resources :notes
|
||||||
resources :preferences
|
resources :preferences
|
||||||
|
|
||||||
|
resources :data do
|
||||||
|
collection do
|
||||||
|
get :import
|
||||||
|
post :csv_map
|
||||||
|
post :csv_import
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue