summaryrefslogtreecommitdiff
path: root/ebus/webapp/__init__.py
blob: 9c98ac3b5970decdd83d6f35aa3be53d5ea433ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# -*- coding: utf-8 -*-
import os
import json
import datetime
import time

import bottle 
from sqlalchemy import text

app = bottle.Bottle("ebus")

def maketime(dt):
    """Rechnet CET DateTime in CET timestamp"""
    return time.mktime(dt.timetuple())*1000 # - time.altzone)*1000

def parsetime(timestamp):
    """Machtaus CET timestamp Local DateTime"""
    return datetime.datetime.fromtimestamp((timestamp/1000)) # + time.altzone)

def now(conn):
    return conn.execute(text("SELECT CURRENT_TIMESTAMP t")).first().t

@app.route('/')
def index_file():
    return static_files("index.html")

@app.route('/static/:filename#.+#')
def static_files(filename):
    return bottle.static_file(filename, root=os.path.join(os.path.dirname(__file__),"static"))

@app.route('/sensor/:name')
def sensor_data(soup,name):
    try:
        sensor_id = soup.sensor.filter(soup.sensor.name == name).one().id

        conn = soup.connection()
        sql = text("""SELECT timestamp, COALESCE(value_int, value_float) as "value_real"
                        FROM value
                       WHERE sensor_id = :sensor_id
                    ORDER BY timestamp DESC
                       LIMIT 1""")
        value = conn.execute(sql, sensor_id=sensor_id).first()

        return {'sensor':name,'data':[maketime(value.timestamp), float(value.value_real)], 'error':None}
    except Exception,e:
        return {'sensor':name,'data':None, 'error':str(e)}

@app.route('/sensor/:name/:startdate/:enddate')
def sensor_data_fromto(soup,name,startdate,enddate):
    try:
        interval = float(enddate) - float(startdate)
        modulo = interval / 500 #500 values

        startdate = datetime.datetime.fromtimestamp(float(startdate))
        enddate = datetime.datetime.fromtimestamp(float(enddate))

        if interval <= 0: raise Exception("Invalid interval")
        if interval >= 14 * 24 * 60 * 60: raise Exception("interval too big")

        sensor_id = soup.sensor.filter(soup.sensor.name == name).one().id

        conn = soup.connection()
        sql = text("""
        SELECT to_timestamp( extract(epoch from timestamp)::int - extract(epoch from timestamp)::int % :modulo ) "round_timestamp", 
               AVG(COALESCE(value_int,value_float)) "value_real" 
         FROM value
        WHERE timestamp > :startdate
          AND timestamp < :enddate
          AND sensor_id = :sensor_id
        GROUP BY "round_timestamp"
        ORDER BY "round_timestamp"
        """)

        values = conn.execute(sql,
                              sensor_id=sensor_id,
                              startdate=startdate, 
                              enddate=enddate, 
                              modulo=modulo).fetchall()

        values = map(lambda row: (maketime(row.round_timestamp), row.value_real),
                     values)

        return {'sensor':name,'data':values, 'error':None}
    except Exception,e:
        return {'sensor':name, 'data':None, 'error':str(e)}

@app.route('/sensor_cached/:name/:timestamp_from')
def sensor_data_cached_fromto(soup, name, timestamp_from):
    try:
        timestamp_from = datetime.datetime.fromtimestamp(float(timestamp_from))
        sensor_id = soup.sensor.filter(soup.sensor.name == name).one().id

        # select data from cache-view
        conn = soup.connection()
        sql = text("""SELECT timestamp, value_real AS "value_real"
                        FROM vi_value_cache 
                       WHERE timestamp >= :timestamp_from
                         AND sensor_id = :sensor_id
                    ORDER BY timestamp""")
        values = conn.execute(sql, timestamp_from=timestamp_from, sensor_id=sensor_id).fetchall()
        values = map(lambda row: (maketime(row.timestamp), row.value_real.__float__()),
                     values)

        return {'sensor':name,'data':values, 'error':None}
    except Exception,e:
        return {'sensor':name, 'data':None, 'error':str(e)}

import select
import psycopg2

@app.route('/stream')
@app.route('/stream/:startdate')
def stream(soup, startdate=None):
    connection = soup.connection()
    conn = connection.connection

    time_start = startdate != None and parsetime(float(startdate)) \
                 or now(connection)

    
    conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)

    cursor = conn.cursor()
    cursor.execute("LISTEN evt_ebus_value_insert;")

    values = []
    fails = 0
    while fails < 5:
        if select.select([conn],[],[],10) == ([],[],[]):
            time_stop = now(connection)
            fails += 1
        else:
            conn.poll()

            notify = conn.notifies.pop()
            if not notify:
                continue

            time_stop = now(connection)
            print "time_stop %s"%time_stop
            sql = text("""SELECT sensor.name,
                                 value.timestamp,
                                 COALESCE(value.value_int,value.value_float) "value_real",
                                 value_string
                            FROM value, sensor
                           WHERE value.sensor_id = sensor.id
                             AND timestamp >= :time_start
                             AND timestamp < :time_stop""")
            values = map(lambda row: {
                "name":row.name,
                "timestamp":maketime(row.timestamp),
                "value_real":row.value_real != None and row.value_real.__float__() or None,
                "value_string":row.value_string
                },
                connection.execute(sql, time_start=time_start, time_stop=time_stop))

            break

    cursor.close()
    return {'time_start' : maketime(time_start), 'time_stop':maketime(time_stop),
            'data':values}

@app.route("/all_values")
def all_values(soup):
    conn = soup.connection()

    sql = text("""
    SELECT sensor.name,
           value.timestamp,
           COALESCE(value.value_int,value.value_float) "value_real",
           value_string
      FROM value, sensor, (SELECT MAX(timestamp) as timestamp,
                                  sensor_id
                             FROM value
                            WHERE timestamp > CURRENT_TIMESTAMP - '15 minutes'::interval
                         GROUP BY sensor_id) last_value
     WHERE value.timestamp = last_value.timestamp
       AND value.sensor_id = last_value.sensor_id
       AND value.sensor_id = sensor.id""")

    time_stop = now(conn)
    values = map(lambda row: {
            "name":row.name,
            "timestamp":maketime(row.timestamp),
            "value_real":row.value_real != None and row.value_real.__float__() or None,
            "value_string":row.value_string},
                conn.execute(sql))

    return {'data':values, 'time_stop':maketime(time_stop)}
                               
# vim: autoindent tabstop=4 shiftwidth=4 expandtab softtabstop=4 filetype=python