1fc798fbb23bb4fe2a7699e420d45082f7a7d2be
[weathermon.git] / bin / weather-filter
1 #!/usr/bin/lua
2
3 local uci = require("uci")
4 local cur = uci.cursor()
5 local json = require "json"
6
7 local logdb = arg[1]
8
9 require "wm_util"
10
11 if not logdb then
12
13   print("no sqlite log defined!")
14   return
15
16 end
17
18 function shallowcopy(orig)
19     local orig_type = type(orig)
20     local copy
21     if orig_type == 'table' then
22         copy = {}
23         for orig_key, orig_value in pairs(orig) do
24             copy[orig_key] = orig_value
25         end
26     else -- number, string, boolean, etc
27         copy = orig
28     end
29     return copy
30 end
31
32 function median(dataset)
33
34   table.sort(shallowcopy(dataset))
35   return dataset[math.floor(#dataset/2)]
36
37 end
38
39 function filter_data(dataset,width)
40
41   if not width then 
42     width = 7
43   end  
44
45   local result = {}
46   
47   local window_spread = math.floor(width/2)
48   local window = {}
49
50   for i = 1,window_spread do
51     window[#window+1] = dataset[i]["y"]
52   end
53   
54   for key,value in pairs(dataset) do
55     nextelem = dataset[key+window_spread]
56     if nextelem then
57       window[#window+1] = nextelem["y"]
58     end  
59     if not nextelem or #window>width then
60       table.remove(window,1)
61     end  
62     row = {}
63     row["t"]=value["t"]
64     row["y"]=median(window)
65     result[#result+1] = row
66   end
67   
68   return result
69
70 end
71
72 function average_results(dataset,con)
73   local name = os.tmpname()
74   touch(name)
75   local tmpcon = assert(env:connect(name))
76   assert(tmpcon:execute("create table series(time_stamp datetime,value float)"))
77   for key,value in pairs(dataset) do
78     assert(tmpcon:execute(string.format("INSERT INTO series(time_stamp,value) VALUES ('%s','%s')",value["t"],value["y"])))
79   end
80   local sql = "select rounded as t,avg(value) as y from (select substr(strftime('%Y-%m-%dT%H:%M',time_stamp),1,15)||'5:00' rounded,value from series) group by rounded order by rounded"
81   results = run_sql(sql,tmpcon)
82   tmpcon:close()
83   os.remove(name)
84   return results
85 end
86
87 function run_sql(sql,con)
88   local result = {}
89
90   cursor = assert(con:execute(sql))
91   row = cursor:fetch ({}, "a")
92   while row do
93     result[#result+1] = row
94     row = cursor:fetch ({}, "a")
95   end
96  
97   return result
98 end
99
100 function get_list(day,con)
101   if day == "-" then
102     sql = string.format("SELECT DISTINCT sensor_id,sensor,param FROM log WHERE time_stamp>=datetime('now','-1 day','localtime') ORDER BY sensor_id,sensor,param")
103   else
104     sql = string.format("SELECT DISTINCT sensor_id,sensor,param FROM log WHERE time_stamp>='%s' and time_stamp<date('%s','+1 day') ORDER BY sensor_id,sensor,param",day,day)
105   end  
106   return run_sql(sql,con)
107 end
108
109 function get_raw(day,con,sensor_id,sensor_type,param)
110   format = '%Y-%m-%dT%H:%M:%S'
111   if day == "-" then
112     sql = string.format("SELECT strftime('%s',time_stamp) as t,value as y FROM log WHERE time_stamp>=datetime('now','-1 day','localtime') and sensor_id='%s' and sensor='%s' and param='%s' ORDER BY time_stamp",format,sensor_id,sensor_type,param)
113   else
114     sql = string.format("SELECT strftime('%s',time_stamp) as t,value as y FROM log WHERE time_stamp>='%s' and time_stamp<date('%s','+1 day') and sensor_id='%s' and sensor='%s' and param='%s' ORDER BY time_stamp",format,day,day,sensor_id,sensor_type,param)
115   end
116   return run_sql(sql,con)
117 end
118
119 function get_filtered(day,con,sensor_id,sensor_type,param,width)
120   format = '%Y-%m-%dT%H:%M:%S'
121   if day == "-" then
122     sql = string.format("SELECT strftime('%s',time_stamp) as t,value as y FROM log WHERE time_stamp>=datetime('now','-1 day','localtime') and sensor_id='%s' and sensor='%s' and param='%s' ORDER BY time_stamp",format,sensor_id,sensor_type,param)
123   else
124     sql = string.format("SELECT strftime('%s',time_stamp) as t,value as y FROM log WHERE time_stamp>='%s' and time_stamp<date('%s','+1 day') and sensor_id='%s' and sensor='%s' and param='%s' ORDER BY time_stamp",format,day,day,sensor_id,sensor_type,param)
125   end
126   return filter_data(run_sql(sql,con),width)
127 end
128
129 function dump_json(dataset,file)
130   local f
131   if file then
132     f = io.open(file,"w")
133     io.output(f)
134   end
135   io.write(json.encode(dataset))
136   if f then
137     io.close(f)
138   end   
139 end
140
141 function dump_txt(dataset,file)
142   local f
143   if file then
144     f = io.open(file,"w")
145     io.output(f)
146   end
147   for key,row in pairs(dataset) do
148     io.write(row["t"].." "..row["y"].."\n")
149   end
150   if f then
151     io.close(f)
152   end   
153 end
154
155 function dump_list(dataset,file)
156   local f
157   if file then
158     f = io.open(file,"w")
159     io.output(f)
160   end
161   for key,row in pairs(dataset) do
162     io.write(row["sensor_id"].." "..row["sensor"].." "..row["param"].."\n")
163   end
164   if f then
165     io.close(f)
166   end   
167 end
168
169 local command = arg[2]
170 local day = arg[3]
171
172 local dbdriver = require "luasql.sqlite3"
173 env = assert(dbdriver.sqlite3())
174 con = assert(env:connect(logdb))
175
176 if command == "list" then
177
178   dump_list(get_list(day,con))
179
180 elseif command == "get" then
181
182   sensor_id = arg[4]
183   sensor_type = arg[5]
184   param = arg[6]
185
186   dump_txt(get_raw(day,con,sensor_id,sensor_type,param))
187   
188 elseif command == "get-filtered" then
189
190   sensor_id = arg[4]
191   sensor_type = arg[5]
192   param = arg[6]
193
194   width = arg[7]
195   if not width then
196     width = 5
197   end  
198
199   dataset =get_filtered(day,con,sensor_id,sensor_type,param)
200   dump_txt(dataset)
201
202 elseif command == "get-compacted" then
203
204   sensor_id = arg[4]
205   sensor_type = arg[5]
206   param = arg[6]
207
208   width = arg[7]
209   if not width then
210     width = 5
211   end  
212
213   dataset =get_filtered(day,con,sensor_id,sensor_type,param)
214   dump_txt(average_results(dataset))
215
216 elseif command == "dump" then
217
218   local directory = arg[4]
219   if not directory then 
220     directory = "."
221   end  
222   
223   list = get_list(day,con)
224   dump_json(list,directory.."/sensors.json")
225   for key,value in pairs(list) do
226     dump_json(get_filtered(day,con,value["sensor_id"],value["sensor"],value["param"]),directory.."/"..value["sensor_id"].."."..value["sensor"].."."..value["param"]..".json")
227   end
228
229 elseif command == "dump-compacted" then
230
231   local directory = arg[4]
232   if not directory then 
233     directory = "."
234   end  
235   
236   list = get_list(day,con)
237   dump_json(list,directory.."/sensors.json")
238   for key,value in pairs(list) do
239     dump_json(average_results(get_filtered(day,con,value["sensor_id"],value["sensor"],value["param"])),directory.."/"..value["sensor_id"].."."..value["sensor"].."."..value["param"]..".json")
240   end
241   
242 end