adding csv import to channels
This commit is contained in:
		@@ -2,6 +2,7 @@ class ChannelsController < ApplicationController
 | 
			
		||||
	before_filter :require_user, :except => [ :show, :post_data ]
 | 
			
		||||
	before_filter :set_channels_menu
 | 
			
		||||
	protect_from_forgery :except => :post_data
 | 
			
		||||
	require 'csv'
 | 
			
		||||
 | 
			
		||||
	def index
 | 
			
		||||
		@channels = current_user.channels
 | 
			
		||||
@@ -72,6 +73,10 @@ class ChannelsController < ApplicationController
 | 
			
		||||
			f.delete
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		# set the channel's last_entry_id to nil
 | 
			
		||||
		channel.last_entry_id = nil
 | 
			
		||||
		channel.save
 | 
			
		||||
 | 
			
		||||
		redirect_to channels_path
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
@@ -146,4 +151,144 @@ class ChannelsController < ApplicationController
 | 
			
		||||
		render :text => status
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	# import view
 | 
			
		||||
	def import
 | 
			
		||||
		get_channel_data
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	# upload csv file to channel
 | 
			
		||||
	def upload
 | 
			
		||||
		# if no data
 | 
			
		||||
		render :text => t(:select_file) and return if params[:upload].blank? or params[:upload][:csv].blank?
 | 
			
		||||
 | 
			
		||||
		channel = Channel.find(params[:channel_id])
 | 
			
		||||
		channel_id = channel.id
 | 
			
		||||
		# make sure channel belongs to current user
 | 
			
		||||
		check_permissions(channel)
 | 
			
		||||
		
 | 
			
		||||
		# set time zone
 | 
			
		||||
		Time.zone = params[:feed][:time_zone]
 | 
			
		||||
 | 
			
		||||
		# read data from uploaded file
 | 
			
		||||
		csv_array = CSV.parse(params[:upload][:csv].read)
 | 
			
		||||
 | 
			
		||||
		# does the column have headers
 | 
			
		||||
		headers = has_headers?(csv_array)
 | 
			
		||||
 | 
			
		||||
		# remember the column positions
 | 
			
		||||
		entry_id_column = -1
 | 
			
		||||
		latitude_column = -1
 | 
			
		||||
		longitude_column = -1
 | 
			
		||||
		elevation_column = -1
 | 
			
		||||
		status_column = -1
 | 
			
		||||
		if headers
 | 
			
		||||
			csv_array[0].each_with_index do |column, index|
 | 
			
		||||
				entry_id_column = index if column.downcase == 'entry_id'
 | 
			
		||||
				latitude_column = index if column.downcase == 'latitude'
 | 
			
		||||
				longitude_column = index if column.downcase == 'longitude'
 | 
			
		||||
				elevation_column = index if column.downcase == 'elevation'
 | 
			
		||||
				status_column = index if column.downcase == 'status'
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		# delete the first row if it contains headers
 | 
			
		||||
		csv_array.delete_at(0) if headers
 | 
			
		||||
 | 
			
		||||
		# determine if the date can be parsed
 | 
			
		||||
		parse_date = date_parsable?(csv_array[0][0])
 | 
			
		||||
 | 
			
		||||
		# if 2 or more rows
 | 
			
		||||
		if !csv_array[1].blank?
 | 
			
		||||
			date1 = parse_date ? Time.parse(csv_array[0][0]) : Time.at(csv_array[0][0])
 | 
			
		||||
			date2 = parse_date ? Time.parse(csv_array[1][0]) : Time.at(csv_array[1][0])
 | 
			
		||||
 | 
			
		||||
			# reverse the array if 1st date is larger than 2nd date
 | 
			
		||||
			csv_array = csv_array.reverse if date1 > date2
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		# loop through each row
 | 
			
		||||
		csv_array.each do |row|
 | 
			
		||||
			# if row isn't blank
 | 
			
		||||
			if !row.blank?
 | 
			
		||||
				feed = Feed.new
 | 
			
		||||
	
 | 
			
		||||
				# set location and status then delete the rows
 | 
			
		||||
				# these 4 deletes must be performed in the proper (reverse) order
 | 
			
		||||
				feed.status = row.delete_at(status_column) if status_column > 0
 | 
			
		||||
				feed.elevation = row.delete_at(elevation_column) if elevation_column > 0
 | 
			
		||||
				feed.longitude = row.delete_at(longitude_column) if longitude_column > 0
 | 
			
		||||
				feed.latitude = row.delete_at(latitude_column) if latitude_column > 0
 | 
			
		||||
 | 
			
		||||
				# remove entry_id column if necessary
 | 
			
		||||
				row.delete_at(entry_id_column) if entry_id_column > 0
 | 
			
		||||
 | 
			
		||||
				# update entry_id for channel and feed
 | 
			
		||||
				entry_id = channel.last_entry_id.nil? ? 1 : channel.last_entry_id + 1
 | 
			
		||||
				channel.last_entry_id = entry_id
 | 
			
		||||
				feed.entry_id = entry_id
 | 
			
		||||
	
 | 
			
		||||
				# set feed data
 | 
			
		||||
				feed.channel_id = channel_id
 | 
			
		||||
				feed.created_at = parse_date ? Time.zone.parse(row[0]) : Time.zone.at(row[0].to_f)
 | 
			
		||||
				feed.raw_data = row.to_s
 | 
			
		||||
				feed.field1 = row[1]
 | 
			
		||||
				feed.field2 = row[2]
 | 
			
		||||
				feed.field3 = row[3]
 | 
			
		||||
				feed.field4 = row[4]
 | 
			
		||||
				feed.field5 = row[5]
 | 
			
		||||
				feed.field6 = row[6]
 | 
			
		||||
				feed.field7 = row[7]
 | 
			
		||||
				feed.field8 = row[8]
 | 
			
		||||
 | 
			
		||||
				# save channel and feed
 | 
			
		||||
				feed.save
 | 
			
		||||
				channel.save
 | 
			
		||||
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		# set the user's time zone back
 | 
			
		||||
		set_time_zone(params)
 | 
			
		||||
 | 
			
		||||
		# redirect 
 | 
			
		||||
		redirect_to channel_path(channel.id)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	# determine if the date can be parsed
 | 
			
		||||
	def date_parsable?(date)
 | 
			
		||||
		return !is_a_number?(date)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	# determine if the csv file has headers
 | 
			
		||||
	def has_headers?(csv_array)
 | 
			
		||||
		headers = false
 | 
			
		||||
 | 
			
		||||
		# if there are at least 2 rows
 | 
			
		||||
		if (csv_array[0] and csv_array[1])
 | 
			
		||||
			row0_integers = 0
 | 
			
		||||
			row1_integers = 0	
 | 
			
		||||
	
 | 
			
		||||
			# if first row, first value contains 'create' or 'date', assume it has headers
 | 
			
		||||
			if (csv_array[0][0].downcase.include?('create') or csv_array[0][0].downcase.include?('date'))
 | 
			
		||||
				headers = true
 | 
			
		||||
			else
 | 
			
		||||
				# count integers in row0
 | 
			
		||||
				csv_array[0].each_with_index do |value, i|
 | 
			
		||||
					row0_integers += 1 if is_a_number?(value)
 | 
			
		||||
				end
 | 
			
		||||
	
 | 
			
		||||
				# count integers in row1
 | 
			
		||||
				csv_array[1].each_with_index do |value, i|
 | 
			
		||||
					row1_integers += 1 if is_a_number?(value)
 | 
			
		||||
				end
 | 
			
		||||
	
 | 
			
		||||
				# if row1 has more integers, assume row0 is headers
 | 
			
		||||
				headers = true if row1_integers > row0_integers
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		return headers
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										16
									
								
								app/views/channels/import.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/views/channels/import.html.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
<h2>
 | 
			
		||||
  <%= link_to t(:channels), channels_path %> »
 | 
			
		||||
  <%= link_to channel_path(@channel.id) do %> <%= t(:channel) %> <%= @channel.id %><% end %> »
 | 
			
		||||
  <%= t(:import) %>
 | 
			
		||||
</h2>
 | 
			
		||||
 | 
			
		||||
<%= t(:upload_select) %>
 | 
			
		||||
<br /><br />
 | 
			
		||||
<%= form_for :upload, :url => {:controller => 'channels', :action => 'upload', :channel_id => params[:channel_id]}, :html => { :multipart => true } do |f| %>
 | 
			
		||||
  <%= f.file_field :csv %>
 | 
			
		||||
  <br /><br />
 | 
			
		||||
  <%= t(:time_zone) %>
 | 
			
		||||
  <%= time_zone_select 'feed', 'time_zone', nil, :default => 'UTC' %>
 | 
			
		||||
  <br /><br />
 | 
			
		||||
  <%= f.submit t(:upload), :disable_with => t(:uploading) %>
 | 
			
		||||
<% end %>
 | 
			
		||||
@@ -4,12 +4,15 @@
 | 
			
		||||
</h2>
 | 
			
		||||
 | 
			
		||||
<% if current_user && current_user.id == @channel.user_id %>
 | 
			
		||||
  <%= link_to t(:channel_edit), edit_channel_path(@channel.id) %> |
 | 
			
		||||
  <%= link_to t(:api_keys_manage), channel_api_keys_path(@channel) %> |
 | 
			
		||||
  <%= link_to "#{t(:charts_view)}", channel_charts_path(@channel) %> |
 | 
			
		||||
	<%= link_to "#{t(:channel_feed)} (json)", channel_feed_index_path(@channel, :key => @key, :format => :json) %> |
 | 
			
		||||
	<%= link_to "#{t(:channel_feed)} (xml)", channel_feed_index_path(@channel, :key => @key, :format => :xml) %> |
 | 
			
		||||
	<%= link_to "#{t(:channel_feed)} (csv)", channel_feed_index_path(@channel, :key => @key, :format => :csv) %>
 | 
			
		||||
  <ul>
 | 
			
		||||
    <li><%= link_to t(:channel_edit), edit_channel_path(@channel.id) %></li>
 | 
			
		||||
    <li><%= link_to t(:api_keys_manage), channel_api_keys_path(@channel) %></li>
 | 
			
		||||
    <li><%= link_to "#{t(:charts_view)}", channel_charts_path(@channel) %></li>
 | 
			
		||||
    <li><%= link_to "#{t(:channel_feed)} (json)", channel_feed_index_path(@channel, :key => @key, :format => :json) %></li>
 | 
			
		||||
    <li><%= link_to "#{t(:channel_feed)} (xml)", channel_feed_index_path(@channel, :key => @key, :format => :xml) %></li>
 | 
			
		||||
    <li><%= link_to "#{t(:channel_feed)} (csv)", channel_feed_index_path(@channel, :key => @key, :format => :csv) %></li>
 | 
			
		||||
		<li><%= link_to t(:import_data), channel_import_path(@channel) %></li>
 | 
			
		||||
  </ul>  
 | 
			
		||||
 | 
			
		||||
  <br /><br />
 | 
			
		||||
 | 
			
		||||
@@ -30,6 +33,10 @@
 | 
			
		||||
    <td class="left"><%= t(:channel_description) %>:</td>
 | 
			
		||||
    <td><%= @channel.description %></td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td class="left"><%= t(:entries) %>:</td>
 | 
			
		||||
    <td><%= (@channel.last_entry_id) ? @channel.last_entry_id : '0' %></td>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <td class="left"><%= t(:created) %>:</td>
 | 
			
		||||
    <td><%= l @channel.created_at, :format => :pretty %></td>
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,10 @@ Thingspeak::Application.routes.draw do
 | 
			
		||||
	match 'channels/:channel_id/field/:field_id(.:format)' => 'feed#index'
 | 
			
		||||
	match 'channels/:channel_id/feed/entry/:id(.:format)' => 'feed#show'
 | 
			
		||||
 | 
			
		||||
	# import
 | 
			
		||||
	match 'channels/:channel_id/import' => 'channels#import', :as => 'channel_import'
 | 
			
		||||
	match 'channels/:channel_id/upload' => 'channels#upload'
 | 
			
		||||
 | 
			
		||||
	# nest feeds into channels
 | 
			
		||||
	resources :channels do
 | 
			
		||||
		resources :feed
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user