Commit e3e4e31d by Ivan Lan

Merge branch 'lan/auto_stations_and_aqi_forecast' into 'master'

Lan/auto stations and aqi forecast See merge request !3
parents 10471a07 f97227b0
class CreateForecasts < ActiveRecord::Migration[5.0]
class CreateCityForecasts < ActiveRecord::Migration[5.0]
def up
create_table :forecasts do |t|
create_table :city_forecasts do |t|
t.datetime :datetime
t.integer :temp_low
t.integer :temp_high
......@@ -14,11 +14,11 @@ class CreateForecasts < ActiveRecord::Migration[5.0]
end
# 两个index 一起查询会不会导致其中一个作用几乎消失
# 需不需要按城市来分表
add_index :forecasts, :cityname
add_index :forecasts, :datetime
add_index :city_forecasts, :cityname
add_index :city_forecasts, :datetime
end
def down
drop_table :forecasts
drop_table :city_forecasts
end
end
class CreateTenMinAutoStations < ActiveRecord::Migration[5.0]
def up
create_table :auto_station_ten_mins do |t|
t.datetime :datetime
t.string :sitenumber
t.string :name
t.string :tempe
t.string :rain
t.string :wind_direction
t.string :wind_speed
t.string :visibility
t.string :humi
t.string :max_tempe
t.string :min_tempe
t.string :max_speed
t.string :max_direction
t.string :pressure
t.timestamps
end
add_index :auto_station_ten_mins, :datetime
add_index :auto_station_ten_mins, :sitenumber
end
def down
drop_table :auto_station_ten_mins
end
end
class CreateAqiForecasts < ActiveRecord::Migration[5.0]
def up
create_table :aqi_forecasts do |t|
t.datetime :datetime
t.string :prompt
t.text :list
t.timestamps
end
add_index :aqi_forecasts, :datetime
end
def down
drop_table :aqi_forecasts
end
end
module JsonColumns
def json_columns *columns
columns.each do |column|
define_method(column) {
value = self.read_attribute(column)
value && JSON.parse(value.gsub('=>', ':'))
}
define_method("#{column}=") { |value|
self.[]=( column, JSON.dump(value) )
}
end
end
end
\ No newline at end of file
......@@ -2,42 +2,13 @@ module WeatherModel
module SourceSchema
def self.sorts
['Forecast']
[:Forecast, :AutoStation, :Aqi]
end
ShanghaiTen = {
sort: 'Forecast',
table_name: :forecasts,
schema: {
datetime: :datetime,
cityname: :string,
temp_high: :string,
temp_low: :string,
weather_text: :string,
weather_pic: :string,
win_dir: :string,
win_speed: :string,
},
options: {
index: [:cityname],
expire: 1,
unique: :mysql_id,
father: nil,
son: nil,
},
factory_name: :forecast_shanghai_ten,
factory: {
datetime: '2017-05-16',
cityname: 'shanghai',
temp_high: '100',
temp_low: '0',
weather_text: '酷热严寒',
weather_pic: 'http://xxx.com',
win_dir: '东南西北风',
win_speed: '1级',
}
Dir['./lib/weather-model/source_schemas/*.rb'].each { |file|
require file
}
end
end
module WeatherModel
module SourceSchema
Forecast =
{
sort: 'Aqi',
table_name: :aqi_forecasts,
schema: {
datetime: :datetime,
prompt: :string,
list: :text
},
options: {
index: %i[datetime],
expire: 2 * 24 * 60 * 60,
unique: nil,
belongs_to: nil,
has_many: nil,
json_columns: :list
},
factory_name: :aqi_forecast,
factory: {
datetime: Time.parse('2011-11-11 11:11'),
prompt: '',
list: [
{
"period"=>"今天夜间(20时—06时)",
"aqi"=>"35-55",
"level"=>"优到良",
"pripoll"=>"PM10"
}, {
"period"=>"明天上午(06时—12时)",
"aqi"=>"35-55",
"level"=>"优到良",
"pripoll"=>"PM10"
}, {
"period"=>"明天下午(12时—20时)",
"aqi"=>"45-65",
"level"=>"优到良",
"pripoll"=>"O3"
}, {
"period"=>"明天夜间(20时—06时)",
"aqi"=>"35-55",
"level"=>"优到良",
"pripoll"=>"PM10"
}, {
"period"=>"后天白天(06时—20时)",
"aqi"=>"55-75",
"level"=>"良",
"pripoll"=>"O3"
}
]
}
}
end
end
\ No newline at end of file
module WeatherModel
module SourceSchema
TenMins =
{
sort: 'AutoStation',
table_name: :auto_stations_ten_mins,
schema: {
datetime: :datetime,
sitenumber: :string,
name: :string,
tempe: :string,
rain: :string,
wind_direction: :string,
wind_speed: :string,
visibility: :string,
humi: :string,
max_tempe: :string,
min_tempe: :string,
max_speed: :string,
max_direction: :string,
pressure: :string,
},
options: {
index: %i[datetime sitenumber],
expire: 4 * 60 * 60,
unique: nil,
father: nil,
son: nil,
},
factory_name: :auto_station_ten_min,
factory: {
datetime: Time.parse('2011-11-11 11:11'),
sitenumber: '99632',
name: '蓬莱公园',
tempe: '25.6',
rain: '0',
wind_direction: '226',
wind_speed: '0.3',
visibility: '////',
humi: '////',
max_tempe: '25.6',
min_tempe: '24.3',
max_speed: '3.1',
max_direction: '308',
pressure: '////',
}
}
end
end
module WeatherModel
module SourceSchema
CityForecast =
{
sort: 'Forecast',
table_name: :city_forecasts,
schema: {
datetime: :datetime,
cityname: :string,
temp_high: :string,
temp_low: :string,
weather_text: :string,
weather_pic: :string,
win_dir: :string,
win_speed: :string,
},
options: {
index: [:cityname, :date],
expire: 11 * 24 * 60 * 60,
unique: nil,
belongs_to: nil,
has_many: nil,
},
factory_name: :city_forecast,
factory: {
datetime: Time.parse('2017-05-16 16:00'),
cityname: '上海',
temp_high: '100',
temp_low: '0',
weather_text: '酷热严寒',
weather_pic: 'http://xxx.com',
win_dir: '东南西北风',
win_speed: '1级',
}
}
end
end
\ No newline at end of file
......@@ -3,15 +3,27 @@ module WeatherModel
require 'active_record'
require 'ohm'
require "ohm/expire"
require 'weather-model/json_columns'
class Mysql < ::ActiveRecord::Base
extend JsonColumns
after_save :update_ohm
def self.use_settings settings
self.table_name = settings[:table_name]
set_options settings
define_ohm settings
define_factory settings
end
def self.set_options settings
[:has_many, :belongs_to, :json_column].each do |option|
attrs = settings[:options][option]
attrs.is_a?(Array) ?
attrs.each{ |attr| method(option).call(attr) if attr } :
method(option).call(attrs) if attrs
end
end
def self.define_ohm settings
const_set('Ohm', Class.new(SourceOhm)).use_settings(settings)
......@@ -30,43 +42,51 @@ module WeatherModel
self.class.const_get('Ohm')
end
class SourceOhm < ::Ohm::Model
include ::Ohm::Expire
def self.use_settings settings
attribute :mysql_id
attribute :updated_at
index :mysql_id
settings[:schema].keys.each { |attr|
attribute attr
}
[:index, :unique, :expire, :son, :dad].each do |option|
attrs = settings[:options][option]
attrs.is_a?(Array) ?
attrs.each{ |attr| method(option).call(attr) if attr } :
method(option).call(attrs) if attrs
end
end
# ohm-expire 只处理了create
# 而且 只把 attributes 的值给 TTL了。。。
def update attributes
obj = super(attributes)
obj.update_ttl self.class.instance_variable_get(:@expire)
end
class SourceOhm < ::Ohm::Model
extend JsonColumns
include ::Ohm::Expire
def self.use_settings settings
attribute :mysql_id
attribute :updated_at
index :mysql_id
unique :mysql_id
settings[:schema].keys.each { |attr|
attribute attr
}
end
def self.all
super.select{ |x| x.attributes.empty?}.map(&:delete)
super
def self.set_options settings
[:index, :unique, :expire, :has_many, :belongs_to, :json_column].each do |option|
attrs = settings[:options][option]
attrs.is_a?(Array) ?
attrs.each{ |attr| method(option).call(attr) if attr } :
method(option).call(attrs) if attrs
end
end
# ohm-expire 只处理了create
# 而且 只把 attributes 的值给 TTL了。。。
def update attributes
obj = super(attributes)
obj.update_ttl self.class.instance_variable_get(:@expire)
end
def self.all
super.select{ |x| x.attributes.empty?}.map(&:delete)
super
end
private
def self.son his_son # input like :Post
def self.has_many his_son # input like :Post
# collection :posts, :Post
collection(his_son.to_s.underscore.pluralize.to_sym, his_son) if his_son && SourceSchema.const_defined?(his_son)
end
def self.dad his_dad # input like :Post
def self.belongs_to his_dad # input like :Post
# reference :post, :Post
reference(his_dad.to_s.underscore.to_sym, his_dad ) if his_dad && SourceSchema.const_defined?(his_dad)
end
......@@ -79,7 +99,15 @@ module WeatherModel
# find 找不到 会自动生成
ohm_obj = get_ohm.find(mysql_id: id).first.try(:update, ohm_attrs) || get_ohm.create(ohm_attrs)
# rescue Ohm::UniqueIndexViolation mysql_id should be unique
end
end
# Forecast -> :forecasts
def has_many name
super name.to_s.underscore.pluralize.to_sym
end
# Forecast -> :forecast
def belongs_to name
super name.to_s.underscore.to_sym
end
end
......
......@@ -2,19 +2,23 @@ require "spec_helper"
require 'active_record'
require "ohm"
include WeatherModel
RSpec.describe WeatherModel do
include WeatherModel
before do
# start an ohm
WeatherModel.set_ohm Redic.new("redis://127.0.0.1:6379/5")
# run migration
Dir[File.dirname(__FILE__) + '/../db/migrate/*.rb'].each { |file| require file }
# drop table
FileUtils.rm 'weather-model-test.sqlite3'
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'weather-model-test.sqlite3'
)
ActiveRecord::Migration[5.0].subclasses.each do |migrate|
migrate.migrate(:up)
migrate.migrate(:up)
end
end
......@@ -24,19 +28,19 @@ RSpec.describe WeatherModel do
describe 'test factory_girl' do
before do
@obj = create(:forecast_shanghai_ten)
@obj = create(:city_forecast)
end
it 'Test factory_girl' do
expect(@obj.class).to eq(Forecast::ShanghaiTen), 'factory_girl 生成成功'
expect(@obj.class).to eq(Forecast::CityForecast), 'factory_girl 生成成功'
end
end
it 'Forecast' do
@expire = 1
Forecast::ShanghaiTen::Ohm.expire @expire
mysql_obj = Forecast::ShanghaiTen.create({
Forecast::CityForecast::Ohm.expire @expire
mysql_obj = Forecast::CityForecast.create({
datetime: '2017-05-16',
cityname: 'shanghai',
temp_high: '100',
......@@ -46,15 +50,36 @@ RSpec.describe WeatherModel do
win_dir: '东南西北风',
win_speed: '1级',
})
expect(Forecast::ShanghaiTen.count).to eq(1)
expect(Forecast::ShanghaiTen::Ohm.all.count).to eq(1)
expect(Forecast::ShanghaiTen::Ohm.all.first.temp_low).to eq(mysql_obj.temp_low.to_s)
expect(Forecast::ShanghaiTen::Ohm.all.first.get_ttl).to eq(@expire)
expect(Forecast::CityForecast.count).to eq(1)
expect(Forecast::CityForecast::Ohm.all.count).to eq(1)
expect(Forecast::CityForecast::Ohm.all.first.temp_low).to eq(mysql_obj.temp_low.to_s)
expect(Forecast::CityForecast::Ohm.all.first.get_ttl).to eq(@expire)
# 'ohm 对象 同步 mysql对象更新'
mysql_obj.update(temp_low: 99)
expect(Forecast::ShanghaiTen::Ohm.all.first.temp_low).to eq('99'), 'ohm 对象 同步 mysql对象更新'
expect(Forecast::CityForecast::Ohm.all.first.temp_low).to eq('99'), 'ohm 对象 同步 mysql对象更新'
sleep 1.5
expect(Forecast::ShanghaiTen::Ohm.all.count).to eq(0), 'TTL过期对象删除成功'
expect(Forecast::CityForecast::Ohm.all.count).to eq(0), 'TTL过期对象删除成功'
end
it 'aqi_forecast' do
@aqi_forecast = Aqi::Forecast.create(
datetime: Time.now,
prompt: '',
list: [
{
"period"=>"今天夜间(20时—06时)",
"aqi"=>"35-55",
"level"=>"优到良",
"pripoll"=>"PM10"
}, {
"period"=>"明天上午(06时—12时)",
"aqi"=>"35-55",
"level"=>"优到良",
"pripoll"=>"PM10"
},
]
)
expect(Aqi::Forecast::Ohm.all.first).not_to be_nil, 'json_column 正常'
end
after do
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment