New fields updated_at and created_at

1.0
Fernando Blat 14 years ago
parent 94c2bda943
commit 706213e2f5

@ -14,7 +14,7 @@ class Table < Sequel::Model(:user_tables)
attr_accessor :force_schema, :import_from_file attr_accessor :force_schema, :import_from_file
CARTODB_COLUMNS = %W{ cartodb_id } CARTODB_COLUMNS = %W{ cartodb_id created_at updated_at }
## Callbacks ## Callbacks
def validate def validate
@ -44,6 +44,8 @@ class Table < Sequel::Model(:user_tables)
String :name String :name
column :location, 'geometry' column :location, 'geometry'
String :description, :text => true String :description, :text => true
DateTime :created_at, :default => "now()"
DateTime :updated_at, :default => "now()"
constraint(:enforce_geotype_location){"(geometrytype(location) = 'POINT'::text OR location IS NULL)"} constraint(:enforce_geotype_location){"(geometrytype(location) = 'POINT'::text OR location IS NULL)"}
end end
else else
@ -57,8 +59,16 @@ class Table < Sequel::Model(:user_tables)
# If import_from_file is blank primary key is added now. # If import_from_file is blank primary key is added now.
# If not we add it after importing the CSV file, becaus the number of columns # If not we add it after importing the CSV file, becaus the number of columns
# will not match # will not match
sanitized_force_schema.unshift("cartodb_id SERIAL PRIMARY KEY") if import_from_file.blank? if import_from_file.blank?
sanitized_force_schema.unshift("cartodb_id SERIAL PRIMARY KEY")
sanitized_force_schema.unshift("created_at timestamp")
sanitized_force_schema.unshift("updated_at timestamp")
end
user_database.run("CREATE TABLE #{self.name} (#{sanitized_force_schema.join(', ')})") user_database.run("CREATE TABLE #{self.name} (#{sanitized_force_schema.join(', ')})")
if import_from_file.blank?
user_database.run("alter table #{self.name} alter column created_at SET DEFAULT now()")
user_database.run("alter table #{self.name} alter column updated_at SET DEFAULT now()")
end
end end
end end
end end
@ -161,9 +171,14 @@ class Table < Sequel::Model(:user_tables)
end end
def schema def schema
owner.in_database do |user_database| temporal_schema = owner.in_database do |user_database|
user_database.schema(name.to_sym).map{ |c| [c.first, c[1][:db_type]] } user_database.schema(name.to_sym).map{ |c| [c.first, c[1][:db_type]] }
end end
schema = temporal_schema.delete([:cartodb_id, "integer"])
schema = [schema] + temporal_schema
created_at = schema.delete([:created_at, "timestamp without time zone"])
updated_at = schema.delete([:updated_at, "timestamp without time zone"])
schema.push([:created_at, "timestamp"]).push([:updated_at, "timestamp"])
end end
def add_column!(options) def add_column!(options)
@ -212,16 +227,20 @@ class Table < Sequel::Model(:user_tables)
end end
def to_json(options = {}) def to_json(options = {})
rows, columns = [], [] rows = []
limit = (options[:rows_per_page] || 10).to_i limit = (options[:rows_per_page] || 10).to_i
offset = (options[:page] || 0).to_i*limit offset = (options[:page] || 0).to_i*limit
owner.in_database do |user_database| owner.in_database do |user_database|
columns = user_database.schema(name.to_sym).map{ |c| [c.first, c[1][:db_type]] } rows = user_database[name.to_sym].limit(limit,offset).
rows = user_database[name.to_sym].limit(limit,offset).order(:cartodb_id).all order(:cartodb_id).select(*schema.map{ |e| e[0]}).all.map do |row|
row[:created_at] = row[:created_at].strftime("%H:%M:%S %Y-%m-%d")
row[:updated_at] = row[:updated_at].strftime("%H:%M:%S %Y-%m-%d")
row
end
end end
{ {
:total_rows => rows_counted, :total_rows => rows_counted,
:columns => columns, :columns => schema,
:rows => rows :rows => rows
} }
end end
@ -270,6 +289,8 @@ class Table < Sequel::Model(:user_tables)
user_database.run("alter table #{self.name} add unique (cartodb_id)") user_database.run("alter table #{self.name} add unique (cartodb_id)")
user_database.run("alter table #{self.name} drop constraint #{self.name}_cartodb_id_key restrict") user_database.run("alter table #{self.name} drop constraint #{self.name}_cartodb_id_key restrict")
user_database.run("alter table #{self.name} add primary key (cartodb_id)") user_database.run("alter table #{self.name} add primary key (cartodb_id)")
user_database.run("alter table #{self.name} add column created_at timestamp DEFAULT now()")
user_database.run("alter table #{self.name} add column updated_at timestamp DEFAULT now()")
end end
ensure ensure
FileUtils.rm filename FileUtils.rm filename
@ -365,6 +386,19 @@ class Table < Sequel::Model(:user_tables)
CREATE TRIGGER protect_data_trigger CREATE TRIGGER protect_data_trigger
BEFORE UPDATE ON #{self.name} BEFORE UPDATE ON #{self.name}
FOR EACH ROW EXECUTE PROCEDURE protect_data(); FOR EACH ROW EXECUTE PROCEDURE protect_data();
DROP TRIGGER IF EXISTS update_updated_at_trigger ON #{self.name};
CREATE OR REPLACE FUNCTION update_updated_at() RETURNS TRIGGER AS $update_updated_at_trigger$
BEGIN
NEW.updated_at := now();
RETURN NEW;
END;
$update_updated_at_trigger$ LANGUAGE plpgsql;
CREATE TRIGGER update_updated_at_trigger
BEFORE UPDATE ON #{self.name}
FOR EACH ROW EXECUTE PROCEDURE update_updated_at();
TRIGGER TRIGGER
) )
end end

@ -24,14 +24,14 @@ feature "Tables JSON API" do
response.status.should == 200 response.status.should == 200
json_response = JSON(response.body) json_response = JSON(response.body)
json_response['total_rows'].should == 100 json_response['total_rows'].should == 100
json_response['rows'][0].symbolize_keys.should == content[0] json_response['rows'][0].symbolize_keys.slice(:cartodb_id, :name, :location, :description).should == content[0].slice(:cartodb_id, :name, :location, :description)
json_response['rows'][1].symbolize_keys.should == content[1] json_response['rows'][1].symbolize_keys.slice(:cartodb_id, :name, :location, :description).should == content[1].slice(:cartodb_id, :name, :location, :description)
get_json "/api/json/tables/#{table.id}?rows_per_page=2&page=1" get_json "/api/json/tables/#{table.id}?rows_per_page=2&page=1"
response.status.should == 200 response.status.should == 200
json_response = JSON(response.body) json_response = JSON(response.body)
json_response['rows'][0].symbolize_keys.should == content[2] json_response['rows'][0].symbolize_keys.slice(:cartodb_id, :name, :location, :description).should == content[2].slice(:cartodb_id, :name, :location, :description)
json_response['rows'][1].symbolize_keys.should == content[3] json_response['rows'][1].symbolize_keys.slice(:cartodb_id, :name, :location, :description).should == content[3].slice(:cartodb_id, :name, :location, :description)
end end
scenario "Update the privacy status of a table" do scenario "Update the privacy status of a table" do
@ -111,7 +111,7 @@ feature "Tables JSON API" do
get_json "/api/json/tables/#{table.id}/schema" get_json "/api/json/tables/#{table.id}/schema"
response.status.should == 200 response.status.should == 200
json_response = JSON(response.body) json_response = JSON(response.body)
json_response.should == [["cartodb_id", "integer"], ["name", "text"], ["location", "geometry"], ["description", "text"]] json_response.should == [["cartodb_id", "integer"], ["name", "text"], ["location", "geometry"], ["description", "text"], ["created_at", "timestamp"], ["updated_at", "timestamp"]]
end end
scenario "Get a list of tables" do scenario "Get a list of tables" do
@ -156,7 +156,11 @@ feature "Tables JSON API" do
json_response = JSON(response.body) json_response = JSON(response.body)
json_response.should == {"name" => "postal_code", "type" => 'integer'} json_response.should == {"name" => "postal_code", "type" => 'integer'}
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:postal_code, "integer"]] table.schema.should == [
[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"],
[:description, "text"], [:postal_code, "integer"],
[:created_at, "timestamp"], [:updated_at, "timestamp"]
]
put_json "/api/json/tables/#{table.id}/update_schema", { put_json "/api/json/tables/#{table.id}/update_schema", {
:what => "modify", :column => { :what => "modify", :column => {
@ -167,7 +171,10 @@ feature "Tables JSON API" do
json_response = JSON(response.body) json_response = JSON(response.body)
json_response.should == {"name" => "postal_code", "type" => 'text'} json_response.should == {"name" => "postal_code", "type" => 'text'}
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:postal_code, "text"]] table.schema.should == [
[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:postal_code, "text"],
[:created_at, "timestamp"], [:updated_at, "timestamp"]
]
put_json "/api/json/tables/#{table.id}/update_schema", { put_json "/api/json/tables/#{table.id}/update_schema", {
:what => "add", :column => { :what => "add", :column => {
@ -185,7 +192,10 @@ feature "Tables JSON API" do
} }
response.status.should == 200 response.status.should == 200
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"]] table.schema.should == [
[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"],
[:created_at, "timestamp"], [:updated_at, "timestamp"]
]
put_json "/api/json/tables/#{table.id}/update_schema", { put_json "/api/json/tables/#{table.id}/update_schema", {
:what => "drop", :column => { :what => "drop", :column => {
@ -331,7 +341,10 @@ feature "Tables JSON API" do
get_json "/api/json/tables/#{response.location.match(/\/(\d+)$/)[1].to_i}/schema" get_json "/api/json/tables/#{response.location.match(/\/(\d+)$/)[1].to_i}/schema"
response.status.should == 200 response.status.should == 200
json_response = JSON(response.body) json_response = JSON(response.body)
json_response.should == [["cartodb_id", "integer"], ["code", "character(5)"], ["title", "character varying(40)"], ["did", "integer"], ["date_prod", "date"], ["kind", "character varying(10)"]] json_response.should == [
["cartodb_id", "integer"], ["code", "character(5)"], ["title", "character varying(40)"], ["did", "integer"],
["date_prod", "date"], ["kind", "character varying(10)"], ["created_at", "timestamp"], ["updated_at", "timestamp"]
]
end end
scenario "Import a file when the schema is wrong" do scenario "Import a file when the schema is wrong" do

@ -45,7 +45,7 @@ feature "Tables" do
page.find("div.performing_op p").text.should == 'Loading...' page.find("div.performing_op p").text.should == 'Loading...'
sleep 1 sleep 1
page.find("div.performing_op p").text.should == 'Your table tags has been updated' page.find("div.performing_op p").text.should == 'Your table tags have been updated'
page.all("span.tags p")[0].text.should == 'twitter' page.all("span.tags p")[0].text.should == 'twitter'
page.all("span.tags p")[1].text.should == 'tag1' page.all("span.tags p")[1].text.should == 'tag1'
@ -53,7 +53,7 @@ feature "Tables" do
page.find("li.tagit-new input.tagit-input").set("tag3,") page.find("li.tagit-new input.tagit-input").set("tag3,")
page.find_link("Save").click page.find_link("Save").click
page.find("div.performing_op p").text.should == 'Your table tags has been updated' page.find("div.performing_op p").text.should == 'Your table tags have been updated'
page.all("span.tags p")[0].text.should == 'twitter' page.all("span.tags p")[0].text.should == 'twitter'
page.all("span.tags p")[1].text.should == 'tag1' page.all("span.tags p")[1].text.should == 'tag1'
page.all("span.tags p")[2].text.should == 'tag3' page.all("span.tags p")[2].text.should == 'tag3'

@ -62,7 +62,7 @@ describe Table do
table = create_table :user_id => user.id table = create_table :user_id => user.id
table.to_json[:total_rows].should == 0 table.to_json[:total_rows].should == 0
table.to_json[:columns].should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"]] table.to_json[:columns].should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
table.to_json[:rows].should be_empty table.to_json[:rows].should be_empty
10.times do 10.times do
@ -123,12 +123,12 @@ describe Table do
it "can return its schema" do it "can return its schema" do
table = create_table table = create_table
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
end end
it "can modify it's schema" do it "can modify it's schema" do
table = create_table table = create_table
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
lambda { lambda {
table.add_column!(:name => "my column with bad status", :type => "textttt") table.add_column!(:name => "my column with bad status", :type => "textttt")
@ -138,32 +138,32 @@ describe Table do
resp = table.add_column!(:name => "my new column", :type => "integer") resp = table.add_column!(:name => "my new column", :type => "integer")
resp.should == {:name => 'my_new_column', :type => 'integer'} resp.should == {:name => 'my_new_column', :type => 'integer'}
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column, "integer"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column, "integer"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
resp = table.modify_column!(:old_name => "my_new_column", :new_name => "my new column new name", :type => "text") resp = table.modify_column!(:old_name => "my_new_column", :new_name => "my new column new name", :type => "text")
resp.should == {:name => 'my_new_column_new_name', :type => 'text'} resp.should == {:name => 'my_new_column_new_name', :type => 'text'}
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column_new_name, "text"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column_new_name, "text"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
resp = table.modify_column!(:old_name => "my_new_column_new_name", :new_name => "my new column") resp = table.modify_column!(:old_name => "my_new_column_new_name", :new_name => "my new column")
resp.should == {:name => 'my_new_column', :type => nil} resp.should == {:name => 'my_new_column', :type => nil}
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column, "text"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column, "text"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
resp = table.modify_column!(:name => "my_new_column", :type => "text") resp = table.modify_column!(:name => "my_new_column", :type => "text")
resp.should == {:name => 'my_new_column', :type => 'text'} resp.should == {:name => 'my_new_column', :type => 'text'}
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column, "text"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column, "text"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
table.drop_column!(:name => "location") table.drop_column!(:name => "location")
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:description, "text"], [:my_new_column, "text"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:description, "text"], [:my_new_column, "text"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
lambda { lambda {
table.drop_column!(:name => "location") table.drop_column!(:name => "location")
}.should raise_error }.should raise_error
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:description, "text"], [:my_new_column, "text"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:description, "text"], [:my_new_column, "text"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
end end
it "cannot modify :cartodb_id column" do it "cannot modify :cartodb_id column" do
@ -191,15 +191,15 @@ describe Table do
it "should be able to modify it's schema with castings that the DB engine doesn't support" do it "should be able to modify it's schema with castings that the DB engine doesn't support" do
table = create_table table = create_table
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
table.add_column!(:name => "my new column", :type => "text") table.add_column!(:name => "my new column", :type => "text")
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column, "text"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column, "text"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
table.modify_column!(:old_name => "my_new_column", :new_name => "my new column new name", :type => "integer", :force_value => "NULL") table.modify_column!(:old_name => "my_new_column", :new_name => "my new column new name", :type => "integer", :force_value => "NULL")
table.reload table.reload
table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column_new_name, "integer"]] table.schema.should == [[:cartodb_id, "integer"], [:name, "text"], [:location, "geometry"], [:description, "text"], [:my_new_column_new_name, "integer"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
end end
it "should be able to insert a new row" do it "should be able to insert a new row" do
@ -258,14 +258,14 @@ describe Table do
table = new_table table = new_table
table.force_schema = "code char(5) CONSTRAINT firstkey PRIMARY KEY, title varchar(40) NOT NULL, did integer NOT NULL, date_prod date, kind varchar(10)" table.force_schema = "code char(5) CONSTRAINT firstkey PRIMARY KEY, title varchar(40) NOT NULL, did integer NOT NULL, date_prod date, kind varchar(10)"
table.save table.save
table.schema.should == [[:cartodb_id, "integer"], [:code, "character(5)"], [:title, "character varying(40)"], [:did, "integer"], [:date_prod, "date"], [:kind, "character varying(10)"]] table.schema.should == [[:cartodb_id, "integer"], [:code, "character(5)"], [:title, "character varying(40)"], [:did, "integer"], [:date_prod, "date"], [:kind, "character varying(10)"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
end end
it "should sanitize columns from a given schema" do it "should sanitize columns from a given schema" do
table = new_table table = new_table
table.force_schema = "\"code wadus\" char(5) CONSTRAINT firstkey PRIMARY KEY, title varchar(40) NOT NULL, did integer NOT NULL, date_prod date, kind varchar(10)" table.force_schema = "\"code wadus\" char(5) CONSTRAINT firstkey PRIMARY KEY, title varchar(40) NOT NULL, did integer NOT NULL, date_prod date, kind varchar(10)"
table.save table.save
table.schema.should == [[:cartodb_id, "integer"], [:code_wadus, "character(5)"], [:title, "character varying(40)"], [:did, "integer"], [:date_prod, "date"], [:kind, "character varying(10)"]] table.schema.should == [[:cartodb_id, "integer"], [:code_wadus, "character(5)"], [:title, "character varying(40)"], [:did, "integer"], [:date_prod, "date"], [:kind, "character varying(10)"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
end end
it "should import a CSV if the schema is given and is valid" do it "should import a CSV if the schema is given and is valid" do
@ -327,7 +327,7 @@ describe Table do
table.reload table.reload
table.rows_counted.should == 7 table.rows_counted.should == 7
table.schema.should == [[:url, "character varying"], [:login, "character varying"], [:country, "character varying"], [:followers_count, "integer"], [:unknow_name_1, "character varying"],[:cartodb_id, "integer"]] table.schema.should == [[:cartodb_id, "integer"], [:url, "character varying"], [:login, "character varying"], [:country, "character varying"], [:followers_count, "integer"], [:unknow_name_1, "character varying"], [:created_at, "timestamp"], [:updated_at, "timestamp"]]
row = table.to_json[:rows][0] row = table.to_json[:rows][0]
row[:url].should == "http://twitter.com/vzlaturistica/statuses/23424668752936961" row[:url].should == "http://twitter.com/vzlaturistica/statuses/23424668752936961"
row[:login].should == "vzlaturistica " row[:login].should == "vzlaturistica "

@ -79,7 +79,7 @@ describe User do
query_result[:time].should_not be_blank query_result[:time].should_not be_blank
query_result[:time].to_s.match(/^\d+\.\d+$/).should be_true query_result[:time].to_s.match(/^\d+\.\d+$/).should be_true
query_result[:total_rows].should == 2 query_result[:total_rows].should == 2
query_result[:columns].should == [:id, :name_of_species, :kingdom, :family, :lat, :lon, :views, :cartodb_id] query_result[:columns].should == [:id, :name_of_species, :kingdom, :family, :lat, :lon, :views, :cartodb_id, :created_at, :updated_at]
query_result[:rows][0][:name_of_species].should == "Barrukia cristata" query_result[:rows][0][:name_of_species].should == "Barrukia cristata"
query_result[:rows][1][:name_of_species].should == "Eulagisca gigantea" query_result[:rows][1][:name_of_species].should == "Eulagisca gigantea"

Loading…
Cancel
Save